#include "compoundfile.h" #include "cfexception.h" using namespace CFCPP; CompoundFile::CompoundFile() : CompoundFile(CFSVersion::Ver_3, CFSConfiguration::Default) {} CompoundFile::CompoundFile(const std::wstring &fileName, CFSUpdateMode updateMode, CFSConfiguration configParameters) { configuration = configParameters; validationExceptionEnabled = !configParameters.HasFlag(CFSConfiguration.NoValidationException); sectorRecycle = configParameters.HasFlag(CFSConfiguration.SectorRecycle); updateMode = updateMode; eraseFreeSectors = configParameters.HasFlag(CFSConfiguration.EraseFreeSectors); LoadFile(fileName); DIFAT_SECTOR_FAT_ENTRIES_COUNT = (GetSectorSize() / 4) - 1; FAT_SECTOR_ENTRIES_COUNT = (GetSectorSize() / 4); } CompoundFile::CompoundFile(CFSVersion cfsVersion, CFSConfiguration configFlags) : header((ushort)cfsVersion) { configuration = configFlags; sectorRecycle = configFlags & CFSConfiguration::SectorRecycle; bool eraseFreeSectors = configFlags & CFSConfiguration::EraseFreeSectors; // if (cfsVersion == CFSVersion::Ver_4) // sectors.OnVer3SizeLimitReached += new Ver3SizeLimitReached(OnSizeLimitReached); DIFAT_SECTOR_FAT_ENTRIES_COUNT = (GetSectorSize() / 4) - 1; FAT_SECTOR_ENTRIES_COUNT = (GetSectorSize() / 4); //Root -- IDirectoryEntry rootDir = DirectoryEntry.New("Root Entry", StgType.StgRoot, directoryEntries); rootDir.StgColor = StgColor.Black; //InsertNewDirectoryEntry(rootDir); rootStorage = new CFStorage(this, rootDir); } CompoundFile::CompoundFile(const std::wstring &fileName) { sectorRecycle = false; updateMode = CFSUpdateMode::ReadOnly; eraseFreeSectors = false; LoadFile(fileName); DIFAT_SECTOR_FAT_ENTRIES_COUNT = (GetSectorSize() / 4) - 1; FAT_SECTOR_ENTRIES_COUNT = (GetSectorSize() / 4); } CompoundFile::CompoundFile(Stream stream) { LoadStream(stream); DIFAT_SECTOR_FAT_ENTRIES_COUNT = (GetSectorSize() / 4) - 1; FAT_SECTOR_ENTRIES_COUNT = (GetSectorSize() / 4); } void CompoundFile::OnSizeLimitReached() { Sector rangeLockSector(GetSectorSize(), sourceStream); rangeLockSector.type = SectorType::RangeLockSector; _transactionLockAdded = true; _lockSectorId = rangeLockSector.Id(); sectors.Add(rangeLockSector); } void CompoundFile::Commit() { Commit(false); } void CompoundFile::Commit(bool releaseMemory) { if (_disposed) throw new CFDisposedException("Compound File closed: cannot commit data"); 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) CheckForLockSector(); sourceStream.Seek(0, SeekOrigin.Begin); sourceStream.Write((byte[])Array.CreateInstance(typeof(byte), GetSectorSize()), 0, sSize); CommitDirectory(); bool gap = true; for (int i = 0; i < sectors.largeArraySlices.size(); i++) { #if FLAT_WRITE //Note: //Here sectors should not be loaded dynamically because //if they are null it means that no change has involved them; Sector s = (Sector)sectors[i]; if (s != null && s.DirtyFlag) { if (gap) sourceStream.Seek((long)((long)(sSize) + (long)i * (long)sSize), SeekOrigin.Begin); sourceStream.Write(s.GetData(), 0, sSize); sourceStream.Flush(); s.DirtyFlag = false; gap = false; } else { gap = true; } if (s != null && releaseMemory) { s.ReleaseData(); s = null; sectors[i] = null; } #else Sector s = sectors[i] as Sector; if (s != null && s.DirtyFlag && flushingQueue.Count < (int)(buffer.Length / sSize)) { //First of a block of contiguous sectors, mark id, start enqueuing if (gap) { sId = s.Id; gap = false; } flushingQueue.Enqueue(s); } else { //Found a gap, stop enqueuing, flush a write operation gap = true; sCount = flushingQueue.Count; 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.Seek(0, SeekOrigin.Begin); header.Write(sourceStream); sourceStream.SetLength((long)(sectors.Count + 1) * sSize); sourceStream.Flush(); if (releaseMemory) GC.Collect(); //} //catch (Exception ex) //{ // throw new CFException("Internal error while committing data", ex); //} } /// /// Load compound file from an existing stream. /// /// Stream to load compound file from private void Load(Stream stream) { try { this.header = new Header(); this.directoryEntries = new List(); this.sourceStream = stream; header.Read(stream); int n_sector = Ceiling(((double)(stream.Length - GetSectorSize()) / (double)GetSectorSize())); if (stream.Length > 0x7FFFFF0) this._transactionLockAllocated = true; sectors = new SectorCollection(); //sectors = new ArrayList(); for (int i = 0; i < n_sector; i++) { sectors.Add(null); } LoadDirectories(); this.rootStorage = new CFStorage(this, directoryEntries[0]); } catch (Exception) { if (stream != null && closeStream) stream.Close(); throw; } } } int CompoundFile::GetSectorSize() { return 2 << (header.sectorShift - 1); } void CompoundFile::CheckForLockSector() { //If transaction lock has been added and not yet allocated in the FAT... if (_transactionLockAdded && !_transactionLockAllocated) { StreamView fatStream = new StreamView(GetFatSectorChain(), GetSectorSize(), sourceStream); fatStream.Seek(_lockSectorId * 4, SeekOrigin.Begin); fatStream.Write(BitConverter.GetBytes(Sector.ENDOFCHAIN), 0, 4); _transactionLockAllocated = true; } }