diff --git a/Common/cppcf/cfcpp.pro b/Common/cppcf/cfcpp.pro index 15c44146e9..3ab4d5054f 100644 --- a/Common/cppcf/cfcpp.pro +++ b/Common/cppcf/cfcpp.pro @@ -36,6 +36,7 @@ HEADERS += \ cfstream.h \ compoundfile.h \ directoryentry.h \ + event.h \ guid.h \ header.h \ idirectoryentry.h \ diff --git a/Common/cppcf/cfitem.h b/Common/cppcf/cfitem.h index aa33b40daf..cbcf2edcf0 100644 --- a/Common/cppcf/cfitem.h +++ b/Common/cppcf/cfitem.h @@ -37,12 +37,12 @@ public: protected: inline CFItem() {}; - inline CFItem(const std::shared_ptr compoundFile) : compoundFile(compoundFile) {} - inline std::shared_ptr getCompoundFile() {return compoundFile;} + inline CFItem(std::shared_ptr compoundFile) : compoundFile(compoundFile) {} + inline std::shared_ptr getCompoundFile() {return compoundFile;} void CheckDisposed() const; protected: - std::shared_ptr compoundFile; + std::shared_ptr compoundFile; }; using PCFItem = std::shared_ptr; diff --git a/Common/cppcf/cfstorage.cpp b/Common/cppcf/cfstorage.cpp index a95b917939..1ad1b495ea 100644 --- a/Common/cppcf/cfstorage.cpp +++ b/Common/cppcf/cfstorage.cpp @@ -7,7 +7,7 @@ using namespace CFCPP; using RedBlackTree::RBTree; -CFStorage::CFStorage(std::shared_ptr compFile, std::shared_ptr dirEntry) : +CFStorage::CFStorage(std::shared_ptr compFile, std::shared_ptr dirEntry) : CFItem(compFile) { if (dirEntry == nullptr || dirEntry->getSid() < 0) @@ -62,7 +62,7 @@ std::shared_ptr CFStorage::AddStream(const std::wstring& streamName) throw new CFDuplicatedItemException(L"An entry with name '" + streamName + L"' is already present in storage '" + Name() + L"' "); } - return std::shared_ptr (new CFStream(compoundFile, dirEntry)); + return std::shared_ptr(new CFStream(compoundFile, dirEntry)); } std::shared_ptr CFStorage::GetStream(const std::wstring& streamName) diff --git a/Common/cppcf/cfstorage.h b/Common/cppcf/cfstorage.h index 0b4f481591..f90e6eb463 100644 --- a/Common/cppcf/cfstorage.h +++ b/Common/cppcf/cfstorage.h @@ -12,7 +12,7 @@ namespace CFCPP class CFStorage : public CFItem { public: - CFStorage(std::shared_ptr compFile, std::shared_ptr dirEntry); + CFStorage(std::shared_ptr compFile, std::shared_ptr dirEntry); std::shared_ptr getChildren(); diff --git a/Common/cppcf/compoundfile.cpp b/Common/cppcf/compoundfile.cpp index ebc31369a3..4f1817b378 100644 --- a/Common/cppcf/compoundfile.cpp +++ b/Common/cppcf/compoundfile.cpp @@ -33,8 +33,11 @@ CompoundFile::CompoundFile(CFSVersion cfsVersion, CFSConfiguration configFlags) sectorRecycle = configFlags & CFSConfiguration::SectorRecycle; bool eraseFreeSectors = configFlags & CFSConfiguration::EraseFreeSectors; - // if (cfsVersion == CFSVersion::Ver_4) - // sectors.OnVer3SizeLimitReached += new Ver3SizeLimitReached(OnSizeLimitReached); + if (cfsVersion == CFSVersion::Ver_4) + { + Ver3SizeLimitReached action = std::bind(&CompoundFile::OnSizeLimitReached, this); + sectors.OnVer3SizeLimitReached += action; + } DIFAT_SECTOR_FAT_ENTRIES_COUNT = (GetSectorSize() / 4) - 1; @@ -70,7 +73,7 @@ CompoundFile::CompoundFile(Stream stream) void CompoundFile::OnSizeLimitReached() { - std::shared_ptr rangeLockSector( new Sector(GetSectorSize(), sourceStream)); + std::shared_ptr rangeLockSector(new Sector(GetSectorSize(), sourceStream)); rangeLockSector->type = SectorType::RangeLockSector; @@ -79,10 +82,6 @@ void CompoundFile::OnSizeLimitReached() sectors.Add(rangeLockSector); } -void CompoundFile::Commit() -{ - Commit(false); -} void CompoundFile::Commit(bool releaseMemory) { @@ -91,12 +90,7 @@ void CompoundFile::Commit(bool releaseMemory) if (updateMode != CFSUpdateMode::Update) throw new CFInvalidOperation("Cannot commit data in Read-Only update mode"); -#if !defined(FLAT_WRITE) - int sId = -1; - int sCount = 0; - int bufOffset = 0; -#endif int sSize = GetSectorSize(); if (header.majorVersion != (ushort)CFSVersion::Ver_3) @@ -115,8 +109,6 @@ void CompoundFile::Commit(bool releaseMemory) for (int i = 0; i < (int)sectors.largeArraySlices.size(); i++) { -#if defined (FLAT_WRITE) - //Note: //Here sectors should not be loaded dynamically because //if they are null it means that no change has involved them; @@ -146,97 +138,14 @@ void CompoundFile::Commit(bool releaseMemory) s.reset(); sectors[i].reset(); } - - - -#else - - - std::shared_ptr s = sectors[i]; - - - if (s.get() != nullptr && s->dirtyFlag && flushingQueue.size() < (int)(buffer.Length / sSize)) - { - //First of a block of contiguous sectors, mark id, start enqueuing - - if (gap) - { - sId = s->id; - gap = false; - } - - flushingQueue.push(s); - - - } - else - { - //Found a gap, stop enqueuing, flush a write operation - - gap = true; - sCount = flushingQueue.size(); - - if (sCount == 0) continue; - - bufOffset = 0; - while (flushingQueue.Count > 0) - { - Sector r = flushingQueue.Dequeue(); - Buffer.BlockCopy(r.GetData(), 0, buffer, bufOffset, sSize); - r.DirtyFlag = false; - - if (releaseMemory) - { - r.ReleaseData(); - } - - bufOffset += sSize; - } - - sourceStream.Seek(((long)sSize + (long)sId * (long)sSize), SeekOrigin.Begin); - sourceStream.Write(buffer, 0, sCount * sSize); - - - - //Console.WriteLine("W - " + (int)(sCount * sSize )); - - } -#endif } -#if !FLAT_WRITE - sCount = flushingQueue.Count; - bufOffset = 0; - - while (flushingQueue.Count > 0) - { - Sector r = flushingQueue.Dequeue(); - Buffer.BlockCopy(r.GetData(), 0, buffer, bufOffset, sSize); - r.DirtyFlag = false; - - if (releaseMemory) - { - r.ReleaseData(); - r = null; - } - - bufOffset += sSize; - } - - if (sCount != 0) - { - sourceStream.Seek((long)sSize + (long)sId * (long)sSize, SeekOrigin.Begin); - sourceStream.Write(buffer, 0, sCount * sSize); - //Console.WriteLine("W - " + (int)(sCount * sSize)); - } - -#endif // Seek to beginning position and save header (first 512 or 4096 bytes) sourceStream->seekg(0, std::ios::beg); header.Write(sourceStream); - sourceStream-> SetLength((long)(sectors.Count + 1) * sSize); + // sourceStream-> SetLength((long)(sectors.Count + 1) * sSize); sourceStream->flush(); if (releaseMemory) @@ -307,10 +216,12 @@ void CompoundFile::Load(Stream stream) } catch (...) { - if (stream.get() != nullptr && closeStream) - // stream->clear(); // close + if (std::dynamic_pointer_cast(stream) != nullptr && stream.get() != nullptr && closeStream) + { + std::static_pointer_cast(stream)->close(); + } - throw; + throw; } } @@ -620,8 +531,6 @@ SVector CompoundFile::GetMiniSectorChain(int secID) StreamView miniStreamView(miniStream, GetSectorSize(), rootStorage->size(), zeroQueue, sourceStream); - BinaryReader miniFATReader = new BinaryReader(miniFATView); - nextSecID = secID; std::unordered_set processedSectors; @@ -642,7 +551,7 @@ SVector CompoundFile::GetMiniSectorChain(int secID) result.push_back(ms); miniFATView.Seek(nextSecID * 4, std::ios::beg); - int next = miniFATReader.ReadInt32(); + int next = miniFATView.ReadInt32(); nextSecID = next; EnsureUniqueSectorIndex(nextSecID, processedSectors); @@ -842,7 +751,7 @@ bool CompoundFile::ValidateSibling(int sid) { if (this->validationExceptionEnabled) { - //this.Close(); + //Close(); throw new CFCorruptedFileException("A Directory Entry references the non-existent sid number " + std::to_string(sid)); } else @@ -854,7 +763,7 @@ bool CompoundFile::ValidateSibling(int sid) { if (this->validationExceptionEnabled) { - //this.Close(); + //Close(); throw new CFCorruptedFileException("A Directory Entry has a valid reference to an Invalid Storage Type directory [" + std::to_string(sid) + "]"); } else @@ -867,7 +776,7 @@ bool CompoundFile::ValidateSibling(int sid) if (this->validationExceptionEnabled) { - //this.Close(); + //Close(); throw new CFCorruptedFileException("A Directory Entry has an invalid Storage Type"); } else @@ -1722,11 +1631,8 @@ std::vector CompoundFile::GetData(CFStream *cFStream) zeroQueue, sourceStream); - BinaryReader br = new BinaryReader(miniView); - - result = br.ReadBytes((int)de->getSize()); - br.Close(); - + result.reserve(de->getSize()); + miniView.Read(reinterpret_cast(result.data()), 0, result.size()); } else { @@ -1795,6 +1701,65 @@ int CompoundFile::ReadData(CFStream *cFStream, std::streamsize position, std::ve return result; } +std::vector CompoundFile::GetDataBySID(int sid) +{ + if (_disposed) + throw new CFDisposedException("Compound File closed: cannot access data"); + if (sid < 0) + return {}; + std::vector result; + try + { + std::shared_ptr de = directoryEntries[sid]; + SList zeroQueue; + if (de->getSize() < header.minSizeStandardStream) + { + StreamView miniView(GetSectorChain(de->getStartSetc(), SectorType::Mini), Sector::MINISECTOR_SIZE, de->getSize(), zeroQueue, sourceStream); + result.reserve(de->getSize()); + miniView.Read(reinterpret_cast(result.data()),0 , result.size()); + } + else + { + StreamView sView(GetSectorChain(de->getStartSetc(), SectorType::Normal), GetSectorSize(), de->getSize(), zeroQueue, sourceStream); + result.reserve(de->getSize()); + sView.Read(reinterpret_cast(result.data()),0 , result.size()); + } + } + catch (...) + { + throw CFException("Cannot get data for SID"); + } + return result; +} + +GUID CompoundFile::getGuidBySID(int sid) +{ + if (_disposed) + throw new CFDisposedException("Compound File closed: cannot access data"); + if (sid < 0) + throw new CFException("Invalid SID"); + std::shared_ptr de = directoryEntries[sid]; + return de->getStorageCLSID(); +} + +GUID CompoundFile::getGuidForStream(int sid) +{ + if (_disposed) + throw new CFDisposedException("Compound File closed: cannot access data"); + if (sid < 0) + throw new CFException("Invalid SID"); + GUID g; + //find first storage containing a non-zero CLSID before SID in directory structure + for (int i = sid - 1; i >= 0; i--) + { + if (directoryEntries[i]->getStorageCLSID() != g && directoryEntries[i]->getStgType() == StgType::StgStorage) + { + return directoryEntries[i]->getStorageCLSID(); + } + } + return g; +} + void CompoundFile::WriteData(PCFItem cfItem, const std::vector &buffer, std::streamsize position, int offset, int count) { if (cfItem->dirEntry == nullptr) @@ -1837,6 +1802,42 @@ int CompoundFile::GetSectorSize() return 2 << (header.sectorShift - 1); } +void CompoundFile::Dispose(bool disposing) +{ + try + { + if (!_disposed) + { + std::lock_guard lock(lockObject); + { + if (disposing) + { + // Call from user code... + sectors.Clear(); + + rootStorage.reset(); // Some problem releasing resources... + header = Header(); + directoryEntries.clear(); + fileName.clear(); + } + + if (std::dynamic_pointer_cast(sourceStream) != nullptr && + closeStream && !(configuration & CFSConfiguration::LeaveOpen)) + { + std::static_pointer_cast(sourceStream)->close(); + } + } + } + //finally + _disposed = true; + } + catch(...) + { + //finally + _disposed = true; + } +} + void CompoundFile::CheckForLockSector() { //If transaction lock has been added and not yet allocated in the FAT... diff --git a/Common/cppcf/compoundfile.h b/Common/cppcf/compoundfile.h index c01d6fc17d..407705a3f4 100644 --- a/Common/cppcf/compoundfile.h +++ b/Common/cppcf/compoundfile.h @@ -9,8 +9,7 @@ #include "idirectoryentry.h" #include "cfstream.h" #include "cfstorage.h" - -#define FLAT_WRITE +#include namespace CFCPP { @@ -46,9 +45,7 @@ public: CompoundFile(const std::wstring &fileName); CompoundFile(Stream stream); CompoundFile(); - void OnSizeLimitReached(); - void Commit(); - void Commit(bool releaseMemory); + void Commit(bool releaseMemory = false); inline bool HasSourceStream() {return sourceStream != nullptr;} void Close(); @@ -71,11 +68,17 @@ public: int ReadData(CFStream* cFStream, std::streamsize position, std::vector& buffer, int count); int ReadData(CFStream* cFStream, std::streamsize position, std::vector& buffer, int offset, int count); + std::vector GetDataBySID(int sid); + GUID getGuidBySID(int sid); + GUID getGuidForStream(int sid); + 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); @@ -147,10 +150,7 @@ private: CFSUpdateMode updateMode; SVector directoryEntries; std::list levelSIDs; + std::mutex lockObject; -#if !defined(FLAT_WRITE) - std::array buffer; - std::queue > flushingQueue; -#endif }; } diff --git a/Common/cppcf/event.h b/Common/cppcf/event.h new file mode 100644 index 0000000000..302f5f9ee2 --- /dev/null +++ b/Common/cppcf/event.h @@ -0,0 +1,31 @@ +#pragma once +#include +#include + +template +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 {events.size();} + +protected: + std::vector events; +}; diff --git a/Common/cppcf/guid.h b/Common/cppcf/guid.h index b017a2926d..1a13697ea2 100644 --- a/Common/cppcf/guid.h +++ b/Common/cppcf/guid.h @@ -24,5 +24,14 @@ struct GUID return *this; } + bool operator!=(const GUID& oth) + { + for (int i = 0; i < 8; i++) + if (Data4[i] != oth.Data4[i]) + return true; + + return Data1 != oth.Data1 || Data2 != oth.Data2 || Data3 != oth.Data3; + } + GUID (){} }; diff --git a/Common/cppcf/sectorcollection.cpp b/Common/cppcf/sectorcollection.cpp index 555e1b10d3..17be4d9959 100644 --- a/Common/cppcf/sectorcollection.cpp +++ b/Common/cppcf/sectorcollection.cpp @@ -9,8 +9,7 @@ SectorCollection::SectorCollection() void SectorCollection::Add(std::shared_ptr item) { - if (DoCheckSizeLimitReached() == false) - return; + OnVer3SizeLimitReached(); add(item); } @@ -35,14 +34,13 @@ std::shared_ptr SectorCollection::operator[](size_t index) return {}; } -bool SectorCollection::DoCheckSizeLimitReached() +void SectorCollection::DoCheckSizeLimitReached() { - if (!sizeLimitReached && (count - 1 > MAX_SECTOR_V4_COUNT_LOCK_RANGE)) + if (OnVer3SizeLimitReached.size() && !sizeLimitReached && (count - 1 > MAX_SECTOR_V4_COUNT_LOCK_RANGE)) { sizeLimitReached = true; - return false; + OnVer3SizeLimitReached(); } - return true; } int SectorCollection::add(std::shared_ptr item) diff --git a/Common/cppcf/sectorcollection.h b/Common/cppcf/sectorcollection.h index 9c52fd93ed..864d49eb54 100644 --- a/Common/cppcf/sectorcollection.h +++ b/Common/cppcf/sectorcollection.h @@ -3,9 +3,11 @@ #include "sector.h" #include #include "svector.h" +#include "event.h" namespace CFCPP { +using Ver3SizeLimitReached = std::function; class SectorCollection { @@ -16,9 +18,10 @@ public: void Clear(); inline int Count()const {return count;} std::shared_ptr operator[](size_t index); + Event OnVer3SizeLimitReached; private: - bool DoCheckSizeLimitReached(); + void DoCheckSizeLimitReached(); int add(std::shared_ptr item); private: const int MAX_SECTOR_V4_COUNT_LOCK_RANGE = 524287; //0x7FFFFF00 for Version 4 diff --git a/Common/cppcf/streamview.cpp b/Common/cppcf/streamview.cpp index 5c777d6795..e0674d6bf8 100644 --- a/Common/cppcf/streamview.cpp +++ b/Common/cppcf/streamview.cpp @@ -76,9 +76,6 @@ void StreamView::Write(const char *buffer, std::streamsize offset, std::streamsi std::copy(buffer+offset, buffer+offset+roundByteWritten, dst); sectorChain[secOffset]->dirtyFlag = true; - - offset += roundByteWritten; - byteWritten += roundByteWritten; } position += count; @@ -174,14 +171,14 @@ void StreamView::SetLength(std::streamsize value) int StreamView::ReadInt32() { - std::iostream::read(reinterpret_cast(&buf), 4); + Read(reinterpret_cast(&buf), 0, 4); return ((buf & 0xFF) << 24) | ((buf & 0x00FF) << 16) | ((buf & 0x0000FF) << 8) | (buf & 0x000000FF); } void StreamView::WriteInt32(int val) { buf = ((val & 0xFF) << 24) | ((val & 0x00FF) << 16) | ((val & 0x0000FF) << 8) | (val & 0x000000FF); - std::iostream::write(reinterpret_cast(&buf), 4); + Write(reinterpret_cast(&buf), 0, 4); } void StreamView::adjustLength(std::streamsize value) diff --git a/Common/cppcf/streamview.h b/Common/cppcf/streamview.h index 04cc496da0..78938e600f 100644 --- a/Common/cppcf/streamview.h +++ b/Common/cppcf/streamview.h @@ -8,7 +8,7 @@ namespace CFCPP { -class StreamView : public std::iostream +class StreamView { public: StreamView(const SVector §orChain, int sectorSize, Stream stream);