Fixed stream seek problem. Fixed CompoundFile::Save()

This commit is contained in:
Ivan Morozov
2022-08-25 16:51:41 +03:00
parent 85b3ccafc4
commit 1d0e88553e
42 changed files with 5458 additions and 0 deletions

View File

@ -0,0 +1,41 @@
#pragma once
//#include "../../DocxFormat/Source/Base/Nullable.h"
#include <memory>
namespace RedBlackTree
{
class IRBNode;
using PIRBNode = std::shared_ptr<IRBNode>;
using WPIRBNode = std::weak_ptr<IRBNode>;
enum Color
{
RED = 0,
BLACK = 1
};
class IRBNode
{
public:
virtual void setLeft(PIRBNode pNode) = 0;
virtual void setRight(PIRBNode pNode) = 0;
virtual PIRBNode getLeft() const = 0;
virtual PIRBNode getRight() const = 0;
virtual void setColor(Color clr) = 0;
virtual Color getColor()const = 0;
virtual void setParent(PIRBNode pParent) = 0;
virtual PIRBNode getParent() const = 0;
virtual PIRBNode Grandparent() const = 0;
virtual PIRBNode Sibling() const = 0;
virtual PIRBNode Uncle() const = 0;
virtual void AssignValueTo(PIRBNode other) = 0;
virtual int CompareTo(const PIRBNode& other) const = 0;
virtual std::wstring ToString() const = 0;
};
}

View File

@ -0,0 +1,434 @@
#include "rbtree.h"
#include "rbtreeexception.h"
using namespace RedBlackTree;
RBTree::RBTree(PIRBNode root) : root(root)
{}
const PIRBNode RBTree::getRoot() const
{
return root;
}
void RBTree::setRoot(const PIRBNode &newRoot)
{
root = newRoot;
}
bool RBTree::TryLookup(PIRBNode templ, PIRBNode &val)
{
PIRBNode n = LookupNode(templ);
if (n == nullptr)
{
val.reset();
return false;
}
else
{
val = n;
return true;
}
}
void RBTree::Insert(PIRBNode newNode)
{
newNode->setColor(Color::RED);
PIRBNode insertedNode = newNode;
if (getRoot() == nullptr)
{
setRoot(insertedNode);
}
else
{
PIRBNode n = getRoot();
while (true)
{
int compResult = newNode->CompareTo(n);
if (compResult == 0)
{
throw RBTreeDuplicatedItemException(L"RBNode " + newNode->ToString() + L" already present in tree");
//n->Value = value;
//return;
}
else if (compResult < 0)
{
if (n->getLeft() == nullptr)
{
n->setLeft(insertedNode);
break;
}
else
{
n = n->getLeft();
}
}
else
{
//assert compResult > 0;
if (n->getRight() == nullptr)
{
n->setRight(insertedNode);
break;
}
else
{
n = n->getRight();
}
}
}
insertedNode->setParent(n);
}
InsertCase1(insertedNode);
// if (NodeInserted != nullptr)
// {
// NodeInserted(insertedNode);
// }
//Trace.WriteLine(" ");
//Print();
}
void RBTree::Delete(PIRBNode templ, PIRBNode &deletedAlt)
{
deletedAlt.reset();
PIRBNode n = LookupNode(templ);
templ = n;
if (n == nullptr)
return; // Key not found, do nothing
if (n->getLeft() != nullptr && n->getRight() != nullptr)
{
// Copy key/value from predecessor and then delete it instead
PIRBNode pred = MaximumNode(n->getLeft());
pred->AssignValueTo(n);
n = pred;
deletedAlt = pred;
}
//assert n->left == null || n->right == null;
PIRBNode child = (n->getRight() == nullptr) ? n->getLeft() : n->getRight();
if (NodeColor(n) == Color::BLACK)
{
n->setColor(NodeColor(child));
DeleteCase1(n);
}
ReplaceNode(n, child);
if (NodeColor(getRoot()) == Color::RED)
{
getRoot()->setColor(Color::BLACK);
}
return;
}
void RBTree::VisitTree(Action<PIRBNode> action)
{
PIRBNode walker = getRoot();
if (walker != nullptr)
DoVisitTree(action, walker);
}
Color RBTree::NodeColor(PIRBNode n)
{
return n == nullptr ? Color::BLACK : n->getColor();
}
PIRBNode RBTree::MaximumNode(PIRBNode n)
{
while (n->getRight() != nullptr)
{
n = n->getRight();
}
return n;
}
PIRBNode RBTree::LookupNode(PIRBNode templ)
{
PIRBNode n = getRoot();
while (n != nullptr)
{
int compResult = templ->CompareTo(n);
if (compResult == 0)
{
return n;
}
else if (compResult < 0)
{
n = n->getLeft();
}
else
{
//assert compResult > 0;
n = n->getRight();
}
}
return n;
}
void RBTree::ReplaceNode(PIRBNode oldn, PIRBNode newn)
{
if (oldn->getParent() == nullptr)
{
setRoot(newn);
}
else
{
if (oldn == oldn->getParent()->getLeft())
oldn->getParent()->setLeft(newn);
else
oldn->getParent()->setRight(newn);
}
if (newn != nullptr)
{
newn->setParent(oldn->getParent());
}
}
void RBTree::RotateLeft(PIRBNode n)
{
PIRBNode r = n->getRight();
ReplaceNode(n, r);
n->setRight(r->getLeft());
if (r->getLeft() != nullptr)
{
r->getLeft()->setParent(n);
}
r->setLeft(n);
n->setParent(r);
}
void RBTree::RotateRight(PIRBNode n)
{
PIRBNode l = n->getLeft();
ReplaceNode(n, l);
n->setLeft(l->getRight());
if (l->getRight() != nullptr)
{
l->getRight()->setParent(n);
}
l->setRight(n);
n->setParent(l);
}
void RBTree::InsertCase1(PIRBNode n)
{
if (n->getParent() == nullptr)
n->setColor(Color::BLACK);
else
InsertCase2(n);
}
void RBTree::InsertCase2(PIRBNode n)
{
if (NodeColor(n->getParent()) == Color::BLACK)
return; // Tree is still valid
else
InsertCase3(n);
}
void RBTree::InsertCase3(PIRBNode n)
{
if (NodeColor(n->Uncle()) == Color::RED)
{
n->getParent()->setColor(Color::BLACK);
n->Uncle()->setColor(Color::BLACK);
n->Grandparent()->setColor(Color::RED);
InsertCase1(n->Grandparent());
}
else
{
InsertCase4(n);
}
}
void RBTree::InsertCase4(PIRBNode n)
{
if (n == n->getParent()->getRight() && n->getParent() == n->Grandparent()->getLeft())
{
RotateLeft(n->getParent());
n = n->getLeft();
}
else if (n == n->getParent()->getLeft() && n->getParent() == n->Grandparent()->getRight())
{
RotateRight(n->getParent());
n = n->getRight();
}
InsertCase5(n);
}
void RBTree::InsertCase5(PIRBNode n)
{
n->getParent()->setColor(Color::BLACK);
n->Grandparent()->setColor(Color::RED);
if (n == n->getParent()->getLeft() && n->getParent() == n->Grandparent()->getLeft())
{
RotateRight(n->Grandparent());
}
else
{
//assert n == n->getParent().right && n->getParent() == n->grandparent().right;
RotateLeft(n->Grandparent());
}
}
void RBTree::DeleteCase1(PIRBNode n)
{
if (n->getParent() == nullptr)
return;
else
DeleteCase2(n);
}
void RBTree::DeleteCase2(PIRBNode n)
{
if (NodeColor(n->Sibling()) == Color::RED)
{
n->getParent()->setColor(Color::RED);
n->Sibling()->setColor(Color::BLACK);
if (n == n->getParent()->getLeft())
RotateLeft(n->getParent());
else
RotateRight(n->getParent());
}
DeleteCase3(n);
}
void RBTree::DeleteCase3(PIRBNode n)
{
if (NodeColor(n->getParent()) == Color::BLACK &&
NodeColor(n->Sibling()) == Color::BLACK &&
NodeColor(n->Sibling()->getLeft()) == Color::BLACK &&
NodeColor(n->Sibling()->getRight()) == Color::BLACK)
{
n->Sibling()->setColor(Color::RED);
DeleteCase1(n->getParent());
}
else
DeleteCase4(n);
}
void RBTree::DeleteCase4(PIRBNode n)
{
if (NodeColor(n->getParent()) == Color::RED &&
NodeColor(n->Sibling()) == Color::BLACK &&
NodeColor(n->Sibling()->getLeft()) == Color::BLACK &&
NodeColor(n->Sibling()->getRight()) == Color::BLACK)
{
n->Sibling()->setColor(Color::RED);
n->getParent()->setColor(Color::BLACK);
}
else
DeleteCase5(n);
}
void RBTree::DeleteCase5(PIRBNode n)
{
if (n == n->getParent()->getLeft() &&
NodeColor(n->Sibling()) == Color::BLACK &&
NodeColor(n->Sibling()->getLeft()) == Color::RED &&
NodeColor(n->Sibling()->getRight()) == Color::BLACK)
{
n->Sibling()->setColor(Color::RED);
n->Sibling()->getLeft()->setColor(Color::BLACK);
RotateRight(n->Sibling());
}
else if (n == n->getParent()->getRight() &&
NodeColor(n->Sibling()) == Color::BLACK &&
NodeColor(n->Sibling()->getRight()) == Color::RED &&
NodeColor(n->Sibling()->getLeft()) == Color::BLACK)
{
n->Sibling()->setColor(Color::RED);
n->Sibling()->getRight()->setColor(Color::BLACK);
RotateLeft(n->Sibling());
}
DeleteCase6(n);
}
void RBTree::DeleteCase6(PIRBNode n)
{
n->Sibling()->setColor(NodeColor(n->getParent()));
n->getParent()->setColor(Color::BLACK);
if (n == n->getParent()->getLeft())
{
//assert nodeColor(n->sibling().right) == Color::RED;
n->Sibling()->getRight()->setColor(Color::BLACK);
RotateLeft(n->getParent());
}
else
{
//assert nodeColor(n->sibling().left) == Color::RED;
n->Sibling()->getLeft()->setColor(Color::BLACK);
RotateRight(n->getParent());
}
}
void RBTree::DoVisitTree(Action<PIRBNode> action, PIRBNode walker)
{
if (walker->getLeft() != nullptr)
{
DoVisitTree(action, walker->getLeft());
}
if (action != nullptr)
action(walker);
if (walker->getRight() != nullptr)
{
DoVisitTree(action, walker->getRight());
}
}
void RBTree::VisitTreeNodes(Action<PIRBNode> action)
{
//IN Order visit
PIRBNode walker = getRoot();
if (walker != nullptr)
DoVisitTreeNodes(action, walker);
}
void RBTree::DoVisitTreeNodes(Action<PIRBNode> action, PIRBNode walker)
{
if (walker->getLeft() != nullptr)
{
DoVisitTreeNodes(action, walker->getLeft());
}
if (action != nullptr)
action(walker);
if (walker->getRight() != nullptr)
{
DoVisitTreeNodes(action, walker->getRight());
}
}
RBTree::iterator::iterator(RBTree &tree, bool end)
{
Action<PIRBNode> inserter = [&] (PIRBNode pNode)
{
heap.push_back(pNode);
};
tree.VisitTreeNodes(inserter);
current = end ? heap.end() : heap.begin();
}

View File

@ -0,0 +1,77 @@
#pragma once
#include "irbnode.h"
#include <iterator>
#include <functional>
#include <list>
namespace RedBlackTree
{
template <class T>
using Action = std::function<void(T)>;
class RBTree
{
public:
RBTree(){}
RBTree(PIRBNode root);
const PIRBNode getRoot() const;
void setRoot(const PIRBNode &newRoot);
bool TryLookup(PIRBNode templ, PIRBNode& val);
void Insert(PIRBNode newNode);
void Delete(PIRBNode templ, PIRBNode& deletedAlt);
void VisitTree(Action<PIRBNode> action);
void VisitTreeNodes(Action<PIRBNode> action);
private:
static Color NodeColor(PIRBNode n);
static PIRBNode MaximumNode(PIRBNode n);
PIRBNode LookupNode(PIRBNode templ);
void ReplaceNode(PIRBNode oldn, PIRBNode newn);
void RotateLeft(PIRBNode n);
void RotateRight(PIRBNode n);
void InsertCase1(PIRBNode n);
void InsertCase2(PIRBNode n);
void InsertCase3(PIRBNode n);
void InsertCase4(PIRBNode n);
void InsertCase5(PIRBNode n);
void DeleteCase1(PIRBNode n);
void DeleteCase2(PIRBNode n);
void DeleteCase3(PIRBNode n);
void DeleteCase4(PIRBNode n);
void DeleteCase5(PIRBNode n);
void DeleteCase6(PIRBNode n);
void DoVisitTree(Action<PIRBNode> action, PIRBNode walker);
void DoVisitTreeNodes(Action<PIRBNode> action, PIRBNode walker);
public:
// todo this from C# and it's weak realization
class iterator : public std::iterator<std::bidirectional_iterator_tag, std::ptrdiff_t, IRBNode, IRBNode*, PIRBNode>
{
std::list<WPIRBNode> heap;
std::list<WPIRBNode>::iterator current;
public:
iterator(RBTree &tree, bool end = false);
inline iterator& operator++() {++current; return *this;}
inline iterator& operator--() {--current; return *this;}
inline bool operator==(const iterator &other) const {return current == other.current;}
inline bool operator!=(const iterator &other) const {return current != other.current;}
inline PIRBNode operator*() {return current->lock();}
inline iterator end() {current = heap.end(); return *this;}
};
iterator&& begin() {return std::move(iterator(*this));}
iterator&& end() {return std::move(iterator(*this).end());}
private:
PIRBNode root;
};
}

View File

@ -0,0 +1,32 @@
#pragma once
#include <exception>
#include <string>
namespace RedBlackTree
{
class RBTreeException : virtual public std::exception
{
public:
RBTreeException() {}
RBTreeException(std::wstring message) : errorMessage(message) {}
RBTreeException(std::wstring message, std::exception& ex) : std::exception(ex), errorMessage(message) {}
virtual ~RBTreeException() throw () {}
virtual const wchar_t* what_w() const throw () {
return errorMessage.c_str();
}
protected:
std::wstring errorMessage;
};
class RBTreeDuplicatedItemException : public RBTreeException
{
public:
RBTreeDuplicatedItemException(std::wstring msg)
: RBTreeException(msg)
{
}
};
}

52
Common/cfcpp/cfcpp.pro Normal file
View File

@ -0,0 +1,52 @@
CONFIG -= qt
TEMPLATE = lib
CONFIG += staticlib
CONFIG += c++11
# You can make your code fail to compile if it uses deprecated APIs.
# In order to do so, uncomment the following line.
#DEFINES += QT_DISABLE_DEPRECATED_BEFORE=0x060000 # disables all the APIs deprecated before Qt 6.0.0
CORE_ROOT_DIR = $$PWD/../..
PWD_ROOT_DIR = $$PWD
include(../base.pri)
ADD_DEPENDENCY(UnicodeConverter, kernel)
SOURCES += \
RBTree/rbtree.cpp \
cfitem.cpp \
cfstorage.cpp \
cfstream.cpp \
compoundfile.cpp \
directoryentry.cpp \
header.cpp \
sector.cpp \
sectorcollection.cpp \
stream.cpp \
streamrw.cpp \
streamview.cpp
HEADERS += \
RBTree/irbnode.h \
RBTree/rbtree.h \
RBTree/rbtreeexception.h \
cfexception.h \
cfitem.h \
cfstorage.h \
cfstream.h \
compoundfile.h \
directoryentry.h \
event.h \
guid.h \
header.h \
idirectoryentry.h \
sector.h \
sectorcollection.h \
slist.h \
stream.h \
streamrw.h \
streamview.h \
svector.h

View File

@ -0,0 +1,80 @@
#pragma once
#include <exception>
#include <string>
namespace CFCPP
{
class CFException : virtual public std::exception
{
public:
CFException() {}
CFException(std::string message) : errorMessage(message) {}
CFException(std::string message, std::exception& ex) : std::exception(ex), errorMessage(message) {}
CFException(std::wstring message) : werrorMessage(message) {}
CFException(std::wstring message, std::exception& ex) : std::exception(ex), werrorMessage(message) {}
virtual ~CFException() throw () {}
virtual const char* what() const throw () {
return errorMessage.c_str();
}
virtual const wchar_t* what_w() const throw () {
return werrorMessage.c_str();
}
protected:
std::string errorMessage;
std::wstring werrorMessage;
};
class CFFileFormatException : public CFException
{
public:
CFFileFormatException() {}
CFFileFormatException(std::string message) : CFException(message) {}
virtual ~CFFileFormatException() throw () {}
};
class CFDisposedException : public CFException
{
public:
CFDisposedException() {}
CFDisposedException(std::string message) : CFException(message) {}
virtual ~CFDisposedException() throw () {}
};
class CFInvalidOperation : public CFException
{
public:
CFInvalidOperation() {}
CFInvalidOperation(std::string message) : CFException(message) {}
virtual ~CFInvalidOperation() throw () {}
};
class CFCorruptedFileException : public CFException
{
public:
CFCorruptedFileException() {}
CFCorruptedFileException(std::string message) : CFException(message) {}
virtual ~CFCorruptedFileException() throw () {}
};
class CFDuplicatedItemException : public CFException
{
public:
CFDuplicatedItemException() {}
CFDuplicatedItemException(std::wstring message) : CFException(message) {}
virtual ~CFDuplicatedItemException() throw () {}
};
class CFItemNotFound : public CFException
{
public:
CFItemNotFound() {}
CFItemNotFound(std::wstring message) : CFException(message) {}
virtual ~CFItemNotFound() throw () {}
};
}

101
Common/cfcpp/cfitem.cpp Normal file
View File

@ -0,0 +1,101 @@
#include "cfitem.h"
#include "idirectoryentry.h"
#include "compoundfile.h"
#include "cfexception.h"
using namespace CFCPP;
int CFItem::CompareTo(const CFItem &other) const
{
return dirEntry.lock()->CompareTo(std::dynamic_pointer_cast<RedBlackTree::IRBNode>(other.dirEntry.lock()));
}
bool CFItem::operator==(const CFItem &rightItem) const
{
return CompareTo(rightItem) == 0;
}
bool CFItem::operator!=(const CFItem &rightItem) const
{
return CompareTo(rightItem) != 0;
}
int CFItem::GetHashCode() const
{
return dirEntry.lock()->GetHashCode();
}
std::wstring CFItem::Name() const
{
auto n = dirEntry.lock()->GetEntryName();
if (n.empty() == false)
{
auto remIter = n.find_last_of('\0');
if (remIter != std::wstring::npos)
n.erase(remIter);
return n;
}
else
return L"";
}
std::streamsize CFItem::size() const
{
return dirEntry.lock()->getSize();
}
bool CFItem::IsStorage() const
{
return dirEntry.lock()->getStgType() == StgType::StgStorage;
}
bool CFItem::IsStream() const
{
return dirEntry.lock()->getStgType() == StgType::StgStream;
}
bool CFItem::ISRoot() const
{
return dirEntry.lock()->getStgType() == StgType::StgRoot;
}
GUID CFItem::getStorageCLSID() const
{
return dirEntry.lock()->getStorageCLSID();
}
void CFItem::setStorageCLSID(GUID value)
{
dirEntry.lock()->setStorageCLSID(value);
}
std::wstring CFItem::ToString() const
{
if (!dirEntry.expired())
return L"[" + std::to_wstring(dirEntry.lock()->getLeftSibling()) + L"," + std::to_wstring(dirEntry.lock()->getSid()) + L"," +
std::to_wstring(dirEntry.lock()->getRightSibling()) + L"] " + dirEntry.lock()->GetEntryName();
else
return L"";
}
void CFItem::setDirEntry(const std::weak_ptr<IDirectoryEntry> &newDirEntry)
{
if (newDirEntry.expired() || newDirEntry.lock()->getSid() < 0)
throw CFException("Attempting to create a CFStorage using an unitialized directory");
dirEntry = newDirEntry;
}
std::shared_ptr<IDirectoryEntry> CFItem::getDirEntry() const
{
return dirEntry.lock();
}
void CFItem::CheckDisposed() const
{
if (compoundFile != nullptr && compoundFile->IsClosed())
throw CFDisposedException("Owner Compound file has been closed and owned items have been invalidated");
}

53
Common/cfcpp/cfitem.h Normal file
View File

@ -0,0 +1,53 @@
#pragma once
#include "guid.h"
#include <memory>
#include <string>
namespace CFCPP
{
class CompoundFile;
class IDirectoryEntry;
struct DataTime
{
// TODO
char data[8] = {0,0,0,0,0,0,0,0};
};
class CFItem : protected std::enable_shared_from_this<CFItem>
{
public:
int CompareTo(const CFItem& other) const;
bool operator==(const CFItem &rightItem) const;
bool operator!=(const CFItem &rightItem) const;
int GetHashCode() const;
std::wstring Name() const;
std::streamsize size() const;
bool IsStorage() const;
bool IsStream() const;
bool ISRoot() const;
inline DataTime getDataTime()const{return DataTime();}
inline void setDataTime(const DataTime& value){};
GUID getStorageCLSID() const;
void setStorageCLSID(GUID value);
int CompareTo(const CFItem& other);
std::wstring ToString() const;
void setDirEntry(const std::weak_ptr<IDirectoryEntry> &newDirEntry);
std::shared_ptr<IDirectoryEntry> getDirEntry() const;
friend class CompoundFile;
protected:
std::weak_ptr<IDirectoryEntry> dirEntry;
CompoundFile* compoundFile;
protected:
CFItem() {};
CFItem(CompoundFile* compoundFile) : compoundFile(compoundFile) {}
inline CompoundFile* getCompoundFile() {return compoundFile;}
void CheckDisposed() const;
};
}

349
Common/cfcpp/cfstorage.cpp Normal file
View File

@ -0,0 +1,349 @@
#include "cfstorage.h"
#include "cfexception.h"
#include "compoundfile.h"
#include "directoryentry.h"
#include "RBTree/rbtreeexception.h"
#include "RBTree/irbnode.h"
using namespace CFCPP;
using RedBlackTree::RBTree;
CFStorage::CFStorage(CompoundFile *compFile, const std::weak_ptr<IDirectoryEntry> &dirEntry) :
CFItem(compFile)
{
setDirEntry(dirEntry);
}
std::shared_ptr<RBTree> CFStorage::getChildren()
{
if (children == nullptr)
{
children = LoadChildren(this->dirEntry.lock()->getSid());
if (children == nullptr)
{
children = CompoundFile::CreateNewTree();
}
}
return children;
}
std::shared_ptr<CFStream> CFStorage::AddStream(const std::wstring& streamName)
{
CheckDisposed();
if (streamName.empty())
throw CFException("Stream name cannot be null or empty");
std::shared_ptr<IDirectoryEntry> dirEntry = DirectoryEntry::TryNew(streamName, StgType::StgStream, compoundFile->GetDirectories());
try
{
// Add object to Siblings tree
children->Insert(dirEntry);
//... and set the root of the tree as new child of the current item directory entry
dirEntry->setChild(std::dynamic_pointer_cast<IDirectoryEntry>(children->getRoot())->getSid());
}
catch (RedBlackTree::RBTreeException &rbex)
{
compoundFile->ResetDirectoryEntry(dirEntry->getSid());
throw CFDuplicatedItemException(L"An entry with name '" + streamName + L"' is already present in storage '" + Name() + L"' ");
}
return std::shared_ptr<CFStream>(new CFStream(compoundFile, dirEntry));
}
std::shared_ptr<CFStream> CFStorage::GetStream(const std::wstring& streamName)
{
CheckDisposed();
std::shared_ptr<IDirectoryEntry> tmp = DirectoryEntry::Mock(streamName, StgType::StgStream);
RedBlackTree::PIRBNode outDe;
if (children->TryLookup(tmp, outDe) &&
((std::static_pointer_cast<IDirectoryEntry>(outDe))->getStgType() == StgType::StgStream))
{
return std::shared_ptr<CFStream>(new CFStream(compoundFile, std::static_pointer_cast<IDirectoryEntry>(outDe)));
}
else
{
throw CFItemNotFound(L"Cannot find item [" + streamName + L"] within the current storage");
}
}
bool CFStorage::TryGetStream(const std::wstring& streamName, std::shared_ptr<CFStream>& cfStream)
{
bool result = false;
cfStream.reset();
try
{
CheckDisposed();
std::shared_ptr<IDirectoryEntry> tmp = DirectoryEntry::Mock(streamName, StgType::StgStream);
RedBlackTree::PIRBNode outDe;
if (children->TryLookup(tmp, outDe) &&
((std::static_pointer_cast<IDirectoryEntry>(outDe))->getStgType() == StgType::StgStream))
{
cfStream = std::shared_ptr<CFStream>(new CFStream(compoundFile, std::static_pointer_cast<IDirectoryEntry>(outDe)));
result = true;
}
}
catch (CFDisposedException& ex)
{
result = false;
}
return result;
}
std::shared_ptr<CFStorage> CFStorage::GetStorage(const std::wstring &storageName)
{
CheckDisposed();
std::shared_ptr<IDirectoryEntry> templ = DirectoryEntry::Mock(storageName, StgType::StgInvalid);
RedBlackTree::PIRBNode outDe;
if (children->TryLookup(templ, outDe) && std::static_pointer_cast<IDirectoryEntry>(outDe)->getStgType() == StgType::StgStorage)
{
return std::shared_ptr<CFStorage>(new CFStorage(compoundFile, std::dynamic_pointer_cast<IDirectoryEntry>(outDe)));
}
else
{
throw CFItemNotFound(L"Cannot find item [" + storageName + L"] within the current storage");
}
}
std::shared_ptr<CFStorage> CFStorage::TryGetStorage(const std::wstring &storageName)
{
CheckDisposed();
std::shared_ptr<IDirectoryEntry> templ = DirectoryEntry::Mock(storageName, StgType::StgInvalid);
RedBlackTree::PIRBNode outDe;
if (children->TryLookup(templ, outDe) && std::static_pointer_cast<IDirectoryEntry>(outDe)->getStgType() == StgType::StgStorage)
{
return std::shared_ptr<CFStorage>(new CFStorage(compoundFile, std::dynamic_pointer_cast<IDirectoryEntry>(outDe)));
}
else
{
return {};
}
}
bool CFStorage::TryGetStorage(const std::wstring &storageName, std::shared_ptr<CFStorage>& cfStorage)
{
bool result = false;
cfStorage.reset();
try
{
CheckDisposed();
std::shared_ptr<IDirectoryEntry> templ = DirectoryEntry::Mock(storageName, StgType::StgInvalid);
RedBlackTree::PIRBNode outDe;
if (children->TryLookup(templ, outDe) && std::static_pointer_cast<IDirectoryEntry>(outDe)->getStgType() == StgType::StgStorage)
{
cfStorage.reset(new CFStorage(compoundFile, std::dynamic_pointer_cast<IDirectoryEntry>(outDe)));
result = true;
}
}
catch (CFDisposedException& ex)
{
result = false;
}
return result;
}
std::shared_ptr<CFStorage> CFStorage::AddStorage(const std::wstring &storageName)
{
CheckDisposed();
if (storageName.empty())
throw CFException("Stream name cannot be null or empty");
// Add new Storage directory entry
std::shared_ptr<IDirectoryEntry> cfo
= DirectoryEntry::New(storageName, StgType::StgStorage, compoundFile->GetDirectories());
//this.CompoundFile.InsertNewDirectoryEntry(cfo);
try
{
// Add object to Siblings tree
children->Insert(cfo);
}
catch (RedBlackTree::RBTreeDuplicatedItemException& ex)
{
compoundFile->ResetDirectoryEntry(cfo->getSid());
cfo.reset();
throw CFDuplicatedItemException(L"An entry with name '" + storageName + L"' is already present in storage '" + Name() + L"' ");
}
std::shared_ptr<IDirectoryEntry> childrenRoot = std::dynamic_pointer_cast<IDirectoryEntry>(children->getRoot());
dirEntry.lock()->setChild(childrenRoot->getSid());
return std::shared_ptr<CFStorage>(new CFStorage(compoundFile, cfo));
}
void CFStorage::VisitEntries(RedBlackTree::Action<std::shared_ptr<CFItem> > action, bool recursive)
{
CheckDisposed();
if (action != nullptr)
{
SVector<RedBlackTree::IRBNode> subStorages;
RedBlackTree::Action<RedBlackTree::PIRBNode> internalAction =
[&] (RedBlackTree::PIRBNode targetNode)
{
auto d = std::dynamic_pointer_cast<IDirectoryEntry>(targetNode);
if (d->getStgType() == StgType::StgStream)
{
std::shared_ptr<CFStream> pstream (new CFStream(compoundFile, d));
action(std::static_pointer_cast<CFItem>(pstream));
}
else
{
std::shared_ptr<CFStorage> pstorage(new CFStorage(compoundFile, d));
action(std::static_pointer_cast<CFItem>(pstorage));
}
if (d->getChild() != DirectoryEntry::NOSTREAM)
subStorages.push_back(targetNode);
};
children->VisitTreeNodes(internalAction);
if (recursive && subStorages.size() > 0)
for (const auto& n : subStorages)
{
auto d = std::dynamic_pointer_cast<IDirectoryEntry>(n);
CFStorage(compoundFile, d).VisitEntries(action, recursive);
}
}
}
void CFStorage::Delete(const std::wstring &entryName)
{
CheckDisposed();
// Find entry to delete
auto tmp = DirectoryEntry::Mock(entryName, StgType::StgInvalid);
RedBlackTree::PIRBNode foundObj;
children->TryLookup(tmp, foundObj);
if (foundObj == nullptr)
throw CFItemNotFound(L"Entry named [" + entryName + L"] was not found");
if (std::dynamic_pointer_cast<IDirectoryEntry>(foundObj)->getStgType() == StgType::StgRoot)
throw CFException("Root storage cannot be removed");
RedBlackTree::PIRBNode altDel;
switch (std::dynamic_pointer_cast<IDirectoryEntry>(foundObj)->getStgType())
{
case StgType::StgStorage:
{
std::shared_ptr<CFStorage> temp(new CFStorage(compoundFile, std::dynamic_pointer_cast<IDirectoryEntry>(foundObj)));
// This is a storage. we have to remove children items first
for (const auto& de : *(temp->getChildren()))
{
auto ded = std::dynamic_pointer_cast<IDirectoryEntry>(de);
temp->Delete(ded->GetEntryName());
}
// ...then we need to rethread the root of siblings tree...
if (children->getRoot() != nullptr)
dirEntry.lock()->setChild(std::dynamic_pointer_cast<IDirectoryEntry>(children->getRoot())->getSid());
else
dirEntry.lock()->setChild(DirectoryEntry::NOSTREAM);
// ...and finally Remove storage item from children tree...
children->Delete(foundObj, altDel);
// ...and remove directory (storage) entry
if (altDel != nullptr)
{
foundObj = altDel;
}
compoundFile->InvalidateDirectoryEntry(std::dynamic_pointer_cast<IDirectoryEntry>(foundObj)->getSid());
break;
}
case StgType::StgStream:
{
// Free directory associated data stream.
compoundFile->FreeAssociatedData(std::dynamic_pointer_cast<IDirectoryEntry>(foundObj)->getSid());
// Remove item from children tree
children->Delete(foundObj, altDel);
// Rethread the root of siblings tree...
if (children->getRoot() != nullptr)
dirEntry.lock()->setChild(std::dynamic_pointer_cast<IDirectoryEntry>(children->getRoot())->getSid());
else
dirEntry.lock()->setChild(DirectoryEntry::NOSTREAM);
// Delete operation could possibly have cloned a directory, changing its SID.
// Invalidate the ACTUALLY deleted directory.
if (altDel != nullptr)
{
foundObj = altDel;
}
compoundFile->InvalidateDirectoryEntry(std::dynamic_pointer_cast<IDirectoryEntry>(foundObj)->getSid());
}
default:
break;
}
}
void CFStorage::RenameItem(const std::wstring &oldItemName, const std::wstring &newItemName)
{
auto templ = DirectoryEntry::Mock(oldItemName, StgType::StgInvalid);
RedBlackTree::PIRBNode item;
if (children->TryLookup(templ, item))
{
std::dynamic_pointer_cast<DirectoryEntry>(item)->SetEntryName(newItemName);
}
else throw CFItemNotFound(L"Item " + oldItemName + L" not found in Storage");
children.reset();
children = LoadChildren(dirEntry.lock()->getSid()); //Rethread
if (children == nullptr)
{
children = compoundFile->CreateNewTree();
}
}
std::shared_ptr<RBTree> CFStorage::LoadChildren(int SID)
{
std::shared_ptr<RBTree> childrenTree = compoundFile->GetChildrenTree(SID);
if (childrenTree->getRoot() != nullptr)
dirEntry.lock()->setChild((std::static_pointer_cast<IDirectoryEntry>(childrenTree->getRoot())->getSid()));
else
dirEntry.lock()->setChild(DirectoryEntry::NOSTREAM);
return childrenTree;
}

36
Common/cfcpp/cfstorage.h Normal file
View File

@ -0,0 +1,36 @@
#pragma once
#include "RBTree/rbtree.h"
#include "idirectoryentry.h"
#include "cfstream.h"
namespace CFCPP
{
class CFStorage : public CFItem
{
public:
CFStorage(CompoundFile* compFile, const std::weak_ptr<IDirectoryEntry> &dirEntry);
std::shared_ptr<RedBlackTree::RBTree> getChildren();
std::shared_ptr<CFStream> AddStream(const std::wstring& streamName);
std::shared_ptr<CFStream> GetStream(const std::wstring& streamName);
bool TryGetStream(const std::wstring& streamName, std::shared_ptr<CFStream> &cfStream);
std::shared_ptr<CFStorage> GetStorage(const std::wstring& storageName);
std::shared_ptr<CFStorage> TryGetStorage(const std::wstring& storageName);
bool TryGetStorage(const std::wstring& storageName, std::shared_ptr<CFStorage> &cfStorage);
std::shared_ptr<CFStorage> AddStorage(const std::wstring& storageName);
void VisitEntries(RedBlackTree::Action<std::shared_ptr<CFItem>> action, bool recursive);
void Delete(const std::wstring& entryName);
void RenameItem(const std::wstring& oldItemName, const std::wstring& newItemName);
std::streamsize size() const {return CFItem::size();}
private:
std::shared_ptr<RedBlackTree::RBTree> LoadChildren(int SID);
private:
std::shared_ptr<RedBlackTree::RBTree> children;
};
}

86
Common/cfcpp/cfstream.cpp Normal file
View File

@ -0,0 +1,86 @@
#include "cfstream.h"
#include "cfexception.h"
#include "idirectoryentry.h"
#include "compoundfile.h"
using namespace CFCPP;
CFStream::CFStream(CompoundFile* compFile, std::weak_ptr<IDirectoryEntry> dirEntry) :
CFItem(compFile)
{
if (dirEntry.expired() || dirEntry.lock()->getSid() < 0)
throw CFException("Attempting to create a CFStorage using an unitialized directory");
this->dirEntry = dirEntry;
}
void CFStream::SetData(const std::vector<BYTE> &data)
{
CheckDisposed();
compoundFile->FreeData(this);
compoundFile->WriteData(shared_from_this(), data);
}
void CFStream::Write(const std::vector<BYTE> &data, std::streamsize position)
{
Write(data, position, 0, data.size());
}
void CFStream::Write(const std::vector<BYTE> &data, std::streamsize position, int offset, int count)
{
CheckDisposed();
compoundFile->WriteData(shared_from_this(), data, position, offset, count);
}
void CFStream::Append(const std::vector<BYTE> &data)
{
CheckDisposed();
if (size() > 0)
{
compoundFile->AppendData(shared_from_this(), data);
}
else
{
compoundFile->WriteData(shared_from_this(), data);
}
}
std::vector<BYTE> CFStream::getData() const
{
CheckDisposed();
return compoundFile->GetData(this);
}
int CFStream::Read(std::vector<BYTE> &buffer, std::streamsize position, int count)
{
CheckDisposed();
return compoundFile->ReadData(this, position, buffer, 0, count);
}
int CFStream::Read(std::vector<BYTE> &buffer, std::streamsize position, int offset, int count)
{
CheckDisposed();
return compoundFile->ReadData(this, position, buffer, offset, count);
}
void CFStream::CopyFrom(const Stream &input)
{
CheckDisposed();
std::vector<BYTE> buffer(Length(input));
// if (input.CanSeek)
{
input->seek(0, std::ios::beg);
}
input->read(reinterpret_cast<char*>(buffer.data()), Length(input));
SetData(buffer);
}
void CFStream::Resize(std::streamsize length)
{
compoundFile->SetStreamLength(shared_from_this(), length);
}

25
Common/cfcpp/cfstream.h Normal file
View File

@ -0,0 +1,25 @@
#pragma once
#include "stream.h"
#include "cfitem.h"
#include <vector>
namespace CFCPP
{
class CFStream : public CFItem
{
public:
CFStream(CompoundFile* compFile, std::weak_ptr<IDirectoryEntry> dirEntry);
void SetData(const std::vector<BYTE>& data);
void Write(const std::vector<BYTE>& data, std::streamsize position);
void Write(const std::vector<BYTE>& data, std::streamsize position, int offset, int count);
void Append(const std::vector<BYTE>& data);
std::vector<BYTE> getData() const;
int Read(std::vector<BYTE>& buffer, std::streamsize position, int count);
int Read(std::vector<BYTE>& buffer, std::streamsize position, int offset, int count);
void CopyFrom(const Stream& input);
void Resize(std::streamsize length);
std::streamsize size() const {return CFItem::size();}
};
}

File diff suppressed because it is too large Load Diff

156
Common/cfcpp/compoundfile.h Normal file
View File

@ -0,0 +1,156 @@
#pragma once
#include "sectorcollection.h"
#include "cfstorage.h"
#include "slist.h"
#include <unordered_set>
#include "RBTree/rbtree.h"
#include "idirectoryentry.h"
#include <mutex>
#include "header.h"
namespace CFCPP
{
class DirectoryEntry;
enum CFSConfiguration
{
Default = 1,
SectorRecycle = 2,
EraseFreeSectors = 4,
NoValidationException = 8,
LeaveOpen = 16
};
enum CFSUpdateMode
{
/// ReadOnly update mode prevents overwriting
/// of the opened file.
/// Data changes are allowed but they have to be
/// persisted on a different file when required
ReadOnly,
/// Update mode allows subsequent data changing operations
/// to be persisted directly on the opened file or stream
/// method when required. Warning: this option may cause existing data loss if misused.
Update
};
class CompoundFile
{
public:
CompoundFile(const std::wstring &fileName, CFSUpdateMode updateMode, CFSConfiguration configParameters);
CompoundFile(CFSVersion cfsVersion, CFSConfiguration configFlags);
CompoundFile(const std::wstring &fileName);
CompoundFile(Stream stream);
CompoundFile();
void Commit(bool releaseMemory = false);
inline bool HasSourceStream() {return sourceStream != nullptr;}
void Close();
static std::shared_ptr<RedBlackTree::RBTree> CreateNewTree();
std::shared_ptr<RedBlackTree::RBTree> GetChildrenTree(int sid);
bool IsClosed()const;
SVector<IDirectoryEntry> &GetDirectories();
void ResetDirectoryEntry(int sid);
void InvalidateDirectoryEntry(int sid);
void FreeAssociatedData(int sid);
void FreeData(CFStream* stream);
void WriteData(std::shared_ptr<CFItem> cfItem, const std::vector<BYTE>& buffer, std::streamsize position, int offset, int count);
void WriteData(std::shared_ptr<CFItem> cfItem, std::streamsize position, const std::vector<BYTE>& buffer);
void WriteData(std::shared_ptr<CFItem> cfItem, const std::vector<BYTE>& buffer);
void AppendData(std::shared_ptr<CFItem> cfItem, const std::vector<BYTE>& buffer);
void SetStreamLength(std::shared_ptr<CFItem> cfItem, std::streamsize length);
SList<Sector> FindFreeSectors(SectorType sType);
std::vector<BYTE> GetData(const CFStream *cFStream);
int ReadData(CFStream* cFStream, std::streamsize position, std::vector<BYTE>& buffer, int count);
int ReadData(CFStream* cFStream, std::streamsize position, std::vector<BYTE>& buffer, int offset, int count);
std::vector<BYTE> GetDataBySID(int sid);
GUID getGuidBySID(int sid);
GUID getGuidForStream(int sid);
void Save(std::wstring wFileName);
void Save(Stream stream);
protected:
int GetSectorSize();
void Dispose(bool disposing);
private:
void CheckForLockSector();
void OnSizeLimitReached();
void LoadFile(std::wstring fileName);
void SetFileName(std::wstring fileName);
void LoadStream(Stream stream);
void Load(Stream stream);
SVector<Sector> GetFatSectorChain();
SVector<Sector> GetDifatSectorChain();
SVector<Sector> GetNormalSectorChain(int secID);
SVector<Sector> GetMiniSectorChain(int secID);
SVector<Sector> GetSectorChain(int secID, SectorType chainType);
void EnsureUniqueSectorIndex(int nextSecID, std::unordered_set<int> &processedSectors);
void CommitDirectory();
void Close(bool closeStream);
std::shared_ptr<IDirectoryEntry> RootEntry();
SVector<IDirectoryEntry> FindDirectoryEntries(std::wstring entryName);
std::shared_ptr<RedBlackTree::RBTree> DoLoadChildrenTrusted(std::shared_ptr<IDirectoryEntry> de);
void DoLoadChildren(std::shared_ptr<RedBlackTree::RBTree> bst, std::shared_ptr<IDirectoryEntry> de);
void NullifyChildNodes(std::shared_ptr<IDirectoryEntry> de);
void LoadSiblings(std::shared_ptr<RedBlackTree::RBTree> bst, std::shared_ptr<IDirectoryEntry> de);
void DoLoadSiblings(std::shared_ptr<RedBlackTree::RBTree> bst, std::shared_ptr<IDirectoryEntry> de);
bool ValidateSibling(int sid);
void LoadDirectories();
// TODO
void FreeMiniChain(SVector<Sector>& sectorChain, bool zeroSector);
void FreeMiniChain(SVector<Sector>& sectorChain, int nth_sector_to_remove, bool zeroSector);
void FreeChain(SVector<Sector>& sectorChain, int nth_sector_to_remove, bool zeroSector);
void FreeChain(SVector<Sector>& sectorChain, bool zeroSector);
void AllocateSectorChain(SVector<Sector>& sectorChain);
void AllocateFATSectorChain(SVector<Sector>& sectorChain);
void AllocateDIFATSectorChain(SVector<Sector>& FATsectorChain);
void AllocateMiniSectorChain(SVector<Sector>& sectorChain);
void PersistMiniStreamToStream(const SVector<Sector>& miniSectorChain);
static int LowSaturation(int i);
void SetSectorChain(SVector<Sector> sectorChain);
CFSVersion getVersion() const;
public:
CFSConfiguration configuration = Default;
std::unique_ptr<Header> header;
Stream sourceStream;
private:
const int HEADER_DIFAT_ENTRIES_COUNT = 109;
int DIFAT_SECTOR_FAT_ENTRIES_COUNT = 127;
int FAT_SECTOR_ENTRIES_COUNT = 128;
const int SIZE_OF_SID = 4;
bool sectorRecycle = false;
bool eraseFreeSectors = false;
static constexpr int FLUSHING_QUEUE_SIZE = 6000;
static constexpr int FLUSHING_BUFFER_MAX_SIZE = 1024 * 1024 * 16;
SectorCollection sectors;
std::fstream stream;
std::string fileName;
std::shared_ptr<CFStorage> rootStorage;
bool closeStream = true;
bool _transactionLockAdded = false;
int _lockSectorId = -1;
bool _transactionLockAllocated = false;
bool validationExceptionEnabled = true;
bool _disposed;//false
CFSUpdateMode updateMode;
SVector<IDirectoryEntry> directoryEntries;
std::list<int> levelSIDs;
std::mutex lockObject;
};
}

View File

@ -0,0 +1,319 @@
#include "directoryentry.h"
#include "cfexception.h"
#include "streamrw.h"
#include <stdexcept>
using namespace CFCPP;
DirectoryEntry::DirectoryEntry(std::wstring name, StgType stgType, SVector<IDirectoryEntry> dirRepository)
{
this->dirRepository = dirRepository;
this->stgType = stgType;
if (stgType == StgType::StgStorage)
{
// creationDate = BitConverter.GetBytes((DateTime.Now.ToFileTime()));
startSetc = ZERO;
}
if (stgType == StgType::StgInvalid)
{
startSetc = ZERO;
}
if (name.size())
{
DirectoryEntry::SetEntryName(name);
}
}
int DirectoryEntry::getSid() const
{
return sid;
}
void DirectoryEntry::setSid(int newSid)
{
sid = newSid;
}
std::wstring DirectoryEntry::GetEntryName() const
{
if (entryName[0] != '\0' && nameLength > 0)
{
wchar_t name[32];
for (int i = 0; i < 32; i++)
{
name[i] = entryName[2*i] + (entryName[2*i+1] << 8);
}
return std::wstring (name, name + nameLength/2 - 1);
}
else
return L"";
}
void DirectoryEntry::SetEntryName(const std::wstring &entryName)
{
if (entryName.empty())
{
std::fill(this->entryName, this->entryName+64, '\0');
this->nameLength = 0;
}
else
{
if (
entryName.find(L"\\") == std::wstring::npos ||
entryName.find(L"/") == std::wstring::npos ||
entryName.find(L":") == std::wstring::npos ||
entryName.find(L"!") == std::wstring::npos
) throw CFException("Invalid character in entry: the characters '\\', '/', ':','!' cannot be used in entry name");
if (entryName.length() > 31)
throw CFException("Entry name MUST NOT exceed 31 characters");
std::copy(entryName.data(), entryName.data() + entryName.length(), this->entryName);
reinterpret_cast<wchar_t*>(this->entryName)[entryName.length()] = L'\0';
this->nameLength = (ushort)entryName.size() + 2;
}
}
int DirectoryEntry::GetHashCode() const
{
return (int)fnv_hash(entryName, nameLength);
}
void DirectoryEntry::Write(Stream stream) const
{
StreamRW rw(stream);
rw.WriteArray(entryName, sizeof (entryName));
rw.Write(nameLength);
rw.Write((BYTE)stgType);
rw.Write((BYTE)stgColor);
rw.Write(leftSibling);
rw.Write(rightSibling);
rw.Write(child);
rw.WriteArray(reinterpret_cast<const char*>(&storageCLSID), sizeof (storageCLSID));
rw.Write(stateBits);
rw.WriteArray(reinterpret_cast<const char*>(&creationDate), sizeof (creationDate));
rw.WriteArray(reinterpret_cast<const char*>(&modifyDate), sizeof (modifyDate));
rw.Write(startSetc);
rw.Write(size);
rw.Close();
}
void DirectoryEntry::Read(Stream stream, CFSVersion ver)
{
StreamRW rw(stream);
rw.ReadArray(entryName, 64);
nameLength = rw.Read<decltype(nameLength)>();
stgType = (StgType)rw.Read<BYTE>();
stgColor = (StgColor)rw.Read<BYTE>();
leftSibling = rw.Read<decltype(leftSibling)>();
rightSibling = rw.Read<decltype(rightSibling)>();
child = rw.Read<decltype(child)>();
// Thanks to bugaccount (BugTrack id 3519554)
if (stgType == StgType::StgInvalid)
{
leftSibling = NOSTREAM;
rightSibling = NOSTREAM;
child = NOSTREAM;
}
rw.ReadArray(reinterpret_cast<char*>(&storageCLSID), 16);
stateBits = rw.Read<decltype (stateBits)>();
rw.ReadArray(reinterpret_cast<char*>(&creationDate), 8);
rw.ReadArray(reinterpret_cast<char*>(&modifyDate), 8);
startSetc = rw.Read<decltype (startSetc)>();
if (ver == CFSVersion::Ver_3)
{
// avoid dirty read for version 3 files (max size: 32bit integer)
// where most significant bits are not initialized to zero
size = rw.Read<int>();
rw.Read<INT>(); //discard most significant 4 (possibly) dirty bytes
}
else
{
size = rw.Read<decltype (size)>();
}
}
std::wstring DirectoryEntry::ToString() const
{
std::wstringstream wss;
wss << Name() << L" [" << sid << L"]" << (stgType == StgType::StgStream ? L"Stream" : L"Storage");
return wss.str();
}
RedBlackTree::PIRBNode DirectoryEntry::getLeft() const
{
if (leftSibling == NOSTREAM)
return {};
return dirRepository[leftSibling];
}
RedBlackTree::PIRBNode DirectoryEntry::getRight() const
{
if (rightSibling == DirectoryEntry::NOSTREAM)
return {};
return dirRepository[rightSibling];
}
void DirectoryEntry::setLeft(RedBlackTree::PIRBNode pNode)
{
leftSibling = pNode != nullptr ? static_cast<IDirectoryEntry*>(pNode.get())->getSid() : DirectoryEntry::NOSTREAM;
if (leftSibling != DirectoryEntry::NOSTREAM)
dirRepository[leftSibling]->setParent(shared_from_this());
}
void DirectoryEntry::setRight(RedBlackTree::PIRBNode pNode)
{
rightSibling = pNode != nullptr ? static_cast<IDirectoryEntry*>(pNode.get())->getSid() : DirectoryEntry::NOSTREAM;
if (rightSibling != DirectoryEntry::NOSTREAM)
dirRepository[rightSibling]->setParent(shared_from_this());
}
RedBlackTree::PIRBNode DirectoryEntry::Sibling() const
{
if (shared_from_this() == getParent()->getLeft())
return getParent()->getRight();
else
return getParent()->getLeft();
}
RedBlackTree::PIRBNode DirectoryEntry::Uncle() const
{
return parent.use_count() != 0 ? getParent()->Sibling() : RedBlackTree::PIRBNode();
}
void DirectoryEntry::AssignValueTo(RedBlackTree::PIRBNode other)
{
auto d = std::dynamic_pointer_cast<DirectoryEntry>(other); // as
if (d == nullptr)
return;
d->SetEntryName(this->GetEntryName());
d->creationDate = creationDate;
d->modifyDate = modifyDate;
d->size = this->size;
d->startSetc = this->startSetc;
d->stateBits = this->stateBits;
d->stgType = this->stgType;
d->storageCLSID = this->storageCLSID;
d->child = this->child;
}
int DirectoryEntry::CompareTo(const RedBlackTree::PIRBNode &other) const
{
IDirectoryEntry* otherDir = dynamic_cast<IDirectoryEntry*>(other.get());
if (otherDir == nullptr)
throw CFException("Invalid casting: compared object does not implement IDirectorEntry interface");
if (this->getNameLength() > otherDir->getNameLength())
{
return THIS_IS_GREATER;
}
else if (this->getNameLength() < otherDir->getNameLength())
{
return OTHER_IS_GREATER;
}
else
{
std::wstring thisName = GetEntryName();
std::wstring otherName = otherDir->GetEntryName();
for (int z = 0; z < (int)thisName.size(); z++)
{
char thisChar = toupper(thisName[z]);
char otherChar = toupper(otherName[z]);
if (thisChar > otherChar)
return THIS_IS_GREATER;
else if (thisChar < otherChar)
return OTHER_IS_GREATER;
}
return 0;
}
}
ULONG64 DirectoryEntry::fnv_hash(const char *buffer, int lenght)
{
ULONG64 h = 2166136261;
int i;
for (i = 0; i < lenght; i++)
h = (h * 16777619) ^ buffer[i];
return h;
}
std::shared_ptr<IDirectoryEntry> DirectoryEntry::New(std::wstring name, StgType stgType, SVector<IDirectoryEntry>& dirRepository)
{
std::shared_ptr<IDirectoryEntry> de;
if (/*dirRepository != null*/true)
{
de.reset(new DirectoryEntry(name, stgType, dirRepository));
// No invalid directory entry found
dirRepository.push_back(de);
de->setSid(dirRepository.size() - 1);
}
else
throw std::invalid_argument("dirRepository Directory repository cannot be null in New() method");
return de;
}
std::shared_ptr<IDirectoryEntry> DirectoryEntry::TryNew(std::wstring name, StgType stgType, SVector<IDirectoryEntry>& dirRepository)
{
std::shared_ptr<DirectoryEntry> de(new DirectoryEntry(name, stgType, dirRepository));
// If we are not adding an invalid dirEntry as
// in a normal loading from file (invalid dirs MAY pad a sector)
if (de != nullptr)
{
// Find first available invalid slot (if any) to reuse it
for (int i = 0; i < (int)dirRepository.size(); i++)
{
if (dirRepository[i]->getStgType() == StgType::StgInvalid)
{
dirRepository[i] = de;
de->sid = i;
return de;
}
}
}
// No invalid directory entry found
dirRepository.push_back(de);
de->sid = dirRepository.size() - 1;
return de;
}
std::shared_ptr<IDirectoryEntry> DirectoryEntry::Mock(std::wstring name, StgType stgType)
{
auto de = std::shared_ptr<IDirectoryEntry>(new DirectoryEntry(name, stgType, {}));
return de;
}

View File

@ -0,0 +1,106 @@
#pragma once
#include "svector.h"
#include "idirectoryentry.h"
namespace CFCPP
{
class DirectoryEntry : public IDirectoryEntry, protected std::enable_shared_from_this<DirectoryEntry>
{
public:
static const int THIS_IS_GREATER = 1;
static const int OTHER_IS_GREATER = -1;
static const int NOSTREAM = 0xFFFFFFFF;
static const int ZERO = 0;
DirectoryEntry(std::wstring name, StgType stgType, SVector<IDirectoryEntry> dirRepository);
RedBlackTree::PIRBNode getLeft() const override;
RedBlackTree::PIRBNode getRight() const override;
void setLeft(RedBlackTree::PIRBNode pNode) override;
void setRight(RedBlackTree::PIRBNode pNode) override;
std::streamsize getSize() const override {return size;}
void setSize(std::streamsize value) override {size = value;}
int getStateBits() const override {return stateBits;}
void setStateBits(int value) override {stateBits = value;}
inline void setColor(RedBlackTree::Color clr) override {stgColor = (StgColor)clr;}
inline RedBlackTree::Color getColor()const override {return (RedBlackTree::Color)stgColor;}
void setParent(RedBlackTree::PIRBNode pParent) override {parent = pParent;}
inline RedBlackTree::PIRBNode getParent() const override {return parent.lock();}
inline RedBlackTree::PIRBNode Grandparent() const override
{return (parent.use_count() ? parent.lock()->getParent() : RedBlackTree::PIRBNode());}
RedBlackTree::PIRBNode Sibling() const override; // check parent before using
RedBlackTree::PIRBNode Uncle() const override;
void AssignValueTo(RedBlackTree::PIRBNode other) override;
int CompareTo(const RedBlackTree::PIRBNode& other) const override;
std::wstring ToString() const override;
inline int getChild() const override {return child;}
inline void setChild(int value) override {child = value;}
inline int getLeftSibling() const override {return leftSibling;}
inline void setLeftSibling(int value) override {leftSibling = value;}
inline int getRightSibling() const override {return rightSibling;}
inline void setRightSibling(int value) override {rightSibling = value;}
inline UINT64 getCreationDate() const override {return creationDate;}
inline void setCreationDate(const UINT64& value) override {creationDate = value;}
inline UINT64 getModifyDate() const override {return modifyDate;}
inline void setModifyDate(const UINT64& value) override {modifyDate = value;}
int getSid() const override;
void setSid(int newSid) override;
std::wstring GetEntryName() const override;
void SetEntryName(const std::wstring &entryName) override;
inline ushort getNameLength() const override {return nameLength;}
void setStartSetc(int value) override {startSetc = value;};
int getStartSetc() const override {return startSetc;};
void Read(Stream stream, CFSVersion ver = CFSVersion::Ver_3) override;
void Write(Stream stream) const override;
inline StgColor getStgColor() const override {return stgColor;}
inline void setStgColor(StgColor value) override {stgColor = value;}
inline StgType getStgType() const override {return stgType;}
inline void setStgType(StgType value) override {stgType = value;}
inline GUID getStorageCLSID() const override {return storageCLSID;}
inline void setStorageCLSID(GUID value) override {storageCLSID = value;}
int GetHashCode() const override;
inline std::wstring Name() const {return GetEntryName();}
public:
UINT64 creationDate = 0;
UINT64 modifyDate = 0;
int startSetc = 0xFFFFFFFE;
LONG64 size;
int leftSibling = NOSTREAM;
int rightSibling = NOSTREAM;
int child = NOSTREAM;
int stateBits;
static std::shared_ptr<IDirectoryEntry> New(std::wstring name, StgType stgType, SVector<IDirectoryEntry>& dirRepository);
static std::shared_ptr<IDirectoryEntry> TryNew(std::wstring name, StgType stgType, SVector<IDirectoryEntry> &dirRepository);
static std::shared_ptr<IDirectoryEntry> Mock(std::wstring name, StgType stgType);
private:
static ULONG64 fnv_hash(const char *buffer, int lenght);
private:
int sid = -1;
char entryName[64];
ushort nameLength;
StgType stgType = StgType::StgInvalid;
StgColor stgColor = StgColor::Red;
SVector<IDirectoryEntry> dirRepository;
std::weak_ptr<RedBlackTree::IRBNode> parent;
GUID storageCLSID;
};
}

31
Common/cfcpp/event.h Normal file
View File

@ -0,0 +1,31 @@
#pragma once
#include <functional>
#include <vector>
template <class T>
class Event
{
public:
Event& operator+=(T& act)
{
events.push_back(act);
return *this;
}
const Event& operator()()const
{
for (auto& event : events)
event();
return *this;
}
void clear()
{
events.clear();
}
inline size_t size() const {return events.size();}
protected:
std::vector<T> events;
};

40
Common/cfcpp/guid.h Normal file
View File

@ -0,0 +1,40 @@
#pragma once
struct GUID
{
unsigned int Data1 = 0;
unsigned short Data2 = 0;
unsigned short Data3 = 0;
unsigned long long Data4 = 0;
unsigned char* getData4()
{
return reinterpret_cast<unsigned char*>(&Data4);
}
GUID (const GUID& o) : Data1(o.Data1), Data2(o.Data2), Data3(o.Data3)
{
}
GUID& operator=(const GUID& o)
{
Data1 = o.Data1;
Data2 = o.Data2;
Data3 = o.Data3;
Data4 = o.Data4;
return *this;
}
bool operator!=(const GUID& oth)const
{
return Data1 != oth.Data1 || Data2 != oth.Data2 || Data3 != oth.Data3 || Data4 != oth.Data4;
}
bool operator==(const GUID& oth)const
{
return !operator!=(oth);
}
GUID (){}
};

116
Common/cfcpp/header.cpp Normal file
View File

@ -0,0 +1,116 @@
#include "header.h"
#include "cfexception.h"
#include "streamrw.h"
using namespace CFCPP;
Header::Header() :
Header(3)
{
}
Header::Header(ushort version)
{
switch (version)
{
case 3:
majorVersion = 3;
sectorShift = 0x0009;
break;
case 4:
majorVersion = 4;
sectorShift = 0x000C;
break;
default:
throw CFException("Invalid Compound File Format version");
}
for (int i = 0; i < 109; i++)
{
difat[i] = Sector::FREESECT;
}
}
void Header::Write(CFCPP::Stream &stream)
{
StreamRW rw(stream);
rw.WriteArray(headerSignature, sizeof(headerSignature));
rw.WriteArray(clsid, sizeof(clsid));
rw.Write(minorVersion);
rw.Write(majorVersion);
rw.Write(byteOrder);
rw.Write(sectorShift);
rw.Write(miniSectorShift);
rw.WriteArray(unUsed, sizeof(unUsed));
rw.Write(directorySectorsNumber);
rw.Write(fatSectorsNumber);
rw.Write(firstDirectorySectorID);
rw.Write(unUsed2);
rw.Write(minSizeStandardStream);
rw.Write(firstMiniFATSectorID);
rw.Write(miniFATSectorsNumber);
rw.Write(firstDIFATSectorID);
rw.Write(difatSectorsNumber);
for (int i : difat)
{
rw.Write(i);
}
if (majorVersion == 4)
{
std::vector<BYTE> zeroHead(3584,0);
rw.WriteArray(zeroHead.data(), zeroHead.size());
}
}
void Header::Read(CFCPP::Stream &stream)
{
StreamRW rw(stream);
rw.ReadArray(headerSignature, sizeof(headerSignature));
CheckSignature();
rw.ReadArray(clsid, sizeof(clsid));
minorVersion = rw.Read<decltype(minorVersion)>();
majorVersion = rw.Read<decltype(majorVersion)>();
CheckVersion();
byteOrder = rw.Read<decltype(majorVersion)>();
sectorShift = rw.Read<decltype(majorVersion)>();
miniSectorShift = rw.Read<decltype(majorVersion)>();
rw.ReadArray(unUsed, sizeof (unUsed));
directorySectorsNumber = rw.Read<decltype(directorySectorsNumber)>();
fatSectorsNumber = rw.Read<decltype(fatSectorsNumber)>();
firstDirectorySectorID = rw.Read<decltype(firstDirectorySectorID)>();
unUsed2 = rw.Read<decltype(unUsed2)>();
minSizeStandardStream = rw.Read<decltype(minSizeStandardStream)>();
firstMiniFATSectorID = rw.Read<decltype(firstMiniFATSectorID)>();
miniFATSectorsNumber = rw.Read<decltype(miniFATSectorsNumber)>();
firstDIFATSectorID = rw.Read<decltype(firstDIFATSectorID)>();
difatSectorsNumber = rw.Read<decltype(difatSectorsNumber)>();
for (int i = 0; i < 109; i++)
{
difat[i] = rw.Read<int>();
}
}
void Header::CheckVersion() const
{
if (majorVersion != 3 && majorVersion != 4)
throw CFFileFormatException("Unsupported Binary File Format version: OpenMcdf only supports Compound Files with major version equal to 3 or 4 ");
}
void Header::CheckSignature() const
{
std::array<BYTE,8> OLE_CFS_SIGNATURE{ 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 };
for (size_t i = 0; i < sizeof(headerSignature); i++)
{
if (headerSignature[i] != OLE_CFS_SIGNATURE[i])
throw CFFileFormatException("Invalid OLE structured storage file");
}
}

41
Common/cfcpp/header.h Normal file
View File

@ -0,0 +1,41 @@
#pragma once
#include "sector.h"
namespace CFCPP
{
class Header
{
public:
Header();
Header(ushort version);
void Write(Stream& stream);
void Read(Stream& stream);
private:
void CheckVersion()const;
void CheckSignature()const;
public:
BYTE headerSignature[8] = {0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1};
BYTE clsid[16];
USHORT minorVersion = 0x003E;
USHORT majorVersion = 0x0003;
USHORT byteOrder = 0xFFFE;
USHORT sectorShift = 9;
USHORT miniSectorShift = 6;
BYTE unUsed[6];
INT directorySectorsNumber;
INT fatSectorsNumber;
INT firstDirectorySectorID = Sector::ENDOFCHAIN;
uint unUsed2;
uint minSizeStandardStream = 4096;
INT firstMiniFATSectorID = 0xFFFFFFFE;
uint miniFATSectorsNumber;
INT firstDIFATSectorID = Sector::ENDOFCHAIN;
uint difatSectorsNumber;
INT difat[109];
};
}

View File

@ -0,0 +1,75 @@
#pragma once
#include <map>
#include "stream.h"
#include "RBTree/irbnode.h"
#include "guid.h"
namespace CFCPP
{
enum StgType : int
{
StgInvalid = 0,
StgStorage = 1,
StgStream = 2,
StgLockbytes = 3,
StgProperty = 4,
StgRoot = 5
};
enum StgColor : int
{
Red = 0,
Black = 1
};
enum CFSVersion : int
{
/// Compound file version 3 - The default and most common version available. Sector size 512 bytes, 2GB max file size.
Ver_3 = 3,
/// Compound file version 4 - Sector size is 4096 bytes. Using this version could bring some compatibility problem with existing applications.
Ver_4 = 4
};
class IDirectoryEntry : public RedBlackTree::IRBNode
{
public:
virtual int getChild() const = 0;
virtual void setChild(int value) = 0;
virtual int getLeftSibling() const = 0;
virtual void setLeftSibling(int value) = 0;
virtual int getRightSibling() const = 0;
virtual void setRightSibling(int value) = 0;
virtual std::streamsize getSize() const = 0;
virtual void setSize(std::streamsize value) = 0;
virtual int getStateBits() const = 0;
virtual void setStateBits(int value) = 0;
virtual UINT64 getCreationDate() const = 0;
virtual void setCreationDate(const UINT64& value) = 0;
virtual UINT64 getModifyDate() const = 0;
virtual void setModifyDate(const UINT64& value) = 0;
virtual int getSid() const = 0;
virtual void setSid(int newSid) = 0;
virtual std::wstring GetEntryName() const = 0;
virtual void SetEntryName(const std::wstring &entryName) = 0;
virtual ushort getNameLength() const = 0;
virtual void setStartSetc(int value) = 0;
virtual int getStartSetc() const = 0;
virtual void Read(Stream stream, CFSVersion ver = CFSVersion::Ver_3) = 0;
virtual void Write(Stream stream) const = 0;
virtual StgColor getStgColor() const = 0;
virtual void setStgColor(StgColor value) = 0;
virtual StgType getStgType() const = 0;
virtual void setStgType(StgType value) = 0;
virtual GUID getStorageCLSID() const = 0;
virtual void setStorageCLSID(GUID value) = 0;
virtual int GetHashCode() const = 0;
};
}

90
Common/cfcpp/sector.cpp Normal file
View File

@ -0,0 +1,90 @@
#include "sector.h"
using namespace CFCPP;
int Sector::MINISECTOR_SIZE = 64;
Sector::Sector(int size, const Stream stream) :
size(size), stream(stream)
{}
Sector::Sector(int size, const std::vector<BYTE>& data) :
size(size), data(data)
{}
Sector::Sector(int size) :
size(size)
{}
bool Sector::IsStreamed()
{
if (stream == nullptr || size == MINISECTOR_SIZE)
return false;
auto fileSize = Length(stream);
return (this->id * size) + size < fileSize;
}
void Sector::ZeroData()
{
std::fill(data.begin(), data.end(), 0);
dirtyFlag = true;
}
void Sector::InitFATData()
{
std::fill(data.begin(), data.end(), 0xff);
dirtyFlag = true;
}
void Sector::ReleaseData()
{
data.clear();
}
void Sector::Dispose(bool disposing)
{
try
{
if (!_disposed)
{
std::lock_guard<std::mutex> lock(lockObject);
if (disposing)
{
// Call from user code...
}
data.clear();
dirtyFlag = false;
id = ENDOFCHAIN;
size = 0;
}
}
catch(...)
{}
_disposed = true;
}
std::vector<BYTE> &Sector::GetData()
{
if (data.empty())
{
data = std::vector<BYTE>(size, 0);
if (IsStreamed())
{
stream->seek(size + id * size, std::ios_base::beg);
stream->read(reinterpret_cast<char*>(data.data()), size);
}
}
return data;
}
int Sector::getSize() const
{
return size;
}

58
Common/cfcpp/sector.h Normal file
View File

@ -0,0 +1,58 @@
#pragma once
#include <memory>
#include <vector>
#include <mutex>
#include "stream.h"
#include "../../DesktopEditor/common/Types.h"
namespace CFCPP
{
enum SectorType
{
Normal,
Mini,
FAT,
DIFAT,
RangeLockSector,
Directory
};
class Sector
{
public:
Sector(int size, const Stream stream);
Sector(int size, const std::vector<BYTE> &data);
Sector(int size);
bool IsStreamed();
void ZeroData();
void InitFATData();
void ReleaseData();
virtual void Dispose(bool disposing=false);
std::vector<BYTE> &GetData();
public:
static int MINISECTOR_SIZE;
const static int FREESECT = 0xFFFFFFFF;
const static int ENDOFCHAIN = 0xFFFFFFFE;
const static int FATSECT = 0xFFFFFFFD;
const static int DIFSECT = 0xFFFFFFFC;
int getSize() const;
SectorType type;
bool dirtyFlag = false;
int id = -1;
private:
int size = 0;
Stream stream;
std::vector<BYTE> data;
std::mutex lockObject;
bool _disposed;//false
};
}

View File

@ -0,0 +1,65 @@
#include "sectorcollection.h"
using namespace CFCPP;
SectorCollection::SectorCollection()
{
}
void SectorCollection::Add(std::shared_ptr<Sector> item)
{
OnVer3SizeLimitReached();
add(item);
}
void SectorCollection::Clear()
{
largeArraySlices.clear();
count = 0;
}
std::shared_ptr<Sector> SectorCollection::operator[](size_t index)
{
size_t globalPos = 0;
for (size_t i = 0; i < largeArraySlices.size(); i ++)
{
size_t sliceSize = largeArraySlices[i].size();
globalPos += sliceSize;
if (globalPos < index)
return largeArraySlices[i][i % sliceSize];
}
return {};
}
void SectorCollection::DoCheckSizeLimitReached()
{
if (OnVer3SizeLimitReached.size() && !sizeLimitReached && (count - 1 > MAX_SECTOR_V4_COUNT_LOCK_RANGE))
{
sizeLimitReached = true;
OnVer3SizeLimitReached();
}
}
int SectorCollection::add(std::shared_ptr<Sector> item)
{
unsigned itemIndex = count / SLICE_SIZE;
if (itemIndex < largeArraySlices.size())
{
largeArraySlices[itemIndex].push_back(item);
count++;
}
else
{
// std::unique_ptr<std::vector<Sector>> ar(new std::vector<Sector>(SLICE_SIZE));
SVector<Sector> ar;
ar.push_back(item);
largeArraySlices.push_back(ar);
count++;
}
return count - 1;
}

View File

@ -0,0 +1,34 @@
#pragma once
#include "sector.h"
#include <list>
#include "svector.h"
#include "event.h"
namespace CFCPP
{
using Ver3SizeLimitReached = std::function<void()>;
class SectorCollection
{
public:
std::vector<SVector<Sector>> largeArraySlices;
SectorCollection();
void Add(std::shared_ptr<Sector> item);
void Clear();
inline int Count()const {return count;}
std::shared_ptr<Sector> operator[](size_t index);
Event<Ver3SizeLimitReached> OnVer3SizeLimitReached;
private:
void DoCheckSizeLimitReached();
int add(std::shared_ptr<Sector> item);
private:
const int MAX_SECTOR_V4_COUNT_LOCK_RANGE = 524287; //0x7FFFFF00 for Version 4
const int SLICE_SIZE = 4096;
bool sizeLimitReached = false;
int count = 0;
};
}

35
Common/cfcpp/slist.h Normal file
View File

@ -0,0 +1,35 @@
#pragma once
#include <memory>
#include <list>
template <class T>
class SList : public std::list<std::shared_ptr<T>>
{
public:
template<class P>
SList<P> cast() const
{
SList<P> res;
for (const auto& spEl : *this)
res.push_back(std::dynamic_pointer_cast<P>(spEl));
return res;
}
std::shared_ptr<T> dequeue()
{
if (this->empty())
return {};
auto spEl = this->front();
this->pop_front();
return spEl;
}
inline void enqueue(std::shared_ptr<T> el)
{
this->push_back(el);
}
};

107
Common/cfcpp/stream.cpp Normal file
View File

@ -0,0 +1,107 @@
#include "stream.h"
#include "../../DesktopEditor/common/File.h"
#include <algorithm>
std::streamsize CFCPP::Length(const CFCPP::Stream& st)
{
if (st.get() == nullptr)
return 0;
auto curPos = st->tell();
st->seek(0, std::ios_base::end);
auto ssize = st->tell();
st->seek(curPos);
return ssize;
}
CFCPP::Stream CFCPP::OpenFileStream(std::wstring filename, bool bRewrite, bool trunc)
{
BYTE* pUtf8 = nullptr;
std::streamsize lLen = 0;
NSFile::CUtf8Converter::GetUtf8StringFromUnicode(filename.c_str(), filename.length(), pUtf8, lLen, false);
std::string utf8filename(pUtf8, pUtf8 + lLen);
delete [] pUtf8;
return OpenFileStream(utf8filename, bRewrite, trunc);
}
CFCPP::Stream CFCPP::OpenFileStream(std::string filename, bool bRewrite, bool trunc)
{
CFCPP::Stream st;
// it's not good, but otherwise file doesn't create or if use ios::app, then the seek for writing will be blocked
if (bRewrite)
std::fstream create(filename, std::ios::app | std::ios::out);
if (trunc && bRewrite)
st.reset(new FStreamWrapper(filename, std::ios::binary | std::ios::in | std::ios::out | std::ios::trunc));
else if (bRewrite)
st.reset(new FStreamWrapper(filename, std::ios::binary | std::ios::in | std::ios::out));
else
st.reset(new FStreamWrapper(filename, std::ios::binary | std::ios::in));
return st;
}
bool CFCPP::IsOpen(const Stream &st)
{
if (std::dynamic_pointer_cast<FStreamWrapper>(st))
return std::static_pointer_cast<FStreamWrapper>(st)->is_open();
return false;
}
std::string CFCPP::CorrectUnixPath(const std::string original)
{
#if !defined(_WIN32) && !defined (_WIN64)
return original;
#else
auto str = original;
std::replace(str.begin(), str.end(), '/', '\\');
return str;
#endif
}
int CFCPP::FileLenght(std::wstring filename)
{
auto stream = OpenFileStream(filename);
auto lenght = Length(stream);
stream->close();
return lenght;
}
ULONG64 CFCPP::FileSimpleHash(std::wstring filename, int len, int offset)
{
auto stream = OpenFileStream(filename);
if (!IsOpen(stream))
return 0;
if (len < 0)
len = Length(stream);
stream->seek(offset);
ULONG64 h = 2166136261;
constexpr int bufLen = 0x2000;
char buffer[bufLen];
while (len > 0)
{
memset(buffer, 0, bufLen);
int readLen = std::min(bufLen, len);
stream->read(buffer, readLen);
int i;
for (i = 0; i < readLen; i++)
h = (h * 16777619) ^ buffer[i];
len -= readLen;
}
return h;
}

51
Common/cfcpp/stream.h Normal file
View File

@ -0,0 +1,51 @@
#pragma once
#include <sstream>
#include <fstream>
#include <memory>
#include "../../DesktopEditor/common/Types.h"
namespace CFCPP
{
class IStream
{
public:
virtual std::streamsize tell() = 0;
virtual std::streamsize seek(std::streamsize offset, std::ios_base::seekdir mode = std::ios::beg) = 0;
virtual std::streamsize read(char* buffer, std::streamsize len) = 0;
virtual void write (const char* buffer, std::streamsize len) = 0;
virtual void flush() = 0;
virtual void close() = 0;
};
using Stream = std::shared_ptr<IStream>;
class FStreamWrapper : public IStream, public std::fstream
{
public:
FStreamWrapper(std::string filename, std::ios_base::openmode openmode) :
std::fstream(filename, openmode) {}
inline std::streamsize tell() override { return std::fstream::tellg(); }
inline std::streamsize seek(std::streamsize offset, std::ios_base::seekdir mode = std::ios::beg) override
{ std::fstream::seekp(offset, mode); std::fstream::seekg(offset, mode); return tell();}
inline std::streamsize read(char* buffer, std::streamsize len) override { std::fstream::read(buffer, len); return tell(); }
inline void write (const char* buffer, std::streamsize len) override { std::fstream::write(buffer, len); }
inline void flush() override { std::fstream::flush(); }
inline void close() override { std::fstream::close(); }
};
std::string CorrectUnixPath(const std::string original);
Stream OpenFileStream(std::wstring filename, bool bRewrite = false, bool trunc = false);
Stream OpenFileStream(std::string filename, bool bRewrite = false, bool trunc = false);
bool IsOpen(const Stream& st);
std::streamsize Length(const Stream& st);
int FileLenght(std::wstring filename);
ULONG64 FileSimpleHash(std::wstring filename, int len = -1, int offset = 0);
}

40
Common/cfcpp/streamrw.cpp Normal file
View File

@ -0,0 +1,40 @@
#include "streamrw.h"
#include <algorithm>
using namespace CFCPP;
StreamRW::StreamRW(CFCPP::Stream stream)
: stream(stream)
{
}
T_LONG64 StreamRW::Seek(T_LONG64 offset)
{
stream->seek(offset, std::ios::beg);
return stream->tell();
}
T_LONG64 CFCPP::StreamRW::Tell()
{
return stream->tell();
}
void StreamRW::ReadArray(char *data, int lenght)
{
stream->read(data, lenght);
}
void StreamRW::ReadArray(BYTE* data, int lenght)
{
stream->read(reinterpret_cast<char*>(data), lenght);
}
void StreamRW::WriteArray(const BYTE *arr, int lenght)
{
stream->write(reinterpret_cast<const char*>(arr), lenght);
}
void StreamRW::WriteArray(const char *arr, int lenght)
{
stream->write(arr, lenght);
}

45
Common/cfcpp/streamrw.h Normal file
View File

@ -0,0 +1,45 @@
#pragma once
#include <fstream>
#include <array>
#include <vector>
#include "stream.h"
namespace CFCPP
{
class StreamRW
{
public:
StreamRW(Stream stream);
T_LONG64 Seek(T_LONG64 offset);
T_LONG64 Tell();
template <class T>
T Read()
{
T value;
char* asByteArr = reinterpret_cast<char*>(&value);
stream->read(asByteArr, sizeof (T));
return value;
}
template<class T>
void Write(T value)
{
char* asByteArr = reinterpret_cast<char*>(&value);
stream->write(asByteArr, sizeof (T));
}
void ReadArray(char* data, int lenght);
void ReadArray(BYTE* data, int lenght);
void WriteArray(const BYTE *arr, int lenght);
void WriteArray(const char *arr, int lenght);
inline void Close(){return;}
private:
Stream stream;
};
}

245
Common/cfcpp/streamview.cpp Normal file
View File

@ -0,0 +1,245 @@
#include "streamview.h"
#include "cfexception.h"
#include <cmath>
using namespace CFCPP;
StreamView::StreamView(const SVector<Sector> &sectorChain, int sectorSize, Stream stream)
: sectorSize(sectorSize), sectorChain(sectorChain), stream(stream)
{
// if (sectorChain == null)
// throw CFException("Sector Chain cannot be null");
auto pos = stream->tell();
if (sectorSize <= 0)
throw CFException("Sector size must be greater than zero");
}
StreamView::StreamView(const SVector<Sector> &sectorChain, int sectorSize, std::streamsize length,
SList<Sector> &availableSectors, Stream stream, bool isFatStream) :
StreamView(sectorChain, sectorSize, stream)
{
this->isFatStream = isFatStream;
adjustLength(length, availableSectors);
}
std::streamsize StreamView::tell()
{
return position;
}
void StreamView::write(const char *buffer, std::streamsize count)
{
int byteWritten = 0;
int roundByteWritten = 0;
int offset = 0;
// Assure length
if ((position + count) > length)
adjustLength((position + count));
if (sectorChain.empty() == false)
{
// First sector
int secOffset = (int)(position / (std::streamsize)sectorSize);
int secShift = (int)(position % sectorSize);
roundByteWritten = (int)std::min(sectorSize - (position % (std::streamsize)sectorSize), count);
if (secOffset < (int)sectorChain.size())
{
char* dst = reinterpret_cast<char*>(sectorChain[secOffset]->GetData().data());
std::copy(buffer+offset, buffer+offset+roundByteWritten, dst + secShift);
sectorChain[secOffset]->dirtyFlag = true;
}
byteWritten += roundByteWritten;
offset += roundByteWritten;
secOffset++;
// Central sectors
while (byteWritten < (count - sectorSize))
{
roundByteWritten = sectorSize;
char* dst = reinterpret_cast<char*>(sectorChain[secOffset]->GetData().data());
std::copy(buffer+offset, buffer+offset+roundByteWritten, dst);
sectorChain[secOffset]->dirtyFlag = true;
byteWritten += roundByteWritten;
offset += roundByteWritten;
secOffset++;
}
// Last sector
roundByteWritten = count - byteWritten;
if (roundByteWritten != 0)
{
char* dst = reinterpret_cast<char*>(sectorChain[secOffset]->GetData().data());
std::copy(buffer+offset, buffer+offset+roundByteWritten, dst);
sectorChain[secOffset]->dirtyFlag = true;
}
position += count;
}
}
void StreamView::close()
{
if (std::dynamic_pointer_cast<std::iostream>(stream) != nullptr)
stream->close();
}
std::streamsize StreamView::read(char *buffer, std::streamsize len)
{
int nRead = 0;
int nToRead = 0;
int offset = 0;
if (sectorChain.empty() == false && sectorChain.size() > 0)
{
// First sector
int secIndex = (int)(position / (std::streamsize)sectorSize);
// Bytes to read count is the min between request count
// and sector border
nToRead = std::min((int)sectorChain[0]->GetData().size() - ((int)position % sectorSize), (int)len);
if (secIndex < (int)sectorChain.size())
{
char* src = reinterpret_cast<char*>(sectorChain[secIndex]->GetData().data() + (int)(position % sectorSize));
char* dst = buffer + offset;
std::copy(src, src + nToRead, dst);
}
nRead += nToRead;
secIndex++;
// Central sectors
while (nRead < (len - sectorSize))
{
nToRead = sectorSize;
char* src = reinterpret_cast<char*>(sectorChain[secIndex]->GetData().data());
char* dst = buffer + offset + nToRead;
std::copy(src, src + nToRead, dst);
nRead += nToRead;
secIndex++;
}
// Last sector
nToRead = len - nRead;
if (nToRead != 0)
{
if (secIndex > (int)sectorChain.size()) throw CFCorruptedFileException("The file is probably corrupted.");
char* src = reinterpret_cast<char*>(sectorChain[secIndex]->GetData().data());
char* dst = buffer + offset + nRead;
std::copy(src, src + nToRead, dst);
nRead += nToRead;
}
position += nRead;
return nRead;
}
else
return 0;
}
std::streamsize StreamView::seek(std::streamsize offset, std::ios_base::seekdir mode)
{
switch (mode)
{
case std::ios_base::beg:
position = offset;
break;
case std::ios_base::cur:
position += offset;
break;
case std::ios_base::end:
default:
position = length - offset;
}
adjustLength(position);
return position;
}
void StreamView::SetLength(std::streamsize value)
{
adjustLength(value);
}
int StreamView::ReadInt32()
{
read(reinterpret_cast<char*>(&buf), 4);
return buf;
}
void StreamView::WriteInt32(int val)
{
buf = ((val & 0xFF) << 24) | ((val & 0x00FF) << 16) | ((val & 0x0000FF) << 8) | (val & 0x000000FF);
write(reinterpret_cast<char*>(&buf), 4);
}
void StreamView::adjustLength(std::streamsize value)
{
SList<Sector> q;
adjustLength(value, q);
}
void StreamView::adjustLength(std::streamsize value, SList<Sector> &availableSectors)
{
this->length = value;
std::streamsize delta = value - ((std::streamsize)this->sectorChain.size() * (std::streamsize)sectorSize);
if (delta > 0)
{
// enlargment required
int nSec = (int)std::ceil(((double)delta / sectorSize));
while (nSec > 0)
{
std::shared_ptr<Sector> t;
if (availableSectors.empty() || availableSectors.size() == 0)
{
t.reset(new Sector(sectorSize, stream));
if (sectorSize == Sector::MINISECTOR_SIZE)
t->type = SectorType::Mini;
}
else
{
t = availableSectors.dequeue();
}
if (isFatStream)
{
t->InitFATData();
}
sectorChain.push_back(t);
nSec--;
}
}
}
std::streamsize StreamView::getLength() const
{
return length;
}

48
Common/cfcpp/streamview.h Normal file
View File

@ -0,0 +1,48 @@
#pragma once
#include <iostream>
#include <list>
#include "sector.h"
#include "slist.h"
#include "svector.h"
namespace CFCPP
{
class StreamView : public IStream
{
public:
StreamView(const SVector<Sector> &sectorChain, int sectorSize, Stream stream);
StreamView(const SVector<Sector> &sectorChain, int sectorSize, std::streamsize length,
SList<Sector> &availableSectors, Stream stream, bool isFatStream = false);
std::streamsize tell() override;
std::streamsize seek(std::streamsize offset, std::ios_base::seekdir mode = std::ios::beg) override;
std::streamsize read(char *buffer, std::streamsize count) override;
void write(const char *buffer, std::streamsize count) override;
void flush() override {}
void close() override;
void SetLength(std::streamsize value);
std::streamsize getLength() const;
inline SVector<Sector>& BaseSectorChain() {return sectorChain;}
int ReadInt32();
void WriteInt32(int val);
private:
void adjustLength(std::streamsize value);
void adjustLength(std::streamsize value, SList<Sector> &availableSectors);
private:
int sectorSize;
std::streamsize length;
SVector<Sector> sectorChain;
bool isFatStream = false;
int buf = 0;
public:
Stream stream;
SList<Sector> freeSectors;
std::streamsize position = 0;
};
}

24
Common/cfcpp/svector.h Normal file
View File

@ -0,0 +1,24 @@
#pragma once
#include <memory>
#include <vector>
template <class T>
class SVector : public std::vector<std::shared_ptr<T>>
{
public:
SVector(size_t res = 0) : std::vector<std::shared_ptr<T>>(res)
{
}
template<class P>
SVector<P> cast() const
{
auto sz = this->size();
SVector<P> res(sz);
for (size_t i = 0; i < sz; i++)
res[i] = std::dynamic_pointer_cast<P> ((*this)[i]);
return res;
}
};

73
Common/cfcpp/test/.gitignore vendored Normal file
View File

@ -0,0 +1,73 @@
# This file is used to ignore files which are generated
# ----------------------------------------------------------------------------
*~
*.autosave
*.a
*.core
*.moc
*.o
*.obj
*.orig
*.rej
*.so
*.so.*
*_pch.h.cpp
*_resource.rc
*.qm
.#*
*.*#
core
!core/
tags
.DS_Store
.directory
*.debug
Makefile*
*.prl
*.app
moc_*.cpp
ui_*.h
qrc_*.cpp
Thumbs.db
*.res
*.rc
/.qmake.cache
/.qmake.stash
# qtcreator generated files
*.pro.user*
# xemacs temporary files
*.flc
# Vim temporary files
.*.swp
# Visual Studio generated files
*.ib_pdb_index
*.idb
*.ilk
*.pdb
*.sln
*.suo
*.vcproj
*vcproj.*.*.user
*.ncb
*.sdf
*.opensdf
*.vcxproj
*vcxproj.*
# MinGW generated files
*.Debug
*.Release
# Python byte code
*.pyc
# Binaries
# --------
*.dll
*.exe

Binary file not shown.

View File

@ -0,0 +1,41 @@
isEmpty(GOOGLETEST_DIR):GOOGLETEST_DIR=$$(GOOGLETEST_DIR)
isEmpty(GOOGLETEST_DIR) {
GOOGLETEST_DIR = $$PWD/../../3dParty/v8_89/v8/third_party/googletest/src
!isEmpty(GOOGLETEST_DIR) {
warning("Using googletest src dir specified at Qt Creator wizard")
message("set GOOGLETEST_DIR as environment variable or qmake variable to get rid of this message")
}
}
!isEmpty(GOOGLETEST_DIR): {
GTEST_SRCDIR = $$GOOGLETEST_DIR/googletest
GMOCK_SRCDIR = $$GOOGLETEST_DIR/googlemock
} else: unix {
exists(/usr/src/gtest):GTEST_SRCDIR=/usr/src/gtest
exists(/usr/src/gmock):GMOCK_SRCDIR=/usr/src/gmock
!isEmpty(GTEST_SRCDIR): message("Using gtest from system")
}
requires(exists($$GTEST_SRCDIR):exists($$GMOCK_SRCDIR))
DEFINES += \
GTEST_LANG_CXX11
!isEmpty(GTEST_SRCDIR) {
INCLUDEPATH *= \
$$GTEST_SRCDIR \
$$GTEST_SRCDIR/include
SOURCES += \
$$GTEST_SRCDIR/src/gtest-all.cc
}
!isEmpty(GMOCK_SRCDIR) {
INCLUDEPATH *= \
$$GMOCK_SRCDIR \
$$GMOCK_SRCDIR/include
SOURCES += \
$$GMOCK_SRCDIR/src/gmock-all.cc
}

View File

@ -0,0 +1,16 @@
#include "tst_streamrw.h"
#include "tst_header.h"
#include "tst_directoryentry.h"
#include "tst_compondfile.h"
using namespace CFCPP;
using namespace std;
int main(int argc, char *argv[])
{
#ifdef _LINUX
system("pwd");
#endif
::testing::InitGoogleTest(&argc, argv);
return RUN_ALL_TESTS();
}

View File

@ -0,0 +1,25 @@
include(gtest_dependency.pri)
TARGET = test
TEMPLATE = app
CONFIG += console c++11
CONFIG -= app_bundle
CONFIG += thread
CONFIG -= qt
CORE_ROOT_DIR = $$PWD/../../..
PWD_ROOT_DIR = $$PWD
include(../../base.pri)
ADD_DEPENDENCY(UnicodeConverter, kernel, cfcpp)
INCLUDEPATH += $$PWD/../
HEADERS += \
tst_compondfile.h \
tst_directoryentry.h \
tst_header.h \
tst_streamrw.h
SOURCES += \
main.cpp

View File

@ -0,0 +1,37 @@
#pragma once
#include <gtest/gtest.h>
#include <gmock/gmock-matchers.h>
#include "compoundfile.h"
#include "../../DesktopEditor/common/File.h"
using namespace testing;
using namespace std;
using namespace CFCPP;
struct CompoundFileTest : testing::Test
{
wstring filename = L"../../../data/ex.ppt";
CompoundFile cf;
CompoundFileTest() : cf(filename)
{
}
};
TEST_F(CompoundFileTest, test_compoundfile_read)
{
EXPECT_TRUE(cf.HasSourceStream());
}
TEST_F(CompoundFileTest, test_compoundfile_write)
{
wstring other_filename = L"../../../data/ex2.ppt";
NSFile::CFileBinary::Remove(other_filename);
cf.Save(other_filename);
EXPECT_EQ(FileLenght(filename), FileLenght(other_filename));
EXPECT_EQ(FileSimpleHash(filename), FileSimpleHash(other_filename));
}

View File

@ -0,0 +1,76 @@
#pragma once
#include <gtest/gtest.h>
#include <gmock/gmock-matchers.h>
#include "directoryentry.h"
#include "../../DesktopEditor/common/File.h"
using namespace testing;
using namespace std;
using namespace CFCPP;
struct DirEntryTest : testing::Test
{
string filename;
Stream stream;
DirEntryTest() :
filename("../../../data/ex.ppt"),
stream(OpenFileStream(filename))
{
}
};
void test_dirEntry_read(const DirectoryEntry& de)
{
EXPECT_EQ(de.GetEntryName(), L"Root Entry");
EXPECT_EQ(de.getNameLength(), 22);
EXPECT_EQ(de.getStgType(), StgRoot);
EXPECT_EQ(de.leftSibling, 0xFFFFFFFF);
EXPECT_EQ(de.rightSibling, 0xFFFFFFFF);
EXPECT_EQ(de.child, 1);
GUID storageCLSID;
storageCLSID.Data1 = 0x64818D10;
storageCLSID.Data2 = 0x4F9B;
storageCLSID.Data3 = 0x11CF;
storageCLSID.Data4 = 0xE829B900AA00EA86;
// EXPECT_EQ(de.getStorageCLSID(), storageCLSID);
EXPECT_EQ(de.stateBits, 0);
// todo
//EXPECT_EQ(de.creationDate, 0);
// EXPECT_EQ(de.modifyDate, 0xC0F1C03A18A1D801);
EXPECT_EQ(de.startSetc, 3);
EXPECT_EQ(de.size, 5632);
}
TEST_F(DirEntryTest, test_directoryentry_read)
{
DirectoryEntry de(L"", StgInvalid, {});
stream->seek(0x400, std::ios::beg);
de.Read(stream);
EXPECT_EQ(stream->tell(), 0x480);
test_dirEntry_read(de);
}
TEST_F(DirEntryTest, test_directoryentry_write)
{
DirectoryEntry de(L"", StgInvalid, {});
stream->seek(0x400, std::ios::beg);
de.Read(stream);
std::string other_filename("../../../data/direntry.bin");
stream = OpenFileStream(other_filename, true);
de.Write(stream);
EXPECT_EQ(stream->tell(), 0x80);
stream->seek(0, std::ios::beg);
DirectoryEntry other(L"", StgInvalid, {});
other.Read(stream);
test_dirEntry_read(other);
remove(other_filename.c_str());
}

View File

@ -0,0 +1,110 @@
#pragma once
#include <gtest/gtest.h>
#include <gmock/gmock-matchers.h>
#include "header.h"
#include "../../DesktopEditor/common/File.h"
#include "streamrw.h"
using namespace testing;
using namespace std;
using namespace CFCPP;
struct HeaderTest : testing::Test
{
string filename;
Stream stream;
Header hd;
HeaderTest() :
filename("../../../data/ex.ppt"),
stream(OpenFileStream(filename, false))
{
}
};
void test_header_state(const Header& hd)
{
ASSERT_THAT(hd.headerSignature, ElementsAre(0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1));
EXPECT_EQ(hd.minorVersion, 0x003E);
EXPECT_EQ(hd.majorVersion, 0x0003);
EXPECT_EQ(hd.byteOrder, 0xFFFE);
EXPECT_EQ(hd.sectorShift, 0x0009);
EXPECT_EQ(hd.miniSectorShift, 0x0006);
EXPECT_EQ(hd.directorySectorsNumber, 0);
EXPECT_EQ(hd.fatSectorsNumber, 1);
EXPECT_EQ(hd.firstDirectorySectorID, 1);
EXPECT_EQ(hd.minSizeStandardStream, 0x1000);
EXPECT_EQ(hd.firstMiniFATSectorID, 2);
EXPECT_EQ(hd.miniFATSectorsNumber, 1);
EXPECT_EQ(hd.firstDIFATSectorID, -2);
EXPECT_EQ(hd.difatSectorsNumber, 0);
int difat[109];
memset(reinterpret_cast<char*>(difat), 0xFF, sizeof(difat));
difat[0] = 0;
ASSERT_FALSE(memcmp(hd.difat, difat, sizeof(difat)));
}
TEST_F(HeaderTest, test_header_open)
{
EXPECT_TRUE(IsOpen(stream));
}
TEST_F(HeaderTest, test_header_read)
{
hd.Read(stream);
test_header_state(hd);
}
TEST_F(HeaderTest, test_header_write)
{
hd.Read(stream);
std::string other_filename("../../../data/header.bin");
stream = OpenFileStream(other_filename, true);
hd.Write(stream);
Header other;
stream->seek(0, std::ios::beg);
other.Read(stream);
test_header_state(other);
remove(other_filename.c_str());
}
TEST_F(HeaderTest, test_header_seek)
{
hd.Read(stream);
std::string other_filename("../../../data/sheader.bin");
remove(other_filename.c_str());
stream = OpenFileStream(other_filename, true);
std::vector<char> zeroArray(512, 0);
stream->write(zeroArray.data(), zeroArray.size());
zeroArray.clear();
EXPECT_EQ(Length(stream), 512);
stream->seek(4);
StreamRW rw(stream);
auto pos = rw.Tell();
EXPECT_EQ(pos, 4);
hd.Write(stream);
EXPECT_EQ(Length(stream), 512+4);
Header other;
stream->seek(0);
int zeroValue = StreamRW(stream).Read<int>();
EXPECT_EQ(zeroValue, 0);
other.Read(stream);
test_header_state(other);
}

View File

@ -0,0 +1,78 @@
#pragma once
#include <gtest/gtest.h>
#include <gmock/gmock-matchers.h>
#include "streamrw.h"
#include "../../DesktopEditor/common/File.h"
#include <array>
using namespace testing;
using namespace std;
using namespace CFCPP;
struct StreamRWTest : testing::Test
{
string filename;
Stream stream;
shared_ptr<StreamRW> rw;
const char symbol = 'a';
const int integer = 13;
StreamRWTest() :
filename("../../../data/types.bin"),
stream(OpenFileStream(filename, true)),
rw(new StreamRW(stream))
{
}
~StreamRWTest()
{
}
};
TEST_F(StreamRWTest, test_stream_open)
{
EXPECT_TRUE(IsOpen(stream));
}
TEST_F(StreamRWTest, test_stream_write)
{
rw->Seek(0);
rw->Write(symbol);
rw->Write(integer);
stream->flush();
EXPECT_EQ((int)Length(stream), 5);
}
TEST_F(StreamRWTest, test_stream_read)
{
EXPECT_EQ(rw->Seek(0), 0);
EXPECT_EQ(rw->Read<char>(), symbol);
EXPECT_EQ(rw->Read<int>(), integer);
remove(filename.c_str());
}
TEST_F(StreamRWTest, test_stream_rw_array)
{
int sarr[3] = {99, 0, -3};
int darr[3] = {-1,-1,-1};
rw->WriteArray(reinterpret_cast<char*>(sarr), sizeof (sarr));
rw->Seek(0);
rw->ReadArray(reinterpret_cast<char*>(darr), sizeof (darr));
EXPECT_EQ(sarr[2], darr[2]);
remove(filename.c_str());
}
TEST_F(StreamRWTest, test_stream_seek)
{
stream->write(std::array<char, 16>().data(), 16);
stream->seek(2);
int dataVal(1234567890);
stream->write(reinterpret_cast<char*>(&dataVal), 4);
EXPECT_EQ(Length(stream), 16);
remove(filename.c_str());
}