├── .gitattributes
├── .gitignore
├── LICENSE.md
├── OpenMcdf.sln
├── OpenMcdf.sln.DotSettings
├── README.md
├── appveyor.yml
├── docs
└── OpenMcdfHelp.shfbproj
├── src
├── CFException.cs
├── CFItem.cs
├── CFItemComparer.cs
├── CFStorage.cs
├── CFStream.cs
├── CompoundFile.cs
├── DirectoryEntry.cs
├── Enums.cs
├── Extensions
│ ├── CFStreamExtensions.cs
│ └── OLEProperties
│ │ ├── Interfaces
│ │ ├── IBinarySerializable.cs
│ │ └── ITypedPropertyValue.cs
│ │ ├── PropertyFactory.cs
│ │ ├── PropertyIdentifierAndOffset.cs
│ │ ├── PropertyReader.cs
│ │ ├── PropertySet.cs
│ │ ├── PropertySetStream.cs
│ │ ├── ProperyIdentifiers.cs
│ │ ├── TypedPropertyValue.cs
│ │ ├── VTPropertyType.cs
│ │ └── VTVectorHeader.cs
├── Header.cs
├── IDirectoryEntry.cs
├── OpenMcdf.csproj
├── Polyfills.cs
├── RBTree
│ └── RBTree.cs
├── Sector.cs
├── SectorCollection.cs
├── StreamRW.cs
└── StreamView.cs
└── tests
├── OpenMcdf.Test
├── AssetDeployer.cs
├── AuthoringTests.txt
├── CFSStreamExtensionsTest.cs
├── CFSStreamTest.cs
├── CFSTorageTest.cs
├── CompoundFileTest.cs
├── Helpers.cs
├── OpenMcdf.Test.csproj
├── Performance
│ ├── MemoryTest.cs
│ ├── PerfTest.cs
│ └── PerformanceTestStuite.cs
├── Properties
│ └── AssemblyInfo.cs
├── RBTreeTest.cs
├── SectorCollectionTest.cs
└── StreamRWTest.cs
└── assets
├── 2_MB-W.ppt
├── BUG_16_.xls
├── CorruptedDoc_bug3547815.doc
├── CorruptedDoc_bug3547815_B.doc
├── CorruptedDoc_bug36.doc
├── CyclicFAT.cfs
├── MultipleStorage.cfs
├── MultipleStorage2.cfs
├── MultipleStorage3.cfs
├── MultipleStorage4.cfs
├── _thumbs_bug_24.db
├── report.xls
├── reportOverwriteMultiple.xls
├── reportREAD.xls
├── report_name_fix.xls
└── testbad.ole
/.gitattributes:
--------------------------------------------------------------------------------
1 | ###############################################################################
2 | # Set default behavior to automatically normalize line endings.
3 | ###############################################################################
4 | * text=auto
5 |
6 | ###############################################################################
7 | # Set default behavior for command prompt diff.
8 | #
9 | # This is need for earlier builds of msysgit that does not have it on by
10 | # default for csharp files.
11 | # Note: This is only used by command line
12 | ###############################################################################
13 | #*.cs diff=csharp
14 |
15 | ###############################################################################
16 | # Set the merge driver for project and solution files
17 | #
18 | # Merging from the command prompt will add diff markers to the files if there
19 | # are conflicts (Merging from VS is not affected by the settings below, in VS
20 | # the diff markers are never inserted). Diff markers may cause the following
21 | # file extensions to fail to load in VS. An alternative would be to treat
22 | # these files as binary and thus will always conflict and require user
23 | # intervention with every merge. To do so, just uncomment the entries below
24 | ###############################################################################
25 | #*.sln merge=binary
26 | #*.csproj merge=binary
27 | #*.vbproj merge=binary
28 | #*.vcxproj merge=binary
29 | #*.vcproj merge=binary
30 | #*.dbproj merge=binary
31 | #*.fsproj merge=binary
32 | #*.lsproj merge=binary
33 | #*.wixproj merge=binary
34 | #*.modelproj merge=binary
35 | #*.sqlproj merge=binary
36 | #*.wwaproj merge=binary
37 |
38 | ###############################################################################
39 | # behavior for image files
40 | #
41 | # image files are treated as binary by default.
42 | ###############################################################################
43 | #*.jpg binary
44 | #*.png binary
45 | #*.gif binary
46 |
47 | ###############################################################################
48 | # diff behavior for common document formats
49 | #
50 | # Convert binary document formats to text before diffing them. This feature
51 | # is only available from the command line. Turn it on by uncommenting the
52 | # entries below.
53 | ###############################################################################
54 | #*.doc diff=astextplain
55 | #*.DOC diff=astextplain
56 | #*.docx diff=astextplain
57 | #*.DOCX diff=astextplain
58 | #*.dot diff=astextplain
59 | #*.DOT diff=astextplain
60 | #*.pdf diff=astextplain
61 | #*.PDF diff=astextplain
62 | #*.rtf diff=astextplain
63 | #*.RTF diff=astextplain
64 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | ## Ignore Visual Studio temporary files, build results, and
2 | ## files generated by popular Visual Studio add-ons.
3 |
4 | # User-specific files
5 | *.suo
6 | *.user
7 | *.userosscache
8 | *.sln.docstates
9 |
10 | # User-specific files (MonoDevelop/Xamarin Studio)
11 | *.userprefs
12 |
13 | # Build results
14 | [Dd]ebug/
15 | [Dd]ebugPublic/
16 | [Rr]elease/
17 | [Rr]eleases/
18 | [Xx]64/
19 | [Xx]86/
20 | [Bb]uild/
21 | bld/
22 | [Bb]in/
23 | [Oo]bj/
24 |
25 | # Visual Studio 2015 cache/options directory
26 | .vs/
27 | # Uncomment if you have tasks that create the project's static files in wwwroot
28 | #wwwroot/
29 |
30 | # MSTest test Results
31 | [Tt]est[Rr]esult*/
32 | [Bb]uild[Ll]og.*
33 |
34 | # NUNIT
35 | *.VisualState.xml
36 | TestResult.xml
37 |
38 | # Build Results of an ATL Project
39 | [Dd]ebugPS/
40 | [Rr]eleasePS/
41 | dlldata.c
42 |
43 | # DNX
44 | project.lock.json
45 | artifacts/
46 |
47 | *_i.c
48 | *_p.c
49 | *_i.h
50 | *.ilk
51 | *.meta
52 | *.obj
53 | *.pch
54 | *.pdb
55 | *.pgc
56 | *.pgd
57 | *.rsp
58 | *.sbr
59 | *.tlb
60 | *.tli
61 | *.tlh
62 | *.tmp
63 | *.tmp_proj
64 | *.log
65 | *.vspscc
66 | *.vssscc
67 | .builds
68 | *.pidb
69 | *.svclog
70 | *.scc
71 |
72 | # Chutzpah Test files
73 | _Chutzpah*
74 |
75 | # Visual C++ cache files
76 | ipch/
77 | *.aps
78 | *.ncb
79 | *.opendb
80 | *.opensdf
81 | *.sdf
82 | *.cachefile
83 | *.VC.db
84 |
85 | # Visual Studio profiler
86 | *.psess
87 | *.vsp
88 | *.vspx
89 | *.sap
90 |
91 | # TFS 2012 Local Workspace
92 | $tf/
93 |
94 | # Guidance Automation Toolkit
95 | *.gpState
96 |
97 | # ReSharper is a .NET coding add-in
98 | _ReSharper*/
99 | *.[Rr]e[Ss]harper
100 | *.DotSettings.user
101 |
102 | # JustCode is a .NET coding add-in
103 | .JustCode
104 |
105 | # TeamCity is a build add-in
106 | _TeamCity*
107 |
108 | # DotCover is a Code Coverage Tool
109 | *.dotCover
110 |
111 | # NCrunch
112 | _NCrunch_*
113 | .*crunch*.local.xml
114 | nCrunchTemp_*
115 |
116 | # MightyMoose
117 | *.mm.*
118 | AutoTest.Net/
119 |
120 | # Web workbench (sass)
121 | .sass-cache/
122 |
123 | # Installshield output folder
124 | [Ee]xpress/
125 |
126 | # DocProject is a documentation generator add-in
127 | DocProject/buildhelp/
128 | DocProject/Help/*.HxT
129 | DocProject/Help/*.HxC
130 | DocProject/Help/*.hhc
131 | DocProject/Help/*.hhk
132 | DocProject/Help/*.hhp
133 | DocProject/Help/Html2
134 | DocProject/Help/html
135 |
136 | # Click-Once directory
137 | publish/
138 |
139 | # Publish Web Output
140 | *.[Pp]ublish.xml
141 | *.azurePubxml
142 |
143 | # TODO: Un-comment the next line if you do not want to checkin
144 | # your web deploy settings because they may include unencrypted
145 | # passwords
146 | #*.pubxml
147 | *.publishproj
148 |
149 | # NuGet Packages
150 | *.nupkg
151 | # The packages folder can be ignored because of Package Restore
152 | **/packages/*
153 | # except build/, which is used as an MSBuild target.
154 | !**/packages/build/
155 | # Uncomment if necessary however generally it will be regenerated when needed
156 | #!**/packages/repositories.config
157 | # NuGet v3's project.json files produces more ignoreable files
158 | *.nuget.props
159 | *.nuget.targets
160 |
161 | # Microsoft Azure Build Output
162 | csx/
163 | *.build.csdef
164 |
165 | # Microsoft Azure Emulator
166 | ecf/
167 | rcf/
168 |
169 | # Windows Store app package directory
170 | AppPackages/
171 | BundleArtifacts/
172 |
173 | # Visual Studio cache files
174 | # files ending in .cache can be ignored
175 | *.[Cc]ache
176 | # but keep track of directories ending in .cache
177 | !*.[Cc]ache/
178 |
179 | # Others
180 | ClientBin/
181 | [Ss]tyle[Cc]op.*
182 | ~$*
183 | *~
184 | *.dbmdl
185 | *.dbproj.schemaview
186 | *.pfx
187 | *.publishsettings
188 | node_modules/
189 | orleans.codegen.cs
190 |
191 | # RIA/Silverlight projects
192 | Generated_Code/
193 |
194 | # Backup & report files from converting an old project file
195 | # to a newer Visual Studio version. Backup files are not needed,
196 | # because we have git ;-)
197 | _UpgradeReport_Files/
198 | Backup*/
199 | UpgradeLog*.XML
200 | UpgradeLog*.htm
201 |
202 | # SQL Server files
203 | *.mdf
204 | *.ldf
205 |
206 | # Business Intelligence projects
207 | *.rdl.data
208 | *.bim.layout
209 | *.bim_*.settings
210 |
211 | # Microsoft Fakes
212 | FakesAssemblies/
213 |
214 | # GhostDoc plugin setting file
215 | *.GhostDoc.xml
216 |
217 | # Node.js Tools for Visual Studio
218 | .ntvs_analysis.dat
219 |
220 | # Visual Studio 6 build log
221 | *.plg
222 |
223 | # Visual Studio 6 workspace options file
224 | *.opt
225 |
226 | # Visual Studio LightSwitch build output
227 | **/*.HTMLClient/GeneratedArtifacts
228 | **/*.DesktopClient/GeneratedArtifacts
229 | **/*.DesktopClient/ModelManifest.xml
230 | **/*.Server/GeneratedArtifacts
231 | **/*.Server/ModelManifest.xml
232 | _Pvt_Extensions
233 |
234 | # LightSwitch generated files
235 | GeneratedArtifacts/
236 | ModelManifest.xml
237 |
238 | # Paket dependency manager
239 | .paket/paket.exe
240 |
241 | # FAKE - F# Make
242 | .fake/
243 |
--------------------------------------------------------------------------------
/OpenMcdf.sln:
--------------------------------------------------------------------------------
1 |
2 | Microsoft Visual Studio Solution File, Format Version 12.00
3 | # Visual Studio 15
4 | VisualStudioVersion = 15.0.27130.2010
5 | MinimumVisualStudioVersion = 10.0.40219.1
6 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{6C619B4F-100F-4D60-BEF1-60B770D24E5E}"
7 | ProjectSection(SolutionItems) = preProject
8 | .gitattributes = .gitattributes
9 | .gitignore = .gitignore
10 | appveyor.yml = appveyor.yml
11 | LICENSE.md = LICENSE.md
12 | README.md = README.md
13 | EndProjectSection
14 | EndProject
15 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "assets", "assets", "{8DC8730A-F254-4848-B272-BDFFCB5FDC00}"
16 | ProjectSection(SolutionItems) = preProject
17 | tests\assets\2_MB-W.ppt = tests\assets\2_MB-W.ppt
18 | tests\assets\_thumbs_bug_24.db = tests\assets\_thumbs_bug_24.db
19 | tests\assets\BUG_16_.xls = tests\assets\BUG_16_.xls
20 | tests\assets\CorruptedDoc_bug3547815.doc = tests\assets\CorruptedDoc_bug3547815.doc
21 | tests\assets\CorruptedDoc_bug3547815_B.doc = tests\assets\CorruptedDoc_bug3547815_B.doc
22 | tests\assets\CyclicFAT.cfs = tests\assets\CyclicFAT.cfs
23 | tests\assets\MultipleStorage.cfs = tests\assets\MultipleStorage.cfs
24 | tests\assets\MultipleStorage2.cfs = tests\assets\MultipleStorage2.cfs
25 | tests\assets\MultipleStorage3.cfs = tests\assets\MultipleStorage3.cfs
26 | tests\assets\MultipleStorage4.cfs = tests\assets\MultipleStorage4.cfs
27 | tests\assets\report.xls = tests\assets\report.xls
28 | tests\assets\report_name_fix.xls = tests\assets\report_name_fix.xls
29 | tests\assets\reportREAD.xls = tests\assets\reportREAD.xls
30 | tests\assets\testbad.ole = tests\assets\testbad.ole
31 | EndProjectSection
32 | EndProject
33 | Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{73814657-FC73-4066-AABD-86062F2A132E}"
34 | EndProject
35 | Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenMcdf.Test", "tests\OpenMcdf.Test\OpenMcdf.Test.csproj", "{FD339266-8842-40B4-9230-F8E84FC42AC2}"
36 | EndProject
37 | Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "OpenMcdf", "src\OpenMcdf.csproj", "{9128CCC3-88DF-4F31-883E-47A04825276F}"
38 | EndProject
39 | Global
40 | GlobalSection(SolutionConfigurationPlatforms) = preSolution
41 | Debug|Any CPU = Debug|Any CPU
42 | Release|Any CPU = Release|Any CPU
43 | EndGlobalSection
44 | GlobalSection(ProjectConfigurationPlatforms) = postSolution
45 | {FD339266-8842-40B4-9230-F8E84FC42AC2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
46 | {FD339266-8842-40B4-9230-F8E84FC42AC2}.Debug|Any CPU.Build.0 = Debug|Any CPU
47 | {FD339266-8842-40B4-9230-F8E84FC42AC2}.Release|Any CPU.ActiveCfg = Release|Any CPU
48 | {FD339266-8842-40B4-9230-F8E84FC42AC2}.Release|Any CPU.Build.0 = Release|Any CPU
49 | {9128CCC3-88DF-4F31-883E-47A04825276F}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
50 | {9128CCC3-88DF-4F31-883E-47A04825276F}.Debug|Any CPU.Build.0 = Debug|Any CPU
51 | {9128CCC3-88DF-4F31-883E-47A04825276F}.Release|Any CPU.ActiveCfg = Release|Any CPU
52 | {9128CCC3-88DF-4F31-883E-47A04825276F}.Release|Any CPU.Build.0 = Release|Any CPU
53 | EndGlobalSection
54 | GlobalSection(SolutionProperties) = preSolution
55 | HideSolutionNode = FALSE
56 | EndGlobalSection
57 | GlobalSection(NestedProjects) = preSolution
58 | {8DC8730A-F254-4848-B272-BDFFCB5FDC00} = {73814657-FC73-4066-AABD-86062F2A132E}
59 | {FD339266-8842-40B4-9230-F8E84FC42AC2} = {73814657-FC73-4066-AABD-86062F2A132E}
60 | EndGlobalSection
61 | GlobalSection(ExtensibilityGlobals) = postSolution
62 | SolutionGuid = {C391C94D-1FBE-4F24-9E09-AB72A6FE9F7B}
63 | EndGlobalSection
64 | EndGlobal
65 |
--------------------------------------------------------------------------------
/OpenMcdf.sln.DotSettings:
--------------------------------------------------------------------------------
1 |
2 | CF
3 | CFS
4 | DIFAT
5 | FAT
6 | OA
7 | RW
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | OpenMCDF
2 | ===========================
3 | **for .NET Framework and .NET Standard (including .NET Core)**
4 |
5 | [](https://www.nuget.org/packages/OpenMcdf-2/)
6 | [](https://www.nuget.org/packages/OpenMcdf-2/)
7 | []()
8 | [](https://github.com/CodeCavePro/OpenMCDF/blob/master/LICENSE.md)
9 |
10 | [](https://ci.appveyor.com/project/salaros/openmcdf)
11 | [](https://ci.appveyor.com/project/salaros/openmcdf/build/tests)
12 |
13 | # Description
14 |
15 | OpenMCDF is a 100% .net / C# component that allows developers to manipulate Microsoft Compound Document File Format for OLE structured storage. It supports read/write operations on streams and storages and traversal of structures tree.
16 |
17 | ## Features
18 |
19 | * COM Structured Storage for your .NET applications
20 | * Read / Write OLE compound files in .NET / C#
21 | * No COM Interop required
22 | * No external dependencies
23 | * Easy to use
24 | * Lazy loading to reduce memory consumption for read operations
25 | * Version 4 of the compound file format supported
26 | * Append data to existing streams
27 | * Get partial stream data from a given offset
28 | * Differential update of existing compound files
29 | * Can handle thousands of structured storage items
30 | * Support for native .net Stream object
31 | * Mono platform supported
32 |
--------------------------------------------------------------------------------
/appveyor.yml:
--------------------------------------------------------------------------------
1 | # Operating system (build VM template)
2 | os: Windows Server 2016
3 |
4 | # If the build configuration does not specify build worker image
5 | # then Visual Studio 2015 image is used.
6 | image: Visual Studio 2017
7 |
8 | # Restrict to Git branches below
9 | branches:
10 | only:
11 | - master
12 |
13 | # Scripts that run after cloning repository
14 | install:
15 | - nuget restore
16 |
17 | # Cache files until appveyor.yml is modified.
18 | cache:
19 | - packages -> **\packages.config # preserve "packages" directory in the root of build folder but will reset it if packages.config is modified
20 | - '%LocalAppData%\NuGet\Cache' # NuGet < v3
21 | - '%LocalAppData%\NuGet\v3-cache' # NuGet v3
22 |
23 | # Version format
24 | version: 2.1.1
25 |
26 | environment:
27 | VERSION_SIMPLE: '{version}'
28 | VERSION_INFORMATIONAL: '{version}'
29 | VERSION_SUFFIX: '-preview-$(APPVEYOR_BUILD_NUMBER)'
30 | VERSION_SUFFIX_TAG: '.$(APPVEYOR_BUILD_NUMBER)'
31 |
32 | init:
33 | - ps: |
34 | $env:VERSION_SIMPLE = $env:APPVEYOR_BUILD_VERSION
35 | $env:VERSION_INFORMATIONAL = $env:APPVEYOR_BUILD_VERSION
36 | Write-Host "Starting with $env:APPVEYOR_BUILD_VERSION as base version and $env:VERSION_INFORMATIONAL as informational version";
37 |
38 | if ($env:APPVEYOR_REPO_TAG -eq "true" -and $env:APPVEYOR_REPO_TAG_NAME)
39 | {
40 | $env:VERSION_SUFFIX = $env:VERSION_SUFFIX_TAG
41 | $tag_version = $env:APPVEYOR_REPO_TAG_NAME.TrimStart("v")
42 | Write-Host "Building a tag: $tag_version";
43 | if ($tag_version -match '^([0-9]+\.[0-9]+\.[0-9]+)$' -Or $tag_version -match '([0-9]+[\.-][0-9]+[\.-][0-9]+-[-A-Za-z0-9]+)')
44 | {
45 | $env:VERSION_INFORMATIONAL = "$tag_version"
46 | }
47 | }
48 | else
49 | {
50 | $env:VERSION_INFORMATIONAL = "$env:VERSION_INFORMATIONAL$env:VERSION_SUFFIX"
51 | }
52 |
53 | Update-AppveyorBuild -Version $env:VERSION_INFORMATIONAL
54 | Write-Host "Using build version: $env:APPVEYOR_BUILD_VERSION";
55 |
56 | configuration: Debug
57 |
58 | dotnet_csproj:
59 | patch: true
60 | file: '**\*.csproj'
61 | assembly_version: $(VERSION_SIMPLE)
62 | file_version: $(VERSION_SIMPLE)
63 | version: $(VERSION_INFORMATIONAL)
64 | package_version: $(VERSION_INFORMATIONAL)
65 | informational_version: $(VERSION_INFORMATIONAL)
66 |
67 | assembly_info:
68 | patch: true
69 | file: '**\AssemblyInfo.*'
70 | assembly_version: $(VERSION_SIMPLE)
71 | assembly_file_version: $(VERSION_SIMPLE)
72 | assembly_informational_version: $(VERSION_INFORMATIONAL)
73 |
74 | # Run scripts below before
75 | before_build:
76 | - where msbuild
77 | - cmd: msbuild /t:Clean
78 |
79 | # To run your custom scripts instead of automatic MSBuild
80 | build_script:
81 | - cmd: msbuild /t:Rebuild /p:Configuration=%configuration%
82 |
83 | # NuGet files qualified as artifacts
84 | artifacts:
85 | - path: 'bin\**\*.nupkg' # find the NuGet files
86 | name: OpenMCDF
87 |
88 | # Deploy to GitHub releases
89 | deploy:
90 | -
91 | provider: GitHub
92 | auth_token:
93 | secure: 2+d0KgCbWQpUR8TZfzvUEzbi4NQP6F/Tt0PUwLn6jXZCyO8FnrFVFJPsFa0QBQFl
94 | artifact: OpenMCDF
95 | description: |-
96 | Supports the following .NET frameworks and standards
97 | * .NET 4.0
98 | * [.NET 4.5.2](https://github.com/Microsoft/dotnet/blob/master/releases/net452/README.md)
99 | * [.NET 4.6.1](https://github.com/Microsoft/dotnet/blob/master/releases/net461/README.md)
100 | * [.NET 4.7](https://github.com/Microsoft/dotnet/blob/master/releases/net47/README.md)
101 | * [.NET Standard 1.6](https://github.com/dotnet/standard/blob/master/docs/versions/netstandard1.6.md) (.NET Core >=1.0, .NET >=4.6.1, Mono >=4.6 etc)
102 | * [.NET Standard 2.0](https://github.com/dotnet/standard/blob/master/docs/versions/netstandard2.0.md) (.NET Core >=2.0, .NET >=4.6.1, Mono >=5.4 etc)
103 | draft: false
104 | force_update: true
105 | prerelease: false
106 | release: "OpenMCDF v$(APPVEYOR_REPO_TAG_NAME)"
107 | tag: $(APPVEYOR_REPO_TAG_NAME)
108 | on:
109 | appveyor_repo_tag: true
110 |
111 | -
112 | provider: NuGet
113 | api_key:
114 | secure: i6oWn60J7ZOM4UuYcvxbuk9OAEp6or+Wq7izyJDPNlcLIhG2UKsxz7G/8erhdY3M
115 | artifact: OpenMCDF
116 | server: # remove to push to NuGet.org
117 | skip_symbols: false
118 | symbol_server: # remove to push symbols to SymbolSource.org
119 | on:
120 | appveyor_repo_tag: true
121 |
122 | -
123 | provider: NuGet
124 | server: https://ci.appveyor.com/nuget/salaros/api/v2/package
125 | symbol_server: https://ci.appveyor.com/nuget/salaros/api/v2/package
126 | api_key:
127 | secure: 3zmnmVBweTgdk4SBM/rWHdC9JOM9s0pxm1bw1d+WHDo=
128 | artifact: OpenMCDF
129 |
130 | # Start builds on tags only (GitHub and BitBucket)
131 | skip_non_tags: false
132 |
133 | # Turn off tests
134 | test:
135 | assemblies:
136 | - 'bin\%configuration%\tests\*.dll'
137 |
--------------------------------------------------------------------------------
/docs/OpenMcdfHelp.shfbproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
7 | Debug
8 | AnyCPU
9 | 2.0
10 | {6b2e0fe5-8246-4f87-9663-d72ebadfc53f}
11 | 2015.6.5.0
12 |
14 | Documentation
15 | Documentation
16 | Documentation
17 |
18 | .\Help\
19 | OpenMCDF
20 | Copyright 2010 - 2015 ©%3b Federico Blaseotto
21 | Open MCDF
22 | Open MCDF
23 | Open MCDF
24 | Summary, AutoDocumentCtors
25 |
26 |
27 |
28 | ironfede%40users.sourceforge.net
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 | VS2013
40 | en-US
41 |
42 |
43 |
44 |
45 | InheritedMembers, InheritedFrameworkMembers
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
55 | 2.0
56 | Hierarchical
57 | 2
58 | False
59 | C#
60 | Blank
61 | False
62 | False
63 | Guid
64 | AboveNamespaces
65 | OnlyWarningsAndErrors
66 | HtmlHelp1
67 | False
68 | .NET Framework 4.0
69 | True
70 | False
71 | False
72 | True
73 |
74 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
--------------------------------------------------------------------------------
/src/CFException.cs:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | *
5 | * The Original Code is OpenMCDF - Compound Document Format library.
6 | *
7 | * The Initial Developer of the Original Code is Federico Blaseotto.*/
8 |
9 | using System;
10 | #if !NETSTANDARD1_6
11 | using System.Runtime.Serialization;
12 | #endif
13 |
14 | namespace OpenMcdf
15 | {
16 | ///
17 | ///
18 | /// OpenMCDF base exception.
19 | ///
20 | [Serializable]
21 | public class CFException : Exception
22 | {
23 | public CFException()
24 | {
25 | }
26 |
27 | #if !NETSTANDARD1_6
28 | protected CFException(SerializationInfo info, StreamingContext context)
29 | : base(info, context)
30 | {
31 | }
32 | #endif
33 |
34 | public CFException(string message)
35 | : base(message, null)
36 | {
37 | }
38 |
39 | public CFException(string message, Exception innerException)
40 | : base(message, innerException)
41 | {
42 | }
43 | }
44 |
45 | ///
46 | ///
47 | /// Raised when a data setter/getter method is invoked
48 | /// on a stream or storage object after the disposal of the owner
49 | /// compound file object.
50 | ///
51 | [Serializable]
52 | public class CFDisposedException : CFException
53 | {
54 | public CFDisposedException()
55 | {
56 | }
57 |
58 | #if !NETSTANDARD1_6
59 | protected CFDisposedException(SerializationInfo info, StreamingContext context)
60 | : base(info, context)
61 | {
62 | }
63 | #endif
64 |
65 | public CFDisposedException(string message)
66 | : base(message, null)
67 | {
68 | }
69 |
70 | public CFDisposedException(string message, Exception innerException)
71 | : base(message, innerException)
72 | {
73 | }
74 | }
75 |
76 | ///
77 | ///
78 | /// Raised when opening a file with invalid header
79 | /// or not supported COM/OLE Structured storage version.
80 | ///
81 | [Serializable]
82 | public class CFFileFormatException : CFException
83 | {
84 | public CFFileFormatException()
85 | {
86 | }
87 |
88 | #if !NETSTANDARD1_6
89 | protected CFFileFormatException(SerializationInfo info, StreamingContext context)
90 | : base(info, context)
91 | {
92 | }
93 | #endif
94 | public CFFileFormatException(string message)
95 | : base(message, null)
96 | {
97 | }
98 |
99 | public CFFileFormatException(string message, Exception innerException)
100 | : base(message, innerException)
101 | {
102 | }
103 | }
104 |
105 | ///
106 | ///
107 | /// Raised when a named stream or a storage object
108 | /// are not found in a parent storage.
109 | ///
110 | [Serializable]
111 | public class CFItemNotFound : CFException
112 | {
113 | #if !NETSTANDARD1_6
114 | protected CFItemNotFound(SerializationInfo info, StreamingContext context)
115 | : base(info, context)
116 | {
117 | }
118 | #endif
119 |
120 | public CFItemNotFound()
121 | : base("Entry not found")
122 | {
123 | }
124 |
125 | public CFItemNotFound(string message)
126 | : base(message, null)
127 | {
128 | }
129 |
130 | public CFItemNotFound(string message, Exception innerException)
131 | : base(message, innerException)
132 | {
133 | }
134 | }
135 |
136 | ///
137 | ///
138 | /// Raised when a method call is invalid for the current object state
139 | ///
140 | [Serializable]
141 | public class CFInvalidOperation : CFException
142 | {
143 | public CFInvalidOperation()
144 | {
145 | }
146 |
147 | #if !NETSTANDARD1_6
148 | protected CFInvalidOperation(SerializationInfo info, StreamingContext context)
149 | : base(info, context)
150 | {
151 | }
152 | #endif
153 |
154 | public CFInvalidOperation(string message)
155 | : base(message, null)
156 | {
157 | }
158 |
159 | public CFInvalidOperation(string message, Exception innerException)
160 | : base(message, innerException)
161 | {
162 | }
163 | }
164 |
165 | ///
166 | ///
167 | /// Raised when trying to add a duplicated CFItem
168 | ///
169 | ///
170 | /// Items are compared by name as indicated by specs.
171 | /// Two items with the same name CANNOT be added within
172 | /// the same storage or sub-storage.
173 | ///
174 | [Serializable]
175 | public class CFDuplicatedItemException : CFException
176 | {
177 | public CFDuplicatedItemException()
178 | {
179 | }
180 |
181 | #if !NETSTANDARD1_6
182 | protected CFDuplicatedItemException(SerializationInfo info, StreamingContext context)
183 | : base(info, context)
184 | {
185 | }
186 | #endif
187 |
188 | public CFDuplicatedItemException(string message)
189 | : base(message, null)
190 | {
191 | }
192 |
193 | public CFDuplicatedItemException(string message, Exception innerException)
194 | : base(message, innerException)
195 | {
196 | }
197 | }
198 |
199 | ///
200 | ///
201 | /// Raised when trying to load a Compound File with invalid, corrupted or mismatched fields (4.1 - specifications)
202 | ///
203 | ///
204 | /// This exception is NOT raised when Compound file has been opened with NO_VALIDATION_EXCEPTION option.
205 | ///
206 | [Serializable]
207 | public class CFCorruptedFileException : CFException
208 | {
209 | public CFCorruptedFileException()
210 | {
211 | }
212 |
213 | #if !NETSTANDARD1_6
214 | protected CFCorruptedFileException(SerializationInfo info, StreamingContext context)
215 | : base(info, context)
216 | {
217 | }
218 | #endif
219 |
220 | public CFCorruptedFileException(string message)
221 | : base(message, null)
222 | {
223 | }
224 |
225 | public CFCorruptedFileException(string message, Exception innerException)
226 | : base(message, innerException)
227 | {
228 | }
229 | }
230 | }
--------------------------------------------------------------------------------
/src/CFItem.cs:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | *
5 | * The Original Code is OpenMCDF - Compound Document Format library.
6 | *
7 | * The Initial Developer of the Original Code is Federico Blaseotto.*/
8 |
9 | using System;
10 |
11 | namespace OpenMcdf
12 | {
13 | ///
14 | ///
15 | /// Abstract base class for Structured Storage entities.
16 | ///
17 | ///
18 | ///
19 | /// const String STORAGE_NAME = "report.xls";
20 | /// CompoundFile cf = new CompoundFile(STORAGE_NAME);
21 | /// FileStream output = new FileStream("LogEntries.txt", FileMode.Create);
22 | /// TextWriter tw = new StreamWriter(output);
23 | /// // CFItem represents both storage and stream items
24 | /// VisitedEntryAction va = delegate(CFItem item)
25 | /// {
26 | /// tw.WriteLine(item.Name);
27 | /// };
28 | /// cf.RootStorage.VisitEntries(va, true);
29 | /// tw.Close();
30 | ///
31 | ///
32 | public abstract class CFItem : IComparable
33 | {
34 | protected CompoundFile CompoundFile { get; }
35 |
36 | protected void CheckDisposed()
37 | {
38 | if (CompoundFile.IsClosed)
39 | throw new CFDisposedException(
40 | "Owner Compound file has been closed and owned items have been invalidated");
41 | }
42 |
43 | protected CFItem()
44 | {
45 | }
46 |
47 | protected CFItem(CompoundFile compoundFile)
48 | {
49 | CompoundFile = compoundFile;
50 | }
51 |
52 | #region IDirectoryEntry Members
53 |
54 | internal IDirectoryEntry DirEntry { get; set; }
55 |
56 | internal int CompareTo(CFItem other)
57 | {
58 | return DirEntry.CompareTo(other.DirEntry);
59 | }
60 |
61 | #endregion
62 |
63 | #region IComparable Members
64 |
65 | public int CompareTo(object obj)
66 | {
67 | return DirEntry.CompareTo(((CFItem) obj).DirEntry);
68 | }
69 |
70 | #endregion
71 |
72 | public static bool operator ==(CFItem leftItem, CFItem rightItem)
73 | {
74 | // If both are null, or both are same instance, return true.
75 | if (ReferenceEquals(leftItem, rightItem))
76 | {
77 | return true;
78 | }
79 |
80 | // If one is null, but not both, return false.
81 | if (((object) leftItem == null) || ((object) rightItem == null))
82 | {
83 | return false;
84 | }
85 |
86 | // Return true if the fields match:
87 | return leftItem.CompareTo(rightItem) == 0;
88 | }
89 |
90 | public static bool operator !=(CFItem leftItem, CFItem rightItem)
91 | {
92 | return !(leftItem == rightItem);
93 | }
94 |
95 | public override bool Equals(object obj)
96 | {
97 | return CompareTo(obj) == 0;
98 | }
99 |
100 | public override int GetHashCode()
101 | {
102 | // ReSharper disable once NonReadonlyMemberInGetHashCode
103 | return DirEntry.GetEntryName().GetHashCode();
104 | }
105 |
106 | ///
107 | /// Get entity name
108 | ///
109 | public string Name
110 | {
111 | get
112 | {
113 | var n = DirEntry.GetEntryName();
114 | return !string.IsNullOrEmpty(n) ? n.TrimEnd('\0') : string.Empty;
115 | }
116 | }
117 |
118 | ///
119 | /// Size in bytes of the item. It has a valid value
120 | /// only if entity is a stream, otherwise it is setted to zero.
121 | ///
122 | public long Size => DirEntry.Size;
123 |
124 |
125 | ///
126 | /// Return true if item is Storage
127 | ///
128 | ///
129 | /// This check doesn't use reflection or runtime type information
130 | /// and doesn't suffer related performance penalties.
131 | ///
132 | public bool IsStorage => DirEntry.StgType == StgType.StgStorage;
133 |
134 | ///
135 | /// Return true if item is a Stream
136 | ///
137 | ///
138 | /// This check doesn't use reflection or runtime type information
139 | /// and doesn't suffer related performance penalties.
140 | ///
141 | public bool IsStream => DirEntry.StgType == StgType.StgStream;
142 |
143 | ///
144 | /// Return true if item is the Root Storage
145 | ///
146 | ///
147 | /// This check doesn't use reflection or runtime type information
148 | /// and doesn't suffer related performance penalties.
149 | ///
150 | public bool IsRoot => DirEntry.StgType == StgType.StgRoot;
151 |
152 | ///
153 | /// Get/Set the Creation Date of the current item
154 | ///
155 | public DateTime CreationDate
156 | {
157 | get => DateTime.FromFileTime(BitConverter.ToInt64(DirEntry.CreationDate, 0));
158 |
159 | set
160 | {
161 | if (DirEntry.StgType != StgType.StgStream && DirEntry.StgType != StgType.StgRoot)
162 | DirEntry.CreationDate = BitConverter.GetBytes((value.ToFileTime()));
163 | else
164 | throw new CFException("Creation Date can only be set on storage entries");
165 | }
166 | }
167 |
168 | ///
169 | /// Get/Set the Modify Date of the current item
170 | ///
171 | public DateTime ModifyDate
172 | {
173 | get => DateTime.FromFileTime(BitConverter.ToInt64(DirEntry.ModifyDate, 0));
174 |
175 | set
176 | {
177 | if (DirEntry.StgType != StgType.StgStream && DirEntry.StgType != StgType.StgRoot)
178 | DirEntry.ModifyDate = BitConverter.GetBytes((value.ToFileTime()));
179 | else
180 | throw new CFException("Modify Date can only be set on storage entries");
181 | }
182 | }
183 |
184 | ///
185 | /// Get/Set Object class Guid for Root and Storage entries.
186 | ///
187 | public Guid CLSID
188 | {
189 | get => DirEntry.StorageCLSID;
190 | set
191 | {
192 | if (DirEntry.StgType != StgType.StgStream)
193 | {
194 | DirEntry.StorageCLSID = value;
195 | }
196 | else
197 | throw new CFException("Object class GUID can only be set on Root and Storage entries");
198 | }
199 | }
200 |
201 | int IComparable.CompareTo(CFItem other)
202 | {
203 | return DirEntry.CompareTo(other.DirEntry);
204 | }
205 |
206 | public override string ToString()
207 | {
208 | if (DirEntry != null)
209 | return "[" + DirEntry.LeftSibling + "," + DirEntry.SID + "," + DirEntry.RightSibling +
210 | "]" + " " + DirEntry.GetEntryName();
211 | return string.Empty;
212 | }
213 | }
214 | }
--------------------------------------------------------------------------------
/src/CFItemComparer.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 |
3 | namespace OpenMcdf
4 | {
5 | internal class CFItemComparer : IComparer
6 | {
7 | public int Compare(CFItem x, CFItem y)
8 | {
9 | // X CompareTo Y : X > Y --> 1 ; X < Y --> -1
10 | return (x.DirEntry.CompareTo(y.DirEntry));
11 |
12 | //Compare X < Y --> -1
13 | }
14 | }
15 | }
16 |
--------------------------------------------------------------------------------
/src/CFStream.cs:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | *
5 | * The Original Code is OpenMCDF - Compound Document Format library.
6 | *
7 | * The Initial Developer of the Original Code is Federico Blaseotto.*/
8 |
9 | using System.IO;
10 |
11 | namespace OpenMcdf
12 | {
13 | ///
14 | /// OLE structured storage stream Object
15 | /// It is contained inside a Storage object in a file-directory
16 | /// relationship and indexed by its name.
17 | ///
18 | public class CFStream : CFItem
19 | {
20 | internal CFStream(CompoundFile compoundFile, IDirectoryEntry dirEntry)
21 | : base(compoundFile)
22 | {
23 | if (dirEntry == null || dirEntry.SID < 0)
24 | throw new CFException("Attempting to add a CFStream using an uninitialized directory");
25 |
26 | DirEntry = dirEntry;
27 | }
28 |
29 | ///
30 | /// Set the data associated with the stream object.
31 | ///
32 | ///
33 | ///
34 | /// byte[] b = new byte[]{0x0,0x1,0x2,0x3};
35 | /// CompoundFile cf = new CompoundFile();
36 | /// CFStream myStream = cf.RootStorage.AddStream("MyStream");
37 | /// myStream.SetData(b);
38 | ///
39 | ///
40 | /// Data bytes to write to this stream
41 | /// Existing associated data will be lost after method invocation
42 | public void SetData(byte[] data)
43 | {
44 | CheckDisposed();
45 |
46 | CompoundFile.FreeData(this);
47 | CompoundFile.WriteData(this, data);
48 | }
49 |
50 | ///
51 | /// Write a data buffer to a specific position into current CFStream object
52 | ///
53 | /// Data buffer to Write
54 | /// Position into the stream object to start writing from
55 | /// Current stream will be extended to receive data buffer over
56 | /// its current size
57 | public void Write(byte[] data, long position)
58 | {
59 | Write(data, position, 0, data.Length);
60 | }
61 |
62 | ///
63 | /// Write count bytes of a data buffer to a specific position into
64 | /// the current CFStream object starting from the specified position.
65 | ///
66 | /// Data buffer to copy bytes from
67 | /// Position into the stream object to start writing from
68 | /// The zero-based byte offset in buffer at which to
69 | /// begin copying bytes to the current CFStream.
70 | /// The number of bytes to be written to the current CFStream
71 | /// Current stream will be extended to receive data buffer over
72 | /// its current size.
73 | internal void Write(byte[] data, long position, int offset, int count)
74 | {
75 | CheckDisposed();
76 | CompoundFile.WriteData(this, data, position, offset, count);
77 | }
78 |
79 | ///
80 | /// Append the provided data to stream data.
81 | ///
82 | ///
83 | ///
84 | /// byte[] b = new byte[]{0x0,0x1,0x2,0x3};
85 | /// byte[] b2 = new byte[]{0x4,0x5,0x6,0x7};
86 | /// CompoundFile cf = new CompoundFile();
87 | /// CFStream myStream = cf.RootStorage.AddStream("MyStream");
88 | /// myStream.SetData(b); // here we could also have invoked .AppendData
89 | /// myStream.AppendData(b2);
90 | /// cf.Save("MyLargeStreamsFile.cfs);
91 | /// cf.Close();
92 | ///
93 | ///
94 | /// Data bytes to append to this stream
95 | ///
96 | /// This method allows user to create stream with more than 2GB of data,
97 | /// appending data to the end of existing ones.
98 | /// Large streams (>2GB) are only supported by CFS version 4.
99 | /// Append data can also be invoked on streams with no data in order
100 | /// to simplify its use inside loops.
101 | ///
102 | public void Append(byte[] data)
103 | {
104 | CheckDisposed();
105 | if (Size > 0)
106 | {
107 | CompoundFile.AppendData(this, data);
108 | }
109 | else
110 | {
111 | CompoundFile.WriteData(this, data);
112 | }
113 | }
114 |
115 | ///
116 | /// Get all the data associated with the stream object.
117 | ///
118 | ///
119 | ///
120 | /// CompoundFile cf2 = new CompoundFile("AFileName.cfs");
121 | /// CFStream st = cf2.RootStorage.GetStream("MyStream");
122 | /// byte[] buffer = st.ReadAll();
123 | ///
124 | ///
125 | /// Array of byte containing stream data
126 | ///
127 | /// Raised when the owner compound file has been closed.
128 | ///
129 | public byte[] GetData()
130 | {
131 | CheckDisposed();
132 |
133 | return CompoundFile.GetData(this);
134 | }
135 |
136 | ///
137 | /// Read bytes associated with the stream object, starting from
138 | /// read.
139 | ///
140 | /// Array of bytes that will contain stream data
141 | /// The zero-based byte position in the stream at which to begin reading
142 | /// the data from.
143 | /// The maximum number of bytes to be read from the current stream.
144 | /// The count of bytes effectively read
145 | /// Method may read a number of bytes lesser then the requested one.
146 | ///
147 | /// CompoundFile cf = null;
148 | /// byte[] b = Helpers.GetBuffer(1024 * 2, 0xAA); //2MB buffer
149 | /// CFStream item = cf.RootStorage.GetStream("AStream");
150 | ///
151 | /// cf = new CompoundFile("$AFILENAME.cfs", CFSUpdateMode.ReadOnly, CFSConfiguration.Default);
152 | /// item = cf.RootStorage.GetStream("AStream");
153 | ///
154 | /// byte[] buffer = new byte[2048];
155 | /// item.Read(buffer, 0, 2048);
156 | /// Assert.IsTrue(Helpers.CompareBuffer(b, buffer));
157 | ///
158 | ///
159 | ///
160 | /// Raised when the owner compound file has been closed.
161 | ///
162 | public int Read(byte[] buffer, long position, int count)
163 | {
164 | CheckDisposed();
165 | return CompoundFile.ReadData(this, position, buffer, 0, count);
166 | }
167 |
168 | ///
169 | /// Read bytes associated with the stream object, starting from
170 | /// a provided . Method returns the effective count of bytes
171 | /// read.
172 | ///
173 | /// Array of bytes that will contain stream data
174 | /// The zero-based byte position in the stream at which to begin reading
175 | /// the data from.
176 | /// The zero-based byte offset in buffer at which to begin storing the data read from the current stream.
177 | /// The maximum number of bytes to be read from the current stream.
178 | /// The count of bytes effectively read
179 | /// Method may read a number of bytes lesser then the requested one.
180 | ///
181 | /// CompoundFile cf = null;
182 | /// byte[] b = Helpers.GetBuffer(1024 * 2, 0xAA); //2MB buffer
183 | /// CFStream item = cf.RootStorage.GetStream("AStream");
184 | ///
185 | /// cf = new CompoundFile("$AFILENAME.cfs", CFSUpdateMode.ReadOnly, CFSConfiguration.Default);
186 | /// item = cf.RootStorage.GetStream("AStream");
187 | ///
188 | /// byte[] buffer = new byte[2048];
189 | /// item.Read(buffer, 0, 2048);
190 | /// Assert.IsTrue(Helpers.CompareBuffer(b, buffer));
191 | ///
192 | ///
193 | ///
194 | /// Raised when the owner compound file has been closed.
195 | ///
196 | internal int Read(byte[] buffer, long position, int offset, int count)
197 | {
198 | CheckDisposed();
199 | return CompoundFile.ReadData(this, position, buffer, offset, count);
200 | }
201 |
202 |
203 | ///
204 | /// Copy data from an existing stream.
205 | ///
206 | /// A stream to read from
207 | ///
208 | /// Input stream will NOT be closed after method invocation.
209 | /// Existing associated data will be deleted.
210 | ///
211 | public void CopyFrom(Stream input)
212 | {
213 | CheckDisposed();
214 |
215 | var buffer = new byte[input.Length];
216 |
217 | if (input.CanSeek)
218 | {
219 | input.Seek(0, SeekOrigin.Begin);
220 | }
221 |
222 | input.Read(buffer, 0, (int) input.Length);
223 | SetData(buffer);
224 | }
225 |
226 | ///
227 | /// Resize stream padding with zero if enlarging, trimming data if reducing size.
228 | ///
229 | /// New length to assign to this stream
230 | public void Resize(long length)
231 | {
232 | CompoundFile.SetStreamLength(this, length);
233 | }
234 | }
235 | }
--------------------------------------------------------------------------------
/src/DirectoryEntry.cs:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | *
5 | * The Original Code is OpenMCDF - Compound Document Format library.
6 | *
7 | * The Initial Developer of the Original Code is Federico Blaseotto.*/
8 |
9 | using System;
10 | using System.Collections.Generic;
11 | using System.Text;
12 | using System.IO;
13 | using RedBlackTree;
14 |
15 | namespace OpenMcdf
16 | {
17 | internal class DirectoryEntry : IDirectoryEntry
18 | {
19 | internal const int THIS_IS_GREATER = 1;
20 | internal const int OTHER_IS_GREATER = -1;
21 | private readonly IList _dirRepository;
22 |
23 | public int SID { get; set; } = -1;
24 |
25 | internal static int nostream
26 | = unchecked((int) 0xFFFFFFFF);
27 |
28 | private DirectoryEntry(string name, StgType stgType, IList dirRepository)
29 | {
30 | _dirRepository = dirRepository;
31 |
32 | StgType = stgType;
33 |
34 | switch (stgType)
35 | {
36 | case StgType.StgStream:
37 |
38 | _storageCLSID = new Guid("00000000000000000000000000000000");
39 | CreationDate = new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
40 | ModifyDate = new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
41 | break;
42 |
43 | case StgType.StgStorage:
44 | CreationDate = BitConverter.GetBytes((DateTime.Now.ToFileTime()));
45 | break;
46 |
47 | case StgType.StgRoot:
48 | CreationDate = new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
49 | ModifyDate = new byte[] {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
50 | break;
51 | }
52 |
53 | SetEntryName(name);
54 | }
55 |
56 | public byte[] EntryName
57 | {
58 | get;
59 | private set;
60 | } = new byte[64];
61 |
62 | public string GetEntryName()
63 | {
64 | if (EntryName != null && EntryName.Length > 0)
65 | {
66 | return Encoding.Unicode.GetString(EntryName).Remove((_nameLength - 1) / 2);
67 | }
68 | return string.Empty;
69 | }
70 |
71 | public void SetEntryName(string entryName)
72 | {
73 | if (
74 | entryName.Contains(@"\") ||
75 | entryName.Contains(@"/") ||
76 | entryName.Contains(@":") ||
77 | entryName.Contains(@"!")
78 | )
79 | throw new CFException(
80 | "Invalid character in entry: the characters '\\', '/', ':','!' cannot be used in entry name");
81 |
82 | if (entryName.Length > 31)
83 | throw new CFException("Entry name MUST be smaller than 31 characters");
84 |
85 |
86 | var temp = Encoding.Unicode.GetBytes(entryName);
87 | var newName = new byte[64];
88 | Buffer.BlockCopy(temp, 0, newName, 0, temp.Length);
89 | newName[temp.Length] = 0x00;
90 | newName[temp.Length + 1] = 0x00;
91 |
92 | EntryName = newName;
93 | _nameLength = (ushort) (temp.Length + 2);
94 | }
95 |
96 | private ushort _nameLength;
97 |
98 | public ushort NameLength
99 | {
100 | get => _nameLength;
101 | set => throw new NotImplementedException();
102 | }
103 |
104 | public StgType StgType { get; set; }
105 |
106 | public StgColor StgColor { get; set; } = StgColor.Black;
107 |
108 | public int LeftSibling { get; set; } = nostream;
109 |
110 | public int RightSibling { get; set; } = nostream;
111 |
112 | public int Child { get; set; } = nostream;
113 |
114 | private Guid _storageCLSID
115 | = Guid.NewGuid();
116 |
117 | public Guid StorageCLSID
118 | {
119 | get => _storageCLSID;
120 | set => _storageCLSID = value;
121 | }
122 |
123 | public int StateBits { get; set; }
124 |
125 | public byte[] CreationDate { get; set; } = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
126 |
127 | public byte[] ModifyDate { get; set; } = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
128 |
129 | public int StartSetc { get; set; } = Sector.ENDOFCHAIN;
130 |
131 | public long Size { get; set; }
132 |
133 |
134 | public int CompareTo(object obj)
135 | {
136 | if (!(obj is IDirectoryEntry otherDir))
137 | throw new CFException("Invalid casting: compared object does not implement IDirectorEntry interface");
138 |
139 | if (NameLength > otherDir.NameLength)
140 | {
141 | return THIS_IS_GREATER;
142 | }
143 | if (NameLength < otherDir.NameLength)
144 | {
145 | return OTHER_IS_GREATER;
146 | }
147 | var thisName = Encoding.Unicode.GetString(EntryName, 0, NameLength);
148 | var otherName = Encoding.Unicode.GetString(otherDir.EntryName, 0, otherDir.NameLength);
149 |
150 | for (var z = 0; z < thisName.Length; z++)
151 | {
152 | var thisChar = char.ToUpperInvariant(thisName[z]);
153 | var otherChar = char.ToUpperInvariant(otherName[z]);
154 |
155 | if (thisChar > otherChar)
156 | return THIS_IS_GREATER;
157 | if (thisChar < otherChar)
158 | return OTHER_IS_GREATER;
159 | }
160 |
161 | return 0;
162 |
163 | // return String.Compare(Encoding.Unicode.GetString(this.EntryName).ToUpper(), Encoding.Unicode.GetString(other.EntryName).ToUpper());
164 | }
165 |
166 | public override bool Equals(object obj)
167 | {
168 | return CompareTo(obj) == 0;
169 | }
170 |
171 | ///
172 | /// FNV hash, short for Fowler/Noll/Vo
173 | ///
174 | ///
175 | /// (not warranted) unique hash for byte array
176 | private static ulong FnvHash(byte[] buffer)
177 | {
178 | ulong h = 2166136261;
179 | int i;
180 |
181 | for (i = 0; i < buffer.Length; i++)
182 | h = (h * 16777619) ^ buffer[i];
183 |
184 | return h;
185 | }
186 |
187 | public override int GetHashCode()
188 | {
189 | // ReSharper disable once NonReadonlyMemberInGetHashCode
190 | return (int) FnvHash(EntryName);
191 | }
192 |
193 | public void Write(Stream stream)
194 | {
195 | var rw = new StreamRW(stream);
196 |
197 | rw.Write(EntryName);
198 | rw.Write(_nameLength);
199 | rw.Write((byte) StgType);
200 | rw.Write((byte) StgColor);
201 | rw.Write(LeftSibling);
202 | rw.Write(RightSibling);
203 | rw.Write(Child);
204 | rw.Write(_storageCLSID.ToByteArray());
205 | rw.Write(StateBits);
206 | rw.Write(CreationDate);
207 | rw.Write(ModifyDate);
208 | rw.Write(StartSetc);
209 | rw.Write(Size);
210 |
211 | rw.Close();
212 | }
213 |
214 | //public Byte[] ToByteArray()
215 | //{
216 | // MemoryStream ms
217 | // = new MemoryStream(128);
218 |
219 | // BinaryWriter bw = new BinaryWriter(ms);
220 |
221 | // byte[] paddedName = new byte[64];
222 | // Array.Copy(entryName, paddedName, entryName.Length);
223 |
224 | // bw.Write(paddedName);
225 | // bw.Write(nameLength);
226 | // bw.Write((byte)stgType);
227 | // bw.Write((byte)stgColor);
228 | // bw.Write(leftSibling);
229 | // bw.Write(rightSibling);
230 | // bw.Write(child);
231 | // bw.Write(storageCLSID.ToByteArray());
232 | // bw.Write(stateBits);
233 | // bw.Write(creationDate);
234 | // bw.Write(modifyDate);
235 | // bw.Write(startSetc);
236 | // bw.Write(size);
237 |
238 | // return ms.ToArray();
239 | //}
240 |
241 | public void Read(Stream stream, CFSVersion ver = CFSVersion.Ver_3)
242 | {
243 | var rw = new StreamRW(stream);
244 |
245 | EntryName = rw.ReadBytes(64);
246 | _nameLength = rw.ReadUInt16();
247 | StgType = (StgType) rw.ReadByte();
248 | //rw.ReadByte();//Ignore color, only black tree
249 | StgColor = (StgColor) rw.ReadByte();
250 | LeftSibling = rw.ReadInt32();
251 | RightSibling = rw.ReadInt32();
252 | Child = rw.ReadInt32();
253 |
254 | // Thanks to bugaccount (BugTrack id 3519554)
255 | if (StgType == StgType.StgInvalid)
256 | {
257 | LeftSibling = nostream;
258 | RightSibling = nostream;
259 | Child = nostream;
260 | }
261 |
262 | _storageCLSID = new Guid(rw.ReadBytes(16));
263 | StateBits = rw.ReadInt32();
264 | CreationDate = rw.ReadBytes(8);
265 | ModifyDate = rw.ReadBytes(8);
266 | StartSetc = rw.ReadInt32();
267 |
268 | if (ver == CFSVersion.Ver_3)
269 | {
270 | // avoid dirty read for version 3 files (max size: 32bit integer)
271 | // where most significant bits are not initialized to zero
272 |
273 | Size = rw.ReadInt32();
274 | rw.ReadBytes(4); //discard most significant 4 (possibly) dirty bytes
275 | }
276 | else
277 | {
278 | Size = rw.ReadInt64();
279 | }
280 | }
281 |
282 | public string Name => GetEntryName();
283 |
284 |
285 | public IRbNode Left
286 | {
287 | get
288 | {
289 | if (LeftSibling == nostream)
290 | return null;
291 |
292 | return _dirRepository[LeftSibling];
293 | }
294 | set
295 | {
296 | LeftSibling = value != null ? ((IDirectoryEntry) value).SID : nostream;
297 |
298 | if (LeftSibling != nostream)
299 | _dirRepository[LeftSibling].Parent = this;
300 | }
301 | }
302 |
303 | public IRbNode Right
304 | {
305 | get
306 | {
307 | if (RightSibling == nostream)
308 | return null;
309 |
310 | return _dirRepository[RightSibling];
311 | }
312 | set
313 | {
314 | RightSibling = value != null ? ((IDirectoryEntry) value).SID : nostream;
315 |
316 | if (RightSibling != nostream)
317 | _dirRepository[RightSibling].Parent = this;
318 | }
319 | }
320 |
321 | public Color Color
322 | {
323 | get => (Color) StgColor;
324 | set => StgColor = (StgColor) value;
325 | }
326 |
327 | private IDirectoryEntry _parent;
328 |
329 | public IRbNode Parent
330 | {
331 | get => _parent;
332 | set => _parent = value as IDirectoryEntry;
333 | }
334 |
335 | public IRbNode Grandparent()
336 | {
337 | return _parent?.Parent;
338 | }
339 |
340 | public IRbNode Sibling()
341 | {
342 | return Equals(this, Parent.Left) ? Parent.Right : Parent.Left;
343 | }
344 |
345 | public IRbNode Uncle()
346 | {
347 | return _parent != null ? Parent.Sibling() : null;
348 | }
349 |
350 | internal static IDirectoryEntry New(string name, StgType stgType, IList dirRepository)
351 | {
352 | DirectoryEntry de;
353 | if (dirRepository != null)
354 | {
355 | de = new DirectoryEntry(name, stgType, dirRepository);
356 | // No invalid directory entry found
357 | dirRepository.Add(de);
358 | de.SID = dirRepository.Count - 1;
359 | }
360 | else
361 | throw new ArgumentNullException("dirRepository", "Directory repository cannot be null in New() method");
362 |
363 | return de;
364 | }
365 |
366 | internal static IDirectoryEntry Mock(string name, StgType stgType)
367 | {
368 | var de = new DirectoryEntry(name, stgType, null);
369 |
370 | return de;
371 | }
372 |
373 | internal static IDirectoryEntry TryNew(string name, StgType stgType, IList dirRepository)
374 | {
375 | var de = new DirectoryEntry(name, stgType, dirRepository);
376 |
377 | // If we are not adding an invalid dirEntry as
378 | // in a normal loading from file (invalid directories MAY pad a sector)
379 | // Find first available invalid slot (if any) to reuse it
380 | for (var i = 0; i < dirRepository.Count; i++)
381 | {
382 | if (dirRepository[i].StgType != StgType.StgInvalid)
383 | continue;
384 |
385 | dirRepository[i] = de;
386 | de.SID = i;
387 | return de;
388 | }
389 |
390 | // No invalid directory entry found
391 | dirRepository.Add(de);
392 | de.SID = dirRepository.Count - 1;
393 |
394 | return de;
395 | }
396 |
397 | public override string ToString()
398 | {
399 | return Name + " [" + SID + "]" + (StgType == StgType.StgStream ? "Stream" : "Storage");
400 | }
401 |
402 | public void AssignValueTo(IRbNode other)
403 | {
404 | if (!(other is DirectoryEntry d))
405 | return;
406 |
407 | d.SetEntryName(GetEntryName());
408 |
409 | d.CreationDate = new byte[CreationDate.Length];
410 | CreationDate.CopyTo(d.CreationDate, 0);
411 |
412 | d.ModifyDate = new byte[ModifyDate.Length];
413 | ModifyDate.CopyTo(d.ModifyDate, 0);
414 |
415 | d.Size = Size;
416 | d.StartSetc = StartSetc;
417 | d.StateBits = StateBits;
418 | d.StgType = StgType;
419 | d._storageCLSID = new Guid(_storageCLSID.ToByteArray());
420 | d.Child = Child;
421 | }
422 | }
423 | }
--------------------------------------------------------------------------------
/src/Enums.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics.CodeAnalysis;
3 |
4 | namespace OpenMcdf
5 | {
6 | ///
7 | /// Configuration parameters for the compound files.
8 | /// They can be OR-combined to configure
9 | /// Compound file behavior.
10 | /// All flags are NOT set by Default.
11 | ///
12 | [Flags]
13 | public enum CFSConfiguration
14 | {
15 | ///
16 | /// Sector Recycling turn off,
17 | /// free sectors erasing off,
18 | /// format validation exception raised
19 | ///
20 | Default = 1,
21 |
22 | ///
23 | /// Sector recycling reduces data writing performances
24 | /// but avoids space wasting in scenarios with frequently
25 | /// data manipulation of the same streams.
26 | ///
27 | SectorRecycle = 2,
28 |
29 | ///
30 | /// Free sectors are erased to avoid information leakage
31 | ///
32 | EraseFreeSectors = 4,
33 |
34 | ///
35 | /// No exception is raised when a validation error occurs.
36 | /// This can possibly lead to a security issue but gives
37 | /// a chance to corrupted files to load.
38 | ///
39 | NoValidationException = 8,
40 |
41 | ///
42 | /// If this flag is set true,
43 | /// backing stream is kept open after CompoundFile disposal
44 | ///
45 | LeaveOpen = 16,
46 | }
47 |
48 | ///
49 | /// Binary File Format Version. Sector size is 512 byte for version 3,
50 | /// 4096 for version 4
51 | ///
52 | [SuppressMessage("ReSharper", "InconsistentNaming")]
53 | public enum CFSVersion
54 | {
55 | ///
56 | /// Compound file version 3 - The default and most common version available. Sector size 512 bytes, 2GB max file size.
57 | ///
58 | Ver_3 = 3,
59 |
60 | ///
61 | /// Compound file version 4 - Sector size is 4096 bytes. Using this version could bring some compatibility problem with existing applications.
62 | ///
63 | Ver_4 = 4
64 | }
65 |
66 | ///
67 | /// Update mode of the compound file.
68 | /// Default is ReadOnly.
69 | ///
70 | public enum CFSUpdateMode
71 | {
72 | ///
73 | /// ReadOnly update mode prevents overwriting
74 | /// of the opened file.
75 | /// Data changes are allowed but they have to be
76 | /// persisted on a different file when required
77 | /// using method
78 | ///
79 | ReadOnly,
80 |
81 | ///
82 | /// Update mode allows subsequent data changing operations
83 | /// to be persisted directly on the opened file or stream
84 | /// using the Commit
85 | /// method when required. Warning: this option may cause existing data loss if misused.
86 | ///
87 | Update
88 | }
89 |
90 | public enum StgType
91 | {
92 | StgInvalid = 0,
93 | StgStorage = 1,
94 | StgStream = 2,
95 | StgLockbytes = 3,
96 | StgProperty = 4,
97 | StgRoot = 5
98 | }
99 |
100 | public enum StgColor
101 | {
102 | Red = 0,
103 | Black = 1
104 | }
105 | }
106 |
--------------------------------------------------------------------------------
/src/Extensions/CFStreamExtensions.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 |
4 | namespace OpenMcdf.Extensions
5 | {
6 | public static class CFStreamExtension
7 | {
8 | private class StreamDecorator : Stream
9 | {
10 | private CFStream _cfStream;
11 | private long _position;
12 |
13 | public StreamDecorator(CFStream cfstream)
14 | {
15 | _cfStream = cfstream;
16 | }
17 |
18 | public override bool CanRead => true;
19 |
20 | public override bool CanSeek => true;
21 |
22 | public override bool CanWrite => true;
23 |
24 | public override void Flush()
25 | {
26 | // nothing to do;
27 | }
28 |
29 | public override long Length => _cfStream.Size;
30 |
31 | public override long Position
32 | {
33 | get => _position;
34 | set => _position = value;
35 | }
36 |
37 | public override int Read(byte[] buffer, int offset, int count)
38 | {
39 | if (count > buffer.Length)
40 | throw new ArgumentException("Count parameter exceeds buffer size");
41 |
42 | if (buffer == null)
43 | throw new ArgumentNullException("Buffer cannot be null");
44 |
45 | if (offset < 0 || count < 0)
46 | throw new ArgumentOutOfRangeException("Offset and Count parameters must be non-negative numbers");
47 |
48 | if (_position >= _cfStream.Size)
49 | return 0;
50 |
51 | count = _cfStream.Read(buffer, _position, offset, count);
52 | _position += count;
53 | return count;
54 | }
55 |
56 | public override long Seek(long offset, SeekOrigin origin)
57 | {
58 | switch (origin)
59 | {
60 | case SeekOrigin.Begin:
61 | _position = offset;
62 | break;
63 | case SeekOrigin.Current:
64 | _position += offset;
65 | break;
66 | case SeekOrigin.End:
67 | _position -= offset;
68 | break;
69 | default:
70 | throw new Exception("Invalid origin selected");
71 | }
72 |
73 | return _position;
74 | }
75 |
76 | public override void SetLength(long value)
77 | {
78 | _cfStream.Resize(value);
79 | }
80 |
81 | public override void Write(byte[] buffer, int offset, int count)
82 | {
83 | _cfStream.Write(buffer, _position, offset, count);
84 | _position += count;
85 | }
86 |
87 | #if !NETSTANDARD1_6 && !NETSTANDARD2_0
88 | public override void Close()
89 | {
90 | // Do nothing
91 | }
92 | #endif
93 | }
94 |
95 | ///
96 | /// Return the current CFStream object
97 | /// as a Stream object.
98 | ///
99 | /// Current CFStream object
100 | /// A Stream object representing structured stream data
101 | public static Stream AsIoStream(this CFStream cfStream)
102 | {
103 | return new StreamDecorator(cfStream);
104 | }
105 |
106 | ///
107 | /// Return the current CFStream object
108 | /// as a OLE properties Stream.
109 | ///
110 | ///
111 | /// A OLE Propertie stream
112 | public static OLEProperties.PropertySetStream AsOleProperties(this CFStream cfStream)
113 | {
114 | var result = new OLEProperties.PropertySetStream();
115 | result.Read(new BinaryReader(new StreamDecorator(cfStream)));
116 | return result;
117 | }
118 | }
119 | }
--------------------------------------------------------------------------------
/src/Extensions/OLEProperties/Interfaces/IBinarySerializable.cs:
--------------------------------------------------------------------------------
1 | using System.IO;
2 |
3 | namespace OpenMcdf.Extensions.OLEProperties.Interfaces
4 | {
5 | public interface IBinarySerializable
6 | {
7 | void Write(BinaryWriter bw);
8 | void Read(BinaryReader br);
9 | }
10 | }
--------------------------------------------------------------------------------
/src/Extensions/OLEProperties/Interfaces/ITypedPropertyValue.cs:
--------------------------------------------------------------------------------
1 | namespace OpenMcdf.Extensions.OLEProperties.Interfaces
2 | {
3 | public interface ITypedPropertyValue : IBinarySerializable
4 | {
5 | bool IsArray { get; set; }
6 |
7 | bool IsVector { get; set; }
8 |
9 | object PropertyValue { get; set; }
10 |
11 | VtPropertyType VtType
12 | {
13 | get;
14 | }
15 | }
16 | }
--------------------------------------------------------------------------------
/src/Extensions/OLEProperties/PropertyFactory.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using System.Text;
4 | using OpenMcdf.Extensions.OLEProperties.Interfaces;
5 |
6 | namespace OpenMcdf.Extensions.OLEProperties
7 | {
8 | internal class PropertyFactory
9 | {
10 | public ITypedPropertyValue NewProperty(VtPropertyType vType, PropertyContext ctx)
11 | {
12 | ITypedPropertyValue pr;
13 |
14 | switch (vType)
15 | {
16 | case VtPropertyType.VtI2:
17 | pr = new VtI2Property(vType);
18 | break;
19 | case VtPropertyType.VtI4:
20 | pr = new VtI4Property(vType);
21 | break;
22 | case VtPropertyType.VtR4:
23 | pr = new VtR4Property(vType);
24 | break;
25 | case VtPropertyType.VtLpstr:
26 | pr = new VtLpstrProperty(vType, ctx.CodePage);
27 | break;
28 | case VtPropertyType.VtFiletime:
29 | pr = new VtFiletimeProperty(vType);
30 | break;
31 | case VtPropertyType.VtDecimal:
32 | pr = new VtDecimalProperty(vType);
33 | break;
34 | case VtPropertyType.VtBool:
35 | pr = new VtBoolProperty(vType);
36 | break;
37 | case VtPropertyType.VtVectorHeader:
38 | pr = new VtVectorHeader(vType);
39 | break;
40 | case VtPropertyType.VtEmpty:
41 | pr = new VtEmptyProperty(vType);
42 | break;
43 | default:
44 | throw new Exception("Unrecognized property type");
45 | }
46 |
47 | return pr;
48 | }
49 |
50 |
51 | #region Property implementations
52 |
53 | internal class VtEmptyProperty : TypedPropertyValue
54 | {
55 | public VtEmptyProperty(VtPropertyType vType) : base(vType)
56 | {
57 | }
58 |
59 | public override void Read(BinaryReader br)
60 | {
61 | propertyValue = null;
62 | }
63 |
64 | public override void Write(BinaryWriter bw)
65 | {
66 | }
67 | }
68 |
69 | internal class VtI2Property : TypedPropertyValue
70 | {
71 | public VtI2Property(VtPropertyType vType) : base(vType)
72 | {
73 | }
74 |
75 | public override void Read(BinaryReader br)
76 | {
77 | propertyValue = br.ReadInt16();
78 | }
79 |
80 | public override void Write(BinaryWriter bw)
81 | {
82 | bw.Write((short) propertyValue);
83 | }
84 | }
85 |
86 | internal class VtI4Property : TypedPropertyValue
87 | {
88 | public VtI4Property(VtPropertyType vType) : base(vType)
89 | {
90 | }
91 |
92 | public override void Read(BinaryReader br)
93 | {
94 | propertyValue = br.ReadInt32();
95 | }
96 |
97 | public override void Write(BinaryWriter bw)
98 | {
99 | bw.Write((int) propertyValue);
100 | }
101 | }
102 |
103 | internal class VtR4Property : TypedPropertyValue
104 | {
105 | public VtR4Property(VtPropertyType vType) : base(vType)
106 | {
107 | }
108 |
109 | public override void Read(BinaryReader br)
110 | {
111 | propertyValue = br.ReadSingle();
112 | }
113 |
114 | public override void Write(BinaryWriter bw)
115 | {
116 | bw.Write((float) propertyValue);
117 | }
118 | }
119 |
120 | internal class VtR8Property : TypedPropertyValue
121 | {
122 | public VtR8Property(VtPropertyType vType) : base(vType)
123 | {
124 | }
125 |
126 | public override void Read(BinaryReader br)
127 | {
128 | propertyValue = br.ReadDouble();
129 | }
130 |
131 | public override void Write(BinaryWriter bw)
132 | {
133 | bw.Write((double) propertyValue);
134 | }
135 | }
136 |
137 | internal class VtCyProperty : TypedPropertyValue
138 | {
139 | public VtCyProperty(VtPropertyType vType) : base(vType)
140 | {
141 | }
142 |
143 | public override void Read(BinaryReader br)
144 | {
145 | propertyValue = br.ReadInt64() / 10000;
146 | }
147 |
148 | public override void Write(BinaryWriter bw)
149 | {
150 | bw.Write((long) propertyValue * 10000);
151 | }
152 | }
153 |
154 | internal class VtDateProperty : TypedPropertyValue
155 | {
156 | public VtDateProperty(VtPropertyType vType) : base(vType)
157 | {
158 | }
159 |
160 | public override void Read(BinaryReader br)
161 | {
162 | var temp = br.ReadDouble();
163 |
164 | #if NETSTANDARD1_6
165 | propertyValue = DateTimeExtensions.FromOADate(temp);
166 | #else
167 | propertyValue = DateTime.FromOADate(temp);
168 | #endif
169 | }
170 |
171 | public override void Write(BinaryWriter bw)
172 | {
173 | bw.Write(((DateTime) propertyValue).ToOADate());
174 | }
175 | }
176 |
177 | internal class VtLpstrProperty : TypedPropertyValue
178 | {
179 | private uint _size;
180 | private byte[] _data;
181 | private readonly int _codePage;
182 |
183 | public VtLpstrProperty(VtPropertyType vType, int codePage) : base(vType)
184 | {
185 | _codePage = codePage;
186 | }
187 |
188 | public override void Read(BinaryReader br)
189 | {
190 | _size = br.ReadUInt32();
191 | _data = br.ReadBytes((int) _size);
192 | propertyValue = Encoding.GetEncoding(_codePage).GetString(_data);
193 | var m = (int) _size % 4;
194 | br.ReadBytes(m); // padding
195 | }
196 |
197 | public override void Write(BinaryWriter bw)
198 | {
199 | _data = Encoding.GetEncoding(_codePage).GetBytes((string) propertyValue);
200 | _size = (uint) _data.Length;
201 | var m = (int) _size % 4;
202 | bw.Write(_data);
203 | for (var i = 0; i < m; i++) // padding
204 | bw.Write(0);
205 | }
206 | }
207 |
208 | internal class VtFiletimeProperty : TypedPropertyValue
209 | {
210 | public VtFiletimeProperty(VtPropertyType vType) : base(vType)
211 | {
212 | }
213 |
214 | public override void Read(BinaryReader br)
215 | {
216 | var tmp = br.ReadInt64();
217 | propertyValue = DateTime.FromFileTime(tmp);
218 | }
219 |
220 | public override void Write(BinaryWriter bw)
221 | {
222 | bw.Write(((DateTime) propertyValue).ToFileTime());
223 | }
224 | }
225 |
226 | internal class VtDecimalProperty : TypedPropertyValue
227 | {
228 | public VtDecimalProperty(VtPropertyType vType) : base(vType)
229 | {
230 | }
231 |
232 | public override void Read(BinaryReader br)
233 | {
234 | decimal d;
235 |
236 | br.ReadInt16(); // wReserved
237 | var scale = br.ReadByte();
238 | var sign = br.ReadByte();
239 |
240 | var u = br.ReadUInt32();
241 | d = Convert.ToDecimal(Math.Pow(2, 64)) * u;
242 | d += br.ReadUInt64();
243 |
244 | if (sign != 0)
245 | d = -d;
246 | d /= (10 << scale);
247 |
248 | propertyValue = d;
249 | }
250 |
251 | public override void Write(BinaryWriter bw)
252 | {
253 | bw.Write((short) propertyValue);
254 | }
255 | }
256 |
257 | internal class VtBoolProperty : TypedPropertyValue
258 | {
259 | public VtBoolProperty(VtPropertyType vType) : base(vType)
260 | {
261 | }
262 |
263 | public override void Read(BinaryReader br)
264 | {
265 | propertyValue = br.ReadUInt16() == 0xFFFF;
266 | //br.ReadUInt16();//padding
267 | }
268 | }
269 |
270 | internal class VtVectorHeader : TypedPropertyValue
271 | {
272 | public VtVectorHeader(VtPropertyType vType) : base(vType)
273 | {
274 | }
275 |
276 | public override void Read(BinaryReader br)
277 | {
278 | propertyValue = br.ReadUInt32();
279 | }
280 | }
281 |
282 | #endregion
283 | }
284 | }
--------------------------------------------------------------------------------
/src/Extensions/OLEProperties/PropertyIdentifierAndOffset.cs:
--------------------------------------------------------------------------------
1 | namespace OpenMcdf.Extensions.OLEProperties
2 | {
3 | public class PropertyIdentifierAndOffset
4 | {
5 | public PropertyIdentifiersSummaryInfo PropertyIdentifier { get; set; }
6 | public uint Offset { get; set; }
7 | }
8 | }
--------------------------------------------------------------------------------
/src/Extensions/OLEProperties/PropertyReader.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using System.IO;
3 | using OpenMcdf.Extensions.OLEProperties.Interfaces;
4 |
5 | namespace OpenMcdf.Extensions.OLEProperties
6 | {
7 | public enum Behavior
8 | {
9 | CaseSensitive,
10 | CaseInsensitive
11 | }
12 |
13 | public class PropertyContext
14 | {
15 | public int CodePage { get; set; }
16 | public Behavior Behavior { get; set; }
17 | public uint Locale { get; set; }
18 | }
19 |
20 | public enum PropertyDimensions
21 | {
22 | IsScalar,
23 | IsVector,
24 | IsArray
25 | }
26 |
27 | public class PropertyReader
28 | {
29 | private readonly PropertyContext _ctx = new PropertyContext();
30 | private readonly PropertyFactory _factory;
31 |
32 | public PropertyReader()
33 | {
34 | _factory = new PropertyFactory();
35 | }
36 |
37 | public List ReadProperty(PropertyIdentifiersSummaryInfo propertyIdentifier,
38 | BinaryReader br)
39 | {
40 | var res = new List();
41 | var dim = PropertyDimensions.IsScalar;
42 |
43 | var pVal = br.ReadUInt16();
44 |
45 | var vType = (VtPropertyType) (pVal & 0x00FF);
46 |
47 | if ((pVal & 0x1000) != 0)
48 | dim = PropertyDimensions.IsVector;
49 | else if ((pVal & 0x2000) != 0)
50 | dim = PropertyDimensions.IsArray;
51 |
52 | var isVariant = ((pVal & 0x00FF) == 0x000C);
53 |
54 | br.ReadUInt16(); // Ushort Padding
55 |
56 | // ReSharper disable once SwitchStatementMissingSomeCases
57 | switch (dim)
58 | {
59 | case PropertyDimensions.IsVector:
60 |
61 | var vectorHeader = _factory.NewProperty(VtPropertyType.VtVectorHeader, _ctx);
62 | vectorHeader.Read(br);
63 |
64 | var nItems = (uint) vectorHeader.PropertyValue;
65 |
66 | for (var i = 0; i < nItems; i++)
67 | {
68 | VtPropertyType vTypeItem;
69 |
70 | if (isVariant)
71 | {
72 | var pValItem = br.ReadUInt16();
73 | vTypeItem = (VtPropertyType) (pValItem & 0x00FF);
74 | br.ReadUInt16(); // Ushort Padding
75 | }
76 | else
77 | {
78 | vTypeItem = vType;
79 | }
80 |
81 | var p = _factory.NewProperty(vTypeItem, _ctx);
82 |
83 | p.Read(br);
84 | res.Add(p);
85 | }
86 |
87 | break;
88 |
89 | //Scalar property
90 | default:
91 | var pr = _factory.NewProperty(vType, _ctx);
92 |
93 | pr.Read(br);
94 |
95 | if (propertyIdentifier == PropertyIdentifiersSummaryInfo.CodePageString)
96 | {
97 | _ctx.CodePage = (short) pr.PropertyValue;
98 | }
99 |
100 | res.Add(pr);
101 | break;
102 | }
103 |
104 | return res;
105 | }
106 | }
107 | }
--------------------------------------------------------------------------------
/src/Extensions/OLEProperties/PropertySet.cs:
--------------------------------------------------------------------------------
1 | using System.Collections.Generic;
2 | using OpenMcdf.Extensions.OLEProperties.Interfaces;
3 |
4 | namespace OpenMcdf.Extensions.OLEProperties
5 | {
6 | public class PropertySet
7 | {
8 | public uint Size { get; set; }
9 |
10 | public uint NumProperties { get; set; }
11 |
12 | public List PropertyIdentifierAndOffsets { get; set; } = new List();
13 |
14 | public List Properties { get; set; } = new List();
15 | }
16 | }
--------------------------------------------------------------------------------
/src/Extensions/OLEProperties/PropertySetStream.cs:
--------------------------------------------------------------------------------
1 | using System;
2 |
3 | namespace OpenMcdf.Extensions.OLEProperties
4 | {
5 | public class PropertySetStream
6 | {
7 | public ushort ByteOrder { get; set; }
8 | public ushort Version { get; set; }
9 | public uint SystemIdentifier { get; set; }
10 | public Guid CLSID { get; set; }
11 | public uint NumPropertySets { get; set; }
12 | public Guid Fmtid0 { get; set; }
13 | public uint Offset0 { get; set; }
14 | public Guid Fmtid1 { get; set; }
15 | public uint Offset1 { get; set; }
16 | public PropertySet PropertySet0 { get; set; }
17 | public PropertySet PropertySet1 { get; set; }
18 |
19 | public void Read(System.IO.BinaryReader br)
20 | {
21 | ByteOrder = br.ReadUInt16();
22 | Version = br.ReadUInt16();
23 | SystemIdentifier = br.ReadUInt32();
24 | CLSID = new Guid(br.ReadBytes(16));
25 | NumPropertySets = br.ReadUInt32();
26 | Fmtid0 = new Guid(br.ReadBytes(16));
27 | Offset0 = br.ReadUInt32();
28 |
29 | if (NumPropertySets == 2)
30 | {
31 | Fmtid1 = new Guid(br.ReadBytes(16));
32 | Offset1 = br.ReadUInt32();
33 | }
34 |
35 | PropertySet0 = new PropertySet
36 | {
37 | Size = br.ReadUInt32(),
38 | NumProperties = br.ReadUInt32()
39 | };
40 |
41 | // Read property offsets
42 | for (var i = 0; i < PropertySet0.NumProperties; i++)
43 | {
44 | var pio = new PropertyIdentifierAndOffset();
45 | pio.PropertyIdentifier = (PropertyIdentifiersSummaryInfo) br.ReadUInt32();
46 | pio.Offset = br.ReadUInt32();
47 | PropertySet0.PropertyIdentifierAndOffsets.Add(pio);
48 | }
49 |
50 | // Read properties
51 | var pr = new PropertyReader();
52 | for (var i = 0; i < PropertySet0.NumProperties; i++)
53 | {
54 | br.BaseStream.Seek(Offset0 + PropertySet0.PropertyIdentifierAndOffsets[i].Offset,
55 | System.IO.SeekOrigin.Begin);
56 | PropertySet0.Properties.AddRange(
57 | pr.ReadProperty(PropertySet0.PropertyIdentifierAndOffsets[i].PropertyIdentifier, br));
58 | }
59 | }
60 |
61 | public void Write(System.IO.BinaryWriter bw)
62 | {
63 | throw new NotImplementedException();
64 | }
65 |
66 | // private void LoadFromStream(Stream inStream)
67 | // {
68 | // BinaryReader br = new BinaryReader(inStream);
69 | // PropertySetStream psStream = new PropertySetStream();
70 | // psStream.Read(br);
71 | // br.Close();
72 |
73 | // propertySets.Clear();
74 |
75 | // if (psStream.NumPropertySets == 1)
76 | // {
77 | // propertySets.Add(psStream.PropertySet0);
78 | // }
79 | // else
80 | // {
81 | // propertySets.Add(psStream.PropertySet0);
82 | // propertySets.Add(psStream.PropertySet1);
83 | // }
84 |
85 | // return;
86 | // }
87 | }
88 | }
--------------------------------------------------------------------------------
/src/Extensions/OLEProperties/ProperyIdentifiers.cs:
--------------------------------------------------------------------------------
1 | namespace OpenMcdf.Extensions.OLEProperties
2 | {
3 | public enum PropertyIdentifiersSummaryInfo : uint
4 | {
5 | CodePageString = 0x00000001,
6 | PidsiTitle = 0x00000002,
7 | PidsiSubject = 0x00000003,
8 | PidsiAuthor = 0x00000004,
9 | PidsiKeywords = 0x00000005,
10 | PidsiComments = 0x00000006,
11 | PidsiTemplate = 0x00000007,
12 | PidsiLastauthor = 0x00000008,
13 | PidsiRevnumber = 0x00000009,
14 | PidsiAppname = 0x00000012,
15 | PidsiEdittime = 0x0000000A,
16 | PidsiLastprinted = 0x0000000B,
17 | PidsiCreateDtm = 0x0000000C,
18 | PidsiLastsaveDtm = 0x0000000D,
19 | PidsiPagecount = 0x0000000E,
20 | PidsiWordcount = 0x0000000F,
21 | PidsiCharcount = 0x00000010,
22 | PidsiDocSecurity = 0x00000013
23 | }
24 |
25 | public enum PropertyIdentifiersDocumentSummaryInfo : uint
26 | {
27 | CodePageString = 0x00000001,
28 | PiddsiCategory = 0x00000002, //Category VT_LPSTR
29 | PiddsiPresformat = 0x00000003, //PresentationTarget VT_LPSTR
30 | PiddsiBytecount = 0x00000004, //Bytes VT_I4
31 | PiddsiLinecount = 0x00000005, // Lines VT_I4
32 | PiddsiParcount = 0x00000006, // Paragraphs VT_I4
33 | PiddsiSlidecount = 0x00000007, // Slides VT_I4
34 | PiddsiNotecount = 0x00000008, // Notes VT_I4
35 | PiddsiHiddencount = 0x00000009, // HiddenSlides VT_I4
36 | PiddsiMmclipcount = 0x0000000A, // MMClips VT_I4
37 | PiddsiScale = 0x0000000B, //ScaleCrop VT_BOOL
38 | PiddsiHeadingpair = 0x0000000C, // HeadingPairs VT_VARIANT | VT_VECTOR
39 | PiddsiDocparts = 0x0000000D, //TitlesofParts VT_VECTOR | VT_LPSTR
40 | PiddsiManager = 0x0000000E, // Manager VT_LPSTR
41 | PiddsiCompany = 0x0000000F, // Company VT_LPSTR
42 | PiddsiLinksdirty = 0x00000010, //LinksUpToDate VT_BOOL
43 | }
44 |
45 | public static class Extensions
46 | {
47 | public static string GetDescription(this PropertyIdentifiersSummaryInfo identifier)
48 | {
49 | switch (identifier)
50 | {
51 | case PropertyIdentifiersSummaryInfo.CodePageString:
52 | return "CodePage";
53 | case PropertyIdentifiersSummaryInfo.PidsiTitle:
54 | return "Title";
55 | case PropertyIdentifiersSummaryInfo.PidsiSubject:
56 | return "Subject";
57 | case PropertyIdentifiersSummaryInfo.PidsiAuthor:
58 | return "Author";
59 | case PropertyIdentifiersSummaryInfo.PidsiLastauthor:
60 | return "Last Author";
61 | case PropertyIdentifiersSummaryInfo.PidsiAppname:
62 | return "Application Name";
63 | case PropertyIdentifiersSummaryInfo.PidsiCreateDtm:
64 | return "Create Time";
65 | case PropertyIdentifiersSummaryInfo.PidsiLastsaveDtm:
66 | return "Last Modified Time";
67 | case PropertyIdentifiersSummaryInfo.PidsiKeywords:
68 | return "Keywords";
69 | case PropertyIdentifiersSummaryInfo.PidsiDocSecurity:
70 | return "Document Security";
71 | default: return string.Empty;
72 | }
73 | }
74 |
75 | public static string GetDescription(this PropertyIdentifiersDocumentSummaryInfo identifier)
76 | {
77 | switch (identifier)
78 | {
79 | case PropertyIdentifiersDocumentSummaryInfo.CodePageString:
80 | return "CodePage";
81 | case PropertyIdentifiersDocumentSummaryInfo.PiddsiCategory:
82 | return "Category";
83 | case PropertyIdentifiersDocumentSummaryInfo.PiddsiCompany:
84 | return "Company";
85 | case PropertyIdentifiersDocumentSummaryInfo.PiddsiDocparts:
86 | return "Titles of Parts";
87 | case PropertyIdentifiersDocumentSummaryInfo.PiddsiHeadingpair:
88 | return "Heading Pairs";
89 | case PropertyIdentifiersDocumentSummaryInfo.PiddsiHiddencount:
90 | return "Hidden Slides";
91 | case PropertyIdentifiersDocumentSummaryInfo.PiddsiLinecount:
92 | return "Line Count";
93 | case PropertyIdentifiersDocumentSummaryInfo.PiddsiLinksdirty:
94 | return "Links up to date";
95 | case PropertyIdentifiersDocumentSummaryInfo.PiddsiManager:
96 | return "Manager";
97 | case PropertyIdentifiersDocumentSummaryInfo.PiddsiMmclipcount:
98 | return "MMClips";
99 | case PropertyIdentifiersDocumentSummaryInfo.PiddsiNotecount:
100 | return "Notes";
101 | case PropertyIdentifiersDocumentSummaryInfo.PiddsiParcount:
102 | return "Paragraphs";
103 | case PropertyIdentifiersDocumentSummaryInfo.PiddsiPresformat:
104 | return "Presenteation Target";
105 | case PropertyIdentifiersDocumentSummaryInfo.PiddsiScale:
106 | return "Scale";
107 | case PropertyIdentifiersDocumentSummaryInfo.PiddsiSlidecount:
108 | return "Slides";
109 | default: return string.Empty;
110 | }
111 | }
112 | }
113 | }
--------------------------------------------------------------------------------
/src/Extensions/OLEProperties/TypedPropertyValue.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using OpenMcdf.Extensions.OLEProperties.Interfaces;
3 |
4 | namespace OpenMcdf.Extensions.OLEProperties
5 | {
6 | public class TypedPropertyValue : ITypedPropertyValue
7 | {
8 | public VtPropertyType VtType
9 | {
10 | get;
11 | //set { _VTType = value; }
12 | }
13 |
14 | protected object propertyValue;
15 |
16 | public TypedPropertyValue(VtPropertyType vtType)
17 | {
18 | VtType = vtType;
19 | }
20 |
21 | public virtual object PropertyValue
22 | {
23 | get => propertyValue;
24 |
25 | set => propertyValue = value;
26 | }
27 |
28 |
29 | public bool IsArray
30 | {
31 | get => throw new NotImplementedException();
32 |
33 | set => throw new NotImplementedException();
34 | }
35 |
36 | public bool IsVector
37 | {
38 | get => throw new NotImplementedException();
39 |
40 | set => throw new NotImplementedException();
41 | }
42 |
43 | public virtual void Read(System.IO.BinaryReader br)
44 | {
45 | }
46 |
47 | public virtual void Write(System.IO.BinaryWriter bw)
48 | {
49 | }
50 | }
51 | }
--------------------------------------------------------------------------------
/src/Extensions/OLEProperties/VTPropertyType.cs:
--------------------------------------------------------------------------------
1 | namespace OpenMcdf.Extensions.OLEProperties
2 | {
3 | public enum VtPropertyType : ushort
4 | {
5 | VtEmpty = 0x0000,
6 | VtNull = 0x0001,
7 | VtI2 = 0x0002,
8 | VtI4 = 0x0003,
9 | VtR4 = 0x0004,
10 | VtR8 = 0x0005,
11 | VtCy = 0x0006,
12 | VtDate = 0x0007,
13 | VtBstr = 0x0008,
14 | VtError = 0x000A,
15 | VtBool = 0x000B,
16 | VtDecimal = 0x000E,
17 | VtI1 = 0x0010,
18 | VtUi1 = 0x0011,
19 | VtUi2 = 0x0012,
20 | VtUi4 = 0x0013,
21 | VtI8 = 0x0014, // MUST be an 8-byte signed integer.
22 | VtUi8 = 0x0015, // MUST be an 8-byte unsigned integer.
23 | VtInt = 0x0016, // MUST be a 4-byte signed integer.
24 | VtUint = 0x0017, // MUST be a 4-byte unsigned integer.
25 | VtLpstr = 0x001E, // MUST be a CodePageString.
26 | VtLpwstr = 0x001F, // MUST be a UnicodeString.
27 | VtFiletime = 0x0040, // MUST be a FILETIME (Packet Version).
28 | VtBlob = 0x0041, // MUST be a BLOB.
29 |
30 | VtStream =
31 | 0x0042, // MUST be an IndirectPropertyName. The storage representing the (non-simple) property set MUST have a stream element with this name.
32 |
33 | VtStorage =
34 | 0x0043, // MUST be an IndirectPropertyName. The storage representing the (non-simple) property set MUST have a storage element with this name.
35 |
36 | VtStreamedObject =
37 | 0x0044, // MUST be an IndirectPropertyName. The storage representing the (non-simple) property set MUST have a stream element with this name.
38 |
39 | VtStoredObject =
40 | 0x0045, // MUST be an IndirectPropertyName. The storage representing the (non-simple) property set MUST have a storage element with this name.
41 | VtBlobObject = 0x0046, //MUST be a BLOB.
42 | VtCF = 0x0047, //MUST be a ClipboardData.
43 | VtCLSID = 0x0048, //MUST be a GUID (Packet Version)
44 | VtVersionedStream = 0x0049, //MUST be a Versioned Stream, NOT allowed in simple property
45 | VtVectorHeader = 0x1000, //--- NOT NORMATIVE
46 | VtArrayHeader = 0x2000, //--- NOT NORMATIVE
47 | }
48 | }
--------------------------------------------------------------------------------
/src/Extensions/OLEProperties/VTVectorHeader.cs:
--------------------------------------------------------------------------------
1 | namespace OpenMcdf.Extensions.OLEProperties
2 | {
3 | }
--------------------------------------------------------------------------------
/src/Header.cs:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | *
5 | * The Original Code is OpenMCDF - Compound Document Format library.
6 | *
7 | * The Initial Developer of the Original Code is Federico Blaseotto.*/
8 |
9 |
10 | using System.IO;
11 | using System.Linq;
12 |
13 | namespace OpenMcdf
14 | {
15 | internal class Header
16 | {
17 | //0 8 Compound document file identifier: D0H CFH 11H E0H A1H B1H 1AH E1H
18 |
19 | public byte[] HeaderSignature { get; private set; }
20 |
21 | //8 16 Unique identifier (UID) of this file (not of interest in the following, may be all 0)
22 |
23 | public byte[] CLSID { get; set; }
24 |
25 | //24 2 Revision number of the file format (most used is 003EH)
26 |
27 | public ushort MinorVersion { get; private set; }
28 |
29 | //26 2 Version number of the file format (most used is 0003H)
30 |
31 | public ushort MajorVersion { get; private set; }
32 |
33 | //28 2 Byte order identifier (➜4.2): FEH FFH = Little-Endian FFH FEH = Big-Endian
34 |
35 | public ushort ByteOrder { get; private set; }
36 |
37 | //30 2 Size of a sector in the compound document file (➜3.1) in power-of-two (ssz), real sector
38 | //size is sec_size = 2ssz bytes (minimum value is 7 which means 128 bytes, most used
39 | //value is 9 which means 512 bytes)
40 |
41 | public ushort SectorShift { get; private set; }
42 |
43 | //32 2 Size of a short-sector in the short-stream container stream (➜6.1) in power-of-two (sssz),
44 | //real short-sector size is short_sec_size = 2sssz bytes (maximum value is sector size
45 | //ssz, see above, most used value is 6 which means 64 bytes)
46 |
47 | public ushort MiniSectorShift { get; private set; }
48 |
49 | //34 10 Not used
50 |
51 | public byte[] UnUsed { get; private set; }
52 |
53 | //44 4 Total number of sectors used Directory (➜5.2)
54 |
55 | public int DirectorySectorsNumber { get; set; }
56 |
57 | //44 4 Total number of sectors used for the sector allocation table (➜5.2)
58 |
59 | public int FATSectorsNumber { get; set; }
60 |
61 | //48 4 SecID of first sector of the directory stream (➜7)
62 |
63 | public int FirstDirectorySectorId { get; set; }
64 |
65 | //52 4 Not used
66 |
67 | public uint UnUsed2 { get; private set; }
68 |
69 | //56 4 Minimum size of a standard stream (in bytes, minimum allowed and most used size is 4096
70 | //bytes), streams with an actual size smaller than (and not equal to) this value are stored as
71 | //short-streams (➜6)
72 |
73 | public uint MinSizeStandardStream { get; set; }
74 |
75 | //60 4 SecID of first sector of the short-sector allocation table (➜6.2), or –2 (End Of Chain
76 | //SecID, ➜3.1) if not extant
77 |
78 | ///
79 | /// This integer field contains the starting sector number for the mini FAT
80 | ///
81 | public int FirstMiniFATSectorId { get; set; }
82 |
83 | //64 4 Total number of sectors used for the short-sector allocation table (➜6.2)
84 |
85 | public uint MiniFATSectorsNumber { get; set; }
86 |
87 | //68 4 SecID of first sector of the master sector allocation table (➜5.1), or –2 (End Of Chain
88 | //SecID, ➜3.1) if no additional sectors used
89 |
90 | public int FirstDIFATSectorId { get; set; }
91 |
92 | //72 4 Total number of sectors used for the master sector allocation table (➜5.1)
93 |
94 | public uint DIFATSectorsNumber { get; set; }
95 |
96 | //76 436 First part of the master sector allocation table (➜5.1) containing 109 SecIDs
97 |
98 | public int[] DIFAT { get; }
99 |
100 | ///
101 | /// Structured Storage signature
102 | ///
103 | protected byte[] OleCFSSignature { get; }
104 |
105 | public Header()
106 | : this(3)
107 | {
108 | }
109 |
110 | public Header(ushort version)
111 | {
112 | DIFAT = new int[109];
113 | FirstDIFATSectorId = Sector.ENDOFCHAIN;
114 | FirstMiniFATSectorId = unchecked((int) 0xFFFFFFFE);
115 | MinSizeStandardStream = 4096;
116 | FirstDirectorySectorId = Sector.ENDOFCHAIN;
117 | UnUsed = new byte[6];
118 | MiniSectorShift = 6;
119 | SectorShift = 9;
120 | ByteOrder = 0xFFFE;
121 | MajorVersion = 0x0003;
122 | MinorVersion = 0x003E;
123 | CLSID = new byte[16];
124 | HeaderSignature = new byte[] {0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1};
125 |
126 | OleCFSSignature = new byte[] { 0xD0, 0xCF, 0x11, 0xE0, 0xA1, 0xB1, 0x1A, 0xE1 };
127 |
128 | switch (version)
129 | {
130 | case 3:
131 | MajorVersion = 3;
132 | SectorShift = 0x0009;
133 | break;
134 |
135 | case 4:
136 | MajorVersion = 4;
137 | SectorShift = 0x000C;
138 | break;
139 |
140 | default:
141 | throw new CFException("Invalid Compound File Format version");
142 | }
143 |
144 | for (var i = 0; i < 109; i++)
145 | {
146 | DIFAT[i] = Sector.FREESECT;
147 | }
148 | }
149 |
150 | public void Write(Stream stream)
151 | {
152 | var rw = new StreamRW(stream);
153 |
154 | rw.Write(HeaderSignature);
155 | rw.Write(CLSID);
156 | rw.Write(MinorVersion);
157 | rw.Write(MajorVersion);
158 | rw.Write(ByteOrder);
159 | rw.Write(SectorShift);
160 | rw.Write(MiniSectorShift);
161 | rw.Write(UnUsed);
162 | rw.Write(DirectorySectorsNumber);
163 | rw.Write(FATSectorsNumber);
164 | rw.Write(FirstDirectorySectorId);
165 | rw.Write(UnUsed2);
166 | rw.Write(MinSizeStandardStream);
167 | rw.Write(FirstMiniFATSectorId);
168 | rw.Write(MiniFATSectorsNumber);
169 | rw.Write(FirstDIFATSectorId);
170 | rw.Write(DIFATSectorsNumber);
171 |
172 | foreach (var i in DIFAT)
173 | {
174 | rw.Write(i);
175 | }
176 |
177 | if (MajorVersion == 4)
178 | {
179 | var zeroHead = new byte[3584];
180 | rw.Write(zeroHead);
181 | }
182 |
183 | rw.Close();
184 | }
185 |
186 | public void Read(Stream stream)
187 | {
188 | var rw = new StreamRW(stream);
189 |
190 | HeaderSignature = rw.ReadBytes(8);
191 | CheckSignature();
192 | CLSID = rw.ReadBytes(16);
193 | MinorVersion = rw.ReadUInt16();
194 | MajorVersion = rw.ReadUInt16();
195 | CheckVersion();
196 | ByteOrder = rw.ReadUInt16();
197 | SectorShift = rw.ReadUInt16();
198 | MiniSectorShift = rw.ReadUInt16();
199 | UnUsed = rw.ReadBytes(6);
200 | DirectorySectorsNumber = rw.ReadInt32();
201 | FATSectorsNumber = rw.ReadInt32();
202 | FirstDirectorySectorId = rw.ReadInt32();
203 | UnUsed2 = rw.ReadUInt32();
204 | MinSizeStandardStream = rw.ReadUInt32();
205 | FirstMiniFATSectorId = rw.ReadInt32();
206 | MiniFATSectorsNumber = rw.ReadUInt32();
207 | FirstDIFATSectorId = rw.ReadInt32();
208 | DIFATSectorsNumber = rw.ReadUInt32();
209 |
210 | for (var i = 0; i < 109; i++)
211 | {
212 | DIFAT[i] = rw.ReadInt32();
213 | }
214 |
215 | rw.Close();
216 | }
217 |
218 | private void CheckVersion()
219 | {
220 | if (MajorVersion != 3 && MajorVersion != 4)
221 | throw new CFFileFormatException(
222 | "Unsupported Binary File Format version: OpenMcdf only supports Compound Files with major version equal to 3 or 4 ");
223 | }
224 |
225 | private void CheckSignature()
226 | {
227 | if (HeaderSignature.Where((t, i) => t != OleCFSSignature[i]).Any())
228 | {
229 | throw new CFFileFormatException("Invalid OLE structured storage file");
230 | }
231 | }
232 | }
233 | }
--------------------------------------------------------------------------------
/src/IDirectoryEntry.cs:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | *
5 | * The Original Code is OpenMCDF - Compound Document Format library.
6 | *
7 | * The Initial Developer of the Original Code is Federico Blaseotto.*/
8 |
9 | using System;
10 | using RedBlackTree;
11 |
12 | namespace OpenMcdf
13 | {
14 | internal interface IDirectoryEntry : IRbNode
15 | {
16 | int Child { get; set; }
17 | byte[] CreationDate { get; set; }
18 | byte[] EntryName { get; }
19 | string GetEntryName();
20 | int LeftSibling { get; set; }
21 | byte[] ModifyDate { get; set; }
22 | string Name { get; }
23 | ushort NameLength { get; set; }
24 | void Read(System.IO.Stream stream, CFSVersion ver = CFSVersion.Ver_3);
25 | int RightSibling { get; set; }
26 | void SetEntryName(string entryName);
27 | int SID { get; set; }
28 | long Size { get; set; }
29 | int StartSetc { get; set; }
30 | int StateBits { get; set; }
31 | StgColor StgColor { get; set; }
32 | StgType StgType { get; set; }
33 | Guid StorageCLSID { get; set; }
34 | void Write(System.IO.Stream stream);
35 | }
36 | }
--------------------------------------------------------------------------------
/src/OpenMcdf.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | true
5 | OpenMcdf-2
6 | OpenMcdf-2
7 | netstandard1.6;netstandard2.0;net40;net461
8 | OpenMcdf
9 | OpenMcdf
10 | MS Compound File Storage .NET Implementation
11 | Federico Blaseotto
12 | Federico Blaseotto, Zhmayev Yaroslav
13 | Copyright © 2010-2015, Federico Blaseotto; 2016-2017 Zhmayev Yaroslav
14 | 2.1.2
15 | 2.1.2.0
16 | 2.1.2.0
17 | en
18 | https://raw.githubusercontent.com/CodeCavePro/OpenMCDF/master/LICENSE.md
19 | true
20 | https://github.com/CodeCavePro/OpenMCDF
21 | https://github.com/CodeCavePro/OpenMCDF
22 | Git
23 | Structured Storage, Compound file, Mono, OLE
24 |
25 | library
26 |
27 |
28 |
29 |
30 | ..\bin\Release\
31 | ..\bin\Release\netstandard2.0\OpenMcdf.xml
32 |
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 | ..\bin\Debug\
42 |
43 |
44 |
45 | TRACE;DEBUG;NETSTANDARD2_0
46 |
47 |
48 |
49 | TRACE;DEBUG;NETSTANDARD1_6
50 |
51 |
52 |
--------------------------------------------------------------------------------
/src/Polyfills.cs:
--------------------------------------------------------------------------------
1 | #if NETSTANDARD1_6
2 |
3 | using System;
4 | using System.IO;
5 |
6 | #endif
7 |
8 | using System.Runtime.CompilerServices;
9 |
10 | // ReSharper disable CheckNamespace
11 |
12 | [assembly: InternalsVisibleTo("OpenMcdf.Test")]
13 | [assembly: InternalsVisibleTo("OpenMcdf.Extensions")]
14 |
15 | #if NETSTANDARD1_6
16 |
17 | public static class StreamExtension
18 | {
19 | public static void Close(this Stream stream)
20 | {
21 | }
22 | }
23 |
24 | public static class BinaryReaderExtension
25 | {
26 | public static void Close(this BinaryReader stream)
27 | {
28 | }
29 | }
30 |
31 | public class SerializableAttribute : Attribute
32 | {
33 | }
34 |
35 | ///
36 | /// Adapted this implementation for .NET Standard 1.6:
37 | /// https://github.com/dotnet/coreclr/blob/release/1.0.0-rc1/src/mscorlib/src/System/DateTime.cs
38 | ///
39 | public static class DateTimeExtensions
40 | {
41 | // Number of 100ns ticks per time unit
42 | private const long TICKS_PER_MILLISECOND = 10000;
43 | private const long TICKS_PER_DAY = TICKS_PER_MILLISECOND * 1000 * 60 * 60 * 24;
44 |
45 | // Number of milliseconds per time unit
46 | private const int MILLIS_PER_DAY = 1000 * 60 * 60 * 24;
47 |
48 | // The minimum OA date is 0100/01/01 (Note it's year 100).
49 | // The maximum OA date is 9999/12/31
50 | private const long OA_DATE_MIN_AS_TICKS = (36524 - 365) * TICKS_PER_DAY;
51 |
52 | // All OA dates must be greater than (not >=) OADateMinAsDouble
53 | private const double OA_DATE_MIN_AS_DOUBLE = -657435.0;
54 | // All OA dates must be less than (not <=) OADateMaxAsDouble
55 | private const double OA_DATE_MAX_AS_DOUBLE = 2958466.0;
56 |
57 | private static readonly long DoubleDateOffset = new DateTime(1899, 1, 1).Ticks;
58 |
59 | // Converts the DateTime instance into an OLE Automation compatible
60 | // double date.
61 | public static double ToOADate(this DateTime date)
62 | {
63 | long value = date.Ticks;
64 | if (value == 0)
65 | return 0.0; // Returns OleAut's zero'ed date value.
66 | if (value < TICKS_PER_DAY
67 | ) // This is a fix for VB. They want the default day to be 1/1/0001 rather then 12/30/1899.
68 | value +=
69 | DoubleDateOffset; // We could have moved this fix down but we would like to keep the bounds check.
70 | if (value < OA_DATE_MIN_AS_TICKS)
71 | throw new OverflowException();
72 |
73 | // Currently, our max date == OA's max date (12/31/9999), so we don't
74 | // need an overflow check in that direction.
75 | long millis = (value - DoubleDateOffset) / TICKS_PER_MILLISECOND;
76 | if (millis < 0)
77 | {
78 | long frac = millis % MILLIS_PER_DAY;
79 | if (frac != 0) millis -= (MILLIS_PER_DAY + frac) * 2;
80 | }
81 | return (double)millis / MILLIS_PER_DAY;
82 | }
83 | ///
84 | /// Creates a DateTime from an OLE Automation Date.
85 | ///
86 | /// The date in double format.
87 | ///
88 | public static DateTime FromOADate(double doubleDate)
89 | {
90 | return new DateTime(DoubleDateToTicks(doubleDate), DateTimeKind.Unspecified);
91 | }
92 |
93 | // Converts an OLE Date to a tick count.
94 | // This function is duplicated in COMDateTime.cpp
95 | internal static long DoubleDateToTicks(double value)
96 | {
97 | // The check done this way will take care of NaN
98 | if (!(value < OA_DATE_MAX_AS_DOUBLE) || !(value > OA_DATE_MIN_AS_DOUBLE))
99 | throw new ArgumentException();
100 |
101 | // Conversion to long will not cause an overflow here, as at this point the "value" is in between OADateMinAsDouble and OADateMaxAsDouble
102 | long millis = (long)(value * MILLIS_PER_DAY + (value >= 0 ? 0.5 : -0.5));
103 | // The interesting thing here is when you have a value like 12.5 it all positive 12 days and 12 hours from 01/01/1899
104 | // However if you a value of -12.25 it is minus 12 days but still positive 6 hours, almost as though you meant -11.75 all negative
105 | // This line below fixes up the millis in the negative case
106 | if (millis < 0)
107 | {
108 | millis -= (millis % MILLIS_PER_DAY) * 2;
109 | }
110 |
111 | millis += DoubleDateOffset / TICKS_PER_MILLISECOND;
112 |
113 | if (millis < 0 || millis >= DateTime.MaxValue.Ticks)
114 | throw new ArgumentException();
115 | return millis * TICKS_PER_MILLISECOND;
116 | }
117 | }
118 |
119 | #endif
--------------------------------------------------------------------------------
/src/RBTree/RBTree.cs:
--------------------------------------------------------------------------------
1 | #define ASSERT
2 |
3 | using System;
4 | using System.Collections.Generic;
5 | using System.Linq;
6 |
7 | #if ASSERT
8 |
9 | using System.Diagnostics;
10 |
11 | #endif
12 |
13 | // -------------------------------------------------------------
14 | // This is a porting from java code, under MIT license of |
15 | // the beautiful Red-Black Tree implementation you can find at |
16 | // http://en.literateprograms.org/Red-black_tree_(Java)#chunk |
17 | // Many Thanks to original Implementors. |
18 | // -------------------------------------------------------------
19 |
20 | // ReSharper disable once CheckNamespace
21 | namespace RedBlackTree
22 | {
23 | public class RbTreeException : Exception
24 | {
25 | public RbTreeException(string msg)
26 | : base(msg)
27 | {
28 | }
29 | }
30 |
31 | public class RbTreeDuplicatedItemException : RbTreeException
32 | {
33 | public RbTreeDuplicatedItemException(string msg)
34 | : base(msg)
35 | {
36 | }
37 | }
38 |
39 | public enum Color
40 | {
41 | Red = 0,
42 | Black = 1
43 | }
44 |
45 | ///
46 | /// Red Black Node interface
47 | ///
48 | public interface IRbNode : IComparable
49 | {
50 | IRbNode Left { get; set; }
51 |
52 | IRbNode Right { get; set; }
53 |
54 |
55 | Color Color { get; set; }
56 |
57 |
58 | IRbNode Parent { get; set; }
59 |
60 |
61 | IRbNode Grandparent();
62 |
63 |
64 | IRbNode Sibling();
65 | // {
66 | //#if ASSERT
67 | // Debug.Assert(Parent != null); // Root node has no sibling
68 | //#endif
69 | // if (this == Parent.Left)
70 | // return Parent.Right;
71 | // else
72 | // return Parent.Left;
73 | // }
74 |
75 | IRbNode Uncle();
76 | // {
77 | //#if ASSERT
78 | // Debug.Assert(Parent != null); // Root node has no uncle
79 | // Debug.Assert(Parent.Parent != null); // Children of root have no uncle
80 | //#endif
81 | // return Parent.Sibling();
82 | // }
83 | // }
84 |
85 | void AssignValueTo(IRbNode other);
86 | }
87 |
88 | public class RbTree
89 | {
90 | public IRbNode Root { get; set; }
91 |
92 | private static Color NodeColor(IRbNode n)
93 | {
94 | return n == null ? Color.Black : n.Color;
95 | }
96 |
97 | public RbTree()
98 | {
99 | }
100 |
101 | public RbTree(IRbNode root)
102 | {
103 | Root = root;
104 | }
105 |
106 |
107 | private IRbNode LookupNode(IRbNode template)
108 | {
109 | var n = Root;
110 |
111 | while (n != null)
112 | {
113 | var compResult = template.CompareTo(n);
114 |
115 | if (compResult == 0)
116 | {
117 | return n;
118 | }
119 | n = compResult < 0 ? n.Left : n.Right;
120 | }
121 |
122 | return null;
123 | }
124 |
125 | public bool TryLookup(IRbNode template, out IRbNode val)
126 | {
127 | var n = LookupNode(template);
128 |
129 | if (n == null)
130 | {
131 | val = null;
132 | return false;
133 | }
134 | val = n;
135 | return true;
136 | }
137 |
138 | private void ReplaceNode(IRbNode oldn, IRbNode newn)
139 | {
140 | if (oldn.Parent == null)
141 | {
142 | Root = newn;
143 | }
144 | else
145 | {
146 | if (Equals(oldn, oldn.Parent.Left))
147 | oldn.Parent.Left = newn;
148 | else
149 | oldn.Parent.Right = newn;
150 | }
151 | if (newn != null)
152 | {
153 | newn.Parent = oldn.Parent;
154 | }
155 | }
156 |
157 | private void RotateLeft(IRbNode n)
158 | {
159 | var r = n.Right;
160 | ReplaceNode(n, r);
161 | n.Right = r.Left;
162 | if (r.Left != null)
163 | {
164 | r.Left.Parent = n;
165 | }
166 | r.Left = n;
167 | n.Parent = r;
168 | }
169 |
170 | private void RotateRight(IRbNode n)
171 | {
172 | var l = n.Left;
173 | ReplaceNode(n, l);
174 | n.Left = l.Right;
175 |
176 | if (l.Right != null)
177 | {
178 | l.Right.Parent = n;
179 | }
180 |
181 | l.Right = n;
182 | n.Parent = l;
183 | }
184 |
185 |
186 | public void Insert(IRbNode newNode)
187 | {
188 | newNode.Color = Color.Red;
189 | var insertedNode = newNode;
190 |
191 | if (Root == null)
192 | {
193 | Root = insertedNode;
194 | }
195 | else
196 | {
197 | var n = Root;
198 | while (true)
199 | {
200 | var compResult = newNode.CompareTo(n);
201 | if (compResult == 0)
202 | {
203 | throw new RbTreeDuplicatedItemException(
204 | "RBNode " + newNode + " already present in tree");
205 | }
206 | if (compResult < 0)
207 | {
208 | if (n.Left == null)
209 | {
210 | n.Left = insertedNode;
211 |
212 | break;
213 | }
214 | n = n.Left;
215 | }
216 | else
217 | {
218 | //assert compResult > 0;
219 | if (n.Right == null)
220 | {
221 | n.Right = insertedNode;
222 |
223 | break;
224 | }
225 | n = n.Right;
226 | }
227 | }
228 | insertedNode.Parent = n;
229 | }
230 |
231 | InsertCase1(insertedNode);
232 |
233 | NodeInserted?.Invoke(insertedNode);
234 | }
235 |
236 | //------------------------------------
237 | private void InsertCase1(IRbNode n)
238 | {
239 | if (n.Parent == null)
240 | n.Color = Color.Black;
241 | else
242 | InsertCase2(n);
243 | }
244 |
245 | //-----------------------------------
246 | private void InsertCase2(IRbNode n)
247 | {
248 | if (NodeColor(n.Parent) == Color.Black)
249 | return; // Tree is still valid
250 | InsertCase3(n);
251 | }
252 |
253 | //----------------------------
254 | private void InsertCase3(IRbNode n)
255 | {
256 | if (NodeColor(n.Uncle()) == Color.Red)
257 | {
258 | n.Parent.Color = Color.Black;
259 | n.Uncle().Color = Color.Black;
260 | n.Grandparent().Color = Color.Red;
261 | InsertCase1(n.Grandparent());
262 | }
263 | else
264 | {
265 | InsertCase4(n);
266 | }
267 | }
268 |
269 | //----------------------------
270 | private void InsertCase4(IRbNode n)
271 | {
272 | if (Equals(n, n.Parent.Right) && Equals(n.Parent, n.Grandparent().Left))
273 | {
274 | RotateLeft(n.Parent);
275 | n = n.Left;
276 | }
277 | else if (Equals(n, n.Parent.Left) && Equals(n.Parent, n.Grandparent().Right))
278 | {
279 | RotateRight(n.Parent);
280 | n = n.Right;
281 | }
282 |
283 | InsertCase5(n);
284 | }
285 |
286 | //----------------------------
287 | private void InsertCase5(IRbNode n)
288 | {
289 | n.Parent.Color = Color.Black;
290 | n.Grandparent().Color = Color.Red;
291 | if (Equals(n, n.Parent.Left) && Equals(n.Parent, n.Grandparent().Left))
292 | {
293 | RotateRight(n.Grandparent());
294 | }
295 | else
296 | {
297 | //assert n == n.parent.right && n.parent == n.grandparent().right;
298 | RotateLeft(n.Grandparent());
299 | }
300 | }
301 |
302 | private static IRbNode MaximumNode(IRbNode n)
303 | {
304 | //assert n != null;
305 | while (n.Right != null)
306 | {
307 | n = n.Right;
308 | }
309 |
310 | return n;
311 | }
312 |
313 |
314 | public void Delete(IRbNode template, out IRbNode deletedAlt)
315 | {
316 | deletedAlt = null;
317 | var n = LookupNode(template);
318 | if (n == null)
319 | return; // Key not found, do nothing
320 | if (n.Left != null && n.Right != null)
321 | {
322 | // Copy key/value from predecessor and then delete it instead
323 | var pred = MaximumNode(n.Left);
324 | pred.AssignValueTo(n);
325 | n = pred;
326 | deletedAlt = pred;
327 | }
328 |
329 | //assert n.left == null || n.right == null;
330 | var child = n.Right ?? n.Left;
331 | if (NodeColor(n) == Color.Black)
332 | {
333 | n.Color = NodeColor(child);
334 | DeleteCase1(n);
335 | }
336 |
337 | ReplaceNode(n, child);
338 |
339 | if (NodeColor(Root) == Color.Red)
340 | {
341 | Root.Color = Color.Black;
342 | }
343 | }
344 |
345 | private void DeleteCase1(IRbNode n)
346 | {
347 | if (n.Parent == null)
348 | return;
349 | DeleteCase2(n);
350 | }
351 |
352 |
353 | private void DeleteCase2(IRbNode n)
354 | {
355 | if (NodeColor(n.Sibling()) == Color.Red)
356 | {
357 | n.Parent.Color = Color.Red;
358 | n.Sibling().Color = Color.Black;
359 | if (Equals(n, n.Parent.Left))
360 | RotateLeft(n.Parent);
361 | else
362 | RotateRight(n.Parent);
363 | }
364 |
365 | DeleteCase3(n);
366 | }
367 |
368 | private void DeleteCase3(IRbNode n)
369 | {
370 | if (NodeColor(n.Parent) == Color.Black &&
371 | NodeColor(n.Sibling()) == Color.Black &&
372 | NodeColor(n.Sibling().Left) == Color.Black &&
373 | NodeColor(n.Sibling().Right) == Color.Black)
374 | {
375 | n.Sibling().Color = Color.Red;
376 | DeleteCase1(n.Parent);
377 | }
378 | else
379 | DeleteCase4(n);
380 | }
381 |
382 | private void DeleteCase4(IRbNode n)
383 | {
384 | if (NodeColor(n.Parent) == Color.Red &&
385 | NodeColor(n.Sibling()) == Color.Black &&
386 | NodeColor(n.Sibling().Left) == Color.Black &&
387 | NodeColor(n.Sibling().Right) == Color.Black)
388 | {
389 | n.Sibling().Color = Color.Red;
390 | n.Parent.Color = Color.Black;
391 | }
392 | else
393 | DeleteCase5(n);
394 | }
395 |
396 | private void DeleteCase5(IRbNode n)
397 | {
398 | if (Equals(n, n.Parent.Left) &&
399 | NodeColor(n.Sibling()) == Color.Black &&
400 | NodeColor(n.Sibling().Left) == Color.Red &&
401 | NodeColor(n.Sibling().Right) == Color.Black)
402 | {
403 | n.Sibling().Color = Color.Red;
404 | n.Sibling().Left.Color = Color.Black;
405 | RotateRight(n.Sibling());
406 | }
407 | else if (Equals(n, n.Parent.Right) &&
408 | NodeColor(n.Sibling()) == Color.Black &&
409 | NodeColor(n.Sibling().Right) == Color.Red &&
410 | NodeColor(n.Sibling().Left) == Color.Black)
411 | {
412 | n.Sibling().Color = Color.Red;
413 | n.Sibling().Right.Color = Color.Black;
414 | RotateLeft(n.Sibling());
415 | }
416 |
417 | DeleteCase6(n);
418 | }
419 |
420 | private void DeleteCase6(IRbNode n)
421 | {
422 | n.Sibling().Color = NodeColor(n.Parent);
423 | n.Parent.Color = Color.Black;
424 | if (Equals(n, n.Parent.Left))
425 | {
426 | //assert nodeColor(n.sibling().right) == Color.RED;
427 | n.Sibling().Right.Color = Color.Black;
428 | RotateLeft(n.Parent);
429 | }
430 | else
431 | {
432 | //assert nodeColor(n.sibling().left) == Color.RED;
433 | n.Sibling().Left.Color = Color.Black;
434 | RotateRight(n.Parent);
435 | }
436 | }
437 |
438 | public void VisitTree(Action action)
439 | {
440 | //IN Order visit
441 | var walker = Root;
442 |
443 | if (walker != null)
444 | DoVisitTree(action, walker);
445 | }
446 |
447 | private static void DoVisitTree(Action action, IRbNode walker)
448 | {
449 | if (walker.Left != null)
450 | {
451 | DoVisitTree(action, walker.Left);
452 | }
453 |
454 | action?.Invoke(walker);
455 |
456 | if (walker.Right != null)
457 | {
458 | DoVisitTree(action, walker.Right);
459 | }
460 | }
461 |
462 | internal void VisitTreeNodes(Action action)
463 | {
464 | //IN Order visit
465 | var walker = Root;
466 |
467 | if (walker != null)
468 | DoVisitTreeNodes(action, walker);
469 | }
470 |
471 | private static void DoVisitTreeNodes(Action action, IRbNode walker)
472 | {
473 | if (walker.Left != null)
474 | {
475 | DoVisitTreeNodes(action, walker.Left);
476 | }
477 |
478 | action?.Invoke(walker);
479 |
480 | if (walker.Right != null)
481 | {
482 | DoVisitTreeNodes(action, walker.Right);
483 | }
484 | }
485 |
486 | public class RbTreeEnumerator : IEnumerator
487 | {
488 | int _position = -1;
489 |
490 | private readonly Queue _heap = new Queue();
491 |
492 | internal RbTreeEnumerator(RbTree tree)
493 | {
494 | tree.VisitTreeNodes(item => _heap.Enqueue(item));
495 | }
496 |
497 | public IRbNode Current => _heap.ElementAt(_position);
498 |
499 | public void Dispose()
500 | {
501 | }
502 |
503 | object System.Collections.IEnumerator.Current => _heap.ElementAt(_position);
504 |
505 | public bool MoveNext()
506 | {
507 | _position++;
508 | return (_position < _heap.Count);
509 | }
510 |
511 | public void Reset()
512 | {
513 | _position = -1;
514 | }
515 | }
516 |
517 | public RbTreeEnumerator GetEnumerator()
518 | {
519 | return new RbTreeEnumerator(this);
520 | }
521 |
522 | private static int _indentStep = 15;
523 |
524 | public void Print()
525 | {
526 | PrintHelper(Root, 0);
527 | }
528 |
529 | private static void PrintHelper(IRbNode n, int indent)
530 | {
531 | if (n == null)
532 | {
533 | Trace.WriteLine("");
534 | return;
535 | }
536 |
537 | if (n.Left != null)
538 | {
539 | PrintHelper(n.Left, indent + _indentStep);
540 | }
541 |
542 | for (var i = 0; i < indent; i++)
543 | Trace.Write(" ");
544 | if (n.Color == Color.Black)
545 | Trace.WriteLine(" " + n + " ");
546 | else
547 | Trace.WriteLine("<" + n + ">");
548 |
549 | if (n.Right != null)
550 | {
551 | PrintHelper(n.Right, indent + _indentStep);
552 | }
553 | }
554 |
555 | internal void FireNodeOperation(IRbNode node, NodeOperation operation)
556 | {
557 | if (NodeOperation != null)
558 | NodeOperation(node, operation);
559 | }
560 |
561 | //internal void FireValueAssigned(RBNode node, V value)
562 | //{
563 | // if (ValueAssignedAction != null)
564 | // ValueAssignedAction(node, value);
565 | //}
566 |
567 | internal event Action NodeInserted;
568 |
569 | //internal event Action> NodeDeleted;
570 | internal event Action NodeOperation;
571 | }
572 |
573 | internal enum NodeOperation
574 | {
575 | LeftAssigned,
576 | RightAssigned,
577 | ColorAssigned,
578 | ParentAssigned,
579 | ValueAssigned
580 | }
581 | }
--------------------------------------------------------------------------------
/src/Sector.cs:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | *
5 | * The Original Code is OpenMCDF - Compound Document Format library.
6 | *
7 | * The Initial Developer of the Original Code is Federico Blaseotto.*/
8 |
9 |
10 | using System;
11 | using System.IO;
12 |
13 |
14 | namespace OpenMcdf
15 | {
16 | internal enum SectorType
17 | {
18 | Normal,
19 | Mini,
20 | FAT,
21 | DIFAT,
22 | RangeLockSector,
23 | Directory
24 | }
25 |
26 | internal class Sector : IDisposable
27 | {
28 | public const int MINISECTOR_SIZE = 64;
29 |
30 | public const int FREESECT = unchecked((int) 0xFFFFFFFF);
31 | public const int ENDOFCHAIN = unchecked((int) 0xFFFFFFFE);
32 | public const int FATSECT = unchecked((int) 0xFFFFFFFD);
33 | public const int DIFSECT = unchecked((int) 0xFFFFFFFC);
34 |
35 | private readonly Stream _stream;
36 |
37 | private readonly object _lockObject = new object();
38 |
39 | public bool DirtyFlag { get; set; }
40 |
41 | public bool IsStreamed => (_stream != null && Size != MINISECTOR_SIZE) && (Id * Size) + Size < _stream.Length;
42 |
43 | public Sector(int size, Stream stream)
44 | {
45 | Size = size;
46 | _stream = stream;
47 | }
48 |
49 | public Sector(int size, byte[] data)
50 | {
51 | Size = size;
52 | _data = data;
53 | _stream = null;
54 | }
55 |
56 | public Sector(int size)
57 | {
58 | Size = size;
59 | _data = null;
60 | _stream = null;
61 | }
62 |
63 | internal SectorType Type { get; set; }
64 |
65 | public int Id { get; set; } = -1;
66 |
67 | public int Size { get; private set; }
68 |
69 | private byte[] _data;
70 |
71 | public byte[] GetData()
72 | {
73 | if (_data != null)
74 | return _data;
75 |
76 | _data = new byte[Size];
77 |
78 | if (!IsStreamed)
79 | return _data;
80 |
81 | _stream.Seek(Size + Id * (long) Size, SeekOrigin.Begin);
82 | _stream.Read(_data, 0, Size);
83 |
84 | return _data;
85 | }
86 |
87 | public void ZeroData()
88 | {
89 | _data = new byte[Size];
90 | DirtyFlag = true;
91 | }
92 |
93 | public void InitFATData()
94 | {
95 | _data = new byte[Size];
96 |
97 | for (var i = 0; i < Size; i++)
98 | _data[i] = 0xFF;
99 |
100 | DirtyFlag = true;
101 | }
102 |
103 | internal void ReleaseData()
104 | {
105 | _data = null;
106 | }
107 |
108 | ///
109 | /// When called from user code, release all resources, otherwise, in the case runtime called it,
110 | /// only unmanaged resources are released.
111 | ///
112 | /// If true, method has been called from User code, if false it's been called from .net runtime
113 | protected virtual void Dispose(bool disposing)
114 | {
115 | try
116 | {
117 | if (_disposed)
118 | return;
119 |
120 | lock (_lockObject)
121 | {
122 | if (disposing)
123 | {
124 | // Call from user code...
125 | }
126 |
127 | _data = null;
128 | DirtyFlag = false;
129 | Id = ENDOFCHAIN;
130 | Size = 0;
131 | }
132 | }
133 | finally
134 | {
135 | _disposed = true;
136 | }
137 | }
138 |
139 | #region IDisposable Members
140 |
141 | private bool _disposed; //false
142 |
143 | void IDisposable.Dispose()
144 | {
145 | Dispose(true);
146 | GC.SuppressFinalize(this);
147 | }
148 |
149 | #endregion
150 | }
151 | }
--------------------------------------------------------------------------------
/src/SectorCollection.cs:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | *
5 | * The Original Code is OpenMCDF - Compound Document Format library.
6 | *
7 | * The Initial Developer of the Original Code is Federico Blaseotto.*/
8 |
9 | using System;
10 | using System.Collections;
11 | using System.Collections.Generic;
12 |
13 | namespace OpenMcdf
14 | {
15 | ///
16 | /// Action to implement when transaction support - sector
17 | /// has to be written to the underlying stream (see specs).
18 | ///
19 | public delegate void Ver3SizeLimitReached();
20 |
21 | ///
22 | ///
23 | /// Ad-hoc Heap Friendly sector collection to avoid using
24 | /// large array that may create some problem to GC collection
25 | /// (see http://www.simple-talk.com/dotnet/.net-framework/the-dangers-of-the-large-object-heap/ )
26 | ///
27 | internal class SectorCollection : IList
28 | {
29 | private const int MAX_SECTOR_V4_COUNT_LOCK_RANGE = 524287; //0x7FFFFF00 for Version 4
30 | private const int SLICE_SIZE = 4096;
31 |
32 | private readonly List _largeArraySlices;
33 |
34 | private bool _sizeLimitReached;
35 |
36 | public SectorCollection()
37 | {
38 | _largeArraySlices = new List();
39 | }
40 |
41 | private void DoCheckSizeLimitReached()
42 | {
43 | if (_sizeLimitReached || (Count - 1 <= MAX_SECTOR_V4_COUNT_LOCK_RANGE))
44 | return;
45 |
46 | OnVer3SizeLimitReached?.Invoke();
47 | _sizeLimitReached = true;
48 | }
49 |
50 | public event Ver3SizeLimitReached OnVer3SizeLimitReached;
51 |
52 | #region IList Members
53 |
54 | public int IndexOf(Sector item)
55 | {
56 | throw new NotImplementedException();
57 | }
58 |
59 | public void Insert(int index, Sector item)
60 | {
61 | throw new NotImplementedException();
62 | }
63 |
64 | public void RemoveAt(int index)
65 | {
66 | throw new NotImplementedException();
67 | }
68 |
69 | public Sector this[int index]
70 | {
71 | get
72 | {
73 | var itemIndex = index / SLICE_SIZE;
74 | var itemOffset = index % SLICE_SIZE;
75 |
76 | if ((index > -1) && (index < Count))
77 | {
78 | return (Sector) _largeArraySlices[itemIndex][itemOffset];
79 | }
80 | throw new ArgumentOutOfRangeException("index", index, "Argument out of range");
81 | }
82 |
83 | set
84 | {
85 | var itemIndex = index / SLICE_SIZE;
86 | var itemOffset = index % SLICE_SIZE;
87 |
88 | if (index > -1 && index < Count)
89 | {
90 | _largeArraySlices[itemIndex][itemOffset] = value;
91 | }
92 | else
93 | throw new ArgumentOutOfRangeException("index", index, "Argument out of range");
94 | }
95 | }
96 |
97 | #endregion
98 |
99 | #region ICollection Members
100 |
101 | public void Add(Sector item)
102 | {
103 | DoCheckSizeLimitReached();
104 |
105 | var itemIndex = Count / SLICE_SIZE;
106 |
107 | if (itemIndex < _largeArraySlices.Count)
108 | {
109 | _largeArraySlices[itemIndex].Add(item);
110 | Count++;
111 | }
112 | else
113 | {
114 | var ar = new ArrayList(SLICE_SIZE) { item };
115 | _largeArraySlices.Add(ar);
116 | Count++;
117 | }
118 | }
119 |
120 | public void Clear()
121 | {
122 | foreach (var slice in _largeArraySlices)
123 | {
124 | slice.Clear();
125 | }
126 |
127 | _largeArraySlices.Clear();
128 |
129 | Count = 0;
130 | }
131 |
132 | public bool Contains(Sector item)
133 | {
134 | throw new NotImplementedException();
135 | }
136 |
137 | public void CopyTo(Sector[] array, int arrayIndex)
138 | {
139 | throw new NotImplementedException();
140 | }
141 |
142 | public int Count { get; private set; }
143 |
144 | public bool IsReadOnly => false;
145 |
146 | public bool Remove(Sector item)
147 | {
148 | throw new NotImplementedException();
149 | }
150 |
151 | #endregion
152 |
153 | #region IEnumerable Members
154 |
155 | public IEnumerator GetEnumerator()
156 | {
157 | for (var i = 0; i < _largeArraySlices.Count; i++)
158 | {
159 | for (var j = 0; j < _largeArraySlices[i].Count; j++)
160 | {
161 | yield return (Sector) _largeArraySlices[i][j];
162 | }
163 | }
164 | }
165 |
166 | #endregion
167 |
168 | #region IEnumerable Members
169 |
170 | IEnumerator IEnumerable.GetEnumerator()
171 | {
172 | foreach (var arrayList in _largeArraySlices)
173 | {
174 | foreach (var obj in arrayList)
175 | {
176 | yield return obj;
177 | }
178 | }
179 | }
180 |
181 | #endregion
182 | }
183 | }
--------------------------------------------------------------------------------
/src/StreamRW.cs:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | *
5 | * The Original Code is OpenMCDF - Compound Document Format library.
6 | *
7 | * The Initial Developer of the Original Code is Federico Blaseotto.*/
8 |
9 | using System.IO;
10 |
11 | namespace OpenMcdf
12 | {
13 | internal class StreamRW
14 | {
15 | private readonly byte[] _buffer;
16 | private readonly Stream _stream;
17 |
18 | public StreamRW(Stream stream)
19 | {
20 | _stream = stream;
21 | _buffer = new byte[8];
22 | }
23 |
24 | public long Seek(long offset)
25 | {
26 | return _stream.Seek(offset, SeekOrigin.Begin);
27 | }
28 |
29 | public byte ReadByte()
30 | {
31 | _stream.Read(_buffer, 0, 1);
32 | return _buffer[0];
33 | }
34 |
35 | public ushort ReadUInt16()
36 | {
37 | _stream.Read(_buffer, 0, 2);
38 | return (ushort) (_buffer[0] | (_buffer[1] << 8));
39 | }
40 |
41 | public int ReadInt32()
42 | {
43 | _stream.Read(_buffer, 0, 4);
44 | return _buffer[0] | (_buffer[1] << 8) | (_buffer[2] << 16) | (_buffer[3] << 24);
45 | }
46 |
47 | public uint ReadUInt32()
48 | {
49 | _stream.Read(_buffer, 0, 4);
50 | return (uint) (_buffer[0] | (_buffer[1] << 8) | (_buffer[2] << 16) | (_buffer[3] << 24));
51 | }
52 |
53 | public long ReadInt64()
54 | {
55 | _stream.Read(_buffer, 0, 8);
56 | var ls = (uint) (_buffer[0] | (_buffer[1] << 8) | (_buffer[2] << 16) | (_buffer[3] << 24));
57 | var ms = (uint) ((_buffer[4]) | (_buffer[5] << 8) | (_buffer[6] << 16) | (_buffer[7] << 24));
58 | return (long) (((ulong) ms << 32) | ls);
59 | }
60 |
61 | public ulong ReadUInt64()
62 | {
63 | _stream.Read(_buffer, 0, 8);
64 | return (ulong) (_buffer[0] | (_buffer[1] << 8) | (_buffer[2] << 16) | (_buffer[3] << 24) | (_buffer[4] << 32) |
65 | (_buffer[5] << 40) | (_buffer[6] << 48) | (_buffer[7] << 56));
66 | }
67 |
68 | public byte[] ReadBytes(int count)
69 | {
70 | var result = new byte[count];
71 | _stream.Read(result, 0, count);
72 | return result;
73 | }
74 |
75 | public byte[] ReadBytes(int count, out int rCount)
76 | {
77 | var result = new byte[count];
78 | rCount = _stream.Read(result, 0, count);
79 | return result;
80 | }
81 |
82 | public void Write(byte b)
83 | {
84 | _buffer[0] = b;
85 | _stream.Write(_buffer, 0, 1);
86 | }
87 |
88 | public void Write(ushort value)
89 | {
90 | _buffer[0] = (byte) value;
91 | _buffer[1] = (byte) (value >> 8);
92 |
93 | _stream.Write(_buffer, 0, 2);
94 | }
95 |
96 | public void Write(int value)
97 | {
98 | _buffer[0] = (byte) value;
99 | _buffer[1] = (byte) (value >> 8);
100 | _buffer[2] = (byte) (value >> 16);
101 | _buffer[3] = (byte) (value >> 24);
102 |
103 | _stream.Write(_buffer, 0, 4);
104 | }
105 |
106 | public void Write(long value)
107 | {
108 | _buffer[0] = (byte) value;
109 | _buffer[1] = (byte) (value >> 8);
110 | _buffer[2] = (byte) (value >> 16);
111 | _buffer[3] = (byte) (value >> 24);
112 | _buffer[4] = (byte) (value >> 32);
113 | _buffer[5] = (byte) (value >> 40);
114 | _buffer[6] = (byte) (value >> 48);
115 | _buffer[7] = (byte) (value >> 56);
116 |
117 | _stream.Write(_buffer, 0, 8);
118 | }
119 |
120 | public void Write(uint value)
121 | {
122 | _buffer[0] = (byte) value;
123 | _buffer[1] = (byte) (value >> 8);
124 | _buffer[2] = (byte) (value >> 16);
125 | _buffer[3] = (byte) (value >> 24);
126 |
127 | _stream.Write(_buffer, 0, 4);
128 | }
129 |
130 | public void Write(byte[] value)
131 | {
132 | _stream.Write(value, 0, value.Length);
133 | }
134 |
135 | public void Close()
136 | {
137 | //Nothing to do ;-)
138 | }
139 | }
140 | }
--------------------------------------------------------------------------------
/src/StreamView.cs:
--------------------------------------------------------------------------------
1 | /* This Source Code Form is subject to the terms of the Mozilla Public
2 | * License, v. 2.0. If a copy of the MPL was not distributed with this
3 | * file, You can obtain one at http://mozilla.org/MPL/2.0/.
4 | *
5 | * The Original Code is OpenMCDF - Compound Document Format library.
6 | *
7 | * The Initial Developer of the Original Code is Federico Blaseotto.*/
8 |
9 |
10 | using System;
11 | using System.Collections.Generic;
12 | using System.IO;
13 |
14 | namespace OpenMcdf
15 | {
16 | ///
17 | ///
18 | /// Stream decorator for a Sector or miniSector chain
19 | ///
20 | internal class StreamView : Stream
21 | {
22 | private readonly int _sectorSize;
23 |
24 | private long _position;
25 |
26 | private readonly Stream _stream;
27 |
28 | private readonly bool _isFatStream;
29 |
30 | public StreamView(IList sectorChain, int sectorSize, Stream stream)
31 | {
32 | if (sectorSize <= 0)
33 | throw new CFException("Sector size must be greater than zero");
34 |
35 | BaseSectorChain = sectorChain ?? throw new CFException("Sector Chain cannot be null");
36 | _sectorSize = sectorSize;
37 | _stream = stream;
38 | }
39 |
40 | public StreamView(IList sectorChain, int sectorSize, long length, Queue availableSectors,
41 | Stream stream, bool isFatStream = false)
42 | : this(sectorChain, sectorSize, stream)
43 | {
44 | _isFatStream = isFatStream;
45 | AdjustLength(length, availableSectors);
46 | }
47 |
48 | public IEnumerable FreeSectors { get; } = new List();
49 |
50 | public IList BaseSectorChain { get; }
51 |
52 | public override bool CanRead => true;
53 |
54 | public override bool CanSeek => true;
55 |
56 | public override bool CanWrite => true;
57 |
58 | public override void Flush()
59 | {
60 | }
61 |
62 | private long _length;
63 |
64 | public override long Length => _length;
65 |
66 | public override long Position
67 | {
68 | get => _position;
69 |
70 | set
71 | {
72 | if (_position > _length - 1)
73 | throw new ArgumentOutOfRangeException(nameof(value));
74 |
75 | _position = value;
76 | }
77 | }
78 |
79 | #if !NETSTANDARD1_6 && !NETSTANDARD2_0
80 | public override void Close()
81 | {
82 | base.Close();
83 | }
84 | #endif
85 |
86 | private byte[] _buf = new byte[4];
87 |
88 | public int ReadInt32()
89 | {
90 | Read(_buf, 0, 4);
91 | return (((_buf[0] | (_buf[1] << 8)) | (_buf[2] << 16)) | (_buf[3] << 24));
92 | }
93 |
94 | public override int Read(byte[] buffer, int offset, int count)
95 | {
96 | var nRead = 0;
97 | int nToRead;
98 |
99 | if (BaseSectorChain == null || BaseSectorChain.Count <= 0)
100 | return 0;
101 |
102 | // First sector
103 | var secIndex = (int) (_position / _sectorSize);
104 |
105 | // Bytes to read count is the min between request count
106 | // and sector border
107 |
108 | nToRead = Math.Min(
109 | BaseSectorChain[0].Size - ((int) _position % _sectorSize),
110 | count);
111 |
112 | if (secIndex < BaseSectorChain.Count)
113 | {
114 | Buffer.BlockCopy(
115 | BaseSectorChain[secIndex].GetData(),
116 | (int) (_position % _sectorSize),
117 | buffer,
118 | offset,
119 | nToRead
120 | );
121 | }
122 |
123 | nRead += nToRead;
124 |
125 | secIndex++;
126 |
127 | // Central sectors
128 | while (nRead < (count - _sectorSize))
129 | {
130 | nToRead = _sectorSize;
131 |
132 | Buffer.BlockCopy(
133 | BaseSectorChain[secIndex].GetData(),
134 | 0,
135 | buffer,
136 | offset + nRead,
137 | nToRead
138 | );
139 |
140 | nRead += nToRead;
141 | secIndex++;
142 | }
143 |
144 | // Last sector
145 | nToRead = count - nRead;
146 |
147 | if (nToRead != 0)
148 | {
149 | Buffer.BlockCopy(
150 | BaseSectorChain[secIndex].GetData(),
151 | 0,
152 | buffer,
153 | offset + nRead,
154 | nToRead
155 | );
156 |
157 | nRead += nToRead;
158 | }
159 |
160 | _position += nRead;
161 |
162 | return nRead;
163 | }
164 |
165 | public override long Seek(long offset, SeekOrigin origin)
166 | {
167 | switch (origin)
168 | {
169 | case SeekOrigin.Begin:
170 | _position = offset;
171 | break;
172 |
173 | case SeekOrigin.Current:
174 | _position += offset;
175 | break;
176 |
177 | case SeekOrigin.End:
178 | _position = Length - offset;
179 | break;
180 |
181 | default:
182 | throw new ArgumentOutOfRangeException(nameof(origin), origin, null);
183 | }
184 |
185 | AdjustLength(_position);
186 |
187 | return _position;
188 | }
189 |
190 | private void AdjustLength(long value, Queue availableSectors = null)
191 | {
192 | _length = value;
193 |
194 | var delta = value - (BaseSectorChain.Count * (long) _sectorSize);
195 |
196 | if (delta <= 0)
197 | return;
198 |
199 | // enlargement required
200 | var nSec = (int) Math.Ceiling(((double) delta / _sectorSize));
201 |
202 | while (nSec > 0)
203 | {
204 | Sector t;
205 |
206 | if (availableSectors == null || availableSectors.Count == 0)
207 | {
208 | t = new Sector(_sectorSize, _stream);
209 |
210 | if (_sectorSize == Sector.MINISECTOR_SIZE)
211 | t.Type = SectorType.Mini;
212 | }
213 | else
214 | {
215 | t = availableSectors.Dequeue();
216 | }
217 |
218 | if (_isFatStream)
219 | {
220 | t.InitFATData();
221 | }
222 | BaseSectorChain.Add(t);
223 | nSec--;
224 | }
225 | }
226 |
227 | public override void SetLength(long value)
228 | {
229 | AdjustLength(value);
230 | }
231 |
232 | public void WriteInt32(int val)
233 | {
234 | var buffer = new byte[4];
235 | buffer[0] = (byte) val;
236 | buffer[1] = (byte) (val << 8);
237 | buffer[2] = (byte) (val << 16);
238 | buffer[3] = (byte) (val << 32);
239 | Write(buffer, 0, 4);
240 | }
241 |
242 | public override void Write(byte[] buffer, int offset, int count)
243 | {
244 | var byteWritten = 0;
245 | int roundByteWritten;
246 |
247 | // Assure length
248 | if ((_position + count) > _length)
249 | AdjustLength((_position + count));
250 |
251 | if (BaseSectorChain == null)
252 | return;
253 |
254 | // First sector
255 | var secOffset = (int) (_position / _sectorSize);
256 | var secShift = (int) _position % _sectorSize;
257 |
258 | roundByteWritten = Math.Min(_sectorSize - (int) (_position % _sectorSize), count);
259 |
260 | if (secOffset < BaseSectorChain.Count)
261 | {
262 | Buffer.BlockCopy(
263 | buffer,
264 | offset,
265 | BaseSectorChain[secOffset].GetData(),
266 | secShift,
267 | roundByteWritten
268 | );
269 |
270 | BaseSectorChain[secOffset].DirtyFlag = true;
271 | }
272 |
273 | byteWritten += roundByteWritten;
274 | offset += roundByteWritten;
275 | secOffset++;
276 |
277 | // Central sectors
278 | while (byteWritten < (count - _sectorSize))
279 | {
280 | roundByteWritten = _sectorSize;
281 |
282 | Buffer.BlockCopy(
283 | buffer,
284 | offset,
285 | BaseSectorChain[secOffset].GetData(),
286 | 0,
287 | roundByteWritten
288 | );
289 |
290 | BaseSectorChain[secOffset].DirtyFlag = true;
291 |
292 | byteWritten += roundByteWritten;
293 | offset += roundByteWritten;
294 | secOffset++;
295 | }
296 |
297 | // Last sector
298 | roundByteWritten = count - byteWritten;
299 |
300 | if (roundByteWritten != 0)
301 | {
302 | Buffer.BlockCopy(
303 | buffer,
304 | offset,
305 | BaseSectorChain[secOffset].GetData(),
306 | 0,
307 | roundByteWritten
308 | );
309 |
310 | BaseSectorChain[secOffset].DirtyFlag = true;
311 |
312 | offset += roundByteWritten;
313 | byteWritten += roundByteWritten;
314 | }
315 |
316 | _position += count;
317 | }
318 | }
319 | }
--------------------------------------------------------------------------------
/tests/OpenMcdf.Test/AssetDeployer.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using NUnit.Framework;
4 |
5 | namespace OpenMcdf.Test
6 | {
7 | [SetUpFixture]
8 | public class AssetDeployer
9 | {
10 | [OneTimeSetUp]
11 | public void DeployAssetOnce()
12 | {
13 | Directory.SetCurrentDirectory(TestContext.CurrentContext.WorkDirectory);
14 |
15 | Console.WriteLine($"Test context working directory: {TestContext.CurrentContext.WorkDirectory}");
16 | Console.WriteLine($"Test context test directory: {TestContext.CurrentContext.TestDirectory}");
17 |
18 | var codeBaseDir = new DirectoryInfo(TestContext.CurrentContext.TestDirectory);
19 | var testAssets = Path.Combine("tests", "assets");
20 | var assetsDir = Path.Combine(codeBaseDir.FullName, testAssets);
21 | while (!Directory.Exists(assetsDir) &&
22 | !Path.GetPathRoot(codeBaseDir.FullName).Equals(codeBaseDir.FullName))
23 | {
24 | codeBaseDir = codeBaseDir.Parent;
25 | assetsDir = Path.Combine(codeBaseDir.FullName, testAssets);
26 | }
27 |
28 | foreach (var assetPath in Directory.GetFiles(assetsDir))
29 | {
30 | var assetInTests = Path.Combine(TestContext.CurrentContext.WorkDirectory, Path.GetFileName(assetPath));
31 | File.Copy(assetPath, assetInTests, true);
32 | }
33 | }
34 | }
35 | }
--------------------------------------------------------------------------------
/tests/OpenMcdf.Test/AuthoringTests.txt:
--------------------------------------------------------------------------------
1 | ==========================================================================
2 | Visual Studio Team System: Overview of Authoring and Running Tests
3 | ==========================================================================
4 |
5 | This overview describes the features for authoring and running tests in
6 | Visual Studio Team System and Visual Studio Team Edition for Software Testers.
7 |
8 | Opening Tests
9 | -------------
10 | To open a test, open a test project or a test metadata file (a file with
11 | extension .vsmdi) that contains the definition of the test. You can find
12 | test projects and metadata files in Solution Explorer.
13 |
14 | Viewing Tests
15 | -------------
16 | To see which tests are available to you, open the Test View window. Or,
17 | if you have installed Team Edition for Software Testers, you can also open
18 | the Test List Editor window to view tests.
19 |
20 | To open the Test View window, click the Test menu, point to Windows, and
21 | then click Test View. To open the Test List Editor window (if you have
22 | installed Team Edition for Software Testers), click Test, point to Windows,
23 | and then click Test List Editor.
24 |
25 | Running Tests
26 | -------------
27 | You can run tests from the Test View window and the Test List Editor window.
28 | See Viewing Tests to learn how to open these windows. To run one or more
29 | tests displayed in the Test View window, first select the tests in that
30 | window; to select multiple tests, hold either the Shift or CTRL key while
31 | clicking tests. Then click the Run Tests button in the Test View window
32 | toolbar.
33 |
34 | If you have installed Visual Studio Team Edition for Software Testers, you can
35 | also use the Test List Editor window to run tests. To run tests in Test List Editor,
36 | select the check box next to each test that you want to run. Then click the
37 | Run Tests button in the Test List Editor window toolbar.
38 |
39 | Viewing Test Results
40 | --------------------
41 | When you run a test or a series of tests, the results of the test run will be
42 | shown in the Test Results window. Each individual test in the run is shown on
43 | a separate line so that you can see its status. The window contains an
44 | embedded status bar in the top half of the window that provides you with
45 | summary details of the complete test run.
46 |
47 | To see more detailed results for a particular test result, double-click it in
48 | the Test Results window. This opens a window that provides more information
49 | about the particular test result, such as any specific error messages returned
50 | by the test.
51 |
52 | Changing the way that tests are run
53 | -----------------------------------
54 | Each time you run one or more tests, a collection of settings is used to
55 | determine how those tests are run. These settings are contained in a “test
56 | run configuration” file.
57 |
58 | Here is a partial list of the changes you can make with a test run
59 | configuration file:
60 |
61 | - Change the naming scheme for each test run.
62 | - Change the test controller that the tests are run on so that you can run
63 | tests remotely.
64 | - Gather code coverage data for the code being tested so that you can see
65 | which lines of code are covered by your tests.
66 | - Enable and disable test deployment.
67 | - Specify additional files to deploy before tests are run.
68 | - Select a different host, ASP.NET, for running ASP.NET unit tests.
69 | - Select a different host, the smart device test host, for running smart device unit tests.
70 | - Set various properties for the test agents that run your tests.
71 | - Run custom scripts at the start and end of each test run so that you can
72 | set up the test environment exactly as required each time tests are run.
73 | - Set time limits for tests and test runs.
74 | - Set the browser mix and the number of times to repeat Web tests in the
75 | test run.
76 |
77 | By default, a test run configuration file is created whenever you create a
78 | new test project. You make changes to this file by double-clicking it in
79 | Solution Explorer and then changing its settings. (Test run configuration
80 | files have the extension .testrunconfig.)
81 |
82 | A solution can contain multiple test run configuration files. Only one of
83 | those files, known as the “Active” test run configuration file, is used to
84 | determine the settings that are currently used for test runs. You select
85 | the active test run configuration by clicking Select Active Test Run
86 | Configuration on the Test menu.
87 |
88 | -------------------------------------------------------------------------------
89 |
90 | Test Types
91 | ----------
92 | Using Visual Studio Team Edition for Software Testers, you can create a number
93 | of different test types:
94 |
95 | Unit test: Use a unit test to create a programmatic test in C++, Visual C# or
96 | Visual Basic that exercises source code. A unit test calls the methods of a
97 | class, passing suitable parameters, and verifies that the returned value is
98 | what you expect.
99 | There are three specialized variants of unit tests:
100 | - Data-driven unit tests are created when you configure a unit test to be
101 | called repeatedly for each row of a data source. The data from each row
102 | is used by the unit test as input data.
103 | - ASP.NET unit tests are unit tests that exercise code in an ASP.NET Web
104 | application.
105 | - Smart device unit tests are unit tests that are deployed to a smart device
106 | or emulator and then executed by the smart device test host.
107 |
108 | Web Test: Web tests consist of an ordered series of HTTP requests that you
109 | record in a browser session using Microsoft Internet Explorer. You can have
110 | the test report specific details about the pages or sites it requests, such
111 | as whether a particular page contains a specified string.
112 |
113 | Load Test: You use a load test to encapsulate non-manual tests, such as
114 | unit, Web, and generic tests, and then run them simultaneously by using
115 | virtual users. Running these tests under load generates test results,
116 | including performance and other counters, in tables and in graphs.
117 |
118 | Generic test: A generic test is an existing program wrapped to function as a
119 | test in Visual Studio. The following are examples of tests or programs that
120 | you can turn into generic tests:
121 | - An existing test that uses process exit codes to communicate whether the
122 | test passed or failed. 0 indicates passing and any other value indicates
123 | a failure.
124 | - A general program to obtain specific functionality during a test scenario.
125 | - A test or program that uses a special XML file (called a “summary results
126 | file”), to communicate detailed results.
127 |
128 | Manual test: The manual test type is used when the test tasks are to be
129 | completed by a test engineer as opposed to an automated script.
130 |
131 | Ordered test: Use an ordered test to execute a set of tests in an order you
132 | specify.
133 |
134 | -------------------------------------------------------------------------------
135 |
136 |
137 |
--------------------------------------------------------------------------------
/tests/OpenMcdf.Test/CFSStreamExtensionsTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using NUnit.Framework;
4 | using OpenMcdf.Extensions;
5 |
6 | namespace OpenMcdf.Test
7 | {
8 | ///
9 | /// Summary description for UnitTest1
10 | ///
11 | [TestFixture]
12 | public class CFSStreamExtensionsTest
13 | {
14 | [Test]
15 | public void Test_AS_IOSTREAM_READ()
16 | {
17 | CompoundFile cf = new CompoundFile("MultipleStorage.cfs");
18 |
19 | Stream s = cf.RootStorage.GetStorage("MyStorage").GetStream("MyStream").AsIoStream();
20 | BinaryReader br = new BinaryReader(s);
21 | byte[] result = br.ReadBytes(32);
22 | Assert.IsTrue(Helpers.CompareBuffer(Helpers.GetBuffer(32, 1), result));
23 | }
24 |
25 | [Test]
26 | public void Test_AS_IOSTREAM_WRITE()
27 | {
28 | const String cmp = "Hello World of BinaryWriter !";
29 |
30 | CompoundFile cf = new CompoundFile();
31 | Stream s = cf.RootStorage.AddStream("ANewStream").AsIoStream();
32 | BinaryWriter bw = new BinaryWriter(s);
33 | bw.Write(cmp);
34 | cf.Save("$ACFFile.cfs");
35 | cf.Close();
36 |
37 | cf = new CompoundFile("$ACFFile.cfs");
38 | BinaryReader br = new BinaryReader(cf.RootStorage.GetStream("ANewStream").AsIoStream());
39 | String st = br.ReadString();
40 | Assert.IsTrue(st == cmp);
41 | cf.Close();
42 | }
43 | }
44 | }
--------------------------------------------------------------------------------
/tests/OpenMcdf.Test/CFSTorageTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using System.IO;
4 | using NUnit.Framework;
5 |
6 | namespace OpenMcdf.Test
7 | {
8 | ///
9 | /// Summary description for CFTorageTest
10 | ///
11 | [TestFixture]
12 | public class CFSTorageTest
13 | {
14 | [Test]
15 | public void Test_CREATE_STORAGE()
16 | {
17 | const String STORAGE_NAME = "NewStorage";
18 | CompoundFile cf = new CompoundFile();
19 |
20 | CFStorage st = cf.RootStorage.AddStorage(STORAGE_NAME);
21 |
22 | Assert.IsNotNull(st);
23 | Assert.AreEqual(STORAGE_NAME, st.Name);
24 | }
25 |
26 | [Test]
27 | public void Test_CREATE_STORAGE_WITH_CREATION_DATE()
28 | {
29 | const String STORAGE_NAME = "NewStorage1";
30 | CompoundFile cf = new CompoundFile();
31 |
32 | CFStorage st = cf.RootStorage.AddStorage(STORAGE_NAME);
33 | st.CreationDate = DateTime.Now;
34 |
35 | Assert.IsNotNull(st);
36 | Assert.AreEqual(STORAGE_NAME, st.Name);
37 |
38 | cf.Save("ProvaData.cfs");
39 | cf.Close();
40 | }
41 |
42 | [Test]
43 | public void Test_VISIT_ENTRIES()
44 | {
45 | const String STORAGE_NAME = "report.xls";
46 | CompoundFile cf = new CompoundFile(STORAGE_NAME);
47 |
48 | FileStream output = new FileStream("LogEntries.txt", FileMode.Create);
49 | TextWriter tw = new StreamWriter(output);
50 |
51 | Action va = delegate(CFItem item) { tw.WriteLine(item.Name); };
52 |
53 | cf.RootStorage.VisitEntries(va, true);
54 |
55 | tw.Close();
56 | }
57 |
58 |
59 | [Test]
60 | public void Test_TRY_GET_STREAM_STORAGE()
61 | {
62 | String FILENAME = "MultipleStorage.cfs";
63 | CompoundFile cf = new CompoundFile(FILENAME);
64 |
65 | CFStorage st = cf.RootStorage.TryGetStorage("MyStorage");
66 | Assert.IsNotNull(st);
67 |
68 | try
69 | {
70 | CFStorage nf = cf.RootStorage.TryGetStorage("IDONTEXIST");
71 | Assert.IsNull(nf);
72 | }
73 | catch (Exception)
74 | {
75 | Assert.Fail("Exception raised for try_get method");
76 | }
77 |
78 | try
79 | {
80 | CFStream s = st.TryGetStream("MyStream");
81 | Assert.IsNotNull(s);
82 | CFStream ns = st.TryGetStream("IDONTEXIST2");
83 | Assert.IsNull(ns);
84 | }
85 | catch (Exception)
86 | {
87 | Assert.Fail("Exception raised for try_get method");
88 | }
89 | }
90 |
91 | [Test]
92 | public void Test_VISIT_ENTRIES_CORRUPTED_FILE_VALIDATION_ON()
93 | {
94 | CompoundFile f = null;
95 |
96 | try
97 | {
98 | f = new CompoundFile("CorruptedDoc_bug3547815.doc", CFSUpdateMode.ReadOnly,
99 | CFSConfiguration.NoValidationException);
100 | }
101 | catch
102 | {
103 | Assert.Fail("No exception has to be fired on creation due to lazy loading");
104 | }
105 |
106 | FileStream output = null;
107 |
108 | try
109 | {
110 | output = new FileStream("LogEntriesCorrupted_1.txt", FileMode.Create);
111 |
112 | using (TextWriter tw = new StreamWriter(output))
113 | {
114 | Action va = delegate(CFItem item) { tw.WriteLine(item.Name); };
115 |
116 | f.RootStorage.VisitEntries(va, true);
117 | tw.Flush();
118 | }
119 | }
120 | catch (Exception ex)
121 | {
122 | Assert.IsTrue(ex is CFCorruptedFileException);
123 | Assert.IsTrue(f != null && f.IsClosed);
124 | }
125 | finally
126 | {
127 | if (output != null)
128 | output.Close();
129 | }
130 | }
131 |
132 | [Test]
133 | public void Test_VISIT_ENTRIES_CORRUPTED_FILE_VALIDATION_OFF_BUT_CAN_LOAD()
134 | {
135 | CompoundFile f = null;
136 |
137 | try
138 | {
139 | //Corrupted file has invalid children item sid reference
140 | f = new CompoundFile("CorruptedDoc_bug3547815_B.doc", CFSUpdateMode.ReadOnly,
141 | CFSConfiguration.NoValidationException);
142 | }
143 | catch
144 | {
145 | Assert.Fail("No exception has to be fired on creation due to lazy loading");
146 | }
147 |
148 | FileStream output = null;
149 |
150 | try
151 | {
152 | output = new FileStream("LogEntriesCorrupted_2.txt", FileMode.Create);
153 |
154 |
155 | using (TextWriter tw = new StreamWriter(output))
156 | {
157 | Action va = delegate(CFItem item) { tw.WriteLine(item.Name); };
158 |
159 | f.RootStorage.VisitEntries(va, true);
160 | tw.Flush();
161 | }
162 | }
163 | catch
164 | {
165 | Assert.Fail("Fail is corrupted but it has to be loaded anyway by test design");
166 | }
167 | finally
168 | {
169 | if (output != null)
170 | output.Close();
171 | }
172 | }
173 |
174 |
175 | [Test]
176 | public void Test_VISIT_STORAGE()
177 | {
178 | String FILENAME = "testVisiting.xls";
179 |
180 | // Remove...
181 | if (File.Exists(FILENAME))
182 | File.Delete(FILENAME);
183 |
184 | //Create...
185 |
186 | CompoundFile ncf = new CompoundFile();
187 |
188 | CFStorage l1 = ncf.RootStorage.AddStorage("Storage Level 1");
189 | l1.AddStream("l1ns1");
190 | l1.AddStream("l1ns2");
191 | l1.AddStream("l1ns3");
192 |
193 | CFStorage l2 = l1.AddStorage("Storage Level 2");
194 | l2.AddStream("l2ns1");
195 | l2.AddStream("l2ns2");
196 |
197 | ncf.Save(FILENAME);
198 | ncf.Close();
199 |
200 |
201 | // Read...
202 |
203 | CompoundFile cf = new CompoundFile(FILENAME);
204 |
205 | FileStream output = new FileStream("reportVisit.txt", FileMode.Create);
206 | TextWriter sw = new StreamWriter(output);
207 |
208 | Console.SetOut(sw);
209 |
210 | Action va = delegate(CFItem target) { sw.WriteLine(target.Name); };
211 |
212 | cf.RootStorage.VisitEntries(va, true);
213 |
214 | cf.Close();
215 | sw.Close();
216 | }
217 |
218 | [Test]
219 | public void Test_DELETE_DIRECTORY()
220 | {
221 | String FILENAME = "MultipleStorage2.cfs";
222 | CompoundFile cf = new CompoundFile(FILENAME, CFSUpdateMode.ReadOnly, CFSConfiguration.Default);
223 |
224 | CFStorage st = cf.RootStorage.GetStorage("MyStorage");
225 |
226 | Assert.IsNotNull(st);
227 |
228 | st.Delete("AnotherStorage");
229 |
230 | cf.Save("MultipleStorage_Delete.cfs");
231 |
232 | cf.Close();
233 | }
234 |
235 | [Test]
236 | public void Test_DELETE_MINISTREAM_STREAM()
237 | {
238 | String FILENAME = "MultipleStorage2.cfs";
239 | CompoundFile cf = new CompoundFile(FILENAME);
240 |
241 | CFStorage found = null;
242 | Action action = delegate(CFItem item)
243 | {
244 | if (item.Name == "AnotherStorage") found = item as CFStorage;
245 | };
246 | cf.RootStorage.VisitEntries(action, true);
247 |
248 | Assert.IsNotNull(found);
249 |
250 | found.Delete("AnotherStream");
251 |
252 | cf.Save("MultipleDeleteMiniStream");
253 | cf.Close();
254 | }
255 |
256 | [Test]
257 | public void Test_DELETE_STREAM()
258 | {
259 | String FILENAME = "MultipleStorage3.cfs";
260 | CompoundFile cf = new CompoundFile(FILENAME);
261 |
262 | CFStorage found = null;
263 | Action action = delegate(CFItem item)
264 | {
265 | if (item.Name == "AnotherStorage")
266 | found = item as CFStorage;
267 | };
268 |
269 | cf.RootStorage.VisitEntries(action, true);
270 |
271 | Assert.IsNotNull(found);
272 |
273 | found.Delete("Another2Stream");
274 |
275 | cf.Save("MultipleDeleteStream");
276 | cf.Close();
277 | }
278 |
279 | [Test]
280 | public void Test_CHECK_DISPOSED_()
281 | {
282 | const String FILENAME = "MultipleStorage.cfs";
283 | CompoundFile cf = new CompoundFile(FILENAME);
284 |
285 | CFStorage st = cf.RootStorage.GetStorage("MyStorage");
286 | cf.Close();
287 |
288 | try
289 | {
290 | byte[] temp = st.GetStream("MyStream").GetData();
291 | Assert.Fail("Stream without media");
292 | }
293 | catch (Exception ex)
294 | {
295 | Assert.IsTrue(ex is CFDisposedException);
296 | }
297 | }
298 |
299 | [Test]
300 | public void Test_LAZY_LOAD_CHILDREN_()
301 | {
302 | using (var cf = new CompoundFile())
303 | {
304 | cf.RootStorage.AddStorage("Level_1")
305 | .AddStorage("Level_2")
306 | .AddStream("Level2Stream")
307 | .SetData(Helpers.GetBuffer(100));
308 |
309 | cf.Save("$Hel1");
310 | cf.Close();
311 | }
312 |
313 | using (var cf = new CompoundFile("$Hel1", CFSUpdateMode.ReadOnly, CFSConfiguration.Default))
314 | {
315 | IList i = cf.GetAllNamedEntries("Level2Stream");
316 | Assert.IsNotNull(i[0]);
317 | Assert.IsTrue(i[0] is CFStream);
318 | Assert.IsTrue((i[0] as CFStream).GetData().Length == 100);
319 | cf.Save("$Hel2");
320 | cf.Close();
321 | }
322 |
323 | if (File.Exists("$Hel1"))
324 | {
325 | File.Delete("$Hel1");
326 | }
327 | if (File.Exists("$Hel2"))
328 | {
329 | File.Delete("$Hel2");
330 | }
331 | }
332 |
333 | [Test]
334 | public void Test_FIX_BUG_31()
335 | {
336 | CompoundFile cf = new CompoundFile();
337 | cf.RootStorage.AddStorage("Level_1")
338 | .AddStream("Level2Stream")
339 | .SetData(Helpers.GetBuffer(100));
340 |
341 | cf.Save("$Hel1");
342 |
343 | cf.Close();
344 |
345 | CompoundFile cf1 = new CompoundFile("$Hel1");
346 | try
347 | {
348 | CFStream cs = cf1.RootStorage.GetStorage("Level_1").AddStream("Level2Stream");
349 | }
350 | catch (Exception ex)
351 | {
352 | Assert.IsTrue(ex.GetType() == typeof(CFDuplicatedItemException));
353 | }
354 | }
355 |
356 | [Test]
357 | public void Test_CORRUPTEDDOC_BUG36_SHOULD_THROW_CORRUPTED_FILE_EXCEPTION()
358 | {
359 | try
360 | {
361 | using (CompoundFile file = new CompoundFile("CorruptedDoc_bug36.doc", CFSUpdateMode.ReadOnly,
362 | CFSConfiguration.NoValidationException))
363 | {
364 | //Many thanks to theseus for bug reporting
365 | }
366 | }
367 | catch (Exception ex)
368 | {
369 | Assert.IsInstanceOf(ex);
370 | }
371 | }
372 | }
373 | }
--------------------------------------------------------------------------------
/tests/OpenMcdf.Test/Helpers.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.IO;
4 |
5 | namespace OpenMcdf.Test
6 | {
7 | public static class Helpers
8 | {
9 | public static byte[] GetBuffer(int count)
10 | {
11 | Random r = new Random();
12 | byte[] b = new byte[count];
13 | r.NextBytes(b);
14 | return b;
15 | }
16 |
17 | public static byte[] GetBuffer(int count, byte c)
18 | {
19 | byte[] b = new byte[count];
20 | for (int i = 0; i < b.Length; i++)
21 | {
22 | b[i] = c;
23 | }
24 |
25 | return b;
26 | }
27 |
28 | public static bool CompareBuffer(byte[] b, byte[] p)
29 | {
30 | bool res = CompareBuffer(b, p, b.Length);
31 | return res && (b.Length == p.Length);
32 | }
33 |
34 | public static bool CompareBuffer(byte[] b, byte[] p, int count)
35 | {
36 | if (b == null && p == null)
37 | throw new Exception("Null buffers");
38 |
39 | if (b == null && p != null)
40 | return false;
41 |
42 | if (b != null && p == null)
43 | return false;
44 |
45 |
46 | for (int i = 0; i < count; i++)
47 | {
48 | if (b[i] != p[i])
49 | return false;
50 | }
51 |
52 | return true;
53 | }
54 |
55 | internal static void StressMemory()
56 | {
57 | const int N_LOOP = 20;
58 | const int MB_SIZE = 10;
59 |
60 | byte[] b = GetBuffer(1024 * 1024 * MB_SIZE); //2GB buffer
61 | byte[] cmp = { 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7 };
62 |
63 | CompoundFile cf = new CompoundFile(CFSVersion.Ver_4, CFSConfiguration.Default);
64 | CFStream st = cf.RootStorage.AddStream("MySuperLargeStream");
65 | cf.Save("LARGE.cfs");
66 | cf.Close();
67 |
68 | //Console.WriteLine("Closed save");
69 | //Console.ReadKey();
70 |
71 | cf = new CompoundFile("LARGE.cfs", CFSUpdateMode.Update, CFSConfiguration.Default);
72 | CFStream cfst = cf.RootStorage.GetStream("MySuperLargeStream");
73 |
74 | Stopwatch sw = new Stopwatch();
75 | sw.Start();
76 | for (int i = 0; i < N_LOOP; i++)
77 | {
78 | cfst.Append(b);
79 | cf.Commit(true);
80 |
81 | Console.WriteLine(" Updated " + i.ToString());
82 | //Console.ReadKey();
83 | }
84 |
85 | cfst.Append(cmp);
86 | cf.Commit(true);
87 | sw.Stop();
88 |
89 |
90 | cf.Close();
91 |
92 | Console.WriteLine(sw.Elapsed.TotalMilliseconds);
93 | sw.Reset();
94 |
95 | //Console.WriteLine(sw.Elapsed.TotalMilliseconds);
96 |
97 | //Console.WriteLine("Closed Transacted");
98 | //Console.ReadKey();
99 |
100 | cf = new CompoundFile("LARGE.cfs");
101 | int count = 8;
102 | sw.Reset();
103 | sw.Start();
104 | byte[] data = new byte[count];
105 | count = cf.RootStorage.GetStream("MySuperLargeStream").Read(data, b.Length * (long)N_LOOP, count);
106 | sw.Stop();
107 | Console.Write(count);
108 | cf.Close();
109 |
110 | Console.WriteLine("Closed Final " + sw.ElapsedMilliseconds);
111 | Console.ReadKey();
112 | }
113 |
114 | internal static void DummyFile()
115 | {
116 | Console.WriteLine("Start");
117 | FileStream fs = new FileStream("myDummyFile", FileMode.Create);
118 | fs.Close();
119 |
120 | Stopwatch sw = new Stopwatch();
121 |
122 | byte[] b = GetBuffer(1024 * 1024 * 50); //2GB buffer
123 |
124 | fs = new FileStream("myDummyFile", FileMode.Open);
125 | sw.Start();
126 | for (int i = 0; i < 42; i++)
127 | {
128 | fs.Seek(b.Length * i, SeekOrigin.Begin);
129 | fs.Write(b, 0, b.Length);
130 | }
131 |
132 | fs.Close();
133 | sw.Stop();
134 | Console.WriteLine("Stop - " + sw.ElapsedMilliseconds);
135 | sw.Reset();
136 |
137 | Console.ReadKey();
138 | }
139 |
140 | internal static void AddNodes(String depth, CFStorage cfs)
141 | {
142 | Action va = delegate (CFItem target)
143 | {
144 | String temp = target.Name + (target is CFStorage ? "" : " (" + target.Size + " bytes )");
145 |
146 | //Stream
147 |
148 | Console.WriteLine(depth + temp);
149 |
150 | if (target is CFStorage)
151 | {
152 | //Storage
153 |
154 | String newDepth = depth + " ";
155 |
156 | //Recursion into the storage
157 | AddNodes(newDepth, (CFStorage)target);
158 | }
159 | };
160 |
161 | //Visit NON-recursively (first level only)
162 | cfs.VisitEntries(va, false);
163 | }
164 |
165 | internal static void CreateFile(string fileName)
166 | {
167 | const int MAX_STREAM_COUNT = 5000;
168 |
169 | CompoundFile cf = new CompoundFile();
170 | for (int i = 0; i < MAX_STREAM_COUNT; i++)
171 | {
172 | cf.RootStorage.AddStream("Test" + i).SetData(GetBuffer(300));
173 | }
174 | cf.Save(fileName);
175 | cf.Close();
176 | }
177 | }
178 | }
--------------------------------------------------------------------------------
/tests/OpenMcdf.Test/OpenMcdf.Test.csproj:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | 15.0
5 | Debug
6 | AnyCPU
7 | 9.0.30729
8 | 2.0
9 | {FD339266-8842-40B4-9230-F8E84FC42AC2}
10 | Library
11 | Properties
12 | OpenMcdf.Test
13 | OpenMcdf.Test
14 | v4.6.2
15 | 512
16 | {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}
17 |
18 |
19 | 3.5
20 |
21 | http://localhost/OpenMcdfTest/
22 | true
23 | Web
24 | true
25 | Foreground
26 | 7
27 | Days
28 | false
29 | false
30 | true
31 | 0
32 | 1.0.0.%2a
33 | true
34 | false
35 | true
36 |
37 |
38 |
39 | true
40 | full
41 | false
42 | ..\..\bin\Debug\tests
43 | DEBUG;TRACE
44 | prompt
45 | 4
46 | AllRules.ruleset
47 | false
48 |
49 |
50 | pdbonly
51 | true
52 | ..\..\bin\Release\tests
53 | TRACE
54 | prompt
55 | 4
56 | AllRules.ruleset
57 | false
58 |
59 |
60 | false
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 | 3.5
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
78 |
79 |
80 |
81 |
82 |
83 |
84 |
85 |
86 |
87 |
88 |
89 |
90 |
91 |
92 |
93 |
94 | False
95 | Microsoft .NET Framework 4 %28x86 and x64%29
96 | true
97 |
98 |
99 | False
100 | .NET Framework 3.5 SP1 Client Profile
101 | false
102 |
103 |
104 | False
105 | .NET Framework 3.5 SP1
106 | false
107 |
108 |
109 | False
110 | Windows Installer 3.1
111 | true
112 |
113 |
114 |
115 |
116 | 1.0.4
117 |
118 |
119 | 1.0.4
120 |
121 |
122 | 3.9.0
123 |
124 |
125 |
126 |
127 | {9128ccc3-88df-4f31-883e-47a04825276f}
128 | OpenMcdf
129 |
130 |
131 |
132 |
139 |
--------------------------------------------------------------------------------
/tests/OpenMcdf.Test/Performance/MemoryTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.IO;
4 | using NBench;
5 |
6 | namespace OpenMcdf.Test.Performance
7 | {
8 | public class MemoryTest : PerformanceTestStuite
9 | {
10 | private Counter _testCounter;
11 |
12 | [PerfSetup]
13 | public void Setup(BenchmarkContext context)
14 | {
15 | _testCounter = context.GetCounter("TestCounter");
16 | }
17 |
18 | [PerfBenchmark(NumberOfIterations = 1, RunMode = RunMode.Iterations, TestMode = TestMode.Test,
19 | SkipWarmups = false)]
20 | [CounterMeasurement("TestCounter")]
21 | [CounterTotalAssertion("TestCounter", MustBe.LessThanOrEqualTo, 7000.0d)] // max 7 sec
22 | [MemoryAssertion(MemoryMetric.TotalBytesAllocated, MustBe.LessThanOrEqualTo,
23 | 450 * 1024 * 1024)] // max 450 Mb in RAM
24 | public void PerfMem_MultipleCodeFeatures()
25 | {
26 | const int N_FACTOR = 1000;
27 |
28 | byte[] bA = Helpers.GetBuffer(20 * 1024 * N_FACTOR, 0x0A);
29 | byte[] bB = Helpers.GetBuffer(5 * 1024, 0x0B);
30 | byte[] bC = Helpers.GetBuffer(5 * 1024, 0x0C);
31 | byte[] bD = Helpers.GetBuffer(5 * 1024, 0x0D);
32 | byte[] bE = Helpers.GetBuffer(8 * 1024 * N_FACTOR + 1, 0x1A);
33 | byte[] bF = Helpers.GetBuffer(16 * 1024 * N_FACTOR, 0x1B);
34 | byte[] bG = Helpers.GetBuffer(14 * 1024 * N_FACTOR, 0x1C);
35 | byte[] bH = Helpers.GetBuffer(12 * 1024 * N_FACTOR, 0x1D);
36 | byte[] bE2 = Helpers.GetBuffer(8 * 1024 * N_FACTOR, 0x2A);
37 | byte[] bMini = Helpers.GetBuffer(1027, 0xEE);
38 |
39 | Stopwatch sw = new Stopwatch();
40 | sw.Start();
41 |
42 | var cf = new CompoundFile(CFSVersion.Ver_3, CFSConfiguration.SectorRecycle);
43 | cf.RootStorage.AddStream("A").SetData(bA);
44 | cf.Save("OneStream.cfs");
45 |
46 | cf.Close();
47 |
48 | cf = new CompoundFile("OneStream.cfs", CFSUpdateMode.ReadOnly, CFSConfiguration.SectorRecycle);
49 |
50 | cf.RootStorage.AddStream("B").SetData(bB);
51 | cf.RootStorage.AddStream("C").SetData(bC);
52 | cf.RootStorage.AddStream("D").SetData(bD);
53 | cf.RootStorage.AddStream("E").SetData(bE);
54 | cf.RootStorage.AddStream("F").SetData(bF);
55 | cf.RootStorage.AddStream("G").SetData(bG);
56 | cf.RootStorage.AddStream("H").SetData(bH);
57 |
58 | cf.Save("8_Streams.cfs");
59 |
60 | cf.Close();
61 |
62 | File.Copy("8_Streams.cfs", "6_Streams.cfs", true);
63 |
64 | cf = new CompoundFile("6_Streams.cfs", CFSUpdateMode.Update,
65 | CFSConfiguration.SectorRecycle | CFSConfiguration.EraseFreeSectors);
66 | cf.RootStorage.Delete("D");
67 | cf.RootStorage.Delete("G");
68 | cf.Commit();
69 |
70 | cf.Close();
71 |
72 | File.Copy("6_Streams.cfs", "6_Streams_Shrinked.cfs", true);
73 |
74 | cf = new CompoundFile("6_Streams_Shrinked.cfs", CFSUpdateMode.Update, CFSConfiguration.SectorRecycle);
75 | cf.RootStorage.AddStream("ZZZ").SetData(bF);
76 | cf.RootStorage.GetStream("E").Append(bE2);
77 | cf.Commit();
78 | cf.Close();
79 |
80 | cf = new CompoundFile("6_Streams_Shrinked.cfs", CFSUpdateMode.Update, CFSConfiguration.SectorRecycle);
81 | cf.RootStorage.CLSID = new Guid("EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE");
82 | cf.Commit();
83 | cf.Close();
84 |
85 | cf = new CompoundFile("6_Streams_Shrinked.cfs", CFSUpdateMode.Update, CFSConfiguration.SectorRecycle);
86 | cf.RootStorage.AddStorage("MyStorage").AddStream("ANS").Append(bE);
87 | cf.Commit();
88 | cf.Close();
89 |
90 | cf = new CompoundFile("6_Streams_Shrinked.cfs", CFSUpdateMode.Update, CFSConfiguration.SectorRecycle);
91 | cf.RootStorage.AddStorage("AnotherStorage").AddStream("ANS").Append(bE);
92 | cf.RootStorage.Delete("MyStorage");
93 | cf.Commit();
94 | cf.Close();
95 |
96 | CompoundFile.ShrinkCompoundFile("6_Streams_Shrinked.cfs");
97 |
98 | cf = new CompoundFile("6_Streams_Shrinked.cfs", CFSUpdateMode.Update, CFSConfiguration.SectorRecycle);
99 | cf.RootStorage.AddStorage("MiniStorage").AddStream("miniSt").Append(bMini);
100 | cf.RootStorage.GetStorage("MiniStorage").AddStream("miniSt2").Append(bMini);
101 | cf.Commit();
102 | cf.Close();
103 |
104 | cf = new CompoundFile("6_Streams_Shrinked.cfs", CFSUpdateMode.Update, CFSConfiguration.SectorRecycle);
105 | cf.RootStorage.GetStorage("MiniStorage").Delete("miniSt");
106 |
107 |
108 | cf.RootStorage.GetStorage("MiniStorage").GetStream("miniSt2").Append(bE);
109 | cf.Commit();
110 | cf.Close();
111 |
112 | cf = new CompoundFile("6_Streams_Shrinked.cfs", CFSUpdateMode.ReadOnly, CFSConfiguration.SectorRecycle);
113 |
114 | var myStream = cf.RootStorage.GetStream("C");
115 | var data = myStream.GetData();
116 | Console.WriteLine(data[0] + " : " + data[data.Length - 1]);
117 |
118 | myStream = cf.RootStorage.GetStream("B");
119 | data = myStream.GetData();
120 | Console.WriteLine(data[0] + " : " + data[data.Length - 1]);
121 |
122 | cf.Close();
123 |
124 | sw.Stop();
125 | Console.WriteLine(sw.ElapsedMilliseconds);
126 |
127 | for (int i = 0; i < sw.ElapsedMilliseconds; i++)
128 | {
129 | _testCounter.Increment();
130 | }
131 | }
132 |
133 | [PerfBenchmark(NumberOfIterations = 1, RunMode = RunMode.Iterations, TestMode = TestMode.Test,
134 | SkipWarmups = false)]
135 | [CounterMeasurement("TestCounter")]
136 | [CounterTotalAssertion("TestCounter", MustBe.LessThanOrEqualTo, 5500.0d)] // max 5.5 sec
137 | [MemoryAssertion(MemoryMetric.TotalBytesAllocated, MustBe.LessThanOrEqualTo,
138 | 8 * 1024 * 1024)] // max 8 Mb in RAM
139 | public void PerfMem_MultipleStreamCommit()
140 | {
141 | File.Copy("report.xls", "reportOverwriteMultiple.xls", true);
142 |
143 | Stopwatch sw = new Stopwatch();
144 |
145 | using (var cf = new CompoundFile("reportOverwriteMultiple.xls", CFSUpdateMode.Update, CFSConfiguration.SectorRecycle))
146 | {
147 | sw.Start();
148 |
149 | Random r = new Random();
150 |
151 | for (int i = 0; i < 1000; i++)
152 | {
153 | byte[] buffer = Helpers.GetBuffer(r.Next(100, 3500), 0x0A);
154 |
155 | if (i > 0)
156 | {
157 | if (r.Next(0, 100) > 50)
158 | {
159 | cf.RootStorage.Delete("MyNewStream" + (i - 1).ToString());
160 | }
161 | }
162 |
163 | CFStream addedStream = cf.RootStorage.AddStream("MyNewStream" + i.ToString());
164 |
165 | addedStream.SetData(buffer);
166 |
167 | // Random commit, not on single addition
168 | if (r.Next(0, 100) > 50)
169 | cf.Commit();
170 | }
171 |
172 | cf.Close();
173 | sw.Stop();
174 | }
175 |
176 | Console.WriteLine(sw.ElapsedMilliseconds);
177 |
178 | for (int i = 0; i < sw.ElapsedMilliseconds; i++)
179 | {
180 | _testCounter.Increment();
181 | }
182 | }
183 | }
184 | }
--------------------------------------------------------------------------------
/tests/OpenMcdf.Test/Performance/PerfTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Diagnostics;
3 | using System.IO;
4 | using NBench;
5 |
6 | namespace OpenMcdf.Test.Performance
7 | {
8 | public class PerfTest : PerformanceTestStuite
9 | {
10 | private string _fileName;
11 | private Counter _testCounter;
12 |
13 | [PerfSetup]
14 | public void Setup(BenchmarkContext context)
15 | {
16 | _fileName = Path.Combine(Path.GetTempPath(), "PerfLoad.cfs");
17 | _testCounter = context.GetCounter("TestCounter");
18 | }
19 |
20 | [PerfBenchmark(Description = "Getting a stream of large file must take less than 200 ms",
21 | NumberOfIterations = 1, RunMode = RunMode.Iterations, TestMode = TestMode.Test, SkipWarmups = true)]
22 | [CounterTotalAssertion("TestCounter", MustBe.LessThanOrEqualTo, 200.0d)] // max 0.2 sec
23 | [CounterMeasurement("TestCounter")]
24 | public void Perf_SteamOfLargeFile()
25 | {
26 | if (!File.Exists(_fileName))
27 | {
28 | Helpers.CreateFile(_fileName);
29 | }
30 |
31 | using (var cf = new CompoundFile(_fileName))
32 | {
33 | Stopwatch sw = new Stopwatch();
34 | sw.Start();
35 | CFStream s = cf.RootStorage.GetStream("Test1");
36 | sw.Stop();
37 |
38 | var executionTime = sw.ElapsedMilliseconds;
39 | for (int i = 0; i < sw.ElapsedMilliseconds; i++)
40 | {
41 | _testCounter.Increment();
42 | }
43 |
44 | Console.WriteLine($"Took {executionTime} seconds");
45 | }
46 | }
47 |
48 | [PerfCleanup]
49 | public void Cleanup()
50 | {
51 | if (File.Exists(_fileName))
52 | {
53 | File.Delete(_fileName);
54 | }
55 | }
56 | }
57 | }
--------------------------------------------------------------------------------
/tests/OpenMcdf.Test/Performance/PerformanceTestStuite.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections;
3 | using System.Linq;
4 | using NBench.Reporting.Targets;
5 | using NBench.Sdk;
6 | using NBench.Sdk.Compiler;
7 | using NUnit.Framework;
8 |
9 | namespace OpenMcdf.Test.Performance
10 | {
11 | public abstract class PerformanceTestStuite
12 | {
13 | [TestCaseSource(nameof(Benchmarks))]
14 | public void PerformanceTests(Benchmark benchmark)
15 | {
16 | Benchmark.PrepareForRun();
17 | benchmark.Run();
18 | benchmark.Finish();
19 | }
20 |
21 | public static IEnumerable Benchmarks()
22 | {
23 | var discovery = new ReflectionDiscovery(new ActionBenchmarkOutput(report => { }, results =>
24 | {
25 | foreach (var assertion in results.AssertionResults)
26 | {
27 | Assert.True(assertion.Passed, results.BenchmarkName + " " + assertion.Message);
28 | Console.WriteLine(assertion.Message);
29 | }
30 | }));
31 |
32 | var benchmarks = discovery.FindBenchmarks(typeof(T)).ToList();
33 |
34 | foreach (var benchmark in benchmarks)
35 | {
36 | var name = benchmark.BenchmarkName.Split('+')[1];
37 | yield return new TestCaseData(benchmark).SetName(name);
38 | }
39 | }
40 | }
41 | }
--------------------------------------------------------------------------------
/tests/OpenMcdf.Test/Properties/AssemblyInfo.cs:
--------------------------------------------------------------------------------
1 | using System.Resources;
2 | using System.Reflection;
3 | using System.Runtime.InteropServices;
4 |
5 | // General Information about an assembly is controlled through the following
6 | // set of attributes. Change these attribute values to modify the information
7 | // associated with an assembly.
8 | [assembly: AssemblyTitle("OpenMcdf.Test")]
9 | [assembly: AssemblyDescription("This project is used for profiling memory and performances of OpenMCDF")]
10 | [assembly: AssemblyCompany("Federico Blaseotto, Zhmayev Yaroslav")]
11 | [assembly: AssemblyProduct("OpenMcdf.Test")]
12 | [assembly: AssemblyCopyright("Copyright © 2010-2017, Federico Blaseotto; 2016-2017 Zhmayev Yaroslav")]
13 |
14 | // Setting ComVisible to false makes the types in this assembly not visible
15 | // to COM componenets. If you need to access a type in this assembly from
16 | // COM, set the ComVisible attribute to true on that type.
17 | [assembly: ComVisible(false)]
18 |
19 | // The following GUID is for the ID of the typelib if this project is exposed to COM
20 | [assembly: Guid("7b918910-a8be-4e22-85d6-d927eae56003")]
21 |
22 | // Version information for an assembly consists of the following four values:
23 | //
24 | // Major Version
25 | // Minor Version
26 | // Build Number
27 | // Revision
28 | //
29 | // You can specify all the values or you can default the Revision and Build Numbers
30 | // by using the '*' as shown below:
31 | [assembly: AssemblyVersion("2.1.1.*")]
32 |
33 | [assembly: NeutralResourcesLanguage("en")]
34 |
--------------------------------------------------------------------------------
/tests/OpenMcdf.Test/RBTreeTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.Collections.Generic;
3 | using NUnit.Framework;
4 | using RedBlackTree;
5 |
6 | namespace OpenMcdf.Test
7 | {
8 | ///
9 | /// Summary description for RBTreeTest
10 | ///
11 | [TestFixture]
12 | public class RBTreeTest
13 | {
14 | internal IList GetDirectoryRepository(int count)
15 | {
16 | List repo = new List();
17 | for (int i = 0; i < count; i++)
18 | {
19 | IDirectoryEntry de = DirectoryEntry.New(i.ToString(), StgType.StgInvalid, repo);
20 | }
21 |
22 | return repo;
23 | }
24 |
25 | [Test]
26 | public void Test_RBTREE_INSERT()
27 | {
28 | RbTree rbTree = new RbTree();
29 | System.Collections.Generic.IList repo = GetDirectoryRepository(25);
30 |
31 | foreach (var item in repo)
32 | {
33 | rbTree.Insert(item);
34 | }
35 |
36 | for (int i = 0; i < repo.Count; i++)
37 | {
38 | IRbNode c;
39 | rbTree.TryLookup(DirectoryEntry.Mock(i.ToString(), StgType.StgInvalid), out c);
40 | Assert.IsTrue(c is IDirectoryEntry);
41 | Assert.IsTrue(((IDirectoryEntry) c).Name == i.ToString());
42 | //Assert.IsTrue(c.IsStream);
43 | }
44 | }
45 |
46 |
47 | [Test]
48 | public void Test_RBTREE_DELETE()
49 | {
50 | RbTree rbTree = new RbTree();
51 | System.Collections.Generic.IList repo = GetDirectoryRepository(25);
52 |
53 |
54 | foreach (var item in repo)
55 | {
56 | rbTree.Insert(item);
57 | }
58 |
59 | try
60 | {
61 | IRbNode n;
62 | rbTree.Delete(DirectoryEntry.Mock("5", StgType.StgInvalid), out n);
63 | rbTree.Delete(DirectoryEntry.Mock("24", StgType.StgInvalid), out n);
64 | rbTree.Delete(DirectoryEntry.Mock("7", StgType.StgInvalid), out n);
65 | }
66 | catch (Exception ex)
67 | {
68 | Assert.Fail("Item removal failed: " + ex.Message);
69 | }
70 |
71 |
72 | // CFItem c;
73 | // bool s = rbTree.TryLookup(new CFMock("7", StgType.StgStream), out c);
74 |
75 |
76 | // Assert.IsFalse(s);
77 |
78 | // c = null;
79 |
80 | // Assert.IsTrue(rbTree.TryLookup(new CFMock("6", StgType.StgStream), out c));
81 | // Assert.IsTrue(c.IsStream);
82 | // Assert.IsTrue(rbTree.TryLookup(new CFMock("12", StgType.StgStream), out c));
83 | // Assert.IsTrue(c.Name == "12");
84 |
85 |
86 | //}
87 | }
88 |
89 | private static void VerifyProperties(RbTree t)
90 | {
91 | VerifyProperty1(t.Root);
92 | VerifyProperty2(t.Root);
93 | // Property 3 is implicit
94 | VerifyProperty4(t.Root);
95 | VerifyProperty5(t.Root);
96 | }
97 |
98 | private static Color NodeColor(IRbNode n)
99 | {
100 | return n == null ? Color.Black : n.Color;
101 | }
102 |
103 | private static void VerifyProperty1(IRbNode n)
104 | {
105 | Assert.IsTrue(NodeColor(n) == Color.Red || NodeColor(n) == Color.Black);
106 |
107 | if (n == null) return;
108 | VerifyProperty1(n.Left);
109 | VerifyProperty1(n.Right);
110 | }
111 |
112 | private static void VerifyProperty2(IRbNode root)
113 | {
114 | Assert.IsTrue(NodeColor(root) == Color.Black);
115 | }
116 |
117 | private static void VerifyProperty4(IRbNode n)
118 | {
119 | if (NodeColor(n) == Color.Red)
120 | {
121 | Assert.IsTrue((NodeColor(n.Left) == Color.Black));
122 | Assert.IsTrue((NodeColor(n.Right) == Color.Black));
123 | Assert.IsTrue((NodeColor(n.Parent) == Color.Black));
124 | }
125 |
126 | if (n == null) return;
127 | VerifyProperty4(n.Left);
128 | VerifyProperty4(n.Right);
129 | }
130 |
131 | private static void VerifyProperty5(IRbNode root)
132 | {
133 | VerifyProperty5Helper(root, 0, -1);
134 | }
135 |
136 | private static int VerifyProperty5Helper(IRbNode n, int blackCount, int pathBlackCount)
137 | {
138 | if (NodeColor(n) == Color.Black)
139 | {
140 | blackCount++;
141 | }
142 | if (n == null)
143 | {
144 | if (pathBlackCount == -1)
145 | {
146 | pathBlackCount = blackCount;
147 | }
148 | else
149 | {
150 | Assert.IsTrue(blackCount == pathBlackCount);
151 | }
152 | return pathBlackCount;
153 | }
154 |
155 | pathBlackCount = VerifyProperty5Helper(n.Left, blackCount, pathBlackCount);
156 | pathBlackCount = VerifyProperty5Helper(n.Right, blackCount, pathBlackCount);
157 |
158 | return pathBlackCount;
159 | }
160 |
161 |
162 | [Test]
163 | public void Test_RBTREE_ENUMERATE()
164 | {
165 | RbTree rbTree = new RbTree();
166 | System.Collections.Generic.IList repo = GetDirectoryRepository(10000);
167 |
168 | foreach (var item in repo)
169 | {
170 | rbTree.Insert(item);
171 | }
172 |
173 | VerifyProperties(rbTree);
174 | //rbTree.Print();
175 | }
176 | }
177 | }
--------------------------------------------------------------------------------
/tests/OpenMcdf.Test/SectorCollectionTest.cs:
--------------------------------------------------------------------------------
1 | using NUnit.Framework;
2 | using System;
3 |
4 | namespace OpenMcdf.Test
5 | {
6 | ///
7 | ///This is a test class for SectorCollectionTest and is intended
8 | ///to contain all SectorCollectionTest Unit Tests
9 | ///
10 | [TestFixture]
11 | public class SectorCollectionTest
12 | {
13 | ///
14 | ///A test for Count
15 | ///
16 | [Test]
17 | public void CountTest()
18 | {
19 | int count = 0;
20 |
21 | SectorCollection target = new SectorCollection(); // TODO: Initialize to an appropriate value
22 | int actual;
23 | actual = target.Count;
24 |
25 | Assert.IsTrue(actual == count);
26 | Sector s = new Sector(4096);
27 |
28 | target.Add(s);
29 | Assert.IsTrue(target.Count == actual + 1);
30 |
31 |
32 | for (int i = 0; i < 5000; i++)
33 | target.Add(s);
34 |
35 | Assert.IsTrue(target.Count == actual + 1 + 5000);
36 | }
37 |
38 | ///
39 | ///A test for Item
40 | ///
41 | [Test]
42 | public void ItemTest()
43 | {
44 | int count = 37;
45 |
46 | SectorCollection target = new SectorCollection();
47 | int index = 0;
48 |
49 | Sector expected = new Sector(4096);
50 | target.Add(null);
51 |
52 | Sector actual;
53 | target[index] = expected;
54 | actual = target[index];
55 |
56 | Assert.AreEqual(expected, actual);
57 | Assert.IsNotNull(actual);
58 | Assert.IsTrue(actual.Id == expected.Id);
59 |
60 | actual = null;
61 |
62 | try
63 | {
64 | actual = target[count + 100];
65 | }
66 | catch (Exception ex)
67 | {
68 | Assert.IsTrue(ex is ArgumentOutOfRangeException);
69 | }
70 |
71 | try
72 | {
73 | actual = target[-1];
74 | }
75 | catch (Exception ex)
76 | {
77 | Assert.IsTrue(ex is ArgumentOutOfRangeException);
78 | }
79 | }
80 |
81 | ///
82 | ///A test for SectorCollection Constructor
83 | ///
84 | [Test]
85 | public void SectorCollectionConstructorTest()
86 | {
87 | SectorCollection target = new SectorCollection();
88 |
89 | Assert.IsNotNull(target);
90 | Assert.IsTrue(target.Count == 0);
91 |
92 | Sector s = new Sector(4096);
93 | target.Add(s);
94 | Assert.IsTrue(target.Count == 1);
95 | }
96 |
97 | ///
98 | ///A test for Add
99 | ///
100 | [Test]
101 | public void AddTest()
102 | {
103 | SectorCollection target = new SectorCollection();
104 | for (int i = 0; i < 579; i++)
105 | {
106 | target.Add(null);
107 | }
108 |
109 |
110 | Sector item = new Sector(4096);
111 | target.Add(item);
112 | Assert.IsTrue(target.Count == 580);
113 | }
114 |
115 | ///
116 | ///A test for GetEnumerator
117 | ///
118 | [Test]
119 | public void GetEnumeratorTest()
120 | {
121 | SectorCollection target = new SectorCollection();
122 | for (int i = 0; i < 579; i++)
123 | {
124 | target.Add(null);
125 | }
126 |
127 |
128 | Sector item = new Sector(4096);
129 | target.Add(item);
130 | Assert.IsTrue(target.Count == 580);
131 |
132 | int cnt = 0;
133 | foreach (Sector s in target)
134 | {
135 | cnt++;
136 | }
137 |
138 | Assert.IsTrue(cnt == target.Count);
139 | }
140 | }
141 | }
--------------------------------------------------------------------------------
/tests/OpenMcdf.Test/StreamRWTest.cs:
--------------------------------------------------------------------------------
1 | using System;
2 | using System.IO;
3 | using NUnit.Framework;
4 |
5 | namespace OpenMcdf.Test
6 | {
7 | [TestFixture]
8 | public class StreamRWTest
9 | {
10 | [Test]
11 | public void ReadInt64_MaxSizeRead()
12 | {
13 | Int64 input = Int64.MaxValue;
14 | byte[] bytes = BitConverter.GetBytes(input);
15 | long actual = 0;
16 | using (MemoryStream memStream = new MemoryStream(bytes))
17 | {
18 | OpenMcdf.StreamRW reader = new OpenMcdf.StreamRW(memStream);
19 | actual = reader.ReadInt64();
20 | }
21 | Assert.AreEqual((long) input, actual);
22 | }
23 |
24 | [Test]
25 | public void ReadInt64_SmallNumber()
26 | {
27 | Int64 input = 1234;
28 | byte[] bytes = BitConverter.GetBytes(input);
29 | long actual = 0;
30 | using (MemoryStream memStream = new MemoryStream(bytes))
31 | {
32 | OpenMcdf.StreamRW reader = new OpenMcdf.StreamRW(memStream);
33 | actual = reader.ReadInt64();
34 | }
35 | Assert.AreEqual((long) input, actual);
36 | }
37 |
38 | [Test]
39 | public void ReadInt64_Int32MaxPlusTen()
40 | {
41 | Int64 input = (Int64) Int32.MaxValue + 10;
42 | byte[] bytes = BitConverter.GetBytes(input);
43 | long actual = 0;
44 | using (MemoryStream memStream = new MemoryStream(bytes))
45 | {
46 | OpenMcdf.StreamRW reader = new OpenMcdf.StreamRW(memStream);
47 | actual = reader.ReadInt64();
48 | }
49 | Assert.AreEqual((long) input, actual);
50 | }
51 | }
52 | }
--------------------------------------------------------------------------------
/tests/assets/2_MB-W.ppt:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodeCavePro/OpenMCDF/564e9675e5af0610ebfa8a2598e86cca7c7952d5/tests/assets/2_MB-W.ppt
--------------------------------------------------------------------------------
/tests/assets/BUG_16_.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodeCavePro/OpenMCDF/564e9675e5af0610ebfa8a2598e86cca7c7952d5/tests/assets/BUG_16_.xls
--------------------------------------------------------------------------------
/tests/assets/CorruptedDoc_bug3547815.doc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodeCavePro/OpenMCDF/564e9675e5af0610ebfa8a2598e86cca7c7952d5/tests/assets/CorruptedDoc_bug3547815.doc
--------------------------------------------------------------------------------
/tests/assets/CorruptedDoc_bug3547815_B.doc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodeCavePro/OpenMCDF/564e9675e5af0610ebfa8a2598e86cca7c7952d5/tests/assets/CorruptedDoc_bug3547815_B.doc
--------------------------------------------------------------------------------
/tests/assets/CorruptedDoc_bug36.doc:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodeCavePro/OpenMCDF/564e9675e5af0610ebfa8a2598e86cca7c7952d5/tests/assets/CorruptedDoc_bug36.doc
--------------------------------------------------------------------------------
/tests/assets/CyclicFAT.cfs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodeCavePro/OpenMCDF/564e9675e5af0610ebfa8a2598e86cca7c7952d5/tests/assets/CyclicFAT.cfs
--------------------------------------------------------------------------------
/tests/assets/MultipleStorage.cfs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodeCavePro/OpenMCDF/564e9675e5af0610ebfa8a2598e86cca7c7952d5/tests/assets/MultipleStorage.cfs
--------------------------------------------------------------------------------
/tests/assets/MultipleStorage2.cfs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodeCavePro/OpenMCDF/564e9675e5af0610ebfa8a2598e86cca7c7952d5/tests/assets/MultipleStorage2.cfs
--------------------------------------------------------------------------------
/tests/assets/MultipleStorage3.cfs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodeCavePro/OpenMCDF/564e9675e5af0610ebfa8a2598e86cca7c7952d5/tests/assets/MultipleStorage3.cfs
--------------------------------------------------------------------------------
/tests/assets/MultipleStorage4.cfs:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodeCavePro/OpenMCDF/564e9675e5af0610ebfa8a2598e86cca7c7952d5/tests/assets/MultipleStorage4.cfs
--------------------------------------------------------------------------------
/tests/assets/_thumbs_bug_24.db:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodeCavePro/OpenMCDF/564e9675e5af0610ebfa8a2598e86cca7c7952d5/tests/assets/_thumbs_bug_24.db
--------------------------------------------------------------------------------
/tests/assets/report.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodeCavePro/OpenMCDF/564e9675e5af0610ebfa8a2598e86cca7c7952d5/tests/assets/report.xls
--------------------------------------------------------------------------------
/tests/assets/reportOverwriteMultiple.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodeCavePro/OpenMCDF/564e9675e5af0610ebfa8a2598e86cca7c7952d5/tests/assets/reportOverwriteMultiple.xls
--------------------------------------------------------------------------------
/tests/assets/reportREAD.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodeCavePro/OpenMCDF/564e9675e5af0610ebfa8a2598e86cca7c7952d5/tests/assets/reportREAD.xls
--------------------------------------------------------------------------------
/tests/assets/report_name_fix.xls:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodeCavePro/OpenMCDF/564e9675e5af0610ebfa8a2598e86cca7c7952d5/tests/assets/report_name_fix.xls
--------------------------------------------------------------------------------
/tests/assets/testbad.ole:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/CodeCavePro/OpenMCDF/564e9675e5af0610ebfa8a2598e86cca7c7952d5/tests/assets/testbad.ole
--------------------------------------------------------------------------------