diff --git a/.gitignore b/.gitignore index 975ad6b7..40be79ea 100644 --- a/.gitignore +++ b/.gitignore @@ -2,13 +2,19 @@ # This .gitignore file was automatically created by Microsoft(R) Visual Studio. ################################################################################ +/.vs/OpenMcdf/v14 +/bin/Debug +/sources/OpenMcdf/obj/Debug +/Performance Test/obj/Debug +/OpenMcdfExtensionsTest/obj/Debug +/OpenMcdf.Extensions/obj/Debug +/Memory Test/obj/Debug +/sources/OpenMcdf.Extensions/obj/Debug +/sources/Structured Storage Explorer/obj/Debug /Unit Test/obj/Debug -/Unit Test/bin/Debug /TestResults /Structured Storage Explorer/obj/Debug -/Structured Storage Explorer/bin/Debug /src/obj/Debug -/src/bin/Debug /sources/Test/OpenMcdf.Test/obj/Debug /sources/Test/OpenMcdf.Test/bin/Debug /sources/Test/OpenMcdf.PerfTest/obj/Debug @@ -17,16 +23,24 @@ /sources/Test/OpenMcdf.MemTest/bin/Debug /sources/Test/OpenMcdf.Extensions.Test/obj/Debug /sources/Test/OpenMcdf.Extensions.Test/bin/Debug -/sources/Structured Storage Explorer/obj/Debug -/sources/OpenMcdf.Extensions/obj/Debug -/sources/OpenMcdf/obj/Debug -/Performance Test/obj/Debug +/Unit Test/bin/Debug +/Structured Storage Explorer/bin/Debug +/src/bin/Debug +/sources/Test/OpenMcdf.Test/obj/Release +/sources/Test/OpenMcdf.Test/bin/Release +/sources/Test/OpenMcdf.PerfTest/obj/Release +/sources/Test/OpenMcdf.PerfTest/bin/Release +/sources/Test/OpenMcdf.MemTest/obj/Release +/sources/Test/OpenMcdf.MemTest/bin/Release +/sources/Test/OpenMcdf.Extensions.Test/obj/Release +/sources/Test/OpenMcdf.Extensions.Test/bin/Release +/sources/Structured Storage Explorer/obj/Release +/sources/OpenMcdf.Extensions/obj/Release +/sources/OpenMcdf/obj/Release /Performance Test/bin/Debug -/OpenMcdfExtensionsTest/obj/Debug /OpenMcdfExtensionsTest/bin/Debug -/OpenMcdf.Extensions/obj/Debug /OpenMcdf.Extensions/bin/Debug -/Memory Test/obj/Debug /Memory Test/bin/Debug -/bin/Debug -/.vs/OpenMcdf/v14 +/bin/Release/StructuredStorageXplorer +/bin/Release/OpenMcdf.Extensions +/bin/Release diff --git a/License.txt b/License.txt index fd46400e..016b985c 100644 --- a/License.txt +++ b/License.txt @@ -1,472 +1,373 @@ - MOZILLA PUBLIC LICENSE - Version 1.1 - - --------------- - -1. Definitions. - - 1.0.1. "Commercial Use" means distribution or otherwise making the - Covered Code available to a third party. - - 1.1. "Contributor" means each entity that creates or contributes to - the creation of Modifications. - - 1.2. "Contributor Version" means the combination of the Original - Code, prior Modifications used by a Contributor, and the Modifications - made by that particular Contributor. - - 1.3. "Covered Code" means the Original Code or Modifications or the - combination of the Original Code and Modifications, in each case - including portions thereof. - - 1.4. "Electronic Distribution Mechanism" means a mechanism generally - accepted in the software development community for the electronic - transfer of data. - - 1.5. "Executable" means Covered Code in any form other than Source - Code. - - 1.6. "Initial Developer" means the individual or entity identified - as the Initial Developer in the Source Code notice required by Exhibit - A. - - 1.7. "Larger Work" means a work which combines Covered Code or - portions thereof with code not governed by the terms of this License. - - 1.8. "License" means this document. - - 1.8.1. "Licensable" means having the right to grant, to the maximum - extent possible, whether at the time of the initial grant or - subsequently acquired, any and all of the rights conveyed herein. - - 1.9. "Modifications" means any addition to or deletion from the - substance or structure of either the Original Code or any previous - Modifications. When Covered Code is released as a series of files, a - Modification is: - - A. Any addition to or deletion from the contents of a file - containing Original Code or previous Modifications. - - B. Any new file that contains any part of the Original Code or - previous Modifications. - - 1.10. "Original Code" means Source Code of computer software code - which is described in the Source Code notice required by Exhibit A as - Original Code, and which, at the time of its release under this - License is not already Covered Code governed by this License. - - 1.10.1. "Patent Claims" means any patent claim(s), now owned or - hereafter acquired, including without limitation, method, process, - and apparatus claims, in any patent Licensable by grantor. - - 1.11. "Source Code" means the preferred form of the Covered Code for - making modifications to it, including all modules it contains, plus - any associated interface definition files, scripts used to control - compilation and installation of an Executable, or source code - differential comparisons against either the Original Code or another - well known, available Covered Code of the Contributor's choice. The - Source Code can be in a compressed or archival form, provided the - appropriate decompression or de-archiving software is widely available - for no charge. - - 1.12. "You" (or "Your") means an individual or a legal entity - exercising rights under, and complying with all of the terms of, this - License or a future version of this License issued under Section 6.1. - For legal entities, "You" includes any entity which controls, is - controlled by, or is under common control with You. For purposes of - this definition, "control" means (a) the power, direct or indirect, - to cause the direction or management of such entity, whether by - contract or otherwise, or (b) ownership of more than fifty percent - (50%) of the outstanding shares or beneficial ownership of such - entity. - -2. Source Code License. - - 2.1. The Initial Developer Grant. - The Initial Developer hereby grants You a world-wide, royalty-free, - non-exclusive license, subject to third party intellectual property - claims: - (a) under intellectual property rights (other than patent or - trademark) Licensable by Initial Developer to use, reproduce, - modify, display, perform, sublicense and distribute the Original - Code (or portions thereof) with or without Modifications, and/or - as part of a Larger Work; and - - (b) under Patents Claims infringed by the making, using or - selling of Original Code, to make, have made, use, practice, - sell, and offer for sale, and/or otherwise dispose of the - Original Code (or portions thereof). - - (c) the licenses granted in this Section 2.1(a) and (b) are - effective on the date Initial Developer first distributes - Original Code under the terms of this License. - - (d) Notwithstanding Section 2.1(b) above, no patent license is - granted: 1) for code that You delete from the Original Code; 2) - separate from the Original Code; or 3) for infringements caused - by: i) the modification of the Original Code or ii) the - combination of the Original Code with other software or devices. - - 2.2. Contributor Grant. - Subject to third party intellectual property claims, each Contributor - hereby grants You a world-wide, royalty-free, non-exclusive license - - (a) under intellectual property rights (other than patent or - trademark) Licensable by Contributor, to use, reproduce, modify, - display, perform, sublicense and distribute the Modifications - created by such Contributor (or portions thereof) either on an - unmodified basis, with other Modifications, as Covered Code - and/or as part of a Larger Work; and - - (b) under Patent Claims infringed by the making, using, or - selling of Modifications made by that Contributor either alone - and/or in combination with its Contributor Version (or portions - of such combination), to make, use, sell, offer for sale, have - made, and/or otherwise dispose of: 1) Modifications made by that - Contributor (or portions thereof); and 2) the combination of - Modifications made by that Contributor with its Contributor - Version (or portions of such combination). - - (c) the licenses granted in Sections 2.2(a) and 2.2(b) are - effective on the date Contributor first makes Commercial Use of - the Covered Code. - - (d) Notwithstanding Section 2.2(b) above, no patent license is - granted: 1) for any code that Contributor has deleted from the - Contributor Version; 2) separate from the Contributor Version; - 3) for infringements caused by: i) third party modifications of - Contributor Version or ii) the combination of Modifications made - by that Contributor with other software (except as part of the - Contributor Version) or other devices; or 4) under Patent Claims - infringed by Covered Code in the absence of Modifications made by - that Contributor. - -3. Distribution Obligations. - - 3.1. Application of License. - The Modifications which You create or to which You contribute are - governed by the terms of this License, including without limitation - Section 2.2. The Source Code version of Covered Code may be - distributed only under the terms of this License or a future version - of this License released under Section 6.1, and You must include a - copy of this License with every copy of the Source Code You - distribute. You may not offer or impose any terms on any Source Code - version that alters or restricts the applicable version of this - License or the recipients' rights hereunder. However, You may include - an additional document offering the additional rights described in - Section 3.5. - - 3.2. Availability of Source Code. - Any Modification which You create or to which You contribute must be - made available in Source Code form under the terms of this License - either on the same media as an Executable version or via an accepted - Electronic Distribution Mechanism to anyone to whom you made an - Executable version available; and if made available via Electronic - Distribution Mechanism, must remain available for at least twelve (12) - months after the date it initially became available, or at least six - (6) months after a subsequent version of that particular Modification - has been made available to such recipients. You are responsible for - ensuring that the Source Code version remains available even if the - Electronic Distribution Mechanism is maintained by a third party. - - 3.3. Description of Modifications. - You must cause all Covered Code to which You contribute to contain a - file documenting the changes You made to create that Covered Code and - the date of any change. You must include a prominent statement that - the Modification is derived, directly or indirectly, from Original - Code provided by the Initial Developer and including the name of the - Initial Developer in (a) the Source Code, and (b) in any notice in an - Executable version or related documentation in which You describe the - origin or ownership of the Covered Code. - - 3.4. Intellectual Property Matters - - (a) Third Party Claims. - If Contributor has knowledge that a license under a third party's - intellectual property rights is required to exercise the rights - granted by such Contributor under Sections 2.1 or 2.2, - Contributor must include a text file with the Source Code - distribution titled "LEGAL" which describes the claim and the - party making the claim in sufficient detail that a recipient will - know whom to contact. If Contributor obtains such knowledge after - the Modification is made available as described in Section 3.2, - Contributor shall promptly modify the LEGAL file in all copies - Contributor makes available thereafter and shall take other steps - (such as notifying appropriate mailing lists or newsgroups) - reasonably calculated to inform those who received the Covered - Code that new knowledge has been obtained. - - (b) Contributor APIs. - If Contributor's Modifications include an application programming - interface and Contributor has knowledge of patent licenses which - are reasonably necessary to implement that API, Contributor must - also include this information in the LEGAL file. - - (c) Representations. - Contributor represents that, except as disclosed pursuant to - Section 3.4(a) above, Contributor believes that Contributor's - Modifications are Contributor's original creation(s) and/or - Contributor has sufficient rights to grant the rights conveyed by - this License. - - 3.5. Required Notices. - You must duplicate the notice in Exhibit A in each file of the Source - Code. If it is not possible to put such notice in a particular Source - Code file due to its structure, then You must include such notice in a - location (such as a relevant directory) where a user would be likely - to look for such a notice. If You created one or more Modification(s) - You may add your name as a Contributor to the notice described in - Exhibit A. You must also duplicate this License in any documentation - for the Source Code where You describe recipients' rights or ownership - rights relating to Covered Code. You may choose to offer, and to - charge a fee for, warranty, support, indemnity or liability - obligations to one or more recipients of Covered Code. However, You - may do so only on Your own behalf, and not on behalf of the Initial - Developer or any Contributor. You must make it absolutely clear than - any such warranty, support, indemnity or liability obligation is - offered by You alone, and You hereby agree to indemnify the Initial - Developer and every Contributor for any liability incurred by the - Initial Developer or such Contributor as a result of warranty, - support, indemnity or liability terms You offer. - - 3.6. Distribution of Executable Versions. - You may distribute Covered Code in Executable form only if the - requirements of Section 3.1-3.5 have been met for that Covered Code, - and if You include a notice stating that the Source Code version of - the Covered Code is available under the terms of this License, - including a description of how and where You have fulfilled the - obligations of Section 3.2. The notice must be conspicuously included - in any notice in an Executable version, related documentation or - collateral in which You describe recipients' rights relating to the - Covered Code. You may distribute the Executable version of Covered - Code or ownership rights under a license of Your choice, which may - contain terms different from this License, provided that You are in - compliance with the terms of this License and that the license for the - Executable version does not attempt to limit or alter the recipient's - rights in the Source Code version from the rights set forth in this - License. If You distribute the Executable version under a different - license You must make it absolutely clear that any terms which differ - from this License are offered by You alone, not by the Initial - Developer or any Contributor. You hereby agree to indemnify the - Initial Developer and every Contributor for any liability incurred by - the Initial Developer or such Contributor as a result of any such - terms You offer. - - 3.7. Larger Works. - You may create a Larger Work by combining Covered Code with other code - not governed by the terms of this License and distribute the Larger - Work as a single product. In such a case, You must make sure the - requirements of this License are fulfilled for the Covered Code. - -4. Inability to Comply Due to Statute or Regulation. - - If it is impossible for You to comply with any of the terms of this - License with respect to some or all of the Covered Code due to - statute, judicial order, or regulation then You must: (a) comply with - the terms of this License to the maximum extent possible; and (b) - describe the limitations and the code they affect. Such description - must be included in the LEGAL file described in Section 3.4 and must - be included with all distributions of the Source Code. Except to the - extent prohibited by statute or regulation, such description must be - sufficiently detailed for a recipient of ordinary skill to be able to - understand it. - -5. Application of this License. - - This License applies to code to which the Initial Developer has - attached the notice in Exhibit A and to related Covered Code. - -6. Versions of the License. - - 6.1. New Versions. - Netscape Communications Corporation ("Netscape") may publish revised - and/or new versions of the License from time to time. Each version - will be given a distinguishing version number. - - 6.2. Effect of New Versions. - Once Covered Code has been published under a particular version of the - License, You may always continue to use it under the terms of that - version. You may also choose to use such Covered Code under the terms - of any subsequent version of the License published by Netscape. No one - other than Netscape has the right to modify the terms applicable to - Covered Code created under this License. - - 6.3. Derivative Works. - If You create or use a modified version of this License (which you may - only do in order to apply it to code which is not already Covered Code - governed by this License), You must (a) rename Your license so that - the phrases "Mozilla", "MOZILLAPL", "MOZPL", "Netscape", - "MPL", "NPL" or any confusingly similar phrase do not appear in your - license (except to note that your license differs from this License) - and (b) otherwise make it clear that Your version of the license - contains terms which differ from the Mozilla Public License and - Netscape Public License. (Filling in the name of the Initial - Developer, Original Code or Contributor in the notice described in - Exhibit A shall not of themselves be deemed to be modifications of - this License.) - -7. DISCLAIMER OF WARRANTY. - - COVERED CODE IS PROVIDED UNDER THIS LICENSE ON AN "AS IS" BASIS, - WITHOUT WARRANTY OF ANY KIND, EITHER EXPRESSED OR IMPLIED, INCLUDING, - WITHOUT LIMITATION, WARRANTIES THAT THE COVERED CODE IS FREE OF - DEFECTS, MERCHANTABLE, FIT FOR A PARTICULAR PURPOSE OR NON-INFRINGING. - THE ENTIRE RISK AS TO THE QUALITY AND PERFORMANCE OF THE COVERED CODE - IS WITH YOU. SHOULD ANY COVERED CODE PROVE DEFECTIVE IN ANY RESPECT, - YOU (NOT THE INITIAL DEVELOPER OR ANY OTHER CONTRIBUTOR) ASSUME THE - COST OF ANY NECESSARY SERVICING, REPAIR OR CORRECTION. THIS DISCLAIMER - OF WARRANTY CONSTITUTES AN ESSENTIAL PART OF THIS LICENSE. NO USE OF - ANY COVERED CODE IS AUTHORIZED HEREUNDER EXCEPT UNDER THIS DISCLAIMER. - -8. TERMINATION. - - 8.1. This License and the rights granted hereunder will terminate - automatically if You fail to comply with terms herein and fail to cure - such breach within 30 days of becoming aware of the breach. All - sublicenses to the Covered Code which are properly granted shall - survive any termination of this License. Provisions which, by their - nature, must remain in effect beyond the termination of this License - shall survive. - - 8.2. If You initiate litigation by asserting a patent infringement - claim (excluding declatory judgment actions) against Initial Developer - or a Contributor (the Initial Developer or Contributor against whom - You file such action is referred to as "Participant") alleging that: - - (a) such Participant's Contributor Version directly or indirectly - infringes any patent, then any and all rights granted by such - Participant to You under Sections 2.1 and/or 2.2 of this License - shall, upon 60 days notice from Participant terminate prospectively, - unless if within 60 days after receipt of notice You either: (i) - agree in writing to pay Participant a mutually agreeable reasonable - royalty for Your past and future use of Modifications made by such - Participant, or (ii) withdraw Your litigation claim with respect to - the Contributor Version against such Participant. If within 60 days - of notice, a reasonable royalty and payment arrangement are not - mutually agreed upon in writing by the parties or the litigation claim - is not withdrawn, the rights granted by Participant to You under - Sections 2.1 and/or 2.2 automatically terminate at the expiration of - the 60 day notice period specified above. - - (b) any software, hardware, or device, other than such Participant's - Contributor Version, directly or indirectly infringes any patent, then - any rights granted to You by such Participant under Sections 2.1(b) - and 2.2(b) are revoked effective as of the date You first made, used, - sold, distributed, or had made, Modifications made by that - Participant. - - 8.3. If You assert a patent infringement claim against Participant - alleging that such Participant's Contributor Version directly or - indirectly infringes any patent where such claim is resolved (such as - by license or settlement) prior to the initiation of patent - infringement litigation, then the reasonable value of the licenses - granted by such Participant under Sections 2.1 or 2.2 shall be taken - into account in determining the amount or value of any payment or - license. - - 8.4. In the event of termination under Sections 8.1 or 8.2 above, - all end user license agreements (excluding distributors and resellers) - which have been validly granted by You or any distributor hereunder - prior to termination shall survive termination. - -9. LIMITATION OF LIABILITY. - - UNDER NO CIRCUMSTANCES AND UNDER NO LEGAL THEORY, WHETHER TORT - (INCLUDING NEGLIGENCE), CONTRACT, OR OTHERWISE, SHALL YOU, THE INITIAL - DEVELOPER, ANY OTHER CONTRIBUTOR, OR ANY DISTRIBUTOR OF COVERED CODE, - OR ANY SUPPLIER OF ANY OF SUCH PARTIES, BE LIABLE TO ANY PERSON FOR - ANY INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES OF ANY - CHARACTER INCLUDING, WITHOUT LIMITATION, DAMAGES FOR LOSS OF GOODWILL, - WORK STOPPAGE, COMPUTER FAILURE OR MALFUNCTION, OR ANY AND ALL OTHER - COMMERCIAL DAMAGES OR LOSSES, EVEN IF SUCH PARTY SHALL HAVE BEEN - INFORMED OF THE POSSIBILITY OF SUCH DAMAGES. THIS LIMITATION OF - LIABILITY SHALL NOT APPLY TO LIABILITY FOR DEATH OR PERSONAL INJURY - RESULTING FROM SUCH PARTY'S NEGLIGENCE TO THE EXTENT APPLICABLE LAW - PROHIBITS SUCH LIMITATION. SOME JURISDICTIONS DO NOT ALLOW THE - EXCLUSION OR LIMITATION OF INCIDENTAL OR CONSEQUENTIAL DAMAGES, SO - THIS EXCLUSION AND LIMITATION MAY NOT APPLY TO YOU. - -10. U.S. GOVERNMENT END USERS. - - The Covered Code is a "commercial item," as that term is defined in - 48 C.F.R. 2.101 (Oct. 1995), consisting of "commercial computer - software" and "commercial computer software documentation," as such - terms are used in 48 C.F.R. 12.212 (Sept. 1995). Consistent with 48 - C.F.R. 12.212 and 48 C.F.R. 227.7202-1 through 227.7202-4 (June 1995), - all U.S. Government End Users acquire Covered Code with only those - rights set forth herein. - -11. MISCELLANEOUS. - - This License represents the complete agreement concerning subject - matter hereof. If any provision of this License is held to be - unenforceable, such provision shall be reformed only to the extent - necessary to make it enforceable. This License shall be governed by - California law provisions (except to the extent applicable law, if - any, provides otherwise), excluding its conflict-of-law provisions. - With respect to disputes in which at least one party is a citizen of, - or an entity chartered or registered to do business in the United - States of America, any litigation relating to this License shall be - subject to the jurisdiction of the Federal Courts of the Northern - District of California, with venue lying in Santa Clara County, - California, with the losing party responsible for costs, including - without limitation, court costs and reasonable attorneys' fees and - expenses. The application of the United Nations Convention on - Contracts for the International Sale of Goods is expressly excluded. - Any law or regulation which provides that the language of a contract - shall be construed against the drafter shall not apply to this - License. - -12. RESPONSIBILITY FOR CLAIMS. - - As between Initial Developer and the Contributors, each party is - responsible for claims and damages arising, directly or indirectly, - out of its utilization of rights under this License and You agree to - work with Initial Developer and Contributors to distribute such - responsibility on an equitable basis. Nothing herein is intended or - shall be deemed to constitute any admission of liability. - -13. MULTIPLE-LICENSED CODE. - - Initial Developer may designate portions of the Covered Code as - "Multiple-Licensed". "Multiple-Licensed" means that the Initial - Developer permits you to utilize portions of the Covered Code under - Your choice of the NPL or the alternative licenses, if any, specified - by the Initial Developer in the file described in Exhibit A. - -EXHIBIT A -Mozilla Public License. - - ``The contents of this file are subject to the Mozilla Public License - Version 1.1 (the "License"); you may not use this file except in - compliance with the License. You may obtain a copy of the License at - http://www.mozilla.org/MPL/ - - Software distributed under the License is distributed on an "AS IS" - basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the - License for the specific language governing rights and limitations - under the License. - - The Original Code is ______________________________________. - - The Initial Developer of the Original Code is ________________________. - Portions created by ______________________ are Copyright (C) ______ - _______________________. All Rights Reserved. - - Contributor(s): ______________________________________. - - Alternatively, the contents of this file may be used under the terms - of the _____ license (the "[___] License"), in which case the - provisions of [______] License are applicable instead of those - above. If you wish to allow use of your version of this file only - under the terms of the [____] License and not to allow others to use - your version of this file under the MPL, indicate your decision by - deleting the provisions above and replace them with the notice and - other provisions required by the [___] License. If you do not delete - the provisions above, a recipient may use your version of this file - under either the MPL or the [___] License." - - [NOTE: The text of this Exhibit A may differ slightly from the text of - the notices in the Source Code files of the Original Code. You should - use the text of this Exhibit A rather than the text found in the - Original Code Source Code for Your Modifications.] +Mozilla Public License Version 2.0 +================================== +1. Definitions +-------------- + +1.1. "Contributor" + means each individual or legal entity that creates, contributes to + the creation of, or owns Covered Software. + +1.2. "Contributor Version" + means the combination of the Contributions of others (if any) used + by a Contributor and that particular Contributor's Contribution. + +1.3. "Contribution" + means Covered Software of a particular Contributor. + +1.4. "Covered Software" + means Source Code Form to which the initial Contributor has attached + the notice in Exhibit A, the Executable Form of such Source Code + Form, and Modifications of such Source Code Form, in each case + including portions thereof. + +1.5. "Incompatible With Secondary Licenses" + means + + (a) that the initial Contributor has attached the notice described + in Exhibit B to the Covered Software; or + + (b) that the Covered Software was made available under the terms of + version 1.1 or earlier of the License, but not also under the + terms of a Secondary License. + +1.6. "Executable Form" + means any form of the work other than Source Code Form. + +1.7. "Larger Work" + means a work that combines Covered Software with other material, in + a separate file or files, that is not Covered Software. + +1.8. "License" + means this document. + +1.9. "Licensable" + means having the right to grant, to the maximum extent possible, + whether at the time of the initial grant or subsequently, any and + all of the rights conveyed by this License. + +1.10. "Modifications" + means any of the following: + + (a) any file in Source Code Form that results from an addition to, + deletion from, or modification of the contents of Covered + Software; or + + (b) any new file in Source Code Form that contains any Covered + Software. + +1.11. "Patent Claims" of a Contributor + means any patent claim(s), including without limitation, method, + process, and apparatus claims, in any patent Licensable by such + Contributor that would be infringed, but for the grant of the + License, by the making, using, selling, offering for sale, having + made, import, or transfer of either its Contributions or its + Contributor Version. + +1.12. "Secondary License" + means either the GNU General Public License, Version 2.0, the GNU + Lesser General Public License, Version 2.1, the GNU Affero General + Public License, Version 3.0, or any later versions of those + licenses. + +1.13. "Source Code Form" + means the form of the work preferred for making modifications. + +1.14. "You" (or "Your") + means an individual or a legal entity exercising rights under this + License. For legal entities, "You" includes any entity that + controls, is controlled by, or is under common control with You. For + purposes of this definition, "control" means (a) the power, direct + or indirect, to cause the direction or management of such entity, + whether by contract or otherwise, or (b) ownership of more than + fifty percent (50%) of the outstanding shares or beneficial + ownership of such entity. + +2. License Grants and Conditions +-------------------------------- + +2.1. Grants + +Each Contributor hereby grants You a world-wide, royalty-free, +non-exclusive license: + +(a) under intellectual property rights (other than patent or trademark) + Licensable by such Contributor to use, reproduce, make available, + modify, display, perform, distribute, and otherwise exploit its + Contributions, either on an unmodified basis, with Modifications, or + as part of a Larger Work; and + +(b) under Patent Claims of such Contributor to make, use, sell, offer + for sale, have made, import, and otherwise transfer either its + Contributions or its Contributor Version. + +2.2. Effective Date + +The licenses granted in Section 2.1 with respect to any Contribution +become effective for each Contribution on the date the Contributor first +distributes such Contribution. + +2.3. Limitations on Grant Scope + +The licenses granted in this Section 2 are the only rights granted under +this License. No additional rights or licenses will be implied from the +distribution or licensing of Covered Software under this License. +Notwithstanding Section 2.1(b) above, no patent license is granted by a +Contributor: + +(a) for any code that a Contributor has removed from Covered Software; + or + +(b) for infringements caused by: (i) Your and any other third party's + modifications of Covered Software, or (ii) the combination of its + Contributions with other software (except as part of its Contributor + Version); or + +(c) under Patent Claims infringed by Covered Software in the absence of + its Contributions. + +This License does not grant any rights in the trademarks, service marks, +or logos of any Contributor (except as may be necessary to comply with +the notice requirements in Section 3.4). + +2.4. Subsequent Licenses + +No Contributor makes additional grants as a result of Your choice to +distribute the Covered Software under a subsequent version of this +License (see Section 10.2) or under the terms of a Secondary License (if +permitted under the terms of Section 3.3). + +2.5. Representation + +Each Contributor represents that the Contributor believes its +Contributions are its original creation(s) or it has sufficient rights +to grant the rights to its Contributions conveyed by this License. + +2.6. Fair Use + +This License is not intended to limit any rights You have under +applicable copyright doctrines of fair use, fair dealing, or other +equivalents. + +2.7. Conditions + +Sections 3.1, 3.2, 3.3, and 3.4 are conditions of the licenses granted +in Section 2.1. + +3. Responsibilities +------------------- + +3.1. Distribution of Source Form + +All distribution of Covered Software in Source Code Form, including any +Modifications that You create or to which You contribute, must be under +the terms of this License. You must inform recipients that the Source +Code Form of the Covered Software is governed by the terms of this +License, and how they can obtain a copy of this License. You may not +attempt to alter or restrict the recipients' rights in the Source Code +Form. + +3.2. Distribution of Executable Form + +If You distribute Covered Software in Executable Form then: + +(a) such Covered Software must also be made available in Source Code + Form, as described in Section 3.1, and You must inform recipients of + the Executable Form how they can obtain a copy of such Source Code + Form by reasonable means in a timely manner, at a charge no more + than the cost of distribution to the recipient; and + +(b) You may distribute such Executable Form under the terms of this + License, or sublicense it under different terms, provided that the + license for the Executable Form does not attempt to limit or alter + the recipients' rights in the Source Code Form under this License. + +3.3. Distribution of a Larger Work + +You may create and distribute a Larger Work under terms of Your choice, +provided that You also comply with the requirements of this License for +the Covered Software. If the Larger Work is a combination of Covered +Software with a work governed by one or more Secondary Licenses, and the +Covered Software is not Incompatible With Secondary Licenses, this +License permits You to additionally distribute such Covered Software +under the terms of such Secondary License(s), so that the recipient of +the Larger Work may, at their option, further distribute the Covered +Software under the terms of either this License or such Secondary +License(s). + +3.4. Notices + +You may not remove or alter the substance of any license notices +(including copyright notices, patent notices, disclaimers of warranty, +or limitations of liability) contained within the Source Code Form of +the Covered Software, except that You may alter any license notices to +the extent required to remedy known factual inaccuracies. + +3.5. Application of Additional Terms + +You may choose to offer, and to charge a fee for, warranty, support, +indemnity or liability obligations to one or more recipients of Covered +Software. However, You may do so only on Your own behalf, and not on +behalf of any Contributor. You must make it absolutely clear that any +such warranty, support, indemnity, or liability obligation is offered by +You alone, and You hereby agree to indemnify every Contributor for any +liability incurred by such Contributor as a result of warranty, support, +indemnity or liability terms You offer. You may include additional +disclaimers of warranty and limitations of liability specific to any +jurisdiction. + +4. Inability to Comply Due to Statute or Regulation +--------------------------------------------------- + +If it is impossible for You to comply with any of the terms of this +License with respect to some or all of the Covered Software due to +statute, judicial order, or regulation then You must: (a) comply with +the terms of this License to the maximum extent possible; and (b) +describe the limitations and the code they affect. Such description must +be placed in a text file included with all distributions of the Covered +Software under this License. Except to the extent prohibited by statute +or regulation, such description must be sufficiently detailed for a +recipient of ordinary skill to be able to understand it. + +5. Termination +-------------- + +5.1. The rights granted under this License will terminate automatically +if You fail to comply with any of its terms. However, if You become +compliant, then the rights granted under this License from a particular +Contributor are reinstated (a) provisionally, unless and until such +Contributor explicitly and finally terminates Your grants, and (b) on an +ongoing basis, if such Contributor fails to notify You of the +non-compliance by some reasonable means prior to 60 days after You have +come back into compliance. Moreover, Your grants from a particular +Contributor are reinstated on an ongoing basis if such Contributor +notifies You of the non-compliance by some reasonable means, this is the +first time You have received notice of non-compliance with this License +from such Contributor, and You become compliant prior to 30 days after +Your receipt of the notice. + +5.2. If You initiate litigation against any entity by asserting a patent +infringement claim (excluding declaratory judgment actions, +counter-claims, and cross-claims) alleging that a Contributor Version +directly or indirectly infringes any patent, then the rights granted to +You by any and all Contributors for the Covered Software under Section +2.1 of this License shall terminate. + +5.3. In the event of termination under Sections 5.1 or 5.2 above, all +end user license agreements (excluding distributors and resellers) which +have been validly granted by You or Your distributors under this License +prior to termination shall survive termination. + +************************************************************************ +* * +* 6. Disclaimer of Warranty * +* ------------------------- * +* * +* Covered Software is provided under this License on an "as is" * +* basis, without warranty of any kind, either expressed, implied, or * +* statutory, including, without limitation, warranties that the * +* Covered Software is free of defects, merchantable, fit for a * +* particular purpose or non-infringing. The entire risk as to the * +* quality and performance of the Covered Software is with You. * +* Should any Covered Software prove defective in any respect, You * +* (not any Contributor) assume the cost of any necessary servicing, * +* repair, or correction. This disclaimer of warranty constitutes an * +* essential part of this License. No use of any Covered Software is * +* authorized under this License except under this disclaimer. * +* * +************************************************************************ + +************************************************************************ +* * +* 7. Limitation of Liability * +* -------------------------- * +* * +* Under no circumstances and under no legal theory, whether tort * +* (including negligence), contract, or otherwise, shall any * +* Contributor, or anyone who distributes Covered Software as * +* permitted above, be liable to You for any direct, indirect, * +* special, incidental, or consequential damages of any character * +* including, without limitation, damages for lost profits, loss of * +* goodwill, work stoppage, computer failure or malfunction, or any * +* and all other commercial damages or losses, even if such party * +* shall have been informed of the possibility of such damages. This * +* limitation of liability shall not apply to liability for death or * +* personal injury resulting from such party's negligence to the * +* extent applicable law prohibits such limitation. Some * +* jurisdictions do not allow the exclusion or limitation of * +* incidental or consequential damages, so this exclusion and * +* limitation may not apply to You. * +* * +************************************************************************ + +8. Litigation +------------- + +Any litigation relating to this License may be brought only in the +courts of a jurisdiction where the defendant maintains its principal +place of business and such litigation shall be governed by laws of that +jurisdiction, without reference to its conflict-of-law provisions. +Nothing in this Section shall prevent a party's ability to bring +cross-claims or counter-claims. + +9. Miscellaneous +---------------- + +This License represents the complete agreement concerning the subject +matter hereof. If any provision of this License is held to be +unenforceable, such provision shall be reformed only to the extent +necessary to make it enforceable. Any law or regulation which provides +that the language of a contract shall be construed against the drafter +shall not be used to construe this License against a Contributor. + +10. Versions of the License +--------------------------- + +10.1. New Versions + +Mozilla Foundation is the license steward. Except as provided in Section +10.3, no one other than the license steward has the right to modify or +publish new versions of this License. Each version will be given a +distinguishing version number. + +10.2. Effect of New Versions + +You may distribute the Covered Software under the terms of the version +of the License under which You originally received the Covered Software, +or under the terms of any subsequent version published by the license +steward. + +10.3. Modified Versions + +If you create software not governed by this License, and you want to +create a new license for such software, you may create and use a +modified version of this License if you rename the license and remove +any references to the name of the license steward (except to note that +such modified license differs from this License). + +10.4. Distributing Source Code Form that is Incompatible With Secondary +Licenses + +If You choose to distribute Source Code Form that is Incompatible With +Secondary Licenses under the terms of this version of the License, the +notice described in Exhibit B of this License must be attached. + +Exhibit A - Source Code Form License Notice +------------------------------------------- + + This Source Code Form is subject to the terms of the Mozilla Public + License, v. 2.0. If a copy of the MPL was not distributed with this + file, You can obtain one at http://mozilla.org/MPL/2.0/. + +If it is not possible or desirable to put the notice in a particular +file, then You may include the notice in a location (such as a LICENSE +file in a relevant directory) where a recipient would be likely to look +for such a notice. + +You may add additional accurate notices of copyright ownership. + +Exhibit B - "Incompatible With Secondary Licenses" Notice +--------------------------------------------------------- + + This Source Code Form is "Incompatible With Secondary Licenses", as + defined by the Mozilla Public License, v. 2.0. \ No newline at end of file diff --git a/LocalTestRun.testrunconfig b/LocalTestRun.testrunconfig index b066323e..72f80404 100644 --- a/LocalTestRun.testrunconfig +++ b/LocalTestRun.testrunconfig @@ -2,7 +2,7 @@ This is a default test run configuration for a local test run. - + diff --git a/MakeRelease.cmd b/MakeRelease.cmd index cbbb987d..c5c8952c 100644 --- a/MakeRelease.cmd +++ b/MakeRelease.cmd @@ -1,5 +1 @@ -msbuild OpenMcdf.sln /property:Configuration=Release -robocopy ./src NEW_RELEASE/src /MIR /xd bin obj -robocopy ./src/bin/Release NEW_RELEASE *.* -robocopy "./Structured Storage Explorer\bin\Release" NEW_RELEASE\StructuredStorageExplorer\ StucturedStorageExplorer.exe Be.Windows.Forms.HexBox.dll OpenMcdf.dll -robocopy ./ NEW_RELEASE "PRE_RELEASE_note.txt" "Release notes.txt" "Support.txt" "License.txt" \ No newline at end of file +msbuild OpenMcdf.sln /property:Configuration=Release \ No newline at end of file diff --git a/OpenMcdf.sln b/OpenMcdf.sln index a15e0d0e..4c7cf72e 100644 --- a/OpenMcdf.sln +++ b/OpenMcdf.sln @@ -7,37 +7,42 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution ProjectSection(SolutionItems) = preProject LocalTestRun.testrunconfig = LocalTestRun.testrunconfig OpenMcdf.vsmdi = OpenMcdf.vsmdi - OpenMcdf2.vsmdi = OpenMcdf2.vsmdi EndProjectSection EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestFiles", "TestFiles", "{AC082D72-85BB-4FE8-AE0A-DB12A5729317}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "TestFiles", "TestFiles", "{8DC8730A-F254-4848-B272-BDFFCB5FDC00}" ProjectSection(SolutionItems) = preProject - TestFiles\2_MB-W.ppt = TestFiles\2_MB-W.ppt - TestFiles\BUG_16_.xls = TestFiles\BUG_16_.xls - TestFiles\CorruptedDoc_bug3547815.doc = TestFiles\CorruptedDoc_bug3547815.doc - TestFiles\CorruptedDoc_bug3547815_B.doc = TestFiles\CorruptedDoc_bug3547815_B.doc - TestFiles\CyclicFAT.cfs = TestFiles\CyclicFAT.cfs - TestFiles\MultipleStorage.cfs = TestFiles\MultipleStorage.cfs - TestFiles\MultipleStorage2.cfs = TestFiles\MultipleStorage2.cfs - TestFiles\MultipleStorage3.cfs = TestFiles\MultipleStorage3.cfs - TestFiles\MultipleStorage4.cfs = TestFiles\MultipleStorage4.cfs - TestFiles\report.xls = TestFiles\report.xls - TestFiles\report_name_fix.xls = TestFiles\report_name_fix.xls + sources\TestFiles\2_MB-W.ppt = sources\TestFiles\2_MB-W.ppt + sources\TestFiles\_thumbs_bug_24.db = sources\TestFiles\_thumbs_bug_24.db + sources\TestFiles\BUG_16_.xls = sources\TestFiles\BUG_16_.xls + sources\TestFiles\CorruptedDoc_bug3547815.doc = sources\TestFiles\CorruptedDoc_bug3547815.doc + sources\TestFiles\CorruptedDoc_bug3547815_B.doc = sources\TestFiles\CorruptedDoc_bug3547815_B.doc + sources\Test\TestFiles\CorruptedDoc_bug36.doc = sources\Test\TestFiles\CorruptedDoc_bug36.doc + sources\TestFiles\CyclicFAT.cfs = sources\TestFiles\CyclicFAT.cfs + sources\TestFiles\MultipleStorage.cfs = sources\TestFiles\MultipleStorage.cfs + sources\TestFiles\MultipleStorage2.cfs = sources\TestFiles\MultipleStorage2.cfs + sources\TestFiles\MultipleStorage3.cfs = sources\TestFiles\MultipleStorage3.cfs + sources\TestFiles\MultipleStorage4.cfs = sources\TestFiles\MultipleStorage4.cfs + sources\TestFiles\report.xls = sources\TestFiles\report.xls + sources\TestFiles\report_name_fix.xls = sources\TestFiles\report_name_fix.xls + sources\TestFiles\reportREAD.xls = sources\TestFiles\reportREAD.xls + sources\TestFiles\testbad.ole = sources\TestFiles\testbad.ole EndProjectSection EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenMcdf", "src\OpenMcdf.csproj", "{56E15D4A-8A37-4C7C-BB44-FD59AFF220C1}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenMcdf", "sources\OpenMcdf\OpenMcdf.csproj", "{56E15D4A-8A37-4C7C-BB44-FD59AFF220C1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenMcdfMemTest", "Memory Test\OpenMcdfMemTest.csproj", "{E2BAD82D-3040-462B-BAA2-6E608A9054F4}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenMcdf.Extensions", "sources\OpenMcdf.Extensions\OpenMcdf.Extensions.csproj", "{DB748C1D-D71C-442B-832D-2E33BE816CBB}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenMcdfPerfTest", "Performance Test\OpenMcdfPerfTest.csproj", "{7077508F-B313-4DF6-8855-4764911BE161}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StructuredStorageExplorer", "sources\Structured Storage Explorer\StructuredStorageExplorer.csproj", "{4F6323A8-9C06-4D94-808F-EBD69B8370D7}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenMcdfTest", "Unit Test\OpenMcdfTest.csproj", "{FD339266-8842-40B4-9230-F8E84FC42AC2}" +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Test", "Test", "{73814657-FC73-4066-AABD-86062F2A132E}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "StructuredStorageExplorer", "Structured Storage Explorer\StructuredStorageExplorer.csproj", "{4F6323A8-9C06-4D94-808F-EBD69B8370D7}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenMcdf.Extensions.Test", "sources\Test\OpenMcdf.Extensions.Test\OpenMcdf.Extensions.Test.csproj", "{B9CB103F-0AA3-486D-9C9C-672924B6169C}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenMcdf.Extensions", "OpenMcdf.Extensions\OpenMcdf.Extensions.csproj", "{DB748C1D-D71C-442B-832D-2E33BE816CBB}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenMcdf.MemTest", "sources\Test\OpenMcdf.MemTest\OpenMcdf.MemTest.csproj", "{E2BAD82D-3040-462B-BAA2-6E608A9054F4}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenMcdfExtensionsTest", "OpenMcdfExtensionsTest\OpenMcdfExtensionsTest.csproj", "{B9CB103F-0AA3-486D-9C9C-672924B6169C}" +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenMcdf.PerfTest", "sources\Test\OpenMcdf.PerfTest\OpenMcdf.PerfTest.csproj", "{7077508F-B313-4DF6-8855-4764911BE161}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "OpenMcdf.Test", "sources\Test\OpenMcdf.Test\OpenMcdf.Test.csproj", "{FD339266-8842-40B4-9230-F8E84FC42AC2}" EndProject Global GlobalSection(SolutionConfigurationPlatforms) = preSolution @@ -49,6 +54,18 @@ Global {56E15D4A-8A37-4C7C-BB44-FD59AFF220C1}.Debug|Any CPU.Build.0 = Debug|Any CPU {56E15D4A-8A37-4C7C-BB44-FD59AFF220C1}.Release|Any CPU.ActiveCfg = Release|Any CPU {56E15D4A-8A37-4C7C-BB44-FD59AFF220C1}.Release|Any CPU.Build.0 = Release|Any CPU + {DB748C1D-D71C-442B-832D-2E33BE816CBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {DB748C1D-D71C-442B-832D-2E33BE816CBB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {DB748C1D-D71C-442B-832D-2E33BE816CBB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {DB748C1D-D71C-442B-832D-2E33BE816CBB}.Release|Any CPU.Build.0 = Release|Any CPU + {4F6323A8-9C06-4D94-808F-EBD69B8370D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {4F6323A8-9C06-4D94-808F-EBD69B8370D7}.Debug|Any CPU.Build.0 = Debug|Any CPU + {4F6323A8-9C06-4D94-808F-EBD69B8370D7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {4F6323A8-9C06-4D94-808F-EBD69B8370D7}.Release|Any CPU.Build.0 = Release|Any CPU + {B9CB103F-0AA3-486D-9C9C-672924B6169C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {B9CB103F-0AA3-486D-9C9C-672924B6169C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {B9CB103F-0AA3-486D-9C9C-672924B6169C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {B9CB103F-0AA3-486D-9C9C-672924B6169C}.Release|Any CPU.Build.0 = Release|Any CPU {E2BAD82D-3040-462B-BAA2-6E608A9054F4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU {E2BAD82D-3040-462B-BAA2-6E608A9054F4}.Debug|Any CPU.Build.0 = Debug|Any CPU {E2BAD82D-3040-462B-BAA2-6E608A9054F4}.Release|Any CPU.ActiveCfg = Release|Any CPU @@ -61,24 +78,16 @@ Global {FD339266-8842-40B4-9230-F8E84FC42AC2}.Debug|Any CPU.Build.0 = Debug|Any CPU {FD339266-8842-40B4-9230-F8E84FC42AC2}.Release|Any CPU.ActiveCfg = Release|Any CPU {FD339266-8842-40B4-9230-F8E84FC42AC2}.Release|Any CPU.Build.0 = Release|Any CPU - {4F6323A8-9C06-4D94-808F-EBD69B8370D7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {4F6323A8-9C06-4D94-808F-EBD69B8370D7}.Debug|Any CPU.Build.0 = Debug|Any CPU - {4F6323A8-9C06-4D94-808F-EBD69B8370D7}.Release|Any CPU.ActiveCfg = Release|Any CPU - {4F6323A8-9C06-4D94-808F-EBD69B8370D7}.Release|Any CPU.Build.0 = Release|Any CPU - {DB748C1D-D71C-442B-832D-2E33BE816CBB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {DB748C1D-D71C-442B-832D-2E33BE816CBB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {DB748C1D-D71C-442B-832D-2E33BE816CBB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {DB748C1D-D71C-442B-832D-2E33BE816CBB}.Release|Any CPU.Build.0 = Release|Any CPU - {B9CB103F-0AA3-486D-9C9C-672924B6169C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {B9CB103F-0AA3-486D-9C9C-672924B6169C}.Debug|Any CPU.Build.0 = Debug|Any CPU - {B9CB103F-0AA3-486D-9C9C-672924B6169C}.Release|Any CPU.ActiveCfg = Release|Any CPU - {B9CB103F-0AA3-486D-9C9C-672924B6169C}.Release|Any CPU.Build.0 = Release|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE EndGlobalSection GlobalSection(NestedProjects) = preSolution - {AC082D72-85BB-4FE8-AE0A-DB12A5729317} = {6C619B4F-100F-4D60-BEF1-60B770D24E5E} + {8DC8730A-F254-4848-B272-BDFFCB5FDC00} = {73814657-FC73-4066-AABD-86062F2A132E} + {B9CB103F-0AA3-486D-9C9C-672924B6169C} = {73814657-FC73-4066-AABD-86062F2A132E} + {E2BAD82D-3040-462B-BAA2-6E608A9054F4} = {73814657-FC73-4066-AABD-86062F2A132E} + {7077508F-B313-4DF6-8855-4764911BE161} = {73814657-FC73-4066-AABD-86062F2A132E} + {FD339266-8842-40B4-9230-F8E84FC42AC2} = {73814657-FC73-4066-AABD-86062F2A132E} EndGlobalSection GlobalSection(TestCaseManagementSettings) = postSolution CategoryFile = OpenMcdf2.vsmdi diff --git a/Html Help/OpenMcdfHelp.shfbproj b/sources/Html Help/OpenMcdfHelp.shfbproj similarity index 54% rename from Html Help/OpenMcdfHelp.shfbproj rename to sources/Html Help/OpenMcdfHelp.shfbproj index 658fb826..16775b96 100644 --- a/Html Help/OpenMcdfHelp.shfbproj +++ b/sources/Html Help/OpenMcdfHelp.shfbproj @@ -1,14 +1,15 @@ - + + - Debug AnyCPU 2.0 {6b2e0fe5-8246-4f87-9663-d72ebadfc53f} - 1.8.0.0 - Documentation Documentation @@ -16,14 +17,14 @@ .\Help\ OpenMCDF - Copyright 2010 - 2012 &#169%3b Federico Blaseotto + Copyright 2010 - 2016 &#169%3b Federico Blaseotto Open MCDF Open MCDF Open MCDF Summary, AutoDocumentCtors - - + + ironfede%40users.sourceforge.net @@ -35,7 +36,7 @@ - Prototype + VS2013 en-US @@ -43,18 +44,32 @@ InheritedMembers, InheritedFrameworkMembers - - - - - - - - 1.5.4.0 + + + + + + 2.1 Hierarchical + 2 + False + C# + Blank + False + False + Guid + AboveNamespaces + OnlyWarningsAndErrors + HtmlHelp1 + False + .NET Framework 4.0 + True + False + False + True - diff --git a/sources/OpenMcdf.Extensions/CFStreamExtensions.cs b/sources/OpenMcdf.Extensions/CFStreamExtensions.cs new file mode 100644 index 00000000..4e922089 --- /dev/null +++ b/sources/OpenMcdf.Extensions/CFStreamExtensions.cs @@ -0,0 +1,139 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; + +namespace OpenMcdf.Extensions +{ + public static class CFStreamExtension + { + private class StreamDecorator : Stream + { + private CFStream cfStream; + private long position = 0; + + public StreamDecorator(CFStream cfstream) + { + this.cfStream = cfstream; + } + + public override bool CanRead + { + get { return true; } + } + + public override bool CanSeek + { + get { return true; } + } + + public override bool CanWrite + { + get { return true; } + } + + public override void Flush() + { + // nothing to do; + } + + public override long Length + { + get { return cfStream.Size; } + } + + public override long Position + { + get + { + return position; + } + set + { + position = value; + } + } + + public override int Read(byte[] buffer, int offset, int count) + { + if (count > buffer.Length) + throw new ArgumentException("Count parameter exceeds buffer size"); + + if (buffer == null) + throw new ArgumentNullException("Buffer cannot be null"); + + if (offset < 0 || count < 0) + throw new ArgumentOutOfRangeException("Offset and Count parameters must be non-negative numbers"); + + if (position >= cfStream.Size) + return 0; + + count = this.cfStream.Read(buffer, position, offset, count); + position += count; + return count; + } + + public override long Seek(long offset, SeekOrigin origin) + { + switch (origin) + { + case SeekOrigin.Begin: + position = offset; + break; + case SeekOrigin.Current: + position += offset; + break; + case SeekOrigin.End: + position -= offset; + break; + default: + throw new Exception("Invalid origin selected"); + } + + return position; + } + + public override void SetLength(long value) + { + this.cfStream.Resize(value); + } + + public override void Write(byte[] buffer, int offset, int count) + { + this.cfStream.Write(buffer, position, offset, count); + position += count; + } + + public override void Close() + { + // Do nothing + } + } + + /// + /// Return the current CFStream object + /// as a Stream object. + /// + /// Current CFStream object + /// A Stream object representing structured stream data + public static Stream AsIOStream(this CFStream cfStream) + { + return new StreamDecorator(cfStream); + } + + /// + /// Return the current CFStream object + /// as a OLE properties Stream. + /// + /// + /// A OLE Propertie stream + public static OLEProperties.PropertySetStream AsOLEProperties(this CFStream cfStream) + { + var result = new OLEProperties.PropertySetStream(); + result.Read(new BinaryReader(new StreamDecorator(cfStream))); + return result; + } + + } +} diff --git a/sources/OpenMcdf.Extensions/OLEProperties/Interfaces/IBinarySerializable.cs b/sources/OpenMcdf.Extensions/OLEProperties/Interfaces/IBinarySerializable.cs new file mode 100644 index 00000000..67966584 --- /dev/null +++ b/sources/OpenMcdf.Extensions/OLEProperties/Interfaces/IBinarySerializable.cs @@ -0,0 +1,14 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace OpenMcdf.Extensions.OLEProperties.Interfaces +{ + public interface IBinarySerializable + { + void Write(BinaryWriter bw); + void Read(BinaryReader br); + } +} diff --git a/sources/OpenMcdf.Extensions/OLEProperties/Interfaces/ITypedPropertyValue.cs b/sources/OpenMcdf.Extensions/OLEProperties/Interfaces/ITypedPropertyValue.cs new file mode 100644 index 00000000..011bf8e2 --- /dev/null +++ b/sources/OpenMcdf.Extensions/OLEProperties/Interfaces/ITypedPropertyValue.cs @@ -0,0 +1,34 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace OpenMcdf.Extensions.OLEProperties.Interfaces +{ + public interface ITypedPropertyValue : IBinarySerializable + { + bool IsArray + { + get; + set; + } + + bool IsVector + { + get; + set; + } + + object PropertyValue + { + get; + set; + } + + VTPropertyType VTType + { + get; + //set; + } + } +} diff --git a/sources/OpenMcdf.Extensions/OLEProperties/PropertyFactory.cs b/sources/OpenMcdf.Extensions/OLEProperties/PropertyFactory.cs new file mode 100644 index 00000000..ebc0351b --- /dev/null +++ b/sources/OpenMcdf.Extensions/OLEProperties/PropertyFactory.cs @@ -0,0 +1,310 @@ +using OpenMcdf.Extensions.OLEProperties.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.IO; + +namespace OpenMcdf.Extensions.OLEProperties +{ + internal class PropertyFactory + { + private PropertyContext ctx; + + public PropertyFactory(PropertyContext ctx) + { + this.ctx = ctx; + } + + private PropertyFactory() + { + + } + + public ITypedPropertyValue NewProperty(VTPropertyType vType, PropertyContext ctx) + { + ITypedPropertyValue pr = null; + + switch (vType) + { + case VTPropertyType.VT_I2: + pr = new VT_I2_Property(vType); + break; + case VTPropertyType.VT_I4: + pr = new VT_I4_Property(vType); + break; + case VTPropertyType.VT_R4: + pr = new VT_R4_Property(vType); + break; + case VTPropertyType.VT_LPSTR: + pr = new VT_LPSTR_Property(vType, ctx.CodePage); + break; + case VTPropertyType.VT_FILETIME: + pr = new VT_FILETIME_Property(vType); + break; + case VTPropertyType.VT_DECIMAL: + pr = new VT_DECIMAL_Property(vType); + break; + case VTPropertyType.VT_BOOL: + pr = new VT_BOOL_Property(vType); + break; + case VTPropertyType.VT_VECTOR_HEADER: + pr = new VT_VectorHeader(vType); + break; + case VTPropertyType.VT_EMPTY: + pr = new VT_EMPTY_Property(vType); + break; + default: + throw new Exception("Unrecognized property type"); + } + + return pr; + } + + + #region Property implementations + private class VT_EMPTY_Property : TypedPropertyValue + { + public VT_EMPTY_Property(VTPropertyType vType) : base(vType) + { + + } + + public override void Read(System.IO.BinaryReader br) + { + this.propertyValue = null; + } + + public override void Write(System.IO.BinaryWriter bw) + { + + } + } + + private class VT_I2_Property : TypedPropertyValue + { + public VT_I2_Property(VTPropertyType vType) : base(vType) + { + + } + + public override void Read(System.IO.BinaryReader br) + { + this.propertyValue = br.ReadInt16(); + } + + public override void Write(System.IO.BinaryWriter bw) + { + bw.Write((short)propertyValue); + } + } + + private class VT_I4_Property : TypedPropertyValue + { + public VT_I4_Property(VTPropertyType vType) : base(vType) + { + + } + + public override void Read(System.IO.BinaryReader br) + { + this.propertyValue = br.ReadInt32(); + } + + public override void Write(System.IO.BinaryWriter bw) + { + bw.Write((int)propertyValue); + } + } + + private class VT_R4_Property : TypedPropertyValue + { + public VT_R4_Property(VTPropertyType vType) : base(vType) + { + + } + + public override void Read(System.IO.BinaryReader br) + { + this.propertyValue = br.ReadSingle(); + } + + public override void Write(System.IO.BinaryWriter bw) + { + bw.Write((Single)propertyValue); + } + } + + private class VT_R8_Property : TypedPropertyValue + { + public VT_R8_Property(VTPropertyType vType) : base(vType) + { + + } + + public override void Read(System.IO.BinaryReader br) + { + this.propertyValue = br.ReadDouble(); + } + + public override void Write(System.IO.BinaryWriter bw) + { + bw.Write((Double)propertyValue); + } + } + + private class VT_CY_Property : TypedPropertyValue + { + public VT_CY_Property(VTPropertyType vType) : base(vType) + { + } + + public override void Read(System.IO.BinaryReader br) + { + Int64 temp = br.ReadInt64(); + + this.propertyValue = (double)(temp /= 10000); + } + + public override void Write(System.IO.BinaryWriter bw) + { + bw.Write((Int64)propertyValue * 10000); + } + } + + private class VT_DATE_Property : TypedPropertyValue + { + public VT_DATE_Property(VTPropertyType vType) : base(vType) + { + + } + + public override void Read(System.IO.BinaryReader br) + { + Double temp = br.ReadDouble(); + + this.propertyValue = DateTime.FromOADate(temp); + } + + public override void Write(System.IO.BinaryWriter bw) + { + bw.Write(((DateTime)propertyValue).ToOADate()); + } + } + + private class VT_LPSTR_Property : TypedPropertyValue + { + private uint size = 0; + private byte[] data; + private int codePage; + + public VT_LPSTR_Property(VTPropertyType vType, int codePage) : base(vType) + { + this.codePage = codePage; + + } + + public override void Read(System.IO.BinaryReader br) + { + size = br.ReadUInt32(); + data = br.ReadBytes((int)size); + this.propertyValue = Encoding.GetEncoding(codePage).GetString(data); + int m = (int)size % 4; + br.ReadBytes(m); // padding + } + + public override void Write(System.IO.BinaryWriter bw) + { + data = Encoding.GetEncoding(codePage).GetBytes((String)propertyValue); + size = (uint)data.Length; + int m = (int)size % 4; + bw.Write(data); + for (int i = 0; i < m; i++) // padding + bw.Write(0); + } + } + + private class VT_FILETIME_Property : TypedPropertyValue + { + + public VT_FILETIME_Property(VTPropertyType vType) : base(vType) + { + + } + + public override void Read(System.IO.BinaryReader br) + { + Int64 tmp = br.ReadInt64(); + propertyValue = DateTime.FromFileTime(tmp); + } + + public override void Write(System.IO.BinaryWriter bw) + { + bw.Write(((DateTime)propertyValue).ToFileTime()); + } + } + + private class VT_DECIMAL_Property : TypedPropertyValue + { + + public VT_DECIMAL_Property(VTPropertyType vType) : base(vType) + { + + } + + public override void Read(System.IO.BinaryReader br) + { + Decimal d; + + br.ReadInt16(); // wReserved + byte scale = br.ReadByte(); + byte sign = br.ReadByte(); + + uint u = br.ReadUInt32(); + d = Convert.ToDecimal(Math.Pow(2, 64)) * u; + d += br.ReadUInt64(); + + if (sign != 0) + d = -d; + d /= (10 << scale); + + this.propertyValue = d; + } + + public override void Write(System.IO.BinaryWriter bw) + { + bw.Write((short)propertyValue); + } + } + + private class VT_BOOL_Property : TypedPropertyValue + { + public VT_BOOL_Property(VTPropertyType vType) : base(vType) + { + + } + + public override void Read(BinaryReader br) + { + this.propertyValue = br.ReadUInt16() == (ushort)0xFFFF ? true : false; + //br.ReadUInt16();//padding + } + } + + private class VT_VectorHeader : TypedPropertyValue + { + public VT_VectorHeader(VTPropertyType vType) : base(vType) + { + + } + + public override void Read(BinaryReader br) + { + propertyValue = br.ReadUInt32(); + } + } + + #endregion + + } +} diff --git a/sources/OpenMcdf.Extensions/OLEProperties/PropertyIdentifierAndOffset.cs b/sources/OpenMcdf.Extensions/OLEProperties/PropertyIdentifierAndOffset.cs new file mode 100644 index 00000000..588d4c5d --- /dev/null +++ b/sources/OpenMcdf.Extensions/OLEProperties/PropertyIdentifierAndOffset.cs @@ -0,0 +1,13 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace OpenMcdf.Extensions.OLEProperties +{ + public class PropertyIdentifierAndOffset + { + public PropertyIdentifiersSummaryInfo PropertyIdentifier { get; set; } + public uint Offset { get; set; } + } +} diff --git a/sources/OpenMcdf.Extensions/OLEProperties/PropertyReader.cs b/sources/OpenMcdf.Extensions/OLEProperties/PropertyReader.cs new file mode 100644 index 00000000..ccc643ad --- /dev/null +++ b/sources/OpenMcdf.Extensions/OLEProperties/PropertyReader.cs @@ -0,0 +1,116 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; +using OpenMcdf.Extensions.OLEProperties.Interfaces; +using System.Collections; + +namespace OpenMcdf.Extensions.OLEProperties +{ + public enum Behavior + { + CaseSensitive, CaseInsensitive + } + + public class PropertyContext + { + + public Int32 CodePage { get; set; } + public Behavior Behavior { get; set; } + public UInt32 Locale { get; set; } + } + + public enum PropertyDimensions + { + IsScalar, IsVector, IsArray + } + + //public class PropertyResult + //{ + // public PropertyDimensions Dimensions { get; set; } + // public uint[] DimSizes { get; set; } + // public List DimValues { get; set; } + //} + + public class PropertyReader + { + + private PropertyContext ctx = new PropertyContext(); + private PropertyFactory factory = null; + + public PropertyReader() + { + factory = new PropertyFactory(ctx); + } + + public List ReadProperty(PropertyIdentifiersSummaryInfo propertyIdentifier, BinaryReader br) + { + List res = new List(); + bool isVariant = false; + PropertyDimensions dim = PropertyDimensions.IsScalar; + + UInt16 pVal = br.ReadUInt16(); + + VTPropertyType vType = (VTPropertyType)(pVal & 0x00FF); + + if ((pVal & 0x1000) != 0) + dim = PropertyDimensions.IsVector; + else if ((pVal & 0x2000) != 0) + dim = PropertyDimensions.IsArray; + + isVariant = ((pVal & 0x00FF) == 0x000C); + + br.ReadUInt16(); // Ushort Padding + + switch (dim) + { + case PropertyDimensions.IsVector: + + ITypedPropertyValue vectorHeader = factory.NewProperty(VTPropertyType.VT_VECTOR_HEADER, ctx); + vectorHeader.Read(br); + + uint nItems = (uint)vectorHeader.PropertyValue; + + for (int i = 0; i < nItems; i++) + { + VTPropertyType vTypeItem = VTPropertyType.VT_EMPTY; + + if (isVariant) + { + UInt16 pValItem = br.ReadUInt16(); + vTypeItem = (VTPropertyType)(pValItem & 0x00FF); + br.ReadUInt16(); // Ushort Padding + } + else + { + vTypeItem = vType; + } + + var p = factory.NewProperty(vTypeItem, ctx); + + p.Read(br); + res.Add(p); + } + + break; + default: + + //Scalar property + ITypedPropertyValue pr = factory.NewProperty(vType, ctx); + + pr.Read(br); + + if (propertyIdentifier == PropertyIdentifiersSummaryInfo.CodePageString) + { + this.ctx.CodePage = (short)pr.PropertyValue; + } + + res.Add(pr); + break; + } + + return res; + } + } +} diff --git a/sources/OpenMcdf.Extensions/OLEProperties/PropertySet.cs b/sources/OpenMcdf.Extensions/OLEProperties/PropertySet.cs new file mode 100644 index 00000000..d7014b25 --- /dev/null +++ b/sources/OpenMcdf.Extensions/OLEProperties/PropertySet.cs @@ -0,0 +1,37 @@ +using OpenMcdf.Extensions.OLEProperties.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace OpenMcdf.Extensions.OLEProperties +{ + public class PropertySet + { + public uint Size { get; set; } + public uint NumProperties { get; set; } + + List propertyIdentifierAndOffsets + = new List(); + + public List PropertyIdentifierAndOffsets + { + get { return propertyIdentifierAndOffsets; } + set { propertyIdentifierAndOffsets = value; } + } + + List properties = new List(); + public List Properties + { + get + { + return properties; + } + set + { + properties = value; + } + } + + } +} diff --git a/sources/OpenMcdf.Extensions/OLEProperties/PropertySetStream.cs b/sources/OpenMcdf.Extensions/OLEProperties/PropertySetStream.cs new file mode 100644 index 00000000..1c1d3767 --- /dev/null +++ b/sources/OpenMcdf.Extensions/OLEProperties/PropertySetStream.cs @@ -0,0 +1,88 @@ +using OpenMcdf.Extensions.OLEProperties.Interfaces; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace OpenMcdf.Extensions.OLEProperties +{ + public class PropertySetStream + { + public ushort ByteOrder { get; set; } + public ushort Version { get; set; } + public uint SystemIdentifier { get; set; } + public Guid CLSID { get; set; } + public uint NumPropertySets { get; set; } + public Guid FMTID0 { get; set; } + public uint Offset0 { get; set; } + public Guid FMTID1 { get; set; } + public uint Offset1 { get; set; } + public PropertySet PropertySet0 { get; set; } + public PropertySet PropertySet1 { get; set; } + + public void Read(System.IO.BinaryReader br) + { + ByteOrder = br.ReadUInt16(); + Version = br.ReadUInt16(); + SystemIdentifier = br.ReadUInt32(); + CLSID = new Guid(br.ReadBytes(16)); + NumPropertySets = br.ReadUInt32(); + FMTID0 = new Guid(br.ReadBytes(16)); + Offset0 = br.ReadUInt32(); + + if (NumPropertySets == 2) + { + FMTID1 = new Guid(br.ReadBytes(16)); + Offset1 = br.ReadUInt32(); + } + + PropertySet0 = new PropertySet(); + PropertySet0.Size = br.ReadUInt32(); + PropertySet0.NumProperties = br.ReadUInt32(); + + // Read property offsets + for (int i = 0; i < PropertySet0.NumProperties; i++) + { + PropertyIdentifierAndOffset pio = new PropertyIdentifierAndOffset(); + pio.PropertyIdentifier = (PropertyIdentifiersSummaryInfo)br.ReadUInt32(); + pio.Offset = br.ReadUInt32(); + PropertySet0.PropertyIdentifierAndOffsets.Add(pio); + } + + // Read properties + PropertyReader pr = new PropertyReader(); + for (int i = 0; i < PropertySet0.NumProperties; i++) + { + br.BaseStream.Seek(Offset0 + PropertySet0.PropertyIdentifierAndOffsets[i].Offset, System.IO.SeekOrigin.Begin); + PropertySet0.Properties.AddRange(pr.ReadProperty(PropertySet0.PropertyIdentifierAndOffsets[i].PropertyIdentifier, br)); + } + } + + public void Write(System.IO.BinaryWriter bw) + { + throw new NotImplementedException(); + } + + // private void LoadFromStream(Stream inStream) + // { + // BinaryReader br = new BinaryReader(inStream); + // PropertySetStream psStream = new PropertySetStream(); + // psStream.Read(br); + // br.Close(); + + // propertySets.Clear(); + + // if (psStream.NumPropertySets == 1) + // { + // propertySets.Add(psStream.PropertySet0); + // } + // else + // { + // propertySets.Add(psStream.PropertySet0); + // propertySets.Add(psStream.PropertySet1); + // } + + // return; + // } + } +} diff --git a/sources/OpenMcdf.Extensions/OLEProperties/ProperyIdentifiers.cs b/sources/OpenMcdf.Extensions/OLEProperties/ProperyIdentifiers.cs new file mode 100644 index 00000000..d5e3a4fa --- /dev/null +++ b/sources/OpenMcdf.Extensions/OLEProperties/ProperyIdentifiers.cs @@ -0,0 +1,118 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace OpenMcdf.Extensions.OLEProperties +{ + public enum PropertyIdentifiersSummaryInfo : uint + { + CodePageString = 0x00000001, + PIDSI_TITLE = 0x00000002, + PIDSI_SUBJECT = 0x00000003, + PIDSI_AUTHOR = 0x00000004, + PIDSI_KEYWORDS = 0x00000005, + PIDSI_COMMENTS = 0x00000006, + PIDSI_TEMPLATE = 0x00000007, + PIDSI_LASTAUTHOR = 0x00000008, + PIDSI_REVNUMBER = 0x00000009, + PIDSI_APPNAME = 0x00000012, + PIDSI_EDITTIME = 0x0000000A, + PIDSI_LASTPRINTED = 0x0000000B, + PIDSI_CREATE_DTM = 0x0000000C, + PIDSI_LASTSAVE_DTM = 0x0000000D, + PIDSI_PAGECOUNT = 0x0000000E, + PIDSI_WORDCOUNT = 0x0000000F, + PIDSI_CHARCOUNT = 0x00000010, + PIDSI_DOC_SECURITY = 0x00000013 + } + + public enum PropertyIdentifiersDocumentSummaryInfo : uint + { + CodePageString = 0x00000001, + PIDDSI_CATEGORY = 0x00000002, //Category VT_LPSTR + PIDDSI_PRESFORMAT = 0x00000003,//PresentationTarget VT_LPSTR + PIDDSI_BYTECOUNT = 0x00000004,//Bytes VT_I4 + PIDDSI_LINECOUNT = 0x00000005,// Lines VT_I4 + PIDDSI_PARCOUNT = 0x00000006,// Paragraphs VT_I4 + PIDDSI_SLIDECOUNT = 0x00000007,// Slides VT_I4 + PIDDSI_NOTECOUNT = 0x00000008,// Notes VT_I4 + PIDDSI_HIDDENCOUNT = 0x00000009,// HiddenSlides VT_I4 + PIDDSI_MMCLIPCOUNT = 0x0000000A,// MMClips VT_I4 + PIDDSI_SCALE = 0x0000000B,//ScaleCrop VT_BOOL + PIDDSI_HEADINGPAIR = 0x0000000C,// HeadingPairs VT_VARIANT | VT_VECTOR + PIDDSI_DOCPARTS = 0x0000000D,//TitlesofParts VT_VECTOR | VT_LPSTR + PIDDSI_MANAGER = 0x0000000E,// Manager VT_LPSTR + PIDDSI_COMPANY = 0x0000000F,// Company VT_LPSTR + PIDDSI_LINKSDIRTY = 0x00000010,//LinksUpToDate VT_BOOL + } + + public static class Extensions + { + public static String GetDescription(this PropertyIdentifiersSummaryInfo identifier) + { + switch (identifier) + { + case PropertyIdentifiersSummaryInfo.CodePageString: + return "CodePage"; + case PropertyIdentifiersSummaryInfo.PIDSI_TITLE: + return "Title"; + case PropertyIdentifiersSummaryInfo.PIDSI_SUBJECT: + return "Subject"; + case PropertyIdentifiersSummaryInfo.PIDSI_AUTHOR: + return "Author"; + case PropertyIdentifiersSummaryInfo.PIDSI_LASTAUTHOR: + return "Last Author"; + case PropertyIdentifiersSummaryInfo.PIDSI_APPNAME: + return "Application Name"; + case PropertyIdentifiersSummaryInfo.PIDSI_CREATE_DTM: + return "Create Time"; + case PropertyIdentifiersSummaryInfo.PIDSI_LASTSAVE_DTM: + return "Last Modified Time"; + case PropertyIdentifiersSummaryInfo.PIDSI_KEYWORDS: + return "Keywords"; + case PropertyIdentifiersSummaryInfo.PIDSI_DOC_SECURITY: + return "Document Security"; + default: return String.Empty; + } + } + + public static String GetDescription(this PropertyIdentifiersDocumentSummaryInfo identifier) + { + switch (identifier) + { + case PropertyIdentifiersDocumentSummaryInfo.CodePageString: + return "CodePage"; + case PropertyIdentifiersDocumentSummaryInfo.PIDDSI_CATEGORY: + return "Category"; + case PropertyIdentifiersDocumentSummaryInfo.PIDDSI_COMPANY: + return "Company"; + case PropertyIdentifiersDocumentSummaryInfo.PIDDSI_DOCPARTS: + return "Titles of Parts"; + case PropertyIdentifiersDocumentSummaryInfo.PIDDSI_HEADINGPAIR: + return "Heading Pairs"; + case PropertyIdentifiersDocumentSummaryInfo.PIDDSI_HIDDENCOUNT: + return "Hidden Slides"; + case PropertyIdentifiersDocumentSummaryInfo.PIDDSI_LINECOUNT: + return "Line Count"; + case PropertyIdentifiersDocumentSummaryInfo.PIDDSI_LINKSDIRTY: + return "Links up to date"; + case PropertyIdentifiersDocumentSummaryInfo.PIDDSI_MANAGER: + return "Manager"; + case PropertyIdentifiersDocumentSummaryInfo.PIDDSI_MMCLIPCOUNT: + return "MMClips"; + case PropertyIdentifiersDocumentSummaryInfo.PIDDSI_NOTECOUNT: + return "Notes"; + case PropertyIdentifiersDocumentSummaryInfo.PIDDSI_PARCOUNT: + return "Paragraphs"; + case PropertyIdentifiersDocumentSummaryInfo.PIDDSI_PRESFORMAT: + return "Presenteation Target"; + case PropertyIdentifiersDocumentSummaryInfo.PIDDSI_SCALE: + return "Scale"; + case PropertyIdentifiersDocumentSummaryInfo.PIDDSI_SLIDECOUNT: + return "Slides"; + default: return String.Empty; + } + } + } +} diff --git a/sources/OpenMcdf.Extensions/OLEProperties/TypedPropertyValue.cs b/sources/OpenMcdf.Extensions/OLEProperties/TypedPropertyValue.cs new file mode 100644 index 00000000..fd489451 --- /dev/null +++ b/sources/OpenMcdf.Extensions/OLEProperties/TypedPropertyValue.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using OpenMcdf.Extensions.OLEProperties.Interfaces; + +namespace OpenMcdf.Extensions.OLEProperties +{ + public class TypedPropertyValue : ITypedPropertyValue, IBinarySerializable + { + private VTPropertyType _VTType; + + public VTPropertyType VTType + { + get { return _VTType; } + //set { _VTType = value; } + } + + protected object propertyValue = null; + public TypedPropertyValue(VTPropertyType vtType) + { + this._VTType = vtType; + } + public virtual object PropertyValue + { + get + { + return propertyValue; + } + + set + { + propertyValue = value; + } + } + + + public bool IsArray + { + get + { + throw new NotImplementedException(); + } + + set + { + throw new NotImplementedException(); + } + } + + public bool IsVector + { + get + { + throw new NotImplementedException(); + } + + set + { + throw new NotImplementedException(); + } + } + + public virtual void Read(System.IO.BinaryReader br) + { + + } + + public virtual void Write(System.IO.BinaryWriter bw) + { + + } + } +} diff --git a/sources/OpenMcdf.Extensions/OLEProperties/VTPropertyType.cs b/sources/OpenMcdf.Extensions/OLEProperties/VTPropertyType.cs new file mode 100644 index 00000000..14aa46fe --- /dev/null +++ b/sources/OpenMcdf.Extensions/OLEProperties/VTPropertyType.cs @@ -0,0 +1,45 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace OpenMcdf.Extensions.OLEProperties +{ + public enum VTPropertyType : ushort + { + VT_EMPTY = 0x0000, + VT_NULL = 0x0001, + VT_I2 = 0x0002, + VT_I4 = 0x0003, + VT_R4 = 0x0004, + VT_R8 = 0x0005, + VT_CY = 0x0006, + VT_DATE = 0x0007, + VT_BSTR = 0x0008, + VT_ERROR = 0x000A, + VT_BOOL = 0x000B, + VT_DECIMAL = 0x000E, + VT_I1 = 0x0010, + VT_UI1 = 0x0011, + VT_UI2 = 0x0012, + VT_UI4 = 0x0013, + VT_I8 = 0x0014, // MUST be an 8-byte signed integer. + VT_UI8 = 0x0015, // MUST be an 8-byte unsigned integer. + VT_INT = 0x0016, // MUST be a 4-byte signed integer. + VT_UINT = 0x0017, // MUST be a 4-byte unsigned integer. + VT_LPSTR = 0x001E, // MUST be a CodePageString. + VT_LPWSTR = 0x001F, // MUST be a UnicodeString. + VT_FILETIME = 0x0040, // MUST be a FILETIME (Packet Version). + VT_BLOB = 0x0041, // MUST be a BLOB. + VT_STREAM = 0x0042, // MUST be an IndirectPropertyName. The storage representing the (non-simple) property set MUST have a stream element with this name. + VT_STORAGE = 0x0043, // MUST be an IndirectPropertyName. The storage representing the (non-simple) property set MUST have a storage element with this name. + VT_STREAMED_OBJECT = 0x0044, // MUST be an IndirectPropertyName. The storage representing the (non-simple) property set MUST have a stream element with this name. + VT_STORED_OBJECT = 0x0045, // MUST be an IndirectPropertyName. The storage representing the (non-simple) property set MUST have a storage element with this name. + VT_BLOB_OBJECT = 0x0046, //MUST be a BLOB. + VT_CF = 0x0047, //MUST be a ClipboardData. + VT_CLSID = 0x0048, //MUST be a GUID (Packet Version) + VT_VERSIONED_STREAM = 0x0049, //MUST be a Verisoned Stream, NOT allowed in simple property + VT_VECTOR_HEADER =0x1000, //--- NOT NORMATIVE + VT_ARRAY_HEADER = 0x2000, //--- NOT NORMATIVE + } +} diff --git a/sources/OpenMcdf.Extensions/OLEProperties/VTVectorHeader.cs b/sources/OpenMcdf.Extensions/OLEProperties/VTVectorHeader.cs new file mode 100644 index 00000000..0cabf687 --- /dev/null +++ b/sources/OpenMcdf.Extensions/OLEProperties/VTVectorHeader.cs @@ -0,0 +1,10 @@ +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace OpenMcdf.Extensions.OLEProperties +{ + +} diff --git a/sources/OpenMcdf.Extensions/OpenMcdf.Extensions.csproj b/sources/OpenMcdf.Extensions/OpenMcdf.Extensions.csproj new file mode 100644 index 00000000..a2278fca --- /dev/null +++ b/sources/OpenMcdf.Extensions/OpenMcdf.Extensions.csproj @@ -0,0 +1,80 @@ + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {DB748C1D-D71C-442B-832D-2E33BE816CBB} + Library + Properties + OpenMcdf.Extensions + OpenMcdf.Extensions + v4.0 + 512 + + + true + full + false + ..\..\bin\Debug\OpenMcdf.Extensions\ + DEBUG;TRACE + prompt + 4 + ..\..\bin\Debug\OpenMcdf.Extensions\OpenMcdf.Extensions.XML + + + pdbonly + true + ..\..\bin\Release\OpenMcdf.Extensions\ + TRACE + prompt + 4 + ..\..\bin\Release\OpenMcdf.Extensions\OpenMcdf.Extensions.XML + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {56e15d4a-8a37-4c7c-bb44-fd59aff220c1} + OpenMcdf + + + + + \ No newline at end of file diff --git a/sources/OpenMcdf.Extensions/OpenMcdf.Extensions.csproj.user b/sources/OpenMcdf.Extensions/OpenMcdf.Extensions.csproj.user new file mode 100644 index 00000000..1efe784d --- /dev/null +++ b/sources/OpenMcdf.Extensions/OpenMcdf.Extensions.csproj.user @@ -0,0 +1,6 @@ + + + + ProjectFiles + + \ No newline at end of file diff --git a/sources/OpenMcdf.Extensions/Properties/AssemblyInfo.cs b/sources/OpenMcdf.Extensions/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..43f70fc1 --- /dev/null +++ b/sources/OpenMcdf.Extensions/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("OpenMcdf.Extensions")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("-")] +[assembly: AssemblyProduct("OpenMcdf.Extensions")] +[assembly: AssemblyCopyright("Copyright © Federico Blaseotto, 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("a514ea5e-b163-4a07-b742-fc4f91c6cbb0")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("2.0.*")] diff --git a/src/CFException.cs b/sources/OpenMcdf/CFException.cs similarity index 84% rename from src/CFException.cs rename to sources/OpenMcdf/CFException.cs index b806dac5..196d45d1 100644 --- a/src/CFException.cs +++ b/sources/OpenMcdf/CFException.cs @@ -1,24 +1,14 @@ -using System; -using System.Collections.Generic; -using System.Text; +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * The Original Code is OpenMCDF - Compound Document Format library. + * + * The Initial Developer of the Original Code is Federico Blaseotto.*/ + +using System; using System.Runtime.Serialization; -/* - The contents of this file are subject to the Mozilla Public License - Version 1.1 (the "License"); you may not use this file except in - compliance with the License. You may obtain a copy of the License at - http://www.mozilla.org/MPL/ - - Software distributed under the License is distributed on an "AS IS" - basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the - License for the specific language governing rights and limitations - under the License. - - The Original Code is OpenMCDF - Compound Document Format library. - - The Initial Developer of the Original Code is Federico Blaseotto. -*/ - namespace OpenMcdf { /// diff --git a/sources/OpenMcdf/CFItem.cs b/sources/OpenMcdf/CFItem.cs new file mode 100644 index 00000000..06ba3885 --- /dev/null +++ b/sources/OpenMcdf/CFItem.cs @@ -0,0 +1,271 @@ + +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * The Original Code is OpenMCDF - Compound Document Format library. + * + * The Initial Developer of the Original Code is Federico Blaseotto.*/ + +using System; + +namespace OpenMcdf +{ + /// + /// Abstract base class for Structured Storage entities. + /// + /// + /// + /// + /// const String STORAGE_NAME = "report.xls"; + /// CompoundFile cf = new CompoundFile(STORAGE_NAME); + /// + /// FileStream output = new FileStream("LogEntries.txt", FileMode.Create); + /// TextWriter tw = new StreamWriter(output); + /// + /// // CFItem represents both storage and stream items + /// VisitedEntryAction va = delegate(CFItem item) + /// { + /// tw.WriteLine(item.Name); + /// }; + /// + /// cf.RootStorage.VisitEntries(va, true); + /// + /// tw.Close(); + /// + /// + /// + public abstract class CFItem : IComparable + { + private CompoundFile compoundFile; + + protected CompoundFile CompoundFile + { + get { return compoundFile; } + } + + protected void CheckDisposed() + { + if (compoundFile.IsClosed) + throw new CFDisposedException("Owner Compound file has been closed and owned items have been invalidated"); + } + + protected CFItem() + { + } + + protected CFItem(CompoundFile compoundFile) + { + this.compoundFile = compoundFile; + } + + #region IDirectoryEntry Members + + private IDirectoryEntry dirEntry; + + internal IDirectoryEntry DirEntry + { + get { return dirEntry; } + set { dirEntry = value; } + } + + + + internal int CompareTo(CFItem other) + { + + return this.dirEntry.CompareTo(other.DirEntry); + } + + + #endregion + + #region IComparable Members + + public int CompareTo(object obj) + { + return this.dirEntry.CompareTo(((CFItem)obj).DirEntry); + } + + #endregion + + public static bool operator ==(CFItem leftItem, CFItem rightItem) + { + // If both are null, or both are same instance, return true. + if (System.Object.ReferenceEquals(leftItem, rightItem)) + { + return true; + } + + // If one is null, but not both, return false. + if (((object)leftItem == null) || ((object)rightItem == null)) + { + return false; + } + + // Return true if the fields match: + return leftItem.CompareTo(rightItem) == 0; + } + + public static bool operator !=(CFItem leftItem, CFItem rightItem) + { + return !(leftItem == rightItem); + } + + public override bool Equals(object obj) + { + return this.CompareTo(obj) == 0; + } + + public override int GetHashCode() + { + return this.dirEntry.GetEntryName().GetHashCode(); + } + + /// + /// Get entity name + /// + public String Name + { + get + { + String n = this.dirEntry.GetEntryName(); + if (n != null && n.Length > 0) + { + return n.TrimEnd('\0'); + } + else + return String.Empty; + } + } + + /// + /// Size in bytes of the item. It has a valid value + /// only if entity is a stream, otherwise it is setted to zero. + /// + public long Size + { + get + { + return this.dirEntry.Size; + } + } + + + /// + /// Return true if item is Storage + /// + /// + /// This check doesn't use reflection or runtime type information + /// and doesn't suffer related performance penalties. + /// + public bool IsStorage + { + get + { + return this.dirEntry.StgType == StgType.StgStorage; + } + } + + /// + /// Return true if item is a Stream + /// + /// + /// This check doesn't use reflection or runtime type information + /// and doesn't suffer related performance penalties. + /// + public bool IsStream + { + get + { + return this.dirEntry.StgType == StgType.StgStream; + } + } + + /// + /// Return true if item is the Root Storage + /// + /// + /// This check doesn't use reflection or runtime type information + /// and doesn't suffer related performance penalties. + /// + public bool IsRoot + { + get + { + return this.dirEntry.StgType == StgType.StgRoot; + } + } + + /// + /// Get/Set the Creation Date of the current item + /// + public DateTime CreationDate + { + get + { + return DateTime.FromFileTime(BitConverter.ToInt64(this.dirEntry.CreationDate, 0)); + } + + set + { + if (this.dirEntry.StgType != StgType.StgStream && this.dirEntry.StgType != StgType.StgRoot) + this.dirEntry.CreationDate = BitConverter.GetBytes((value.ToFileTime())); + else + throw new CFException("Creation Date can only be set on storage entries"); + } + } + + /// + /// Get/Set the Modify Date of the current item + /// + public DateTime ModifyDate + { + get + { + return DateTime.FromFileTime(BitConverter.ToInt64(this.dirEntry.ModifyDate, 0)); + } + + set + { + if (this.dirEntry.StgType != StgType.StgStream && this.dirEntry.StgType != StgType.StgRoot) + this.dirEntry.ModifyDate = BitConverter.GetBytes((value.ToFileTime())); + else + throw new CFException("Modify Date can only be set on storage entries"); + } + } + + /// + /// Get/Set Object class Guid for Root and Storage entries. + /// + public Guid CLSID + { + get + { + return this.dirEntry.StorageCLSID; + } + set + { + if (this.dirEntry.StgType != StgType.StgStream) + { + this.dirEntry.StorageCLSID = value; + } + else + throw new CFException("Object class GUID can only be set on Root and Storage entries"); + } + } + + int IComparable.CompareTo(CFItem other) + { + return this.dirEntry.CompareTo(other.DirEntry); + } + + public override string ToString() + { + if (this.dirEntry != null) + return "[" + this.dirEntry.LeftSibling + "," + this.dirEntry.SID + "," + this.dirEntry.RightSibling + "]" + " " + this.dirEntry.GetEntryName(); + else + return String.Empty; + } + } +} diff --git a/sources/OpenMcdf/CFStorage.cs b/sources/OpenMcdf/CFStorage.cs new file mode 100644 index 00000000..552b2276 --- /dev/null +++ b/sources/OpenMcdf/CFStorage.cs @@ -0,0 +1,547 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * The Original Code is OpenMCDF - Compound Document Format library. + * + * The Initial Developer of the Original Code is Federico Blaseotto.*/ + + +using System; +using System.Collections.Generic; +using RedBlackTree; + + +namespace OpenMcdf +{ + /// + /// Action to apply to visited items in the OLE structured storage + /// + /// Currently visited item + /// + /// + /// + /// //We assume that xls file should be a valid OLE compound file + /// const String STORAGE_NAME = "report.xls"; + /// CompoundFile cf = new CompoundFile(STORAGE_NAME); + /// + /// FileStream output = new FileStream("LogEntries.txt", FileMode.Create); + /// TextWriter tw = new StreamWriter(output); + /// + /// VisitedEntryAction va = delegate(CFItem item) + /// { + /// tw.WriteLine(item.Name); + /// }; + /// + /// cf.RootStorage.VisitEntries(va, true); + /// + /// tw.Close(); + /// + /// + /// + public delegate void VisitedEntryAction(CFItem item); + + /// + /// Storage entity that acts like a logic container for streams + /// or substorages in a compound file. + /// + public class CFStorage : CFItem + { + private RBTree children; + + internal RBTree Children + { + get + { + // Lazy loading of children tree. + if (children == null) + { + //if (this.CompoundFile.HasSourceStream) + //{ + children = LoadChildren(this.DirEntry.SID); + //} + //else + if (children == null) + { + children = this.CompoundFile.CreateNewTree(); + } + } + + return children; + } + } + + + /// + /// Create a CFStorage using an existing directory (previously loaded). + /// + /// The Storage Owner - CompoundFile + /// An existing Directory Entry + internal CFStorage(CompoundFile compFile, IDirectoryEntry dirEntry) + : base(compFile) + { + if (dirEntry == null || dirEntry.SID < 0) + throw new CFException("Attempting to create a CFStorage using an unitialized directory"); + + this.DirEntry = dirEntry; + } + + private RBTree LoadChildren(int SID) + { + RBTree childrenTree = this.CompoundFile.GetChildrenTree(SID); + + if (childrenTree.Root != null) + this.DirEntry.Child = (childrenTree.Root as IDirectoryEntry).SID; + else + this.DirEntry.Child = DirectoryEntry.NOSTREAM; + + return childrenTree; + } + + /// + /// Create a new child stream inside the current storage + /// + /// The new stream name + /// The new stream reference + /// Raised when adding an item with the same name of an existing one + /// Raised when adding a stream to a closed compound file + /// Raised when adding a stream with null or empty name + /// + /// + /// + /// String filename = "A_NEW_COMPOUND_FILE_YOU_CAN_WRITE_TO.cfs"; + /// + /// CompoundFile cf = new CompoundFile(); + /// + /// CFStorage st = cf.RootStorage.AddStorage("MyStorage"); + /// CFStream sm = st.AddStream("MyStream"); + /// byte[] b = Helpers.GetBuffer(220, 0x0A); + /// sm.SetData(b); + /// + /// cf.Save(filename); + /// + /// + /// + public CFStream AddStream(String streamName) + { + CheckDisposed(); + + if (String.IsNullOrEmpty(streamName)) + throw new CFException("Stream name cannot be null or empty"); + + + + IDirectoryEntry dirEntry = DirectoryEntry.TryNew(streamName, StgType.StgStream, this.CompoundFile.GetDirectories()); + + // Add new Stream directory entry + //cfo = new CFStream(this.CompoundFile, streamName); + + try + { + // Add object to Siblings tree + this.Children.Insert(dirEntry); + + //... and set the root of the tree as new child of the current item directory entry + this.DirEntry.Child = (Children.Root as IDirectoryEntry).SID; + } + catch (RBTreeException) + { + CompoundFile.ResetDirectoryEntry(dirEntry.SID); + + throw new CFDuplicatedItemException("An entry with name '" + streamName + "' is already present in storage '" + this.Name + "' "); + } + + return new CFStream(this.CompoundFile, dirEntry); + } + + + /// + /// Get a named stream contained in the current storage if existing. + /// + /// Name of the stream to look for + /// A stream reference if existing + /// Raised if trying to delete item from a closed compound file + /// Raised if item to delete is not found + /// + /// + /// String filename = "report.xls"; + /// + /// CompoundFile cf = new CompoundFile(filename); + /// CFStream foundStream = cf.RootStorage.GetStream("Workbook"); + /// + /// byte[] temp = foundStream.GetData(); + /// + /// Assert.IsNotNull(temp); + /// + /// cf.Close(); + /// + /// + public CFStream GetStream(String streamName) + { + CheckDisposed(); + + IDirectoryEntry tmp = DirectoryEntry.Mock(streamName, StgType.StgStream); + + //if (children == null) + //{ + // children = compoundFile.GetChildrenTree(SID); + //} + + IRBNode outDe = null; + + if (Children.TryLookup(tmp, out outDe) && (((IDirectoryEntry)outDe).StgType == StgType.StgStream)) + { + return new CFStream(this.CompoundFile, (IDirectoryEntry)outDe); + } + else + { + throw new CFItemNotFound("Cannot find item [" + streamName + "] within the current storage"); + } + } + + + /// + /// Get a named stream contained in the current storage if existing. + /// + /// Name of the stream to look for + /// A stream reference if found, else null + /// Raised if trying to delete item from a closed compound file + /// + /// + /// String filename = "report.xls"; + /// + /// CompoundFile cf = new CompoundFile(filename); + /// CFStream foundStream = cf.RootStorage.TryGetStream("Workbook"); + /// + /// byte[] temp = foundStream.GetData(); + /// + /// Assert.IsNotNull(temp); + /// + /// cf.Close(); + /// + /// + public CFStream TryGetStream(String streamName) + { + CheckDisposed(); + + IDirectoryEntry tmp = DirectoryEntry.Mock(streamName, StgType.StgStream); + + //if (children == null) + //{ + // children = compoundFile.GetChildrenTree(SID); + //} + + IRBNode outDe = null; + + if (Children.TryLookup(tmp, out outDe) && (((IDirectoryEntry)outDe).StgType == StgType.StgStream)) + { + return new CFStream(this.CompoundFile, (IDirectoryEntry)outDe); + } + else + { + return null; + } + } + + + /// + /// Get a named storage contained in the current one if existing. + /// + /// Name of the storage to look for + /// A storage reference if existing. + /// Raised if trying to delete item from a closed compound file + /// Raised if item to delete is not found + /// + /// + /// + /// String FILENAME = "MultipleStorage2.cfs"; + /// CompoundFile cf = new CompoundFile(FILENAME, UpdateMode.ReadOnly, false, false); + /// + /// CFStorage st = cf.RootStorage.GetStorage("MyStorage"); + /// + /// Assert.IsNotNull(st); + /// cf.Close(); + /// + /// + public CFStorage GetStorage(String storageName) + { + CheckDisposed(); + + IDirectoryEntry template = DirectoryEntry.Mock(storageName, StgType.StgInvalid); + IRBNode outDe = null; + + if (Children.TryLookup(template, out outDe) && ((IDirectoryEntry)outDe).StgType == StgType.StgStorage) + { + return new CFStorage(this.CompoundFile, outDe as IDirectoryEntry); + } + else + { + throw new CFItemNotFound("Cannot find item [" + storageName + "] within the current storage"); + } + } + + /// + /// Get a named storage contained in the current one if existing. + /// + /// Name of the storage to look for + /// A storage reference if found else null + /// Raised if trying to delete item from a closed compound file + /// + /// + /// + /// String FILENAME = "MultipleStorage2.cfs"; + /// CompoundFile cf = new CompoundFile(FILENAME, UpdateMode.ReadOnly, false, false); + /// + /// CFStorage st = cf.RootStorage.TryGetStorage("MyStorage"); + /// + /// Assert.IsNotNull(st); + /// cf.Close(); + /// + /// + public CFStorage TryGetStorage(String storageName) + { + CheckDisposed(); + + IDirectoryEntry template = DirectoryEntry.Mock(storageName, StgType.StgInvalid); + IRBNode outDe = null; + + if (Children.TryLookup(template, out outDe) && ((IDirectoryEntry)outDe).StgType == StgType.StgStorage) + { + return new CFStorage(this.CompoundFile, outDe as IDirectoryEntry); + } + else + { + return null; + } + } + + + /// + /// Create new child storage directory inside the current storage. + /// + /// The new storage name + /// Reference to the new storage + /// Raised when adding an item with the same name of an existing one + /// Raised when adding a storage to a closed compound file + /// Raised when adding a storage with null or empty name + /// + /// + /// + /// String filename = "A_NEW_COMPOUND_FILE_YOU_CAN_WRITE_TO.cfs"; + /// + /// CompoundFile cf = new CompoundFile(); + /// + /// CFStorage st = cf.RootStorage.AddStorage("MyStorage"); + /// CFStream sm = st.AddStream("MyStream"); + /// byte[] b = Helpers.GetBuffer(220, 0x0A); + /// sm.SetData(b); + /// + /// cf.Save(filename); + /// + /// + /// + public CFStorage AddStorage(String storageName) + { + CheckDisposed(); + + if (String.IsNullOrEmpty(storageName)) + throw new CFException("Stream name cannot be null or empty"); + + // Add new Storage directory entry + IDirectoryEntry cfo + = DirectoryEntry.New(storageName, StgType.StgStorage, this.CompoundFile.GetDirectories()); + + //this.CompoundFile.InsertNewDirectoryEntry(cfo); + + try + { + // Add object to Siblings tree + Children.Insert(cfo); + } + catch (RBTreeDuplicatedItemException) + { + CompoundFile.ResetDirectoryEntry(cfo.SID); + cfo = null; + throw new CFDuplicatedItemException("An entry with name '" + storageName + "' is already present in storage '" + this.Name + "' "); + } + + IDirectoryEntry childrenRoot = Children.Root as IDirectoryEntry; + this.DirEntry.Child = childrenRoot.SID; + + return new CFStorage(this.CompoundFile, cfo); + } + + /// + /// Visit all entities contained in the storage applying a user provided action + /// + /// Raised when visiting items of a closed compound file + /// User action to apply to visited entities + /// Visiting recursion level. True means substorages are visited recursively, false indicates that only the direct children of this storage are visited + /// + /// + /// const String STORAGE_NAME = "report.xls"; + /// CompoundFile cf = new CompoundFile(STORAGE_NAME); + /// + /// FileStream output = new FileStream("LogEntries.txt", FileMode.Create); + /// TextWriter tw = new StreamWriter(output); + /// + /// VisitedEntryAction va = delegate(CFItem item) + /// { + /// tw.WriteLine(item.Name); + /// }; + /// + /// cf.RootStorage.VisitEntries(va, true); + /// + /// tw.Close(); + /// + /// + public void VisitEntries(Action action, bool recursive) + { + CheckDisposed(); + + if (action != null) + { + List subStorages + = new List(); + + Action internalAction = + delegate(IRBNode targetNode) + { + IDirectoryEntry d = targetNode as IDirectoryEntry; + if (d.StgType == StgType.StgStream) + action(new CFStream(this.CompoundFile, d)); + else + action(new CFStorage(this.CompoundFile, d)); + + if (d.Child != DirectoryEntry.NOSTREAM) + subStorages.Add(targetNode); + + return; + }; + + this.Children.VisitTreeNodes(internalAction); + + if (recursive && subStorages.Count > 0) + foreach (IRBNode n in subStorages) + { + IDirectoryEntry d = n as IDirectoryEntry; + (new CFStorage(this.CompoundFile, d)).VisitEntries(action, recursive); + } + } + } + + /// + /// Remove an entry from the current storage and compound file. + /// + /// The name of the entry in the current storage to delete + /// + /// + /// cf = new CompoundFile("A_FILE_YOU_CAN_CHANGE.cfs", UpdateMode.Update, true, false); + /// cf.RootStorage.Delete("AStream"); // AStream item is assumed to exist. + /// cf.Commit(true); + /// cf.Close(); + /// + /// + /// Raised if trying to delete item from a closed compound file + /// Raised if item to delete is not found + /// Raised if trying to delete root storage + public void Delete(String entryName) + { + CheckDisposed(); + + // Find entry to delete + IDirectoryEntry tmp = DirectoryEntry.Mock(entryName, StgType.StgInvalid); + + IRBNode foundObj = null; + + this.Children.TryLookup(tmp, out foundObj); + + if (foundObj == null) + throw new CFItemNotFound("Entry named [" + entryName + "] was not found"); + + //if (foundObj.GetType() != typeCheck) + // throw new CFException("Entry named [" + entryName + "] has not the correct type"); + + if (((IDirectoryEntry)foundObj).StgType == StgType.StgRoot) + throw new CFException("Root storage cannot be removed"); + + + IRBNode altDel = null; + switch (((IDirectoryEntry)foundObj).StgType) + { + case StgType.StgStorage: + + CFStorage temp = new CFStorage(this.CompoundFile, ((IDirectoryEntry)foundObj)); + + // This is a storage. we have to remove children items first + foreach (IRBNode de in temp.Children) + { + IDirectoryEntry ded = de as IDirectoryEntry; + temp.Delete(ded.Name); + } + + + + + // ...then we need to rethread the root of siblings tree... + if (this.Children.Root != null) + this.DirEntry.Child = (this.Children.Root as IDirectoryEntry).SID; + else + this.DirEntry.Child = DirectoryEntry.NOSTREAM; + + // ...and finally Remove storage item from children tree... + this.Children.Delete(foundObj, out altDel); + + // ...and remove directory (storage) entry + + if (altDel != null) + { + foundObj = altDel; + } + + this.CompoundFile.InvalidateDirectoryEntry(((IDirectoryEntry)foundObj).SID); + + break; + + case StgType.StgStream: + + // Free directory associated data stream. + CompoundFile.FreeAssociatedData((foundObj as IDirectoryEntry).SID); + + // Remove item from children tree + this.Children.Delete(foundObj, out altDel); + + // Rethread the root of siblings tree... + if (this.Children.Root != null) + this.DirEntry.Child = (this.Children.Root as IDirectoryEntry).SID; + else + this.DirEntry.Child = DirectoryEntry.NOSTREAM; + + // Delete operation could possibly have cloned a directory, changing its SID. + // Invalidate the ACTUALLY deleted directory. + if (altDel != null) + { + foundObj = altDel; + } + + this.CompoundFile.InvalidateDirectoryEntry(((IDirectoryEntry)foundObj).SID); + + + + break; + } + + //// Refresh recursively all SIDs (invariant for tree sorting) + //VisitedEntryAction action = delegate(CFSItem target) + //{ + // if( ((IDirectoryEntry)target).SID>foundObj.SID ) + // { + // ((IDirectoryEntry)target).SID--; + // } + + + // ((IDirectoryEntry)target).LeftSibling--; + //}; + } + } +} diff --git a/sources/OpenMcdf/CFStream.cs b/sources/OpenMcdf/CFStream.cs new file mode 100644 index 00000000..80544d45 --- /dev/null +++ b/sources/OpenMcdf/CFStream.cs @@ -0,0 +1,245 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * The Original Code is OpenMCDF - Compound Document Format library. + * + * The Initial Developer of the Original Code is Federico Blaseotto.*/ + +using System; +using System.IO; + + + +namespace OpenMcdf +{ + + /// + /// OLE structured storage stream Object + /// It is contained inside a Storage object in a file-directory + /// relationship and indexed by its name. + /// + public class CFStream : CFItem + { + internal CFStream(CompoundFile compoundFile, IDirectoryEntry dirEntry) + : base(compoundFile) + { + if (dirEntry == null || dirEntry.SID < 0) + throw new CFException("Attempting to add a CFStream using an unitialized directory"); + + this.DirEntry = dirEntry; + } + + /// + /// Set the data associated with the stream object. + /// + /// + /// + /// byte[] b = new byte[]{0x0,0x1,0x2,0x3}; + /// CompoundFile cf = new CompoundFile(); + /// CFStream myStream = cf.RootStorage.AddStream("MyStream"); + /// myStream.SetData(b); + /// + /// + /// Data bytes to write to this stream + /// Existing associated data will be lost after method invocation + public void SetData(Byte[] data) + { + CheckDisposed(); + + this.CompoundFile.FreeData(this); + this.CompoundFile.WriteData(this, data); + } + + + /// + /// Write a data buffer to a specific position into current CFStream object + /// + /// Data buffer to Write + /// Position into the stream object to start writing from + /// Current stream will be extended to receive data buffer over + /// its current size + public void Write(byte[] data, long position) + { + this.Write(data, position, 0, data.Length); + } + + /// + /// Write count bytes of a data buffer to a specific position into + /// the current CFStream object starting from the specified position. + /// + /// Data buffer to copy bytes from + /// Position into the stream object to start writing from + /// The zero-based byte offset in buffer at which to + /// begin copying bytes to the current CFStream. + /// The number of bytes to be written to the current CFStream + /// Current stream will be extended to receive data buffer over + /// its current size. + internal void Write(byte[] data, long position, int offset, int count) + { + CheckDisposed(); + this.CompoundFile.WriteData(this, data, position, offset, count); + } + + /// + /// Append the provided data to stream data. + /// + /// + /// + /// byte[] b = new byte[]{0x0,0x1,0x2,0x3}; + /// byte[] b2 = new byte[]{0x4,0x5,0x6,0x7}; + /// CompoundFile cf = new CompoundFile(); + /// CFStream myStream = cf.RootStorage.AddStream("MyStream"); + /// myStream.SetData(b); // here we could also have invoked .AppendData + /// myStream.AppendData(b2); + /// cf.Save("MyLargeStreamsFile.cfs); + /// cf.Close(); + /// + /// + /// Data bytes to append to this stream + /// + /// This method allows user to create stream with more than 2GB of data, + /// appending data to the end of existing ones. + /// Large streams (>2GB) are only supported by CFS version 4. + /// Append data can also be invoked on streams with no data in order + /// to simplify its use inside loops. + /// + public void Append(Byte[] data) + { + CheckDisposed(); + if (this.Size > 0) + { + this.CompoundFile.AppendData(this, data); + } + else + { + this.CompoundFile.WriteData(this, data); + } + } + + /// + /// Get all the data associated with the stream object. + /// + /// + /// + /// CompoundFile cf2 = new CompoundFile("AFileName.cfs"); + /// CFStream st = cf2.RootStorage.GetStream("MyStream"); + /// byte[] buffer = st.ReadAll(); + /// + /// + /// Array of byte containing stream data + /// + /// Raised when the owner compound file has been closed. + /// + public Byte[] GetData() + { + CheckDisposed(); + + return this.CompoundFile.GetData(this); + } + + + /// + /// Read bytes associated with the stream object, starting from + /// a provided . Method returns the effective count of bytes + /// read. + /// + /// Array of bytes that will contain stream data + /// The zero-based byte position in the stream at which to begin reading + /// the data from. + /// The maximum number of bytes to be read from the current stream. + /// The count of bytes effectively read + /// Method may read a number of bytes lesser then the requested one. + /// + /// CompoundFile cf = null; + /// byte[] b = Helpers.GetBuffer(1024 * 2, 0xAA); //2MB buffer + /// CFStream item = cf.RootStorage.GetStream("AStream"); + /// + /// cf = new CompoundFile("$AFILENAME.cfs", CFSUpdateMode.ReadOnly, CFSConfiguration.Default); + /// item = cf.RootStorage.GetStream("AStream"); + /// + /// byte[] buffer = new byte[2048]; + /// item.Read(buffer, 0, 2048); + /// Assert.IsTrue(Helpers.CompareBuffer(b, buffer)); + /// + /// + /// + /// Raised when the owner compound file has been closed. + /// + public int Read(byte[] buffer, long position, int count) + { + CheckDisposed(); + return this.CompoundFile.ReadData(this, position, buffer, 0, count); + } + + + + /// + /// Read bytes associated with the stream object, starting from + /// a provided . Method returns the effective count of bytes + /// read. + /// + /// Array of bytes that will contain stream data + /// The zero-based byte position in the stream at which to begin reading + /// the data from. + /// The zero-based byte offset in buffer at which to begin storing the data read from the current stream. + /// The maximum number of bytes to be read from the current stream. + /// The count of bytes effectively read + /// Method may read a number of bytes lesser then the requested one. + /// + /// CompoundFile cf = null; + /// byte[] b = Helpers.GetBuffer(1024 * 2, 0xAA); //2MB buffer + /// CFStream item = cf.RootStorage.GetStream("AStream"); + /// + /// cf = new CompoundFile("$AFILENAME.cfs", CFSUpdateMode.ReadOnly, CFSConfiguration.Default); + /// item = cf.RootStorage.GetStream("AStream"); + /// + /// byte[] buffer = new byte[2048]; + /// item.Read(buffer, 0, 2048); + /// Assert.IsTrue(Helpers.CompareBuffer(b, buffer)); + /// + /// + /// + /// Raised when the owner compound file has been closed. + /// + internal int Read(byte[] buffer, long position, int offset, int count) + { + CheckDisposed(); + return this.CompoundFile.ReadData(this, position, buffer, offset, count); + } + + + /// + /// Copy data from an existing stream. + /// + /// A stream to read from + /// + /// Input stream will NOT be closed after method invocation. + /// Existing associated data will be deleted. + /// + public void CopyFrom(Stream input) + { + CheckDisposed(); + + byte[] buffer = new byte[input.Length]; + + if (input.CanSeek) + { + input.Seek(0, SeekOrigin.Begin); + } + + input.Read(buffer, 0, (int)input.Length); + this.SetData(buffer); + } + + + /// + /// Resize stream padding with zero if enlarging, trimming data if reducing size. + /// + /// New length to assign to this stream + public void Resize(long length) + { + this.CompoundFile.SetStreamLength(this, length); + } + } +} diff --git a/sources/OpenMcdf/CompoundFile.cs b/sources/OpenMcdf/CompoundFile.cs new file mode 100644 index 00000000..698ac9e8 --- /dev/null +++ b/sources/OpenMcdf/CompoundFile.cs @@ -0,0 +1,2779 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * The Original Code is OpenMCDF - Compound Document Format library. + * + * The Initial Developer of the Original Code is Federico Blaseotto.*/ + +#define FLAT_WRITE // No optimization on the number of write operations + +using System; +using System.Collections.Generic; +using System.IO; +using RedBlackTree; + +namespace OpenMcdf +{ + internal class CFItemComparer : IComparer + { + public int Compare(CFItem x, CFItem y) + { + // X CompareTo Y : X > Y --> 1 ; X < Y --> -1 + return (x.DirEntry.CompareTo(y.DirEntry)); + + //Compare X < Y --> -1 + } + } + + /// + /// Configuration parameters for the compund files. + /// They can be OR-combined to configure + /// Compound file behaviour. + /// All flags are NOT set by Default. + /// + [Flags] + public enum CFSConfiguration + { + /// + /// Sector Recycling turn off, + /// free sectors erasing off, + /// format validation exception raised + /// + Default = 1, + + /// + /// Sector recycling reduces data writing performances + /// but avoids space wasting in scenarios with frequently + /// data manipulation of the same streams. + /// + SectorRecycle = 2, + + /// + /// Free sectors are erased to avoid information leakage + /// + EraseFreeSectors = 4, + + /// + /// No exception is raised when a validation error occurs. + /// This can possibly lead to a security issue but gives + /// a chance to corrupted files to load. + /// + NoValidationException = 8, + + /// + /// If this flag is set true, + /// backing stream is kept open after CompoundFile disposal + /// + LeaveOpen = 16, + } + + /// + /// Binary File Format Version. Sector size is 512 byte for version 3, + /// 4096 for version 4 + /// + public enum CFSVersion : int + { + /// + /// Compound file version 3 - The default and most common version available. Sector size 512 bytes, 2GB max file size. + /// + Ver_3 = 3, + /// + /// Compound file version 4 - Sector size is 4096 bytes. Using this version could bring some compatibility problem with existing applications. + /// + Ver_4 = 4 + } + + /// + /// Update mode of the compound file. + /// Default is ReadOnly. + /// + public enum CFSUpdateMode + { + /// + /// ReadOnly update mode prevents overwriting + /// of the opened file. + /// Data changes are allowed but they have to be + /// persisted on a different file when required + /// using method + /// + ReadOnly, + + /// + /// Update mode allows subsequent data changing operations + /// to be persisted directly on the opened file or stream + /// using the Commit + /// method when required. Warning: this option may cause existing data loss if misused. + /// + Update + } + + /// + /// Standard Microsoft© Compound File implementation. + /// It is also known as OLE/COM structured storage + /// and contains a hierarchy of storage and stream objects providing + /// efficent storage of multiple kinds of documents in a single file. + /// Version 3 and 4 of specifications are supported. + /// + public class CompoundFile : IDisposable + { + private CFSConfiguration configuration + = CFSConfiguration.Default; + + /// + /// Get the configuration parameters of the CompoundFile object. + /// + public CFSConfiguration Configuration + { + get + { + return configuration; + } + } + + /// + /// Returns the size of standard sectors switching on CFS version (3 or 4) + /// + /// Standard sector size + internal int GetSectorSize() + { + return 2 << (header.SectorShift - 1); + } + + /// + /// Number of DIFAT entries in the header + /// + private const int HEADER_DIFAT_ENTRIES_COUNT = 109; + + /// + /// Number of FAT entries in a DIFAT Sector + /// + private readonly int DIFAT_SECTOR_FAT_ENTRIES_COUNT = 127; + + /// + /// Sectors ID entries in a FAT Sector + /// + private readonly int FAT_SECTOR_ENTRIES_COUNT = 128; + + /// + /// Sector ID Size (int) + /// + private const int SIZE_OF_SID = 4; + + /// + /// Flag for sector recycling. + /// + private bool sectorRecycle = false; + + /// + /// Flag for unallocated sector zeroing out. + /// + private bool eraseFreeSectors = false; + + /// + /// Initial capacity of the flushing queue used + /// to optimize commit writing operations + /// + private const int FLUSHING_QUEUE_SIZE = 6000; + + /// + /// Maximum size of the flushing buffer used + /// to optimize commit writing operations + /// + private const int FLUSHING_BUFFER_MAX_SIZE = 1024 * 1024 * 16; + + + private SectorCollection sectors = new SectorCollection(); + + + /// + /// CompoundFile header + /// + private Header header; + + /// + /// Compound underlying stream. Null when new CF has been created. + /// + internal Stream sourceStream = null; + + + /// + /// Create a blank, version 3 compound file. + /// Sector recycle is turned off to achieve the best reading/writing + /// performance in most common scenarios. + /// + /// + /// + /// + /// byte[] b = new byte[10000]; + /// for (int i = 0; i < 10000; i++) + /// { + /// b[i % 120] = (byte)i; + /// } + /// + /// CompoundFile cf = new CompoundFile(); + /// CFStream myStream = cf.RootStorage.AddStream("MyStream"); + /// + /// Assert.IsNotNull(myStream); + /// myStream.SetData(b); + /// cf.Save("MyCompoundFile.cfs"); + /// cf.Close(); + /// + /// + /// + public CompoundFile() + { + + this.header = new Header(); + this.sectorRecycle = false; + + this.sectors.OnVer3SizeLimitReached += new Ver3SizeLimitReached(OnSizeLimitReached); + + DIFAT_SECTOR_FAT_ENTRIES_COUNT = (GetSectorSize() / 4) - 1; + FAT_SECTOR_ENTRIES_COUNT = (GetSectorSize() / 4); + + //Root -- + IDirectoryEntry de = DirectoryEntry.New("Root Entry", StgType.StgRoot, directoryEntries); + rootStorage = new CFStorage(this, de); + rootStorage.DirEntry.StgType = StgType.StgRoot; + rootStorage.DirEntry.StgColor = StgColor.Black; + + //this.InsertNewDirectoryEntry(rootStorage.DirEntry); + } + + void OnSizeLimitReached() + { + + Sector rangeLockSector = new Sector(GetSectorSize(), sourceStream); + sectors.Add(rangeLockSector); + + rangeLockSector.Type = SectorType.RangeLockSector; + + _transactionLockAdded = true; + _lockSectorId = rangeLockSector.Id; + } + + + /// + /// Create a new, blank, compound file. + /// + /// Use a specific Compound File Version to set 512 or 4096 bytes sectors + /// Set configuration parameters for the new compound file + /// + /// + /// + /// byte[] b = new byte[10000]; + /// for (int i = 0; i < 10000; i++) + /// { + /// b[i % 120] = (byte)i; + /// } + /// + /// CompoundFile cf = new CompoundFile(CFSVersion.Ver_4, CFSConfiguration.Default); + /// CFStream myStream = cf.RootStorage.AddStream("MyStream"); + /// + /// Assert.IsNotNull(myStream); + /// myStream.SetData(b); + /// cf.Save("MyCompoundFile.cfs"); + /// cf.Close(); + /// + /// + /// + public CompoundFile(CFSVersion cfsVersion, CFSConfiguration configFlags) + { + this.configuration = configFlags; + + bool sectorRecycle = configFlags.HasFlag(CFSConfiguration.SectorRecycle); + bool eraseFreeSectors = configFlags.HasFlag(CFSConfiguration.EraseFreeSectors); + + this.header = new Header((ushort)cfsVersion); + this.sectorRecycle = sectorRecycle; + + + DIFAT_SECTOR_FAT_ENTRIES_COUNT = (GetSectorSize() / 4) - 1; + FAT_SECTOR_ENTRIES_COUNT = (GetSectorSize() / 4); + + //Root -- + IDirectoryEntry rootDir = DirectoryEntry.New("Root Entry", StgType.StgRoot, directoryEntries); + rootDir.StgColor = StgColor.Black; + //this.InsertNewDirectoryEntry(rootDir); + + rootStorage = new CFStorage(this, rootDir); + + + // + } + + + /// + /// Load an existing compound file. + /// + /// Compound file to read from + /// + /// + /// //A xls file should have a Workbook stream + /// String filename = "report.xls"; + /// + /// CompoundFile cf = new CompoundFile(filename); + /// CFStream foundStream = cf.RootStorage.GetStream("Workbook"); + /// + /// byte[] temp = foundStream.GetData(); + /// + /// Assert.IsNotNull(temp); + /// + /// cf.Close(); + /// + /// + /// + /// File will be open in read-only mode: it has to be saved + /// with a different filename. A wrapping implementation has to be provided + /// in order to remove/substitute an existing file. Version will be + /// automatically recognized from the file. Sector recycle is turned off + /// to achieve the best reading/writing performance in most common scenarios. + /// + public CompoundFile(String fileName) + { + this.sectorRecycle = false; + this.updateMode = CFSUpdateMode.ReadOnly; + this.eraseFreeSectors = false; + + LoadFile(fileName); + + DIFAT_SECTOR_FAT_ENTRIES_COUNT = (GetSectorSize() / 4) - 1; + FAT_SECTOR_ENTRIES_COUNT = (GetSectorSize() / 4); + } + + /// + /// Load an existing compound file. + /// + /// Compound file to read from + /// If true, recycle unused sectors + /// Select the update mode of the underlying data file + /// If true, overwrite with zeros unallocated sectors + /// + /// + /// String srcFilename = "data_YOU_CAN_CHANGE.xls"; + /// + /// CompoundFile cf = new CompoundFile(srcFilename, UpdateMode.Update, true, true); + /// + /// Random r = new Random(); + /// + /// byte[] buffer = GetBuffer(r.Next(3, 4095), 0x0A); + /// + /// cf.RootStorage.AddStream("MyStream").SetData(buffer); + /// + /// //This will persist data to the underlying media. + /// cf.Commit(); + /// cf.Close(); + /// + /// + /// + public CompoundFile(String fileName, CFSUpdateMode updateMode, CFSConfiguration configParameters) + { + this.validationExceptionEnabled = !configParameters.HasFlag(CFSConfiguration.NoValidationException); + this.sectorRecycle = configParameters.HasFlag(CFSConfiguration.SectorRecycle); + this.updateMode = updateMode; + this.eraseFreeSectors = configParameters.HasFlag(CFSConfiguration.EraseFreeSectors); + + LoadFile(fileName); + + DIFAT_SECTOR_FAT_ENTRIES_COUNT = (GetSectorSize() / 4) - 1; + FAT_SECTOR_ENTRIES_COUNT = (GetSectorSize() / 4); + } + + private bool validationExceptionEnabled = true; + + public bool ValidationExceptionEnabled + { + get { return validationExceptionEnabled; } + } + + + /// + /// Load an existing compound file. + /// + /// A stream containing a compound file to read + /// If true, recycle unused sectors + /// Select the update mode of the underlying data file + /// If true, overwrite with zeros unallocated sectors + /// + /// + /// + /// String filename = "reportREAD.xls"; + /// + /// FileStream fs = new FileStream(filename, FileMode.Open); + /// CompoundFile cf = new CompoundFile(fs, UpdateMode.ReadOnly, false, false); + /// CFStream foundStream = cf.RootStorage.GetStream("Workbook"); + /// + /// byte[] temp = foundStream.GetData(); + /// + /// Assert.IsNotNull(temp); + /// + /// cf.Close(); + /// + /// + /// + /// Raised when trying to open a non-seekable stream + /// Raised stream is null + public CompoundFile(Stream stream, CFSUpdateMode updateMode, CFSConfiguration configParameters) + { + this.validationExceptionEnabled = !configParameters.HasFlag(CFSConfiguration.NoValidationException); + this.sectorRecycle = configParameters.HasFlag(CFSConfiguration.SectorRecycle); + this.eraseFreeSectors = configParameters.HasFlag(CFSConfiguration.EraseFreeSectors); + this.closeStream = !configParameters.HasFlag(CFSConfiguration.LeaveOpen); + + this.updateMode = updateMode; + LoadStream(stream); + + DIFAT_SECTOR_FAT_ENTRIES_COUNT = (GetSectorSize() / 4) - 1; + FAT_SECTOR_ENTRIES_COUNT = (GetSectorSize() / 4); + } + + + /// + /// Load an existing compound file from a stream. + /// + /// Streamed compound file + /// + /// + /// + /// String filename = "reportREAD.xls"; + /// + /// FileStream fs = new FileStream(filename, FileMode.Open); + /// CompoundFile cf = new CompoundFile(fs); + /// CFStream foundStream = cf.RootStorage.GetStream("Workbook"); + /// + /// byte[] temp = foundStream.GetData(); + /// + /// Assert.IsNotNull(temp); + /// + /// cf.Close(); + /// + /// + /// + /// Raised when trying to open a non-seekable stream + /// Raised stream is null + public CompoundFile(Stream stream) + { + LoadStream(stream); + + DIFAT_SECTOR_FAT_ENTRIES_COUNT = (GetSectorSize() / 4) - 1; + FAT_SECTOR_ENTRIES_COUNT = (GetSectorSize() / 4); + } + + private CFSUpdateMode updateMode = CFSUpdateMode.ReadOnly; + private String fileName = String.Empty; + + + + /// + /// Commit data changes since the previously commit operation + /// to the underlying supporting stream or file on the disk. + /// + /// + /// This method can be used + /// only if the supporting stream has been opened in + /// Update mode. + /// + public void Commit() + { + Commit(false); + } + +#if !FLAT_WRITE + private byte[] buffer = new byte[FLUSHING_BUFFER_MAX_SIZE]; + private Queue flushingQueue = new Queue(FLUSHING_QUEUE_SIZE); +#endif + + + /// + /// Commit data changes since the previously commit operation + /// to the underlying supporting stream or file on the disk. + /// + /// If true, release loaded sectors to limit memory usage but reduces following read operations performance + /// + /// This method can be used only if + /// the supporting stream has been opened in + /// Update mode. + /// + public void Commit(bool releaseMemory) + { + if (_disposed) + throw new CFDisposedException("Compound File closed: cannot commit data"); + + if (updateMode != CFSUpdateMode.Update) + throw new CFInvalidOperation("Cannot commit data in Read-Only update mode"); + + //try + //{ +#if !FLAT_WRITE + + int sId = -1; + int sCount = 0; + int bufOffset = 0; +#endif + int sSize = GetSectorSize(); + + if (header.MajorVersion != (ushort)CFSVersion.Ver_3) + CheckForLockSector(); + + sourceStream.Seek(0, SeekOrigin.Begin); + sourceStream.Write((byte[])Array.CreateInstance(typeof(byte), GetSectorSize()), 0, sSize); + + CommitDirectory(); + + bool gap = true; + + + for (int i = 0; i < sectors.Count; i++) + { +#if FLAT_WRITE + + //Note: + //Here sectors should not be loaded dynamically because + //if they are null it means that no change has involved them; + + Sector s = (Sector)sectors[i]; + + if (s != null && s.DirtyFlag) + { + if (gap) + sourceStream.Seek((long)((long)(sSize) + (long)i * (long)sSize), SeekOrigin.Begin); + + sourceStream.Write(s.GetData(), 0, sSize); + sourceStream.Flush(); + s.DirtyFlag = false; + gap = false; + + } + else + { + gap = true; + } + + if (releaseMemory) + { + + s.ReleaseData(); + s = null; + sectors[i] = null; + } + + + +#else + + + Sector s = sectors[i] as Sector; + + + if (s != null && s.DirtyFlag && flushingQueue.Count < (int)(buffer.Length / sSize)) + { + //First of a block of contiguous sectors, mark id, start enqueuing + + if (gap) + { + sId = s.Id; + gap = false; + } + + flushingQueue.Enqueue(s); + + + } + else + { + //Found a gap, stop enqueuing, flush a write operation + + gap = true; + sCount = flushingQueue.Count; + + if (sCount == 0) continue; + + bufOffset = 0; + while (flushingQueue.Count > 0) + { + Sector r = flushingQueue.Dequeue(); + Buffer.BlockCopy(r.GetData(), 0, buffer, bufOffset, sSize); + r.DirtyFlag = false; + + if (releaseMemory) + { + r.ReleaseData(); + } + + bufOffset += sSize; + } + + sourceStream.Seek(((long)sSize + (long)sId * (long)sSize), SeekOrigin.Begin); + sourceStream.Write(buffer, 0, sCount * sSize); + + + + //Console.WriteLine("W - " + (int)(sCount * sSize )); + + } +#endif + } + +#if !FLAT_WRITE + sCount = flushingQueue.Count; + bufOffset = 0; + + while (flushingQueue.Count > 0) + { + Sector r = flushingQueue.Dequeue(); + Buffer.BlockCopy(r.GetData(), 0, buffer, bufOffset, sSize); + r.DirtyFlag = false; + + if (releaseMemory) + { + r.ReleaseData(); + r = null; + } + + bufOffset += sSize; + } + + if (sCount != 0) + { + sourceStream.Seek((long)sSize + (long)sId * (long)sSize, SeekOrigin.Begin); + sourceStream.Write(buffer, 0, sCount * sSize); + //Console.WriteLine("W - " + (int)(sCount * sSize)); + } + +#endif + + // Seek to beginning position and save header (first 512 or 4096 bytes) + sourceStream.Seek(0, SeekOrigin.Begin); + header.Write(sourceStream); + + sourceStream.SetLength((sectors.Count + 1) * sSize); + sourceStream.Flush(); + + if (releaseMemory) + GC.Collect(); + + //} + //catch (Exception ex) + //{ + // throw new CFException("Internal error while committing data", ex); + //} + } + + /// + /// Load compound file from an existing stream. + /// + /// Stream to load compound file from + private void Load(Stream stream) + { + try + { + this.header = new Header(); + this.directoryEntries = new List(); + + this.sourceStream = stream; + + header.Read(stream); + + int n_sector = Ceiling(((double)(stream.Length - GetSectorSize()) / (double)GetSectorSize())); + + if (stream.Length > 0x7FFFFF0) + this._transactionLockAllocated = true; + + + sectors = new SectorCollection(); + //sectors = new ArrayList(); + for (int i = 0; i < n_sector; i++) + { + sectors.Add(null); + } + + LoadDirectories(); + + this.rootStorage + = new CFStorage(this, directoryEntries[0]); + } + catch (Exception) + { + if (stream != null && closeStream) + stream.Close(); + + throw; + } + } + + private void LoadFile(String fileName) + { + + this.fileName = fileName; + + FileStream fs = null; + + try + { + if (this.updateMode == CFSUpdateMode.ReadOnly) + { + fs = new FileStream(fileName, FileMode.Open, FileAccess.Read, FileShare.ReadWrite); + + } + else + { + fs = new FileStream(fileName, FileMode.Open, FileAccess.ReadWrite, FileShare.Read); + } + + Load(fs); + + } + catch + { + if (fs != null) + fs.Close(); + + throw; + } + } + + private void LoadStream(Stream stream) + { + if (stream == null) + throw new CFException("Stream parameter cannot be null"); + + if (!stream.CanSeek) + throw new CFException("Cannot load a non-seekable Stream"); + + + stream.Seek(0, SeekOrigin.Begin); + + Load(stream); + } + + /// + /// Return true if this compound file has been + /// loaded from an existing file or stream + /// + public bool HasSourceStream + { + get { return sourceStream != null; } + } + + + private void PersistMiniStreamToStream(List miniSectorChain) + { + List miniStream + = GetSectorChain(RootEntry.StartSetc, SectorType.Normal); + + StreamView miniStreamView + = new StreamView( + miniStream, + GetSectorSize(), + this.rootStorage.Size, + null, + sourceStream); + + for (int i = 0; i < miniSectorChain.Count; i++) + { + Sector s = miniSectorChain[i]; + + if (s.Id == -1) + throw new CFException("Invalid minisector index"); + + // Ministream sectors already allocated + miniStreamView.Seek(Sector.MINISECTOR_SIZE * s.Id, SeekOrigin.Begin); + miniStreamView.Write(s.GetData(), 0, Sector.MINISECTOR_SIZE); + } + } + + /// + /// Allocate space, setup sectors id and refresh header + /// for the new or updated mini sector chain. + /// + /// The new MINI sector chain + private void AllocateMiniSectorChain(List sectorChain) + { + List miniFAT + = GetSectorChain(header.FirstMiniFATSectorID, SectorType.Normal); + + List miniStream + = GetSectorChain(RootEntry.StartSetc, SectorType.Normal); + + StreamView miniFATView + = new StreamView( + miniFAT, + GetSectorSize(), + header.MiniFATSectorsNumber * Sector.MINISECTOR_SIZE, + null, + this.sourceStream, + true + ); + + StreamView miniStreamView + = new StreamView( + miniStream, + GetSectorSize(), + this.rootStorage.Size, + null, + sourceStream); + + + // Set updated/new sectors within the ministream + // We are writing data in a NORMAL Sector chain. + for (int i = 0; i < sectorChain.Count; i++) + { + Sector s = sectorChain[i]; + + if (s.Id == -1) + { + // Allocate, position ministream at the end of already allocated + // ministream's sectors + + miniStreamView.Seek(this.rootStorage.Size + Sector.MINISECTOR_SIZE, SeekOrigin.Begin); + //miniStreamView.Write(s.GetData(), 0, Sector.MINISECTOR_SIZE); + s.Id = (int)(miniStreamView.Position - Sector.MINISECTOR_SIZE) / Sector.MINISECTOR_SIZE; + + this.rootStorage.DirEntry.Size = miniStreamView.Length; + } + } + + // Update miniFAT + for (int i = 0; i < sectorChain.Count - 1; i++) + { + Int32 currentId = sectorChain[i].Id; + Int32 nextId = sectorChain[i + 1].Id; + + miniFATView.Seek(currentId * 4, SeekOrigin.Begin); + miniFATView.Write(BitConverter.GetBytes(nextId), 0, 4); + } + + // Write End of Chain in MiniFAT + miniFATView.Seek(sectorChain[sectorChain.Count - 1].Id * SIZE_OF_SID, SeekOrigin.Begin); + miniFATView.Write(BitConverter.GetBytes(Sector.ENDOFCHAIN), 0, 4); + + // Update sector chains + AllocateSectorChain(miniStreamView.BaseSectorChain); + AllocateSectorChain(miniFATView.BaseSectorChain); + + //Update HEADER and root storage when ministream changes + if (miniFAT.Count > 0) + { + this.rootStorage.DirEntry.StartSetc = miniStream[0].Id; + header.MiniFATSectorsNumber = (uint)miniFAT.Count; + header.FirstMiniFATSectorID = miniFAT[0].Id; + } + } + + internal void FreeData(CFStream stream) + { + if (stream.Size == 0) + return; + + List sectorChain = null; + + if (stream.Size < header.MinSizeStandardStream) + { + sectorChain = GetSectorChain(stream.DirEntry.StartSetc, SectorType.Mini); + FreeMiniChain(sectorChain, this.eraseFreeSectors); + } + else + { + sectorChain = GetSectorChain(stream.DirEntry.StartSetc, SectorType.Normal); + FreeChain(sectorChain, this.eraseFreeSectors); + } + + stream.DirEntry.StartSetc = Sector.ENDOFCHAIN; + stream.DirEntry.Size = 0; + } + + private void FreeChain(List sectorChain, bool zeroSector) + { + FreeChain(sectorChain, 0, zeroSector); + } + + private void FreeChain(List sectorChain, int nth_sector_to_remove, bool zeroSector) + { + // Dummy zero buffer + byte[] ZEROED_SECTOR = new byte[GetSectorSize()]; + + List FAT + = GetSectorChain(-1, SectorType.FAT); + + StreamView FATView + = new StreamView(FAT, GetSectorSize(), FAT.Count * GetSectorSize(), null, sourceStream); + + // Zeroes out sector data (if required)------------- + if (zeroSector) + { + for (int i = nth_sector_to_remove; i < sectorChain.Count; i++) + { + Sector s = sectorChain[i]; + s.ZeroData(); + } + } + + // Update FAT marking unallocated sectors ---------- + for (int i = nth_sector_to_remove; i < sectorChain.Count; i++) + { + Int32 currentId = sectorChain[i].Id; + + FATView.Seek(currentId * 4, SeekOrigin.Begin); + FATView.Write(BitConverter.GetBytes(Sector.FREESECT), 0, 4); + } + + // Write new end of chain if partial free ---------- + if (nth_sector_to_remove > 0 && sectorChain.Count > 0) + { + FATView.Seek(sectorChain[nth_sector_to_remove - 1].Id * 4, SeekOrigin.Begin); + FATView.Write(BitConverter.GetBytes(Sector.ENDOFCHAIN), 0, 4); + } + } + + private void FreeMiniChain(List sectorChain, bool zeroSector) + { + FreeMiniChain(sectorChain, 0, zeroSector); + } + + private void FreeMiniChain(List sectorChain, int nth_sector_to_remove, bool zeroSector) + { + byte[] ZEROED_MINI_SECTOR = new byte[Sector.MINISECTOR_SIZE]; + + List miniFAT + = GetSectorChain(header.FirstMiniFATSectorID, SectorType.Normal); + + List miniStream + = GetSectorChain(RootEntry.StartSetc, SectorType.Normal); + + StreamView miniFATView + = new StreamView(miniFAT, GetSectorSize(), header.MiniFATSectorsNumber * Sector.MINISECTOR_SIZE, null, sourceStream); + + StreamView miniStreamView + = new StreamView(miniStream, GetSectorSize(), this.rootStorage.Size, null, sourceStream); + + // Set updated/new sectors within the ministream ---------- + if (zeroSector) + { + for (int i = nth_sector_to_remove; i < sectorChain.Count; i++) + { + Sector s = sectorChain[i]; + + if (s.Id != -1) + { + // Overwrite + miniStreamView.Seek(Sector.MINISECTOR_SIZE * s.Id, SeekOrigin.Begin); + miniStreamView.Write(ZEROED_MINI_SECTOR, 0, Sector.MINISECTOR_SIZE); + } + } + } + + // Update miniFAT --------------------------------------- + for (int i = nth_sector_to_remove; i < sectorChain.Count; i++) + { + Int32 currentId = sectorChain[i].Id; + + miniFATView.Seek(currentId * 4, SeekOrigin.Begin); + miniFATView.Write(BitConverter.GetBytes(Sector.FREESECT), 0, 4); + } + + // Write End of Chain in MiniFAT --------------------------------------- + //miniFATView.Seek(sectorChain[(sectorChain.Count - 1) - nth_sector_to_remove].Id * SIZE_OF_SID, SeekOrigin.Begin); + //miniFATView.Write(BitConverter.GetBytes(Sector.ENDOFCHAIN), 0, 4); + + // Write End of Chain in MiniFAT --------------------------------------- + if (nth_sector_to_remove > 0 && sectorChain.Count > 0) + { + miniFATView.Seek(sectorChain[nth_sector_to_remove - 1].Id * 4, SeekOrigin.Begin); + miniFATView.Write(BitConverter.GetBytes(Sector.ENDOFCHAIN), 0, 4); + } + + // Update sector chains --------------------------------------- + AllocateSectorChain(miniStreamView.BaseSectorChain); + AllocateSectorChain(miniFATView.BaseSectorChain); + + //Update HEADER and root storage when ministream changes + if (miniFAT.Count > 0) + { + this.rootStorage.DirEntry.StartSetc = miniStream[0].Id; + header.MiniFATSectorsNumber = (uint)miniFAT.Count; + header.FirstMiniFATSectorID = miniFAT[0].Id; + } + } + + /// + /// Allocate space, setup sectors id in the FAT and refresh header + /// for the new or updated sector chain (Normal or Mini sectors) + /// + /// The new or updated normal or mini sector chain + private void SetSectorChain(List sectorChain) + { + if (sectorChain == null || sectorChain.Count == 0) + return; + + SectorType _st = sectorChain[0].Type; + + if (_st == SectorType.Normal) + { + AllocateSectorChain(sectorChain); + } + else if (_st == SectorType.Mini) + { + AllocateMiniSectorChain(sectorChain); + } + } + + /// + /// Allocate space, setup sectors id and refresh header + /// for the new or updated sector chain. + /// + /// The new or updated generic sector chain + private void AllocateSectorChain(List sectorChain) + { + + foreach (Sector s in sectorChain) + { + if (s.Id == -1) + { + sectors.Add(s); + s.Id = sectors.Count - 1; + + } + } + + AllocateFATSectorChain(sectorChain); + } + + internal bool _transactionLockAdded = false; + internal int _lockSectorId = -1; + internal bool _transactionLockAllocated = false; + + /// + /// Check for transaction lock sector addition and mark it in the FAT. + /// + private void CheckForLockSector() + { + //If transaction lock has been added and not yet allocated in the FAT... + if (_transactionLockAdded && !_transactionLockAllocated) + { + StreamView fatStream = new StreamView(GetFatSectorChain(), GetSectorSize(), sourceStream); + + fatStream.Seek(_lockSectorId * 4, SeekOrigin.Begin); + fatStream.Write(BitConverter.GetBytes(Sector.ENDOFCHAIN), 0, 4); + + _transactionLockAllocated = true; + } + + } + /// + /// Allocate space, setup sectors id and refresh header + /// for the new or updated FAT sector chain. + /// + /// The new or updated generic sector chain + private void AllocateFATSectorChain(List sectorChain) + { + List fatSectors = GetSectorChain(-1, SectorType.FAT); + + StreamView fatStream = + new StreamView( + fatSectors, + GetSectorSize(), + header.FATSectorsNumber * GetSectorSize(), + null, + sourceStream, + true + ); + + // Write FAT chain values -- + + for (int i = 0; i < sectorChain.Count - 1; i++) + { + + Sector sN = sectorChain[i + 1]; + Sector sC = sectorChain[i]; + + fatStream.Seek(sC.Id * 4, SeekOrigin.Begin); + fatStream.Write(BitConverter.GetBytes(sN.Id), 0, 4); + } + + fatStream.Seek(sectorChain[sectorChain.Count - 1].Id * 4, SeekOrigin.Begin); + fatStream.Write(BitConverter.GetBytes(Sector.ENDOFCHAIN), 0, 4); + + // Merge chain to CFS + AllocateDIFATSectorChain(fatStream.BaseSectorChain); + } + + /// + /// Setup the DIFAT sector chain + /// + /// A FAT sector chain + private void AllocateDIFATSectorChain(List FATsectorChain) + { + // Get initial sector's count + header.FATSectorsNumber = FATsectorChain.Count; + + // Allocate Sectors + foreach (Sector s in FATsectorChain) + { + if (s.Id == -1) + { + sectors.Add(s); + s.Id = sectors.Count - 1; + s.Type = SectorType.FAT; + } + } + + // Sector count... + int nCurrentSectors = sectors.Count; + + // Temp DIFAT count + int nDIFATSectors = (int)header.DIFATSectorsNumber; + + if (FATsectorChain.Count > HEADER_DIFAT_ENTRIES_COUNT) + { + nDIFATSectors = Ceiling((double)(FATsectorChain.Count - HEADER_DIFAT_ENTRIES_COUNT) / DIFAT_SECTOR_FAT_ENTRIES_COUNT); + nDIFATSectors = LowSaturation(nDIFATSectors - (int)header.DIFATSectorsNumber); //required DIFAT + } + + // ...sum with new required DIFAT sectors count + nCurrentSectors += nDIFATSectors; + + // ReCheck FAT bias + while (header.FATSectorsNumber * FAT_SECTOR_ENTRIES_COUNT < nCurrentSectors) + { + Sector extraFATSector = new Sector(GetSectorSize(), sourceStream); + sectors.Add(extraFATSector); + + extraFATSector.Id = sectors.Count - 1; + extraFATSector.Type = SectorType.FAT; + + FATsectorChain.Add(extraFATSector); + + header.FATSectorsNumber++; + nCurrentSectors++; + + //... so, adding a FAT sector may induce DIFAT sectors to increase by one + // and consequently this may induce ANOTHER FAT sector (TO-THINK: May this condition occure ?) + if (nDIFATSectors * DIFAT_SECTOR_FAT_ENTRIES_COUNT < + (header.FATSectorsNumber > HEADER_DIFAT_ENTRIES_COUNT ? + header.FATSectorsNumber - HEADER_DIFAT_ENTRIES_COUNT : + 0)) + { + nDIFATSectors++; + nCurrentSectors++; + } + } + + + List difatSectors = + GetSectorChain(-1, SectorType.DIFAT); + + StreamView difatStream + = new StreamView(difatSectors, GetSectorSize(), sourceStream); + + // Write DIFAT Sectors (if required) + // Save room for the following chaining + for (int i = 0; i < FATsectorChain.Count; i++) + { + if (i < HEADER_DIFAT_ENTRIES_COUNT) + { + header.DIFAT[i] = FATsectorChain[i].Id; + } + else + { + // room for DIFAT chaining at the end of any DIFAT sector (4 bytes) + if (i != HEADER_DIFAT_ENTRIES_COUNT && (i - HEADER_DIFAT_ENTRIES_COUNT) % DIFAT_SECTOR_FAT_ENTRIES_COUNT == 0) + { + byte[] temp = new byte[sizeof(int)]; + difatStream.Write(temp, 0, sizeof(int)); + } + + difatStream.Write(BitConverter.GetBytes(FATsectorChain[i].Id), 0, sizeof(int)); + + } + } + + // Allocate room for DIFAT sectors + for (int i = 0; i < difatStream.BaseSectorChain.Count; i++) + { + if (difatStream.BaseSectorChain[i].Id == -1) + { + sectors.Add(difatStream.BaseSectorChain[i]); + difatStream.BaseSectorChain[i].Id = sectors.Count - 1; + difatStream.BaseSectorChain[i].Type = SectorType.DIFAT; + } + } + + header.DIFATSectorsNumber = (uint)nDIFATSectors; + + + // Chain first sector + if (difatStream.BaseSectorChain != null && difatStream.BaseSectorChain.Count > 0) + { + header.FirstDIFATSectorID = difatStream.BaseSectorChain[0].Id; + + // Update header information + header.DIFATSectorsNumber = (uint)difatStream.BaseSectorChain.Count; + + // Write chaining information at the end of DIFAT Sectors + for (int i = 0; i < difatStream.BaseSectorChain.Count - 1; i++) + { + Buffer.BlockCopy( + BitConverter.GetBytes(difatStream.BaseSectorChain[i + 1].Id), + 0, + difatStream.BaseSectorChain[i].GetData(), + GetSectorSize() - sizeof(int), + 4); + } + + Buffer.BlockCopy( + BitConverter.GetBytes(Sector.ENDOFCHAIN), + 0, + difatStream.BaseSectorChain[difatStream.BaseSectorChain.Count - 1].GetData(), + GetSectorSize() - sizeof(int), + sizeof(int) + ); + } + else + header.FirstDIFATSectorID = Sector.ENDOFCHAIN; + + // Mark DIFAT Sectors in FAT + StreamView fatSv = + new StreamView(FATsectorChain, GetSectorSize(), header.FATSectorsNumber * GetSectorSize(), null, sourceStream); + + for (int i = 0; i < header.DIFATSectorsNumber; i++) + { + fatSv.Seek(difatStream.BaseSectorChain[i].Id * 4, SeekOrigin.Begin); + fatSv.Write(BitConverter.GetBytes(Sector.DIFSECT), 0, 4); + } + + for (int i = 0; i < header.FATSectorsNumber; i++) + { + fatSv.Seek(fatSv.BaseSectorChain[i].Id * 4, SeekOrigin.Begin); + fatSv.Write(BitConverter.GetBytes(Sector.FATSECT), 0, 4); + } + + //fatSv.Seek(fatSv.BaseSectorChain[fatSv.BaseSectorChain.Count - 1].Id * 4, SeekOrigin.Begin); + //fatSv.Write(BitConverter.GetBytes(Sector.ENDOFCHAIN), 0, 4); + + header.FATSectorsNumber = fatSv.BaseSectorChain.Count; + } + + + /// + /// Get the DIFAT Sector chain + /// + /// A list of DIFAT sectors + private List GetDifatSectorChain() + { + int validationCount = 0; + + List result + = new List(); + + int nextSecID + = Sector.ENDOFCHAIN; + + if (header.DIFATSectorsNumber != 0) + { + validationCount = (int)header.DIFATSectorsNumber; + + Sector s = sectors[header.FirstDIFATSectorID] as Sector; + + if (s == null) //Lazy loading + { + s = new Sector(GetSectorSize(), sourceStream); + s.Type = SectorType.DIFAT; + s.Id = header.FirstDIFATSectorID; + sectors[header.FirstDIFATSectorID] = s; + } + + result.Add(s); + + while (true && validationCount >= 0) + { + nextSecID = BitConverter.ToInt32(s.GetData(), GetSectorSize() - 4); + + // Strictly speaking, the following condition is not correct from + // a specification point of view: + // only ENDOFCHAIN should break DIFAT chain but + // a lot of existing compound files use FREESECT as DIFAT chain termination + if (nextSecID == Sector.FREESECT || nextSecID == Sector.ENDOFCHAIN) break; + + validationCount--; + + if (validationCount < 0) + { + this.Close(); + throw new CFCorruptedFileException("DIFAT sectors count mismatched. Corrupted compound file"); + } + + s = sectors[nextSecID] as Sector; + + if (s == null) + { + s = new Sector(GetSectorSize(), sourceStream); + s.Id = nextSecID; + sectors[nextSecID] = s; + } + + result.Add(s); + } + } + + return result; + } + + /// + /// Get the FAT sector chain + /// + /// List of FAT sectors + private List GetFatSectorChain() + { + int N_HEADER_FAT_ENTRY = 109; //Number of FAT sectors id in the header + + List result + = new List(); + + int nextSecID + = Sector.ENDOFCHAIN; + + List difatSectors = GetDifatSectorChain(); + + int idx = 0; + + // Read FAT entries from the header Fat entry array (max 109 entries) + while (idx < header.FATSectorsNumber && idx < N_HEADER_FAT_ENTRY) + { + nextSecID = header.DIFAT[idx]; + Sector s = sectors[nextSecID] as Sector; + + if (s == null) + { + s = new Sector(GetSectorSize(), sourceStream); + s.Id = nextSecID; + s.Type = SectorType.FAT; + sectors[nextSecID] = s; + } + + result.Add(s); + + idx++; + } + + //Is there any DIFAT sector containing other FAT entries ? + if (difatSectors.Count > 0) + { + StreamView difatStream + = new StreamView + ( + difatSectors, + GetSectorSize(), + header.FATSectorsNumber > N_HEADER_FAT_ENTRY ? + (header.FATSectorsNumber - N_HEADER_FAT_ENTRY) * 4 : + 0, + null, + sourceStream + ); + + byte[] nextDIFATSectorBuffer = new byte[4]; + + difatStream.Read(nextDIFATSectorBuffer, 0, 4); + nextSecID = BitConverter.ToInt32(nextDIFATSectorBuffer, 0); + + int i = 0; + int nFat = N_HEADER_FAT_ENTRY; + + while (nFat < header.FATSectorsNumber) + { + if (difatStream.Position == ((GetSectorSize() - 4) + i * GetSectorSize())) + { + difatStream.Seek(4, SeekOrigin.Current); + i++; + continue; + } + + Sector s = sectors[nextSecID] as Sector; + + if (s == null) + { + s = new Sector(GetSectorSize(), sourceStream); + s.Type = SectorType.FAT; + s.Id = nextSecID; + sectors[nextSecID] = s;//UUU + } + + result.Add(s); + + difatStream.Read(nextDIFATSectorBuffer, 0, 4); + nextSecID = BitConverter.ToInt32(nextDIFATSectorBuffer, 0); + nFat++; + } + } + + return result; + + } + + /// + /// Get a standard sector chain + /// + /// First SecID of the required chain + /// A list of sectors + private List GetNormalSectorChain(int secID) + { + List result + = new List(); + + int nextSecID = secID; + + List fatSectors = GetFatSectorChain(); + + StreamView fatStream + = new StreamView(fatSectors, GetSectorSize(), fatSectors.Count * GetSectorSize(), null, sourceStream); + + while (true) + { + if (nextSecID == Sector.ENDOFCHAIN) break; + + if (nextSecID < 0) + throw new CFCorruptedFileException(String.Format("Next Sector ID reference is below zero. NextID : {0}", nextSecID)); + + if (nextSecID >= sectors.Count) + throw new CFCorruptedFileException(String.Format("Next Sector ID reference an out of range sector. NextID : {0} while sector count {1}", nextSecID, sectors.Count)); + + Sector s = sectors[nextSecID] as Sector; + if (s == null) + { + s = new Sector(GetSectorSize(), sourceStream); + s.Id = nextSecID; + s.Type = SectorType.Normal; + sectors[nextSecID] = s; + } + + result.Add(s); + + fatStream.Seek(nextSecID * 4, SeekOrigin.Begin); + int next = fatStream.ReadInt32(); + + if (next != nextSecID) + nextSecID = next; + else + throw new CFCorruptedFileException("Cyclic sector chain found. File is corrupted"); + } + + + return result; + } + + /// + /// Get a mini sector chain + /// + /// First SecID of the required chain + /// A list of mini sectors (64 bytes) + private List GetMiniSectorChain(int secID) + { + List result + = new List(); + + if (secID != Sector.ENDOFCHAIN) + { + int nextSecID = secID; + + List miniFAT = GetNormalSectorChain(header.FirstMiniFATSectorID); + List miniStream = GetNormalSectorChain(RootEntry.StartSetc); + + StreamView miniFATView + = new StreamView(miniFAT, GetSectorSize(), header.MiniFATSectorsNumber * Sector.MINISECTOR_SIZE, null, sourceStream); + + StreamView miniStreamView = + new StreamView(miniStream, GetSectorSize(), rootStorage.Size, null, sourceStream); + + BinaryReader miniFATReader = new BinaryReader(miniFATView); + + nextSecID = secID; + + while (true) + { + if (nextSecID == Sector.ENDOFCHAIN) + break; + + Sector ms = new Sector(Sector.MINISECTOR_SIZE, sourceStream); + byte[] temp = new byte[Sector.MINISECTOR_SIZE]; + + ms.Id = nextSecID; + ms.Type = SectorType.Mini; + + miniStreamView.Seek(nextSecID * Sector.MINISECTOR_SIZE, SeekOrigin.Begin); + miniStreamView.Read(ms.GetData(), 0, Sector.MINISECTOR_SIZE); + + result.Add(ms); + + miniFATView.Seek(nextSecID * 4, SeekOrigin.Begin); + nextSecID = miniFATReader.ReadInt32(); + } + } + return result; + } + + + /// + /// Get a sector chain from a compound file given the first sector ID + /// and the required sector type. + /// + /// First chain sector's id + /// Type of Sectors in the required chain (mini sectors, normal sectors or FAT) + /// A list of Sectors as the result of their concatenation + internal List GetSectorChain(int secID, SectorType chainType) + { + + switch (chainType) + { + case SectorType.DIFAT: + return GetDifatSectorChain(); + + case SectorType.FAT: + return GetFatSectorChain(); + + case SectorType.Normal: + return GetNormalSectorChain(secID); + + case SectorType.Mini: + return GetMiniSectorChain(secID); + + default: + throw new CFException("Unsupproted chain type"); + } + } + + private CFStorage rootStorage; + + /// + /// The entry point object that represents the + /// root of the structures tree to get or set storage or + /// stream data. + /// + /// + /// + /// + /// //Create a compound file + /// string FILENAME = "MyFileName.cfs"; + /// CompoundFile ncf = new CompoundFile(); + /// + /// CFStorage l1 = ncf.RootStorage.AddStorage("Storage Level 1"); + /// + /// l1.AddStream("l1ns1"); + /// l1.AddStream("l1ns2"); + /// l1.AddStream("l1ns3"); + /// CFStorage l2 = l1.AddStorage("Storage Level 2"); + /// l2.AddStream("l2ns1"); + /// l2.AddStream("l2ns2"); + /// + /// ncf.Save(FILENAME); + /// ncf.Close(); + /// + /// + public CFStorage RootStorage + { + get + { + return rootStorage as CFStorage; + } + } + + public CFSVersion Version + { + get + { + return (CFSVersion)this.header.MajorVersion; + } + } + + + + /// + /// Reset a directory entry setting it to StgInvalid in the Directory. + /// + /// Sid of the directory to invalidate + internal void ResetDirectoryEntry(int sid) + { + directoryEntries[sid].SetEntryName(String.Empty); + directoryEntries[sid].Left = null; + directoryEntries[sid].Right = null; + directoryEntries[sid].Parent = null; + directoryEntries[sid].StgType = StgType.StgInvalid; + } + + + + //internal class NodeFactory : IRBTreeDeserializer + //{ + + // public RBNode DeserizlizeFromValues() + // { + // RBNode node = new RBNode(value,(Color)value.DirEntry.StgColor, + // } + //} + + internal RBTree CreateNewTree() + { + RBTree bst = new RBTree(); + //bst.NodeInserted += OnNodeInsert; + //bst.NodeOperation += OnNodeOperation; + //bst.NodeDeleted += new Action>(OnNodeDeleted); + // bst.ValueAssignedAction += new Action, CFItem>(OnValueAssigned); + return bst; + } + + //void OnValueAssigned(RBNode node, CFItem from) + //{ + // if (from.DirEntry != null && from.DirEntry.LeftSibling != DirectoryEntry.NOSTREAM) + + // if (from.DirEntry != null && from.DirEntry.LeftSibling != DirectoryEntry.NOSTREAM) + // node.Value.DirEntry.LeftSibling = from.DirEntry.LeftSibling; + + // if (from.DirEntry != null && from.DirEntry.RightSibling != DirectoryEntry.NOSTREAM) + // node.Value.DirEntry.RightSibling = from.DirEntry.RightSibling; + //} + + + internal RBTree GetChildrenTree(int sid) + { + RBTree bst = new RBTree(); + + + // Load children from their original tree. + DoLoadChildren(bst, directoryEntries[sid]); + //bst = DoLoadChildrenTrusted(directoryEntries[sid]); + + //bst.Print(); + //bst.Print(); + //Trace.WriteLine("#### After rethreading"); + + return bst; + } + + private RBTree DoLoadChildrenTrusted(IDirectoryEntry de) + { + RBTree bst = null; + + if (de.Child != DirectoryEntry.NOSTREAM) + { + bst = new RBTree(directoryEntries[de.Child]); + } + + return bst; + } + + + private void DoLoadChildren(RBTree bst, IDirectoryEntry de) + { + + if (de.Child != DirectoryEntry.NOSTREAM) + { + if (directoryEntries[de.Child].StgType == StgType.StgInvalid) return; + + LoadSiblings(bst, directoryEntries[de.Child]); + NullifyChildNodes(directoryEntries[de.Child]); + bst.Insert(directoryEntries[de.Child]); + } + } + + private void NullifyChildNodes(IDirectoryEntry de) + { + de.Parent = null; + de.Left = null; + de.Right = null; + } + + private List levelSIDs = new List(); + + // Doubling methods allows iterative behavior while avoiding + // to insert duplicate items + private void LoadSiblings(RBTree bst, IDirectoryEntry de) + { + levelSIDs.Clear(); + + if (de.LeftSibling != DirectoryEntry.NOSTREAM) + { + + + // If there're more left siblings load them... + DoLoadSiblings(bst, directoryEntries[de.LeftSibling]); + //NullifyChildNodes(directoryEntries[de.LeftSibling]); + } + + if (de.RightSibling != DirectoryEntry.NOSTREAM) + { + levelSIDs.Add(de.RightSibling); + + // If there're more right siblings load them... + DoLoadSiblings(bst, directoryEntries[de.RightSibling]); + //NullifyChildNodes(directoryEntries[de.RightSibling]); + } + } + + private void DoLoadSiblings(RBTree bst, IDirectoryEntry de) + { + if (ValidateSibling(de.LeftSibling)) + { + levelSIDs.Add(de.LeftSibling); + + // If there're more left siblings load them... + DoLoadSiblings(bst, directoryEntries[de.LeftSibling]); + } + + if (ValidateSibling(de.RightSibling)) + { + levelSIDs.Add(de.RightSibling); + + // If there're more right siblings load them... + DoLoadSiblings(bst, directoryEntries[de.RightSibling]); + } + + NullifyChildNodes(de); + bst.Insert(de); + } + + private bool ValidateSibling(int sid) + { + if (sid != DirectoryEntry.NOSTREAM) + { + // if this siblings id does not overflow current list + if (sid >= directoryEntries.Count) + { + if (this.validationExceptionEnabled) + { + //this.Close(); + throw new CFCorruptedFileException("A Directory Entry references the non-existent sid number " + sid.ToString()); + } + else + return false; + } + + //if this sibling is valid... + if (directoryEntries[sid].StgType == StgType.StgInvalid) + { + if (this.validationExceptionEnabled) + { + //this.Close(); + throw new CFCorruptedFileException("A Directory Entry has a valid reference to an Invalid Storage Type directory [" + sid + "]"); + } + else + return false; + } + + if (!Enum.IsDefined(typeof(StgType), directoryEntries[sid].StgType)) + { + + if (this.validationExceptionEnabled) + { + //this.Close(); + throw new CFCorruptedFileException("A Directory Entry has an invalid Storage Type"); + } + else + return false; + } + + if (levelSIDs.Contains(sid)) + throw new CFCorruptedFileException("Cyclic reference of directory item"); + + return true; //No fault condition encountered for sid being validated + } + + return false; + } + + + /// + /// Load directory entries from compound file. Header and FAT MUST be already loaded. + /// + private void LoadDirectories() + { + List directoryChain + = GetSectorChain(header.FirstDirectorySectorID, SectorType.Normal); + + if (header.FirstDirectorySectorID == Sector.ENDOFCHAIN) + header.FirstDirectorySectorID = directoryChain[0].Id; + + StreamView dirReader + = new StreamView(directoryChain, GetSectorSize(), directoryChain.Count * GetSectorSize(), null, sourceStream); + + + while (dirReader.Position < directoryChain.Count * GetSectorSize()) + { + IDirectoryEntry de + = DirectoryEntry.New(String.Empty, StgType.StgInvalid, directoryEntries); + + //We are not inserting dirs. Do not use 'InsertNewDirectoryEntry' + de.Read(dirReader); + + } + } + + + + /// + /// Commit directory entries change on the Current Source stream + /// + private void CommitDirectory() + { + const int DIRECTORY_SIZE = 128; + + List directorySectors + = GetSectorChain(header.FirstDirectorySectorID, SectorType.Normal); + + StreamView sv = new StreamView(directorySectors, GetSectorSize(), 0, null, sourceStream); + + foreach (IDirectoryEntry di in directoryEntries) + { + di.Write(sv); + } + + int delta = directoryEntries.Count; + + while (delta % (GetSectorSize() / DIRECTORY_SIZE) != 0) + { + IDirectoryEntry dummy = DirectoryEntry.New(String.Empty, StgType.StgInvalid, directoryEntries); + dummy.Write(sv); + delta++; + } + + foreach (Sector s in directorySectors) + { + s.Type = SectorType.Directory; + } + + AllocateSectorChain(directorySectors); + + header.FirstDirectorySectorID = directorySectors[0].Id; + + //Version 4 supports directory sectors count + if (header.MajorVersion == 3) + { + header.DirectorySectorsNumber = 0; + } + else + { + header.DirectorySectorsNumber = directorySectors.Count; + } + } + + + /// + /// Saves the in-memory image of Compound File to a file. + /// + /// File name to write the compound file to + /// Raised if destination file is not seekable + + public void Save(String fileName) + { + if (_disposed) + throw new CFException("Compound File closed: cannot save data"); + + FileStream fs = null; + + try + { + fs = new FileStream(fileName, FileMode.Create); + Save(fs); + } + catch (Exception ex) + { + throw new CFException("Error saving file [" + fileName + "]", ex); + } + finally + { + if (fs != null) + fs.Flush(); + + if (fs != null) + fs.Close(); + + } + } + + /// + /// Saves the in-memory image of Compound File to a stream. + /// + /// + /// Destination Stream must be seekable. Uncommitted data will be persisted to the destination stream. + /// + /// The stream to save compound File to + /// Raised if destination stream is not seekable + /// Raised if Compound File Storage has been already disposed + /// + /// + /// MemoryStream ms = new MemoryStream(size); + /// + /// CompoundFile cf = new CompoundFile(); + /// CFStorage st = cf.RootStorage.AddStorage("MyStorage"); + /// CFStream sm = st.AddStream("MyStream"); + /// + /// byte[] b = new byte[]{0x00,0x01,0x02,0x03}; + /// + /// sm.SetData(b); + /// cf.Save(ms); + /// cf.Close(); + /// + /// + public void Save(Stream stream) + { + if (_disposed) + throw new CFDisposedException("Compound File closed: cannot save data"); + + if (!stream.CanSeek) + throw new CFException("Cannot save on a non-seekable stream"); + + CheckForLockSector(); + int sSize = GetSectorSize(); + + try + { + stream.Write((byte[])Array.CreateInstance(typeof(byte), sSize), 0, sSize); + + CommitDirectory(); + + for (int i = 0; i < sectors.Count; i++) + { + Sector s = sectors[i] as Sector; + + if (s == null) + { + // Load source (unmodified) sectors + // Here we have to ignore "Dirty flag" of + // sectors because we are NOT modifying the source + // in a differential way but ALL sectors need to be + // persisted on the destination stream + s = new Sector(sSize, sourceStream); + s.Id = i; + + //sectors[i] = s; + } + + + stream.Write(s.GetData(), 0, sSize); + + //s.ReleaseData(); + + } + + stream.Seek(0, SeekOrigin.Begin); + header.Write(stream); + } + catch (Exception ex) + { + throw new CFException("Internal error while saving compound file to stream ", ex); + } + } + + + /// + /// Scan FAT o miniFAT for free sectors to reuse. + /// + /// Type of sector to look for + /// A Queue of available sectors or minisectors already allocated + internal Queue FindFreeSectors(SectorType sType) + { + Queue freeList = new Queue(); + + if (sType == SectorType.Normal) + { + + List FatChain = GetSectorChain(-1, SectorType.FAT); + StreamView fatStream = new StreamView(FatChain, GetSectorSize(), header.FATSectorsNumber * GetSectorSize(), null, sourceStream); + + int idx = 0; + + while (idx < sectors.Count) + { + int id = fatStream.ReadInt32(); + + if (id == Sector.FREESECT) + { + if (sectors[idx] == null) + { + Sector s = new Sector(GetSectorSize(), sourceStream); + s.Id = idx; + sectors[idx] = s; + + } + + freeList.Enqueue(sectors[idx] as Sector); + } + + idx++; + } + } + else + { + List miniFAT + = GetSectorChain(header.FirstMiniFATSectorID, SectorType.Normal); + + StreamView miniFATView + = new StreamView(miniFAT, GetSectorSize(), header.MiniFATSectorsNumber * Sector.MINISECTOR_SIZE, null, sourceStream); + + List miniStream + = GetSectorChain(RootEntry.StartSetc, SectorType.Normal); + + StreamView miniStreamView + = new StreamView(miniStream, GetSectorSize(), rootStorage.Size, null, sourceStream); + + int idx = 0; + + int nMinisectors = (int)(miniStreamView.Length / Sector.MINISECTOR_SIZE); + + while (idx < nMinisectors) + { + //AssureLength(miniStreamView, (int)miniFATView.Length); + + int nextId = miniFATView.ReadInt32(); + + if (nextId == Sector.FREESECT) + { + Sector ms = new Sector(Sector.MINISECTOR_SIZE, sourceStream); + byte[] temp = new byte[Sector.MINISECTOR_SIZE]; + + ms.Id = idx; + ms.Type = SectorType.Mini; + + miniStreamView.Seek(ms.Id * Sector.MINISECTOR_SIZE, SeekOrigin.Begin); + miniStreamView.Read(ms.GetData(), 0, Sector.MINISECTOR_SIZE); + + freeList.Enqueue(ms); + } + + idx++; + } + } + + return freeList; + } + + /// + /// INTERNAL DEVELOPMENT. DO NOT CALL. + /// + /// + internal void AppendData(CFItem cfItem, Byte[] buffer) + { + WriteData(cfItem, cfItem.Size, buffer); + } + + /// + /// Resize stream length + /// + /// + /// + internal void SetStreamLength(CFItem cfItem, long length) + { + if (cfItem.Size == length) + return; + + SectorType newSectorType = SectorType.Normal; + int newSectorSize = GetSectorSize(); + + if (length < header.MinSizeStandardStream) + { + newSectorType = SectorType.Mini; + newSectorSize = Sector.MINISECTOR_SIZE; + } + + SectorType oldSectorType = SectorType.Normal; + int oldSectorSize = GetSectorSize(); + + if (cfItem.Size < header.MinSizeStandardStream) + { + oldSectorType = SectorType.Mini; + oldSectorSize = Sector.MINISECTOR_SIZE; + } + + long oldSize = cfItem.Size; + + + // Get Sector chain and delta size induced by client + List sectorChain = GetSectorChain(cfItem.DirEntry.StartSetc, oldSectorType); + long delta = length - cfItem.Size; + + // Check for transition ministream -> stream: + // Only in this case we need to free old sectors, + // otherwise they will be overwritten. + + bool transitionToMini = false; + bool transitionToNormal = false; + List oldChain = null; + + if (cfItem.DirEntry.StartSetc != Sector.ENDOFCHAIN) + { + if ( + (length < header.MinSizeStandardStream && cfItem.DirEntry.Size > header.MinSizeStandardStream) + || (length > header.MinSizeStandardStream && cfItem.DirEntry.Size < header.MinSizeStandardStream) + ) + { + if (cfItem.DirEntry.Size < header.MinSizeStandardStream) + { + transitionToNormal = true; + oldChain = sectorChain; + } + else + { + transitionToMini = true; + oldChain = sectorChain; + } + + // No transition caused by size change + + } + } + + + Queue freeList = null; + StreamView sv = null; + + if (!transitionToMini && !transitionToNormal) //############ NO TRANSITION + { + if (delta > 0) // Enlarging stream... + { + if (this.sectorRecycle) + freeList = FindFreeSectors(newSectorType); // Collect available free sectors + + sv = new StreamView(sectorChain, newSectorSize, length, freeList, sourceStream); + + //Set up destination chain + SetSectorChain(sectorChain); + } + else if (delta < 0) // Reducing size... + { + + int nSec = (int)Math.Floor(((double)(Math.Abs(delta)) / newSectorSize)); //number of sectors to mark as free + + if (newSectorSize == Sector.MINISECTOR_SIZE) + FreeMiniChain(sectorChain, nSec, this.eraseFreeSectors); + else + FreeChain(sectorChain, nSec, this.eraseFreeSectors); + } + + if (sectorChain.Count > 0) + { + cfItem.DirEntry.StartSetc = sectorChain[0].Id; + cfItem.DirEntry.Size = length; + } + else + { + cfItem.DirEntry.StartSetc = Sector.ENDOFCHAIN; + cfItem.DirEntry.Size = 0; + } + + } + else if (transitionToMini) //############## TRANSITION TO MINISTREAM + { + // Transition Normal chain -> Mini chain + + // Collect available MINI free sectors + + if (this.sectorRecycle) + freeList = FindFreeSectors(SectorType.Mini); + + sv = new StreamView(oldChain, oldSectorSize, oldSize, null, sourceStream); + + // Reset start sector and size of dir entry + cfItem.DirEntry.StartSetc = Sector.ENDOFCHAIN; + cfItem.DirEntry.Size = 0; + + List newChain = GetMiniSectorChain(Sector.ENDOFCHAIN); + StreamView destSv = new StreamView(newChain, Sector.MINISECTOR_SIZE, length, freeList, sourceStream); + + // Buffered trimmed copy from old (larger) to new (smaller) + int cnt = 4096 < length ? 4096 : (int)length; + + byte[] buf = new byte[4096]; + long toRead = length; + + //Copy old to new chain + while (toRead > cnt) + { + cnt = sv.Read(buf, 0, cnt); + toRead -= cnt; + destSv.Write(buf, 0, cnt); + } + + sv.Read(buf, 0, (int)toRead); + destSv.Write(buf, 0, (int)toRead); + + //Free old chain + FreeChain(oldChain, this.eraseFreeSectors); + + //Set up destination chain + AllocateMiniSectorChain(destSv.BaseSectorChain); + + // Persist to normal strea + PersistMiniStreamToStream(destSv.BaseSectorChain); + + //Update dir item + if (destSv.BaseSectorChain.Count > 0) + { + cfItem.DirEntry.StartSetc = destSv.BaseSectorChain[0].Id; + cfItem.DirEntry.Size = length; + } + else + { + cfItem.DirEntry.StartSetc = Sector.ENDOFCHAIN; + cfItem.DirEntry.Size = 0; + } + } + else if (transitionToNormal) //############## TRANSITION TO NORMAL STREAM + { + // Transition Mini chain -> Normal chain + + if (this.sectorRecycle) + freeList = FindFreeSectors(SectorType.Normal); // Collect available Normal free sectors + + sv = new StreamView(oldChain, oldSectorSize, oldSize, null, sourceStream); + + List newChain = GetNormalSectorChain(Sector.ENDOFCHAIN); + StreamView destSv = new StreamView(newChain, GetSectorSize(), length, freeList, sourceStream); + + int cnt = 256 < length ? 256 : (int)length; + + byte[] buf = new byte[256]; + long toRead = Math.Min(length, cfItem.Size); + + //Copy old to new chain + while (toRead > cnt) + { + cnt = sv.Read(buf, 0, cnt); + toRead -= cnt; + destSv.Write(buf, 0, cnt); + } + + sv.Read(buf, 0, (int)toRead); + destSv.Write(buf, 0, (int)toRead); + + //Free old mini chain + int oldChainCount = oldChain.Count; + FreeMiniChain(oldChain, this.eraseFreeSectors); + + //Set up normal destination chain + AllocateSectorChain(destSv.BaseSectorChain); + + //Update dir item + if (destSv.BaseSectorChain.Count > 0) + { + cfItem.DirEntry.StartSetc = destSv.BaseSectorChain[0].Id; + cfItem.DirEntry.Size = length; + } + else + { + cfItem.DirEntry.StartSetc = Sector.ENDOFCHAIN; + cfItem.DirEntry.Size = 0; + } + } + } + + internal void WriteData(CFItem cfItem, long position, byte[] buffer) + { + WriteData(cfItem, buffer, position, 0, buffer.Length); + } + + internal void WriteData(CFItem cfItem, byte[] buffer, long position, int offset, int count) + { + + if (buffer == null) + throw new CFInvalidOperation("Parameter [buffer] cannot be null"); + + if (cfItem.DirEntry == null) + throw new CFException("Internal error [cfItem.DirEntry] cannot be null"); + + if (buffer.Length == 0) return; + + // Get delta size induced by client + long delta = (position + count) - cfItem.Size < 0 ? 0 : (position + count) - cfItem.Size; + long newLength = cfItem.Size + delta; + + SetStreamLength(cfItem, newLength); + + // Calculate NEW sectors SIZE + SectorType _st = SectorType.Normal; + int _sectorSize = GetSectorSize(); + + if (cfItem.Size < header.MinSizeStandardStream) + { + _st = SectorType.Mini; + _sectorSize = Sector.MINISECTOR_SIZE; + } + + List sectorChain = GetSectorChain(cfItem.DirEntry.StartSetc, _st); + StreamView sv = new StreamView(sectorChain, _sectorSize, newLength, null, sourceStream); + + sv.Seek(position, SeekOrigin.Begin); + sv.Write(buffer, offset, count); + + if (cfItem.Size < header.MinSizeStandardStream) + { + PersistMiniStreamToStream(sv.BaseSectorChain); + //SetSectorChain(sv.BaseSectorChain); + } + } + + internal void WriteData(CFItem cfItem, Byte[] buffer) + { + WriteData(cfItem, 0, buffer); + } + + /// + /// Check file size limit ( 2GB for version 3 ) + /// + private void CheckFileLength() + { + throw new NotImplementedException(); + } + + + internal int ReadData(CFStream cFStream, long position, byte[] buffer, int count) + { + if (count > buffer.Length) + throw new ArgumentException("count parameter exceeds buffer size"); + + IDirectoryEntry de = cFStream.DirEntry; + + count = (int)Math.Min((long)(de.Size - position), (long)count); + + StreamView sView = null; + + + if (de.Size < header.MinSizeStandardStream) + { + sView + = new StreamView(GetSectorChain(de.StartSetc, SectorType.Mini), Sector.MINISECTOR_SIZE, de.Size, null, sourceStream); + } + else + { + + sView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Normal), GetSectorSize(), de.Size, null, sourceStream); + } + + + sView.Seek(position, SeekOrigin.Begin); + int result = sView.Read(buffer, 0, count); + + return result; + } + + internal int ReadData(CFStream cFStream, long position, byte[] buffer, int offset, int count) + { + + IDirectoryEntry de = cFStream.DirEntry; + + count = (int)Math.Min((long)(de.Size - offset), (long)count); + + StreamView sView = null; + + + if (de.Size < header.MinSizeStandardStream) + { + sView + = new StreamView(GetSectorChain(de.StartSetc, SectorType.Mini), Sector.MINISECTOR_SIZE, de.Size, null, sourceStream); + } + else + { + + sView = new StreamView(GetSectorChain(de.StartSetc, SectorType.Normal), GetSectorSize(), de.Size, null, sourceStream); + } + + + sView.Seek(position, SeekOrigin.Begin); + int result = sView.Read(buffer, offset, count); + + return result; + } + + + internal byte[] GetData(CFStream cFStream) + { + + if (_disposed) + throw new CFDisposedException("Compound File closed: cannot access data"); + + byte[] result = null; + + IDirectoryEntry de = cFStream.DirEntry; + + //IDirectoryEntry root = directoryEntries[0]; + + if (de.Size < header.MinSizeStandardStream) + { + + StreamView miniView + = new StreamView(GetSectorChain(de.StartSetc, SectorType.Mini), Sector.MINISECTOR_SIZE, de.Size, null, sourceStream); + + BinaryReader br = new BinaryReader(miniView); + + result = br.ReadBytes((int)de.Size); + br.Close(); + + } + else + { + StreamView sView + = new StreamView(GetSectorChain(de.StartSetc, SectorType.Normal), GetSectorSize(), de.Size, null, sourceStream); + + result = new byte[(int)de.Size]; + + sView.Read(result, 0, result.Length); + + } + + return result; + } + + private static int Ceiling(double d) + { + return (int)Math.Ceiling(d); + } + + private static int LowSaturation(int i) + { + return i > 0 ? i : 0; + } + + + internal void InvalidateDirectoryEntry(int sid) + { + if (sid >= directoryEntries.Count) + throw new CFException("Invalid SID of the directory entry to remove"); + + //Random r = new Random(); + directoryEntries[sid].SetEntryName("_DELETED_NAME_" + sid.ToString()); + directoryEntries[sid].StgType = StgType.StgInvalid; + } + + internal void FreeAssociatedData(int sid) + { + // Clear the associated stream (or ministream) if required + if (directoryEntries[sid].Size > 0) //thanks to Mark Bosold for this ! + { + if (directoryEntries[sid].Size < header.MinSizeStandardStream) + { + List miniChain + = GetSectorChain(directoryEntries[sid].StartSetc, SectorType.Mini); + FreeMiniChain(miniChain, this.eraseFreeSectors); + } + else + { + List chain + = GetSectorChain(directoryEntries[sid].StartSetc, SectorType.Normal); + FreeChain(chain, this.eraseFreeSectors); + } + } + } + + /// + /// Close the Compound File object CompoundFile and + /// free all associated resources (e.g. open file handle and allocated memory). + /// + /// When the Close method is called, + /// all the associated stream and storage objects are invalidated: + /// any operation invoked on them will produce a CFDisposedException. + /// + /// + /// + /// + /// const String FILENAME = "CompoundFile.cfs"; + /// CompoundFile cf = new CompoundFile(FILENAME); + /// + /// CFStorage st = cf.RootStorage.GetStorage("MyStorage"); + /// cf.Close(); + /// + /// try + /// { + /// byte[] temp = st.GetStream("MyStream").GetData(); + /// + /// // The following line will fail because back-end object has been closed + /// Assert.Fail("Stream without media"); + /// } + /// catch (Exception ex) + /// { + /// Assert.IsTrue(ex is CFDisposedException); + /// } + /// + /// + public void Close() + { + this.Close(true); + } + + private bool closeStream = true; + + [Obsolete("Use flag LeaveOpen in CompoundFile constructor")] + public void Close(bool closeStream) + { + this.closeStream = closeStream; + ((IDisposable)this).Dispose(); + } + + #region IDisposable Members + + private bool _disposed;//false + + void IDisposable.Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + #endregion + + private object lockObject = new Object(); + + /// + /// When called from user code, release all resources, otherwise, in the case runtime called it, + /// only unmanagd resources are released. + /// + /// If true, method has been called from User code, if false it's been called from .net runtime + protected virtual void Dispose(bool disposing) + { + try + { + if (!_disposed) + { + lock (lockObject) + { + if (disposing) + { + // Call from user code... + + if (sectors != null) + { + sectors.Clear(); + sectors = null; + } + + this.rootStorage = null; // Some problem releasing resources... + this.header = null; + this.directoryEntries.Clear(); + this.directoryEntries = null; + this.fileName = null; + //this.lockObject = null; +#if !FLAT_WRITE + this.buffer = null; +#endif + } + + if (this.sourceStream != null && closeStream && !configuration.HasFlag(CFSConfiguration.LeaveOpen)) + this.sourceStream.Close(); + } + } + } + finally + { + _disposed = true; + } + + } + + internal bool IsClosed + { + get + { + return _disposed; + } + } + + private List directoryEntries + = new List(); + + internal IList GetDirectories() + { + return directoryEntries; + } + + //internal List DirectoryEntries + //{ + // get { return directoryEntries; } + //} + + + internal IDirectoryEntry RootEntry + { + get + { + return directoryEntries[0]; + } + } + + private IList FindDirectoryEntries(String entryName) + { + List result = new List(); + + foreach (IDirectoryEntry d in directoryEntries) + { + if (d.GetEntryName() == entryName && d.StgType != StgType.StgInvalid) + result.Add(d); + } + + return result; + } + + + + /// + /// Get a list of all entries with a given name contained in the document. + /// + /// Name of entries to retrive + /// A list of name-matching entries + /// This function is aimed to speed up entity lookup in + /// flat-structure files (only one or little more known entries) + /// without the performance penalty related to entities hierarchy constraints. + /// There is no implied hierarchy in the returned list. + /// + public IList GetAllNamedEntries(String entryName) + { + IList r = FindDirectoryEntries(entryName); + List result = new List(); + + foreach (IDirectoryEntry id in r) + { + if (id.GetEntryName() == entryName && id.StgType != StgType.StgInvalid) + { + CFItem i = id.StgType == StgType.StgStorage ? (CFItem)new CFStorage(this, id) : (CFItem)new CFStream(this, id); + result.Add(i); + } + } + + return result; + } + + /// + /// Compress free space by removing unallocated sectors from compound file + /// effectively reducing stream or file size. + /// + /// + /// Current implementation supports compression only for ver. 3 compound files. + /// + /// + /// + /// + /// //This code has been extracted from unit test + /// + /// String FILENAME = "MultipleStorage3.cfs"; + /// + /// FileInfo srcFile = new FileInfo(FILENAME); + /// + /// File.Copy(FILENAME, "MultipleStorage_Deleted_Compress.cfs", true); + /// + /// CompoundFile cf = new CompoundFile("MultipleStorage_Deleted_Compress.cfs", UpdateMode.Update, true, true); + /// + /// CFStorage st = cf.RootStorage.GetStorage("MyStorage"); + /// st = st.GetStorage("AnotherStorage"); + /// + /// Assert.IsNotNull(st); + /// st.Delete("Another2Stream"); //17Kb + /// cf.Commit(); + /// cf.Close(); + /// + /// CompoundFile.ShrinkCompoundFile("MultipleStorage_Deleted_Compress.cfs"); + /// + /// FileInfo dstFile = new FileInfo("MultipleStorage_Deleted_Compress.cfs"); + /// + /// Assert.IsTrue(srcFile.Length > dstFile.Length); + /// + /// + /// + public static void ShrinkCompoundFile(Stream s) + { + CompoundFile cf = new CompoundFile(s); + + if (cf.header.MajorVersion != (ushort)CFSVersion.Ver_3) + throw new CFException("Current implementation of free space compression does not support version 4 of Compound File Format"); + + using (CompoundFile tempCF = new CompoundFile((CFSVersion)cf.header.MajorVersion, cf.Configuration)) + { + //Copy Root CLSID + tempCF.RootStorage.CLSID = new Guid(cf.RootStorage.CLSID.ToByteArray()); + + DoCompression(cf.RootStorage, tempCF.RootStorage); + + MemoryStream tmpMS = new MemoryStream((int)cf.sourceStream.Length); //This could be a problem for v4 + + tempCF.Save(tmpMS); + tempCF.Close(); + + // If we were based on a writable stream, we update + // the stream and do reload from the compressed one... + + s.Seek(0, SeekOrigin.Begin); + tmpMS.WriteTo(s); + + s.Seek(0, SeekOrigin.Begin); + s.SetLength(tmpMS.Length); + + tmpMS.Close(); + cf.Close(false); + } + } + + /// + /// Remove unallocated sectors from compound file in order to reduce its size. + /// + /// + /// Current implementation supports compression only for ver. 3 compound files. + /// + /// + /// + /// + /// //This code has been extracted from unit test + /// + /// String FILENAME = "MultipleStorage3.cfs"; + /// + /// FileInfo srcFile = new FileInfo(FILENAME); + /// + /// File.Copy(FILENAME, "MultipleStorage_Deleted_Compress.cfs", true); + /// + /// CompoundFile cf = new CompoundFile("MultipleStorage_Deleted_Compress.cfs", UpdateMode.Update, true, true); + /// + /// CFStorage st = cf.RootStorage.GetStorage("MyStorage"); + /// st = st.GetStorage("AnotherStorage"); + /// + /// Assert.IsNotNull(st); + /// st.Delete("Another2Stream"); //17Kb + /// cf.Commit(); + /// cf.Close(); + /// + /// CompoundFile.ShrinkCompoundFile("MultipleStorage_Deleted_Compress.cfs"); + /// + /// FileInfo dstFile = new FileInfo("MultipleStorage_Deleted_Compress.cfs"); + /// + /// Assert.IsTrue(srcFile.Length > dstFile.Length); + /// + /// + /// + public static void ShrinkCompoundFile(String fileName) + { + FileStream fs = new FileStream(fileName, FileMode.Open, FileAccess.ReadWrite); + ShrinkCompoundFile(fs); + fs.Close(); + } + + /// + /// Recursively clones valid structures, avoiding to copy free sectors. + /// + /// Current source storage to clone + /// Current cloned destination storage + private static void DoCompression(CFStorage currSrcStorage, CFStorage currDstStorage) + { + Action va = + delegate (CFItem item) + { + if (item.IsStream) + { + CFStream itemAsStream = item as CFStream; + CFStream st = ((CFStorage)currDstStorage).AddStream(itemAsStream.Name); + st.SetData(itemAsStream.GetData()); + } + else if (item.IsStorage) + { + CFStorage itemAsStorage = item as CFStorage; + CFStorage strg = ((CFStorage)currDstStorage).AddStorage(itemAsStorage.Name); + strg.CLSID = new Guid(itemAsStorage.CLSID.ToByteArray()); + DoCompression(itemAsStorage, strg); // recursion, one level deeper + } + }; + + currSrcStorage.VisitEntries(va, false); + } + } +} \ No newline at end of file diff --git a/src/Directory.cs b/sources/OpenMcdf/Directory.cs similarity index 100% rename from src/Directory.cs rename to sources/OpenMcdf/Directory.cs diff --git a/sources/OpenMcdf/DirectoryEntry.cs b/sources/OpenMcdf/DirectoryEntry.cs new file mode 100644 index 00000000..818c6b70 --- /dev/null +++ b/sources/OpenMcdf/DirectoryEntry.cs @@ -0,0 +1,577 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * The Original Code is OpenMCDF - Compound Document Format library. + * + * The Initial Developer of the Original Code is Federico Blaseotto.*/ + +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; + +namespace OpenMcdf +{ + public enum StgType : int + { + StgInvalid = 0, + StgStorage = 1, + StgStream = 2, + StgLockbytes = 3, + StgProperty = 4, + StgRoot = 5 + } + + public enum StgColor : int + { + Red = 0, + Black = 1 + } + + internal class DirectoryEntry : IDirectoryEntry + { + internal const int THIS_IS_GREATER = 1; + internal const int OTHER_IS_GREATER = -1; + private IList dirRepository; + + private int sid = -1; + public int SID + { + get { return sid; } + set { sid = value; } + } + + internal static Int32 NOSTREAM + = unchecked((int)0xFFFFFFFF); + + private DirectoryEntry(String name, StgType stgType, IList dirRepository) + { + this.dirRepository = dirRepository; + + this.stgType = stgType; + + switch (stgType) + { + case StgType.StgStream: + + this.storageCLSID = new Guid("00000000000000000000000000000000"); + this.creationDate = new byte[8] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + this.modifyDate = new byte[8] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + break; + + case StgType.StgStorage: + this.creationDate = BitConverter.GetBytes((DateTime.Now.ToFileTime())); + break; + + case StgType.StgRoot: + this.creationDate = new byte[8] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + this.modifyDate = new byte[8] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + break; + } + + this.SetEntryName(name); + + } + + private byte[] entryName = new byte[64]; + + public byte[] EntryName + { + get + { + return entryName; + } + //set + //{ + // entryName = value; + //} + } + + public String GetEntryName() + { + if (entryName != null && entryName.Length > 0) + { + return Encoding.Unicode.GetString(entryName).Remove((this.nameLength - 1) / 2); + } + else + return String.Empty; + } + + public void SetEntryName(String entryName) + { + if ( + entryName.Contains(@"\") || + entryName.Contains(@"/") || + entryName.Contains(@":") || + entryName.Contains(@"!") + + ) + throw new CFException("Invalid character in entry: the characters '\\', '/', ':','!' cannot be used in entry name"); + + if (entryName.Length > 31) + throw new CFException("Entry name MUST be smaller than 31 characters"); + + + + byte[] newName = null; + byte[] temp = Encoding.Unicode.GetBytes(entryName); + newName = new byte[64]; + Buffer.BlockCopy(temp, 0, newName, 0, temp.Length); + newName[temp.Length] = 0x00; + newName[temp.Length + 1] = 0x00; + + this.entryName = newName; + this.nameLength = (ushort)(temp.Length + 2); + + } + + private ushort nameLength; + public ushort NameLength + { + get + { + return nameLength; + } + set + { + throw new NotImplementedException(); + } + } + + private StgType stgType = StgType.StgInvalid; + public StgType StgType + { + get + { + return stgType; + } + set + { + stgType = value; + } + } + private StgColor stgColor = StgColor.Black; + + public StgColor StgColor + { + get + { + return stgColor; + } + set + { + stgColor = value; + } + } + + private Int32 leftSibling = NOSTREAM; + public Int32 LeftSibling + { + get { return leftSibling; } + set { leftSibling = value; } + } + + private Int32 rightSibling = NOSTREAM; + public Int32 RightSibling + { + get { return rightSibling; } + set { rightSibling = value; } + } + + private Int32 child = NOSTREAM; + public Int32 Child + { + get { return child; } + set { child = value; } + } + + private Guid storageCLSID + = Guid.NewGuid(); + + public Guid StorageCLSID + { + get + { + return storageCLSID; + } + set + { + this.storageCLSID = value; + } + } + + + private Int32 stateBits; + + public Int32 StateBits + { + get { return stateBits; } + set { stateBits = value; } + } + + private byte[] creationDate = new byte[8] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + public byte[] CreationDate + { + get + { + return creationDate; + } + set + { + creationDate = value; + } + } + + private byte[] modifyDate = new byte[8] { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + public byte[] ModifyDate + { + get + { + return modifyDate; + } + set + { + modifyDate = value; + } + } + + private Int32 startSetc = Sector.ENDOFCHAIN; + public Int32 StartSetc + { + get + { + return startSetc; + } + set + { + startSetc = value; + } + } + private long size; + public long Size + { + get + { + return size; + } + set + { + size = value; + } + } + + + public int CompareTo(object obj) + { + + IDirectoryEntry otherDir = obj as IDirectoryEntry; + + if (otherDir == null) + throw new CFException("Invalid casting: compared object does not implement IDirectorEntry interface"); + + if (this.NameLength > otherDir.NameLength) + { + return THIS_IS_GREATER; + } + else if (this.NameLength < otherDir.NameLength) + { + return OTHER_IS_GREATER; + } + else + { + String thisName = Encoding.Unicode.GetString(this.EntryName, 0, this.NameLength); + String otherName = Encoding.Unicode.GetString(otherDir.EntryName, 0, otherDir.NameLength); + + for (int z = 0; z < thisName.Length; z++) + { + char thisChar = char.ToUpperInvariant(thisName[z]); + char otherChar = char.ToUpperInvariant(otherName[z]); + + if (thisChar > otherChar) + return THIS_IS_GREATER; + else if (thisChar < otherChar) + return OTHER_IS_GREATER; + } + + return 0; + + } + + // return String.Compare(Encoding.Unicode.GetString(this.EntryName).ToUpper(), Encoding.Unicode.GetString(other.EntryName).ToUpper()); + } + + public override bool Equals(object obj) + { + return this.CompareTo(obj) == 0; + } + + /// + /// FNV hash, short for Fowler/Noll/Vo + /// + /// + /// (not warranted) unique hash for byte array + private static ulong fnv_hash(byte[] buffer) + { + + ulong h = 2166136261; + int i; + + for (i = 0; i < buffer.Length; i++) + h = (h * 16777619) ^ buffer[i]; + + return h; + } + + public override int GetHashCode() + { + return (int)fnv_hash(this.entryName); + } + + public void Write(Stream stream) + { + StreamRW rw = new StreamRW(stream); + + rw.Write(entryName); + rw.Write(nameLength); + rw.Write((byte)stgType); + rw.Write((byte)stgColor); + rw.Write(leftSibling); + rw.Write(rightSibling); + rw.Write(child); + rw.Write(storageCLSID.ToByteArray()); + rw.Write(stateBits); + rw.Write(creationDate); + rw.Write(modifyDate); + rw.Write(startSetc); + rw.Write(size); + + rw.Close(); + } + + //public Byte[] ToByteArray() + //{ + // MemoryStream ms + // = new MemoryStream(128); + + // BinaryWriter bw = new BinaryWriter(ms); + + // byte[] paddedName = new byte[64]; + // Array.Copy(entryName, paddedName, entryName.Length); + + // bw.Write(paddedName); + // bw.Write(nameLength); + // bw.Write((byte)stgType); + // bw.Write((byte)stgColor); + // bw.Write(leftSibling); + // bw.Write(rightSibling); + // bw.Write(child); + // bw.Write(storageCLSID.ToByteArray()); + // bw.Write(stateBits); + // bw.Write(creationDate); + // bw.Write(modifyDate); + // bw.Write(startSetc); + // bw.Write(size); + + // return ms.ToArray(); + //} + + public void Read(Stream stream) + { + StreamRW rw = new StreamRW(stream); + + entryName = rw.ReadBytes(64); + nameLength = rw.ReadUInt16(); + stgType = (StgType)rw.ReadByte(); + //rw.ReadByte();//Ignore color, only black tree + stgColor = (StgColor)rw.ReadByte(); + leftSibling = rw.ReadInt32(); + rightSibling = rw.ReadInt32(); + child = rw.ReadInt32(); + + // Thanks to bugaccount (BugTrack id 3519554) + if (stgType == StgType.StgInvalid) + { + leftSibling = NOSTREAM; + rightSibling = NOSTREAM; + child = NOSTREAM; + } + + storageCLSID = new Guid(rw.ReadBytes(16)); + stateBits = rw.ReadInt32(); + creationDate = rw.ReadBytes(8); + modifyDate = rw.ReadBytes(8); + startSetc = rw.ReadInt32(); + size = rw.ReadInt64(); + } + + public string Name + { + get { return GetEntryName(); } + } + + + public RedBlackTree.IRBNode Left + { + get + { + if (leftSibling == DirectoryEntry.NOSTREAM) + return null; + + return dirRepository[leftSibling]; + } + set + { + leftSibling = value != null ? ((IDirectoryEntry)value).SID : DirectoryEntry.NOSTREAM; + + if (leftSibling != DirectoryEntry.NOSTREAM) + dirRepository[leftSibling].Parent = this; + } + } + + public RedBlackTree.IRBNode Right + { + get + { + if (rightSibling == DirectoryEntry.NOSTREAM) + return null; + + return dirRepository[rightSibling]; + } + set + { + + rightSibling = value != null ? ((IDirectoryEntry)value).SID : DirectoryEntry.NOSTREAM; + + if (rightSibling != DirectoryEntry.NOSTREAM) + dirRepository[rightSibling].Parent = this; + + } + } + + public RedBlackTree.Color Color + { + get + { + return (RedBlackTree.Color)StgColor; + } + set + { + StgColor = (StgColor)value; + } + } + + private IDirectoryEntry parent = null; + + public RedBlackTree.IRBNode Parent + { + get + { + return parent; + } + set + { + parent = value as IDirectoryEntry; + } + } + + public RedBlackTree.IRBNode Grandparent() + { + return parent != null ? parent.Parent : null; + } + + public RedBlackTree.IRBNode Sibling() + { + if (this == Parent.Left) + return Parent.Right; + else + return Parent.Left; + } + + public RedBlackTree.IRBNode Uncle() + { + return parent != null ? Parent.Sibling() : null; + } + + internal static IDirectoryEntry New(String name, StgType stgType, IList dirRepository) + { + DirectoryEntry de = null; + if (dirRepository != null) + { + de = new DirectoryEntry(name, stgType, dirRepository); + // No invalid directory entry found + dirRepository.Add(de); + de.SID = dirRepository.Count - 1; + } + else + throw new ArgumentNullException("dirRepository", "Directory repository cannot be null in New() method"); + + return de; + } + + internal static IDirectoryEntry Mock(String name, StgType stgType) + { + DirectoryEntry de = new DirectoryEntry(name, stgType, null); + + return de; + } + + internal static IDirectoryEntry TryNew(String name, StgType stgType, IList dirRepository) + { + DirectoryEntry de = new DirectoryEntry(name, stgType, dirRepository); + + // If we are not adding an invalid dirEntry as + // in a normal loading from file (invalid dirs MAY pad a sector) + if (de != null) + { + // Find first available invalid slot (if any) to reuse it + for (int i = 0; i < dirRepository.Count; i++) + { + if (dirRepository[i].StgType == StgType.StgInvalid) + { + dirRepository[i] = de; + de.SID = i; + return de; + } + } + } + + // No invalid directory entry found + dirRepository.Add(de); + de.SID = dirRepository.Count - 1; + + return de; + } + + + + + public override string ToString() + { + return this.Name + " [" + this.sid + "]" + (this.stgType == StgType.StgStream ? "Stream" : "Storage"); + } + + + public void AssignValueTo(RedBlackTree.IRBNode other) + { + DirectoryEntry d = other as DirectoryEntry; + + d.SetEntryName(this.GetEntryName()); + + d.creationDate = new byte[this.creationDate.Length]; + this.creationDate.CopyTo(d.creationDate, 0); + + d.modifyDate = new byte[this.modifyDate.Length]; + this.modifyDate.CopyTo(d.modifyDate, 0); + + d.size = this.size; + d.startSetc = this.startSetc; + d.stateBits = this.stateBits; + d.stgType = this.stgType; + d.storageCLSID = new Guid(this.storageCLSID.ToByteArray()); + d.Child = this.Child; + } + } +} diff --git a/src/Header.cs b/sources/OpenMcdf/Header.cs similarity index 89% rename from src/Header.cs rename to sources/OpenMcdf/Header.cs index dc334677..62794dd1 100644 --- a/src/Header.cs +++ b/sources/OpenMcdf/Header.cs @@ -1,23 +1,13 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.IO; - -/* - The contents of this file are subject to the Mozilla Public License - Version 1.1 (the "License"); you may not use this file except in - compliance with the License. You may obtain a copy of the License at - http://www.mozilla.org/MPL/ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * The Original Code is OpenMCDF - Compound Document Format library. + * + * The Initial Developer of the Original Code is Federico Blaseotto.*/ - Software distributed under the License is distributed on an "AS IS" - basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the - License for the specific language governing rights and limitations - under the License. - The Original Code is OpenMCDF - Compound Document Format library. - - The Initial Developer of the Original Code is Federico Blaseotto. -*/ +using System.IO; namespace OpenMcdf { diff --git a/sources/OpenMcdf/IDirectoryEntry.cs b/sources/OpenMcdf/IDirectoryEntry.cs new file mode 100644 index 00000000..cbb13b4b --- /dev/null +++ b/sources/OpenMcdf/IDirectoryEntry.cs @@ -0,0 +1,38 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * The Original Code is OpenMCDF - Compound Document Format library. + * + * The Initial Developer of the Original Code is Federico Blaseotto.*/ + + +using RedBlackTree; +using System; + + +namespace OpenMcdf +{ + internal interface IDirectoryEntry : IComparable, IRBNode + { + int Child { get; set; } + byte[] CreationDate { get; set; } + byte[] EntryName { get; } + string GetEntryName(); + int LeftSibling { get; set; } + byte[] ModifyDate { get; set; } + string Name { get; } + ushort NameLength { get; set; } + void Read(System.IO.Stream stream); + int RightSibling { get; set; } + void SetEntryName(string entryName); + int SID { get; set; } + long Size { get; set; } + int StartSetc { get; set; } + int StateBits { get; set; } + StgColor StgColor { get; set; } + StgType StgType { get; set; } + Guid StorageCLSID { get; set; } + void Write(System.IO.Stream stream); + } +} diff --git a/sources/OpenMcdf/OpenMcdf.csproj b/sources/OpenMcdf/OpenMcdf.csproj new file mode 100644 index 00000000..933e16b7 --- /dev/null +++ b/sources/OpenMcdf/OpenMcdf.csproj @@ -0,0 +1,114 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {56E15D4A-8A37-4C7C-BB44-FD59AFF220C1} + Library + Properties + OpenMcdf + OpenMcdf + v4.0 + 512 + + + 3.5 + + http://localhost/OpenMcdf/ + true + Web + true + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + true + false + true + + + + true + full + false + ..\..\bin\Debug\OpenMcdf\ + DEBUG;TRACE + prompt + 4 + ..\..\bin\Debug\OpenMcdf\OpenMcdf.xml + 1591,1592,1573,1571,1570,1572 + AllRules.ruleset + + + pdbonly + true + ..\..\bin\Release\OpenMcdf\ + TRACE + prompt + 4 + ..\..\bin\Release\OpenMcdf\OpenMcdf.xml + 1591,1592,1573,1571,1570,1572 + AllRules.ruleset + + + false + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + + \ No newline at end of file diff --git a/sources/OpenMcdf/OpenMcdfClassDiagram.cd b/sources/OpenMcdf/OpenMcdfClassDiagram.cd new file mode 100644 index 00000000..5128ac48 --- /dev/null +++ b/sources/OpenMcdf/OpenMcdfClassDiagram.cd @@ -0,0 +1,140 @@ + + + + + + + + + + + + + + + + + + + + + + + + gAAEAAAQIBQIAgEUgEIBYgDg0GEAwUAAABAAAAGQgCA= + Header.cs + + + + + + + + + + + AAAAAAAAAAAAAEYAAAQEIBAAAAEABAAAAAACAAAAAAA= + CFStorage.cs + + + + + + AAAAAAACAAAAQAAgGkoAIABgAAAAAAQACAAAAAGIACA= + StreamView.cs + + + + + + QIQAwEAAkAAAgMQAgIDAgAQkEADAAKwgAAAsIAHEAAA= + DirectoryEntry.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + oTUGgNQVUmMEQBB0AARISKAAEAMEAmRARs0BQeeQFCg= + CompoundFile.cs + + + + + + + + + + + + + + + + + + + + + + + + + + QAAEAAAABAAAgAUggAAAgAQAAAAAAIAAAQACCAAAAiQ= + CFItem.cs + + + + + + + + + + AAAAAAABEAAAACAEAAAAAAAAAAAAAAAAAAAAAAAAAAA= + CFStream.cs + + + + + + AGIAAEAAAAAAABAQDAAABAQAAAQAAAQAGEAAggBSIAA= + SectorCollection.cs + + + + + + + + + + + QAAAQAAAEAAAgMAAAAAAgAQkAAAAACwAAAAAIAHEAAA= + IDirectoryEntry.cs + + + + \ No newline at end of file diff --git a/sources/OpenMcdf/Properties/AssemblyInfo.cs b/sources/OpenMcdf/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..62fed8f0 --- /dev/null +++ b/sources/OpenMcdf/Properties/AssemblyInfo.cs @@ -0,0 +1,38 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("OpenMcdf")] +[assembly: AssemblyDescription("MS Compound File Storage .NET Implementation")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Federico Blaseotto")] +[assembly: AssemblyProduct("OpenMcdf 2.1")] +[assembly: AssemblyCopyright("Copyright © 2010-2016, Federico Blaseotto")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("ffc13791-ddf0-4d14-bd64-575aba119190")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("2.1.0.*")] + +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("OpenMcdf.Test")] +[assembly: System.Runtime.CompilerServices.InternalsVisibleTo("OpenMcdf.Extensions")] \ No newline at end of file diff --git a/sources/OpenMcdf/RBTree/RBTree.cs b/sources/OpenMcdf/RBTree/RBTree.cs new file mode 100644 index 00000000..da4564c8 --- /dev/null +++ b/sources/OpenMcdf/RBTree/RBTree.cs @@ -0,0 +1,634 @@ +#define ASSERT + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +#if ASSERT +using System.Diagnostics; +#endif + +// ------------------------------------------------------------- +// This is a porting from java code, under MIT license of | +// the beautiful Red-Black Tree implementation you can find at | +// http://en.literateprograms.org/Red-black_tree_(Java)#chunk | +// Many Thanks to original Implementors. | +// ------------------------------------------------------------- + +namespace RedBlackTree +{ + public class RBTreeException : Exception + { + public RBTreeException(String msg) + : base(msg) + { + } + } + public class RBTreeDuplicatedItemException : RBTreeException + { + public RBTreeDuplicatedItemException(String msg) + : base(msg) + { + } + } + + public enum Color { RED = 0, BLACK = 1 } + + /// + /// Red Black Node interface + /// + public interface IRBNode : IComparable + { + + IRBNode Left + { + get; + set; + } + + IRBNode Right + { + get; + set; + } + + + Color Color + + { get; set; } + + + + IRBNode Parent { get; set; } + + + IRBNode Grandparent(); + + + IRBNode Sibling(); + // { + //#if ASSERT + // Debug.Assert(Parent != null); // Root node has no sibling + //#endif + // if (this == Parent.Left) + // return Parent.Right; + // else + // return Parent.Left; + // } + + IRBNode Uncle(); + // { + //#if ASSERT + // Debug.Assert(Parent != null); // Root node has no uncle + // Debug.Assert(Parent.Parent != null); // Children of root have no uncle + //#endif + // return Parent.Sibling(); + // } + // } + + void AssignValueTo(IRBNode other); + } + + public class RBTree + { + public IRBNode Root { get; set; } + + private static Color NodeColor(IRBNode n) + { + return n == null ? Color.BLACK : n.Color; + } + + public RBTree() + { + + } + + public RBTree(IRBNode root) + { + this.Root = root; + } + + + private IRBNode LookupNode(IRBNode template) + { + IRBNode n = Root; + + while (n != null) + { + int compResult = template.CompareTo(n); + + if (compResult == 0) + { + return n; + } + else if (compResult < 0) + { + n = n.Left; + } + else + { + //assert compResult > 0; + n = n.Right; + } + } + + return n; + } + + public bool TryLookup(IRBNode template, out IRBNode val) + { + IRBNode n = LookupNode(template); + + if (n == null) + { + val = null; + return false; + } + else + { + val = n; + return true; + } + } + + private void ReplaceNode(IRBNode oldn, IRBNode newn) + { + if (oldn.Parent == null) + { + Root = newn; + } + else + { + if (oldn == oldn.Parent.Left) + oldn.Parent.Left = newn; + else + oldn.Parent.Right = newn; + } + if (newn != null) + { + newn.Parent = oldn.Parent; + } + } + + private void RotateLeft(IRBNode n) + { + IRBNode r = n.Right; + ReplaceNode(n, r); + n.Right = r.Left; + if (r.Left != null) + { + r.Left.Parent = n; + } + r.Left = n; + n.Parent = r; + } + + private void RotateRight(IRBNode n) + { + IRBNode l = n.Left; + ReplaceNode(n, l); + n.Left = l.Right; + + if (l.Right != null) + { + l.Right.Parent = n; + } + + l.Right = n; + n.Parent = l; + } + + + + public void Insert(IRBNode newNode) + { + newNode.Color = Color.RED; + IRBNode insertedNode = newNode; + + if (Root == null) + { + Root = insertedNode; + } + else + { + IRBNode n = Root; + while (true) + { + int compResult = newNode.CompareTo(n); + if (compResult == 0) + { + throw new RBTreeDuplicatedItemException("RBNode " + newNode.ToString() + " already present in tree"); + //n.Value = value; + //return; + } + else if (compResult < 0) + { + if (n.Left == null) + { + n.Left = insertedNode; + + break; + } + else + { + n = n.Left; + } + } + else + { + //assert compResult > 0; + if (n.Right == null) + { + n.Right = insertedNode; + + break; + } + else + { + n = n.Right; + } + } + } + insertedNode.Parent = n; + } + + InsertCase1(insertedNode); + + if (NodeInserted != null) + { + NodeInserted(insertedNode); + } + + //Trace.WriteLine(" "); + //Print(); + } + + //------------------------------------ + private void InsertCase1(IRBNode n) + { + if (n.Parent == null) + n.Color = Color.BLACK; + else + InsertCase2(n); + } + + //----------------------------------- + private void InsertCase2(IRBNode n) + { + if (NodeColor(n.Parent) == Color.BLACK) + return; // Tree is still valid + else + InsertCase3(n); + } + + //---------------------------- + private void InsertCase3(IRBNode n) + { + if (NodeColor(n.Uncle()) == Color.RED) + { + n.Parent.Color = Color.BLACK; + n.Uncle().Color = Color.BLACK; + n.Grandparent().Color = Color.RED; + InsertCase1(n.Grandparent()); + } + else + { + InsertCase4(n); + } + } + + //---------------------------- + private void InsertCase4(IRBNode n) + { + if (n == n.Parent.Right && n.Parent == n.Grandparent().Left) + { + RotateLeft(n.Parent); + n = n.Left; + } + else if (n == n.Parent.Left && n.Parent == n.Grandparent().Right) + { + RotateRight(n.Parent); + n = n.Right; + } + + InsertCase5(n); + } + + //---------------------------- + private void InsertCase5(IRBNode n) + { + n.Parent.Color = Color.BLACK; + n.Grandparent().Color = Color.RED; + if (n == n.Parent.Left && n.Parent == n.Grandparent().Left) + { + RotateRight(n.Grandparent()); + } + else + { + //assert n == n.parent.right && n.parent == n.grandparent().right; + RotateLeft(n.Grandparent()); + } + } + + private static IRBNode MaximumNode(IRBNode n) + { + //assert n != null; + while (n.Right != null) + { + n = n.Right; + } + + return n; + } + + + public void Delete(IRBNode template, out IRBNode deletedAlt) + { + deletedAlt = null; + IRBNode n = LookupNode(template); + template = n; + if (n == null) + return; // Key not found, do nothing + if (n.Left != null && n.Right != null) + { + // Copy key/value from predecessor and then delete it instead + IRBNode pred = MaximumNode(n.Left); + pred.AssignValueTo(n); + n = pred; + deletedAlt = pred; + } + + //assert n.left == null || n.right == null; + IRBNode child = (n.Right == null) ? n.Left : n.Right; + if (NodeColor(n) == Color.BLACK) + { + n.Color = NodeColor(child); + DeleteCase1(n); + } + + ReplaceNode(n, child); + + if (NodeColor(Root) == Color.RED) + { + Root.Color = Color.BLACK; + } + + + return; + } + + private void DeleteCase1(IRBNode n) + { + if (n.Parent == null) + return; + else + DeleteCase2(n); + } + + + private void DeleteCase2(IRBNode n) + { + if (NodeColor(n.Sibling()) == Color.RED) + { + n.Parent.Color = Color.RED; + n.Sibling().Color = Color.BLACK; + if (n == n.Parent.Left) + RotateLeft(n.Parent); + else + RotateRight(n.Parent); + } + + DeleteCase3(n); + } + + private void DeleteCase3(IRBNode n) + { + if (NodeColor(n.Parent) == Color.BLACK && + NodeColor(n.Sibling()) == Color.BLACK && + NodeColor(n.Sibling().Left) == Color.BLACK && + NodeColor(n.Sibling().Right) == Color.BLACK) + { + n.Sibling().Color = Color.RED; + DeleteCase1(n.Parent); + } + else + DeleteCase4(n); + } + + private void DeleteCase4(IRBNode n) + { + if (NodeColor(n.Parent) == Color.RED && + NodeColor(n.Sibling()) == Color.BLACK && + NodeColor(n.Sibling().Left) == Color.BLACK && + NodeColor(n.Sibling().Right) == Color.BLACK) + { + n.Sibling().Color = Color.RED; + n.Parent.Color = Color.BLACK; + } + else + DeleteCase5(n); + } + + private void DeleteCase5(IRBNode n) + { + if (n == n.Parent.Left && + NodeColor(n.Sibling()) == Color.BLACK && + NodeColor(n.Sibling().Left) == Color.RED && + NodeColor(n.Sibling().Right) == Color.BLACK) + { + n.Sibling().Color = Color.RED; + n.Sibling().Left.Color = Color.BLACK; + RotateRight(n.Sibling()); + } + else if (n == n.Parent.Right && + NodeColor(n.Sibling()) == Color.BLACK && + NodeColor(n.Sibling().Right) == Color.RED && + NodeColor(n.Sibling().Left) == Color.BLACK) + { + n.Sibling().Color = Color.RED; + n.Sibling().Right.Color = Color.BLACK; + RotateLeft(n.Sibling()); + } + + DeleteCase6(n); + } + + private void DeleteCase6(IRBNode n) + { + n.Sibling().Color = NodeColor(n.Parent); + n.Parent.Color = Color.BLACK; + if (n == n.Parent.Left) + { + //assert nodeColor(n.sibling().right) == Color.RED; + n.Sibling().Right.Color = Color.BLACK; + RotateLeft(n.Parent); + } + else + { + //assert nodeColor(n.sibling().left) == Color.RED; + n.Sibling().Left.Color = Color.BLACK; + RotateRight(n.Parent); + } + } + + public void VisitTree(Action action) + { + //IN Order visit + IRBNode walker = Root; + + if (walker != null) + DoVisitTree(action, walker); + } + + private void DoVisitTree(Action action, IRBNode walker) + { + if (walker.Left != null) + { + DoVisitTree(action, walker.Left); + } + + if (action != null) + action(walker); + + if (walker.Right != null) + { + DoVisitTree(action, walker.Right); + } + + } + + internal void VisitTreeNodes(Action action) + { + //IN Order visit + IRBNode walker = Root; + + if (walker != null) + DoVisitTreeNodes(action, walker); + } + + private void DoVisitTreeNodes(Action action, IRBNode walker) + { + if (walker.Left != null) + { + DoVisitTreeNodes(action, walker.Left); + } + + if (action != null) + action(walker); + + if (walker.Right != null) + { + + DoVisitTreeNodes(action, walker.Right); + } + + } + + public class RBTreeEnumerator : IEnumerator + { + int position = -1; + private Queue heap = new Queue(); + + internal RBTreeEnumerator(RBTree tree) + { + tree.VisitTreeNodes(item => heap.Enqueue(item)); + } + + public IRBNode Current + { + get + { + return heap.ElementAt(position); + } + } + + public void Dispose() + { + } + + object System.Collections.IEnumerator.Current + { + get + { + return heap.ElementAt(position); + } + } + + public bool MoveNext() + { + position++; + return (position < heap.Count); + } + + public void Reset() + { + position = -1; + } + } + + public RBTreeEnumerator GetEnumerator() + { + return new RBTreeEnumerator(this); + } + + private static int INDENT_STEP = 15; + + public void Print() + { + PrintHelper(Root, 0); + } + + private static void PrintHelper(IRBNode n, int indent) + { + if (n == null) + { + Trace.WriteLine(""); + return; + } + + if (n.Left != null) + { + PrintHelper(n.Left, indent + INDENT_STEP); + } + + for (int i = 0; i < indent; i++) + Trace.Write(" "); + if (n.Color == Color.BLACK) + Trace.WriteLine(" " + n.ToString() + " "); + else + Trace.WriteLine("<" + n.ToString() + ">"); + + if (n.Right != null) + { + PrintHelper(n.Right, indent + INDENT_STEP); + } + } + + internal void FireNodeOperation(IRBNode node, NodeOperation operation) + { + if (NodeOperation != null) + NodeOperation(node, operation); + } + + //internal void FireValueAssigned(RBNode node, V value) + //{ + // if (ValueAssignedAction != null) + // ValueAssignedAction(node, value); + //} + + internal event Action NodeInserted; + //internal event Action> NodeDeleted; + internal event Action NodeOperation; + + + } + + internal enum NodeOperation + { + LeftAssigned, RightAssigned, ColorAssigned, ParentAssigned, + ValueAssigned + } + + +} diff --git a/src/Sector.cs b/sources/OpenMcdf/Sector.cs similarity index 80% rename from src/Sector.cs rename to sources/OpenMcdf/Sector.cs index 4807f7c6..7eca9cc2 100644 --- a/src/Sector.cs +++ b/sources/OpenMcdf/Sector.cs @@ -1,202 +1,202 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Collections; -using System.IO; -using System.Collections.Specialized; - -/* - The contents of this file are subject to the Mozilla Public License - Version 1.1 (the "License"); you may not use this file except in - compliance with the License. You may obtain a copy of the License at - http://www.mozilla.org/MPL/ - - Software distributed under the License is distributed on an "AS IS" - basis, WITHOUT WARRANTY OF ANY KIND, either express or implied. See the - License for the specific language governing rights and limitations - under the License. - - The Original Code is OpenMCDF - Compound Document Format library. - - The Initial Developer of the Original Code is Federico Blaseotto. -*/ - -namespace OpenMcdf -{ - internal enum SectorType - { - Normal, Mini, FAT, DIFAT, RangeLockSector, Directory - } - - internal class Sector : IDisposable - { - public static int MINISECTOR_SIZE = 64; - - public const int FREESECT = unchecked((int)0xFFFFFFFF); - public const int ENDOFCHAIN = unchecked((int)0xFFFFFFFE); - public const int FATSECT = unchecked((int)0xFFFFFFFD); - public const int DIFSECT = unchecked((int)0xFFFFFFFC); - - private bool dirtyFlag = false; - - public bool DirtyFlag - { - get { return dirtyFlag; } - set { dirtyFlag = value; } - } - - public bool IsStreamed - { - get { return (stream != null && size != MINISECTOR_SIZE) ? (this.id * size) + size < stream.Length : false; } - } - - private int size = 0; - private Stream stream; - - - public Sector(int size, Stream stream) - { - this.size = size; - this.stream = stream; - } - - public Sector(int size, byte[] data) - { - this.size = size; - this.data = data; - this.stream = null; - } - - public Sector(int size) - { - this.size = size; - this.data = null; - this.stream = null; - } - - private SectorType type; - - internal SectorType Type - { - get { return type; } - set { type = value; } - } - - private int id = -1; - - public int Id - { - get { return id; } - set - { - id = value; - } - } - - public int Size - { - get - { - return size; - } - } - - private byte[] data; - - public byte[] GetData() - { - if (this.data == null) - { - data = new byte[size]; - - if (IsStreamed) - { - stream.Seek((long)size + (long)this.id * (long)size, SeekOrigin.Begin); - stream.Read(data, 0, size); - } - } - - return data; - } - - //public void SetSectorData(byte[] b) - //{ - // this.data = b; - //} - - //public void FillData(byte b) - //{ - // if (data != null) - // { - // for (int i = 0; i < data.Length; i++) - // { - // data[i] = b; - // } - // } - //} - - public void ZeroData() - { - data = new byte[size]; - dirtyFlag = true; - } - - internal void ReleaseData() - { - this.data = null; - } - - private object lockObject = new Object(); - - /// - /// When called from user code, release all resources, otherwise, in the case runtime called it, - /// only unmanagd resources are released. - /// - /// If true, method has been called from User code, if false it's been called from .net runtime - protected virtual void Dispose(bool disposing) - { - try - { - if (!_disposed) - { - lock (lockObject) - { - if (disposing) - { - // Call from user code... - - - } - - this.data = null; - this.dirtyFlag = false; - this.id = Sector.ENDOFCHAIN; - this.size = 0; - - } - } - } - finally - { - _disposed = true; - } - - } - - #region IDisposable Members - - private bool _disposed;//false - - void IDisposable.Dispose() - { - Dispose(true); - GC.SuppressFinalize(this); - } - - #endregion - } - - - - -} +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * The Original Code is OpenMCDF - Compound Document Format library. + * + * The Initial Developer of the Original Code is Federico Blaseotto.*/ + + +using System; +using System.IO; + + +namespace OpenMcdf +{ + internal enum SectorType + { + Normal, Mini, FAT, DIFAT, RangeLockSector, Directory + } + + internal class Sector : IDisposable + { + public static int MINISECTOR_SIZE = 64; + + public const int FREESECT = unchecked((int)0xFFFFFFFF); + public const int ENDOFCHAIN = unchecked((int)0xFFFFFFFE); + public const int FATSECT = unchecked((int)0xFFFFFFFD); + public const int DIFSECT = unchecked((int)0xFFFFFFFC); + + private bool dirtyFlag = false; + + public bool DirtyFlag + { + get { return dirtyFlag; } + set { dirtyFlag = value; } + } + + public bool IsStreamed + { + get { return (stream != null && size != MINISECTOR_SIZE) ? (this.id * size) + size < stream.Length : false; } + } + + private int size = 0; + private Stream stream; + + + public Sector(int size, Stream stream) + { + this.size = size; + this.stream = stream; + } + + public Sector(int size, byte[] data) + { + this.size = size; + this.data = data; + this.stream = null; + } + + public Sector(int size) + { + this.size = size; + this.data = null; + this.stream = null; + } + + private SectorType type; + + internal SectorType Type + { + get { return type; } + set { type = value; } + } + + private int id = -1; + + public int Id + { + get { return id; } + set + { + id = value; + } + } + + public int Size + { + get + { + return size; + } + } + + private byte[] data; + + public byte[] GetData() + { + if (this.data == null) + { + data = new byte[size]; + + if (IsStreamed) + { + stream.Seek((long)size + (long)this.id * (long)size, SeekOrigin.Begin); + stream.Read(data, 0, size); + } + } + + return data; + } + + //public void SetSectorData(byte[] b) + //{ + // this.data = b; + //} + + //public void FillData(byte b) + //{ + // if (data != null) + // { + // for (int i = 0; i < data.Length; i++) + // { + // data[i] = b; + // } + // } + //} + + public void ZeroData() + { + data = new byte[size]; + dirtyFlag = true; + } + + public void InitFATData() + { + data = new byte[size]; + + for (int i = 0; i < size; i++) + data[i] = 0xFF; + + dirtyFlag = true; + } + + internal void ReleaseData() + { + this.data = null; + } + + private object lockObject = new Object(); + + /// + /// When called from user code, release all resources, otherwise, in the case runtime called it, + /// only unmanagd resources are released. + /// + /// If true, method has been called from User code, if false it's been called from .net runtime + protected virtual void Dispose(bool disposing) + { + try + { + if (!_disposed) + { + lock (lockObject) + { + if (disposing) + { + // Call from user code... + + + } + + this.data = null; + this.dirtyFlag = false; + this.id = Sector.ENDOFCHAIN; + this.size = 0; + + } + } + } + finally + { + _disposed = true; + } + + } + + #region IDisposable Members + + private bool _disposed;//false + + void IDisposable.Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + #endregion + } + + + + +} diff --git a/src/SectorCollection.cs b/sources/OpenMcdf/SectorCollection.cs similarity index 89% rename from src/SectorCollection.cs rename to sources/OpenMcdf/SectorCollection.cs index df4ad902..0212d591 100644 --- a/src/SectorCollection.cs +++ b/sources/OpenMcdf/SectorCollection.cs @@ -1,9 +1,16 @@ -using System; +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * The Original Code is OpenMCDF - Compound Document Format library. + * + * The Initial Developer of the Original Code is Federico Blaseotto.*/ + +using System; using System.Text; using System.Collections; using System.Collections.Generic; - namespace OpenMcdf { /// diff --git a/sources/OpenMcdf/StreamRW.cs b/sources/OpenMcdf/StreamRW.cs new file mode 100644 index 00000000..557d7b71 --- /dev/null +++ b/sources/OpenMcdf/StreamRW.cs @@ -0,0 +1,142 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * The Original Code is OpenMCDF - Compound Document Format library. + * + * The Initial Developer of the Original Code is Federico Blaseotto.*/ + +using System; +using System.Collections.Generic; +using System.Text; +using System.IO; + +namespace OpenMcdf +{ + internal class StreamRW + { + private byte[] buffer = new byte[8]; + private Stream stream; + + public StreamRW(Stream stream) + { + + this.stream = stream; + } + + public long Seek(long offset) + { + return stream.Seek(offset, SeekOrigin.Begin); + } + + public byte ReadByte() + { + this.stream.Read(buffer, 0, 1); + return buffer[0]; + } + + public ushort ReadUInt16() + { + this.stream.Read(buffer, 0, 2); + return (ushort)(buffer[0] | (buffer[1] << 8)); + } + + public int ReadInt32() + { + this.stream.Read(buffer, 0, 4); + return (int)(buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24)); + } + + public uint ReadUInt32() + { + this.stream.Read(buffer, 0, 4); + return (uint)(buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24)); + } + + public long ReadInt64() + { + this.stream.Read(buffer, 0, 8); + uint ls = (uint)(buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24)); + uint ms = (uint)((buffer[4]) | (buffer[5] << 8) | (buffer[6] << 16) | (buffer[7] << 24)); + return (long)(((ulong)ms << 32) | ls); + } + + public ulong ReadUInt64() + { + this.stream.Read(buffer, 0, 8); + return (ulong)(buffer[0] | (buffer[1] << 8) | (buffer[2] << 16) | (buffer[3] << 24) | (buffer[4] << 32) | (buffer[5] << 40) | (buffer[6] << 48) | (buffer[7] << 56)); + } + + public byte[] ReadBytes(int count) + { + byte[] result = new byte[count]; + this.stream.Read(result, 0, count); + return result; + } + + public byte[] ReadBytes(int count, out int r_count) + { + byte[] result = new byte[count]; + r_count = this.stream.Read(result, 0, count); + return result; + } + + public void Write(byte b) + { + buffer[0] = b; + this.stream.Write(buffer, 0, 1); + } + + public void Write(ushort value) + { + buffer[0] = (byte)value; + buffer[1] = (byte)(value >> 8); + + this.stream.Write(buffer, 0, 2); + } + + public void Write(int value) + { + buffer[0] = (byte)value; + buffer[1] = (byte)(value >> 8); + buffer[2] = (byte)(value >> 16); + buffer[3] = (byte)(value >> 24); + + this.stream.Write(buffer, 0, 4); + } + + public void Write(long value) + { + buffer[0] = (byte)value; + buffer[1] = (byte)(value >> 8); + buffer[2] = (byte)(value >> 16); + buffer[3] = (byte)(value >> 24); + buffer[4] = (byte)(value >> 32); + buffer[5] = (byte)(value >> 40); + buffer[6] = (byte)(value >> 48); + buffer[7] = (byte)(value >> 56); + + this.stream.Write(buffer, 0, 8); + } + + public void Write(uint value) + { + buffer[0] = (byte)value; + buffer[1] = (byte)(value >> 8); + buffer[2] = (byte)(value >> 16); + buffer[3] = (byte)(value >> 24); + + this.stream.Write(buffer, 0, 4); + } + + public void Write(byte[] value) + { + this.stream.Write(value, 0, value.Length); + } + + public void Close() + { + //Nothing to do ;-) + } + } +} diff --git a/sources/OpenMcdf/StreamView.cs b/sources/OpenMcdf/StreamView.cs new file mode 100644 index 00000000..c58d0f32 --- /dev/null +++ b/sources/OpenMcdf/StreamView.cs @@ -0,0 +1,378 @@ +/* This Source Code Form is subject to the terms of the Mozilla Public + * License, v. 2.0. If a copy of the MPL was not distributed with this + * file, You can obtain one at http://mozilla.org/MPL/2.0/. + * + * The Original Code is OpenMCDF - Compound Document Format library. + * + * The Initial Developer of the Original Code is Federico Blaseotto.*/ + + +using System; +using System.Collections.Generic; +using System.Text; +using System.Collections; +using System.IO; +using System.Collections.Specialized; +using System.Diagnostics; + +namespace OpenMcdf +{ + /// + /// Stream decorator for a Sector or miniSector chain + /// + internal class StreamView : Stream + { + private int sectorSize; + + private long position; + + private List sectorChain; + private Stream stream; + private bool isFatStream = false; + private List freeSectors = new List(); + public IEnumerable FreeSectors + { + get { return freeSectors; } + } + + public StreamView(List sectorChain, int sectorSize, Stream stream) + { + if (sectorChain == null) + throw new CFException("Sector Chain cannot be null"); + + if (sectorSize <= 0) + throw new CFException("Sector size must be greater than zero"); + + this.sectorChain = sectorChain; + this.sectorSize = sectorSize; + this.stream = stream; + } + + public StreamView(List sectorChain, int sectorSize, long length, Queue availableSectors, Stream stream, bool isFatStream = false) + : this(sectorChain, sectorSize, stream) + { + this.isFatStream = isFatStream; + adjustLength(length, availableSectors); + } + + + + + public List BaseSectorChain + { + get { return sectorChain; } + } + + public override bool CanRead + { + get { return true; } + } + + public override bool CanSeek + { + get { return true; } + } + + public override bool CanWrite + { + get { return true; } + } + + public override void Flush() + { + + } + + private long length; + + public override long Length + { + get + { + return length; + } + } + + public override long Position + { + get + { + return position; + } + + set + { + if (position > length - 1) + throw new ArgumentOutOfRangeException("value"); + + position = value; + } + } + + public override void Close() + { + base.Close(); + } + + private byte[] buf = new byte[4]; + + public int ReadInt32() + { + this.Read(buf, 0, 4); + return (((this.buf[0] | (this.buf[1] << 8)) | (this.buf[2] << 16)) | (this.buf[3] << 24)); + } + + public override int Read(byte[] buffer, int offset, int count) + { + int nRead = 0; + int nToRead = 0; + + if (sectorChain != null && sectorChain.Count > 0) + { + // First sector + int secIndex = (int)(position / (long)sectorSize); + + // Bytes to read count is the min between request count + // and sector border + + nToRead = Math.Min( + sectorChain[0].Size - ((int)position % sectorSize), + count); + + if (secIndex < sectorChain.Count) + { + Buffer.BlockCopy( + sectorChain[secIndex].GetData(), + (int)(position % sectorSize), + buffer, + offset, + nToRead + ); + } + + nRead += nToRead; + + secIndex++; + + // Central sectors + while (nRead < (count - sectorSize)) + { + nToRead = sectorSize; + + Buffer.BlockCopy( + sectorChain[secIndex].GetData(), + 0, + buffer, + offset + nRead, + nToRead + ); + + nRead += nToRead; + secIndex++; + } + + // Last sector + nToRead = count - nRead; + + if (nToRead != 0) + { + Buffer.BlockCopy( + sectorChain[secIndex].GetData(), + 0, + buffer, + offset + nRead, + nToRead + ); + + nRead += nToRead; + } + + position += nRead; + + return nRead; + + } + else + return 0; + + } + + public override long Seek(long offset, SeekOrigin origin) + { + switch (origin) + { + case SeekOrigin.Begin: + position = offset; + break; + + case SeekOrigin.Current: + position += offset; + break; + + case SeekOrigin.End: + position = Length - offset; + break; + } + + adjustLength(position); + + return position; + } + + private void adjustLength(long value) + { + adjustLength(value, null); + } + + private void adjustLength(long value, Queue availableSectors) + { + this.length = value; + + long delta = value - ((long)this.sectorChain.Count * (long)sectorSize); + + if (delta > 0) + { + // enlargment required + + int nSec = (int)Math.Ceiling(((double)delta / sectorSize)); + + while (nSec > 0) + { + Sector t = null; + + if (availableSectors == null || availableSectors.Count == 0) + { + t = new Sector(sectorSize, stream); + + if (sectorSize == Sector.MINISECTOR_SIZE) + t.Type = SectorType.Mini; + } + else + { + t = availableSectors.Dequeue(); + } + + if (isFatStream) + { + t.InitFATData(); + } + sectorChain.Add(t); + nSec--; + } + + //if (((int)delta % sectorSize) != 0) + //{ + // Sector t = new Sector(sectorSize); + // sectorChain.Add(t); + //} + } + //else + //{ + // // FREE Sectors + // delta = Math.Abs(delta); + + // int nSec = (int)Math.Floor(((double)delta / sectorSize)); + + // while (nSec > 0) + // { + // freeSectors.Add(sectorChain[sectorChain.Count - 1]); + // sectorChain.RemoveAt(sectorChain.Count - 1); + // nSec--; + // } + //} + } + + public override void SetLength(long value) + { + adjustLength(value); + } + + public void WriteInt32(int val) + { + byte[] buffer = new byte[4]; + buffer[0] = (byte)val; + buffer[1] = (byte)(val << 8); + buffer[2] = (byte)(val << 16); + buffer[3] = (byte)(val << 32); + Write(buffer, 0, 4); + } + + public override void Write(byte[] buffer, int offset, int count) + { + int byteWritten = 0; + int roundByteWritten = 0; + + // Assure length + if ((position + count) > length) + adjustLength((position + count)); + + if (sectorChain != null) + { + // First sector + int secOffset = (int)(position / (long)sectorSize); + int secShift = (int)position % sectorSize; + + roundByteWritten = (int)Math.Min(sectorSize - (int)(position % (long)sectorSize), count); + + if (secOffset < sectorChain.Count) + { + Buffer.BlockCopy( + buffer, + offset, + sectorChain[secOffset].GetData(), + secShift, + roundByteWritten + ); + + sectorChain[secOffset].DirtyFlag = true; + } + + byteWritten += roundByteWritten; + offset += roundByteWritten; + secOffset++; + + // Central sectors + while (byteWritten < (count - sectorSize)) + { + roundByteWritten = sectorSize; + + Buffer.BlockCopy( + buffer, + offset, + sectorChain[secOffset].GetData(), + 0, + roundByteWritten + ); + + sectorChain[secOffset].DirtyFlag = true; + + byteWritten += roundByteWritten; + offset += roundByteWritten; + secOffset++; + } + + // Last sector + roundByteWritten = count - byteWritten; + + if (roundByteWritten != 0) + { + Buffer.BlockCopy( + buffer, + offset, + sectorChain[secOffset].GetData(), + 0, + roundByteWritten + ); + + sectorChain[secOffset].DirtyFlag = true; + + offset += roundByteWritten; + byteWritten += roundByteWritten; + } + + position += count; + + } + } + } +} \ No newline at end of file diff --git a/Structured Storage Explorer/InputBox.cs b/sources/Structured Storage Explorer/InputBox.cs similarity index 97% rename from Structured Storage Explorer/InputBox.cs rename to sources/Structured Storage Explorer/InputBox.cs index f3e97b18..f088942b 100644 --- a/Structured Storage Explorer/InputBox.cs +++ b/sources/Structured Storage Explorer/InputBox.cs @@ -1,53 +1,53 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Windows.Forms; -using System.Drawing; - -namespace StructuredStorageExplorer -{ - class Utils - { - public static DialogResult InputBox(string title, string promptText, ref string value) - { - Form form = new Form(); - Label label = new Label(); - TextBox textBox = new TextBox(); - Button buttonOk = new Button(); - Button buttonCancel = new Button(); - - form.Text = title; - label.Text = promptText; - textBox.Text = value; - - buttonOk.Text = "OK"; - buttonCancel.Text = "Cancel"; - buttonOk.DialogResult = DialogResult.OK; - buttonCancel.DialogResult = DialogResult.Cancel; - - label.SetBounds(9, 20, 372, 13); - textBox.SetBounds(12, 36, 372, 20); - buttonOk.SetBounds(228, 72, 75, 23); - buttonCancel.SetBounds(309, 72, 75, 23); - - label.AutoSize = true; - textBox.Anchor = textBox.Anchor | AnchorStyles.Right; - buttonOk.Anchor = AnchorStyles.Bottom | AnchorStyles.Right; - buttonCancel.Anchor = AnchorStyles.Bottom | AnchorStyles.Right; - - form.ClientSize = new Size(396, 107); - form.Controls.AddRange(new Control[] { label, textBox, buttonOk, buttonCancel }); - form.ClientSize = new Size(Math.Max(300, label.Right + 10), form.ClientSize.Height); - form.FormBorderStyle = FormBorderStyle.FixedDialog; - form.StartPosition = FormStartPosition.CenterScreen; - form.MinimizeBox = false; - form.MaximizeBox = false; - form.AcceptButton = buttonOk; - form.CancelButton = buttonCancel; - - DialogResult dialogResult = form.ShowDialog(); - value = textBox.Text; - return dialogResult; - } - } -} +using System; +using System.Collections.Generic; +using System.Text; +using System.Windows.Forms; +using System.Drawing; + +namespace StructuredStorageExplorer +{ + class Utils + { + public static DialogResult InputBox(string title, string promptText, ref string value) + { + Form form = new Form(); + Label label = new Label(); + TextBox textBox = new TextBox(); + Button buttonOk = new Button(); + Button buttonCancel = new Button(); + + form.Text = title; + label.Text = promptText; + textBox.Text = value; + + buttonOk.Text = "OK"; + buttonCancel.Text = "Cancel"; + buttonOk.DialogResult = DialogResult.OK; + buttonCancel.DialogResult = DialogResult.Cancel; + + label.SetBounds(9, 20, 372, 13); + textBox.SetBounds(12, 36, 372, 20); + buttonOk.SetBounds(228, 72, 75, 23); + buttonCancel.SetBounds(309, 72, 75, 23); + + label.AutoSize = true; + textBox.Anchor = textBox.Anchor | AnchorStyles.Right; + buttonOk.Anchor = AnchorStyles.Bottom | AnchorStyles.Right; + buttonCancel.Anchor = AnchorStyles.Bottom | AnchorStyles.Right; + + form.ClientSize = new Size(396, 107); + form.Controls.AddRange(new Control[] { label, textBox, buttonOk, buttonCancel }); + form.ClientSize = new Size(Math.Max(300, label.Right + 10), form.ClientSize.Height); + form.FormBorderStyle = FormBorderStyle.FixedDialog; + form.StartPosition = FormStartPosition.CenterScreen; + form.MinimizeBox = false; + form.MaximizeBox = false; + form.AcceptButton = buttonOk; + form.CancelButton = buttonCancel; + + DialogResult dialogResult = form.ShowDialog(); + value = textBox.Text; + return dialogResult; + } + } +} diff --git a/Structured Storage Explorer/MainForm.Designer.cs b/sources/Structured Storage Explorer/MainForm.Designer.cs similarity index 79% rename from Structured Storage Explorer/MainForm.Designer.cs rename to sources/Structured Storage Explorer/MainForm.Designer.cs index 165486a1..dd87e900 100644 --- a/Structured Storage Explorer/MainForm.Designer.cs +++ b/sources/Structured Storage Explorer/MainForm.Designer.cs @@ -1,334 +1,396 @@ -namespace StructuredStorageExplorer -{ - partial class MainForm - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.components = new System.ComponentModel.Container(); - this.openFileDialog1 = new System.Windows.Forms.OpenFileDialog(); - this.treeView1 = new System.Windows.Forms.TreeView(); - this.contextMenuStrip1 = new System.Windows.Forms.ContextMenuStrip(this.components); - this.importDataStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); - this.exportDataToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.addStorageStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); - this.addStreamToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.removeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.saveFileDialog1 = new System.Windows.Forms.SaveFileDialog(); - this.menuStrip1 = new System.Windows.Forms.MenuStrip(); - this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.openFileMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.newStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); - this.closeStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); - this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator(); - this.updateCurrentFileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.saveAsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); - this.openDataFileDialog = new System.Windows.Forms.OpenFileDialog(); - this.statusStrip1 = new System.Windows.Forms.StatusStrip(); - this.fileNameLabel = new System.Windows.Forms.ToolStripStatusLabel(); - this.splitContainer1 = new System.Windows.Forms.SplitContainer(); - this.propertyGrid1 = new System.Windows.Forms.PropertyGrid(); - this.splitContainer2 = new System.Windows.Forms.SplitContainer(); - this.hexEditor = new Be.Windows.Forms.HexBox(); - this.contextMenuStrip1.SuspendLayout(); - this.menuStrip1.SuspendLayout(); - this.statusStrip1.SuspendLayout(); - this.splitContainer1.Panel1.SuspendLayout(); - this.splitContainer1.Panel2.SuspendLayout(); - this.splitContainer1.SuspendLayout(); - this.splitContainer2.Panel1.SuspendLayout(); - this.splitContainer2.Panel2.SuspendLayout(); - this.splitContainer2.SuspendLayout(); - this.SuspendLayout(); - // - // openFileDialog1 - // - this.openFileDialog1.Filter = "Office files (*.xls *.doc *.ppt)|*.xls;*.doc;*.ppt|Thumbs db files (Thumbs.db)|*." + - "db|MSI Setup files (*.msi)|*.msi|All files (*.*)|*.*"; - this.openFileDialog1.Title = "Open OLE Structured Storae file"; - // - // treeView1 - // - this.treeView1.ContextMenuStrip = this.contextMenuStrip1; - this.treeView1.Dock = System.Windows.Forms.DockStyle.Fill; - this.treeView1.HideSelection = false; - this.treeView1.Location = new System.Drawing.Point(0, 0); - this.treeView1.Name = "treeView1"; - this.treeView1.Size = new System.Drawing.Size(241, 240); - this.treeView1.TabIndex = 4; - this.treeView1.MouseUp += new System.Windows.Forms.MouseEventHandler(this.treeView1_MouseUp); - // - // contextMenuStrip1 - // - this.contextMenuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.importDataStripMenuItem1, - this.exportDataToolStripMenuItem, - this.addStorageStripMenuItem1, - this.addStreamToolStripMenuItem, - this.removeToolStripMenuItem}); - this.contextMenuStrip1.Name = "contextMenuStrip1"; - this.contextMenuStrip1.Size = new System.Drawing.Size(157, 114); - this.contextMenuStrip1.Opening += new System.ComponentModel.CancelEventHandler(this.contextMenuStrip1_Opening); - // - // importDataStripMenuItem1 - // - this.importDataStripMenuItem1.Name = "importDataStripMenuItem1"; - this.importDataStripMenuItem1.Size = new System.Drawing.Size(156, 22); - this.importDataStripMenuItem1.Text = "Import data..."; - this.importDataStripMenuItem1.Click += new System.EventHandler(this.importDataStripMenuItem1_Click); - // - // exportDataToolStripMenuItem - // - this.exportDataToolStripMenuItem.Name = "exportDataToolStripMenuItem"; - this.exportDataToolStripMenuItem.Size = new System.Drawing.Size(156, 22); - this.exportDataToolStripMenuItem.Text = "Export data..."; - this.exportDataToolStripMenuItem.Click += new System.EventHandler(this.exportDataToolStripMenuItem_Click); - // - // addStorageStripMenuItem1 - // - this.addStorageStripMenuItem1.Name = "addStorageStripMenuItem1"; - this.addStorageStripMenuItem1.Size = new System.Drawing.Size(156, 22); - this.addStorageStripMenuItem1.Text = "Add storage..."; - this.addStorageStripMenuItem1.Click += new System.EventHandler(this.addStorageStripMenuItem1_Click); - // - // addStreamToolStripMenuItem - // - this.addStreamToolStripMenuItem.Name = "addStreamToolStripMenuItem"; - this.addStreamToolStripMenuItem.Size = new System.Drawing.Size(156, 22); - this.addStreamToolStripMenuItem.Text = "Add stream..."; - this.addStreamToolStripMenuItem.Click += new System.EventHandler(this.addStreamToolStripMenuItem_Click); - // - // removeToolStripMenuItem - // - this.removeToolStripMenuItem.Name = "removeToolStripMenuItem"; - this.removeToolStripMenuItem.Size = new System.Drawing.Size(156, 22); - this.removeToolStripMenuItem.Text = "Remove"; - this.removeToolStripMenuItem.Click += new System.EventHandler(this.removeToolStripMenuItem_Click); - // - // saveFileDialog1 - // - this.saveFileDialog1.DefaultExt = "*.bin"; - this.saveFileDialog1.Filter = "Exported data files (*.bin)|*.bin|All files (*.*)|*.*"; - // - // menuStrip1 - // - this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.fileToolStripMenuItem}); - this.menuStrip1.Location = new System.Drawing.Point(0, 0); - this.menuStrip1.Name = "menuStrip1"; - this.menuStrip1.Size = new System.Drawing.Size(729, 24); - this.menuStrip1.TabIndex = 5; - this.menuStrip1.Text = "menuStrip1"; - // - // fileToolStripMenuItem - // - this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.openFileMenuItem, - this.newStripMenuItem1, - this.closeStripMenuItem1, - this.toolStripSeparator2, - this.updateCurrentFileToolStripMenuItem, - this.saveAsToolStripMenuItem}); - this.fileToolStripMenuItem.Name = "fileToolStripMenuItem"; - this.fileToolStripMenuItem.Size = new System.Drawing.Size(35, 20); - this.fileToolStripMenuItem.Text = "File"; - // - // openFileMenuItem - // - this.openFileMenuItem.Image = global::StructuredStorageExplorer.Properties.Resources.folder; - this.openFileMenuItem.Name = "openFileMenuItem"; - this.openFileMenuItem.Size = new System.Drawing.Size(179, 22); - this.openFileMenuItem.Text = "Open..."; - this.openFileMenuItem.Click += new System.EventHandler(this.openFileMenuItem_Click); - // - // newStripMenuItem1 - // - this.newStripMenuItem1.Image = global::StructuredStorageExplorer.Properties.Resources.page_white; - this.newStripMenuItem1.Name = "newStripMenuItem1"; - this.newStripMenuItem1.Size = new System.Drawing.Size(179, 22); - this.newStripMenuItem1.Text = "New Compound File"; - this.newStripMenuItem1.Click += new System.EventHandler(this.newStripMenuItem1_Click); - // - // closeStripMenuItem1 - // - this.closeStripMenuItem1.Name = "closeStripMenuItem1"; - this.closeStripMenuItem1.Size = new System.Drawing.Size(179, 22); - this.closeStripMenuItem1.Text = "Close file"; - this.closeStripMenuItem1.Click += new System.EventHandler(this.closeStripMenuItem1_Click); - // - // toolStripSeparator2 - // - this.toolStripSeparator2.Name = "toolStripSeparator2"; - this.toolStripSeparator2.Size = new System.Drawing.Size(176, 6); - // - // updateCurrentFileToolStripMenuItem - // - this.updateCurrentFileToolStripMenuItem.Image = global::StructuredStorageExplorer.Properties.Resources.disk; - this.updateCurrentFileToolStripMenuItem.Name = "updateCurrentFileToolStripMenuItem"; - this.updateCurrentFileToolStripMenuItem.Size = new System.Drawing.Size(179, 22); - this.updateCurrentFileToolStripMenuItem.Text = "Save"; - this.updateCurrentFileToolStripMenuItem.Click += new System.EventHandler(this.updateCurrentFileToolStripMenuItem_Click); - // - // saveAsToolStripMenuItem - // - this.saveAsToolStripMenuItem.Name = "saveAsToolStripMenuItem"; - this.saveAsToolStripMenuItem.Size = new System.Drawing.Size(179, 22); - this.saveAsToolStripMenuItem.Text = "Save As..."; - this.saveAsToolStripMenuItem.Click += new System.EventHandler(this.saveAsToolStripMenuItem_Click); - // - // statusStrip1 - // - this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { - this.fileNameLabel}); - this.statusStrip1.Location = new System.Drawing.Point(0, 509); - this.statusStrip1.Name = "statusStrip1"; - this.statusStrip1.Size = new System.Drawing.Size(729, 22); - this.statusStrip1.TabIndex = 6; - this.statusStrip1.Text = "statusStrip1"; - // - // fileNameLabel - // - this.fileNameLabel.Name = "fileNameLabel"; - this.fileNameLabel.Size = new System.Drawing.Size(0, 17); - // - // splitContainer1 - // - this.splitContainer1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; - this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; - this.splitContainer1.Location = new System.Drawing.Point(0, 0); - this.splitContainer1.Name = "splitContainer1"; - this.splitContainer1.Orientation = System.Windows.Forms.Orientation.Horizontal; - // - // splitContainer1.Panel1 - // - this.splitContainer1.Panel1.Controls.Add(this.treeView1); - // - // splitContainer1.Panel2 - // - this.splitContainer1.Panel2.Controls.Add(this.propertyGrid1); - this.splitContainer1.Size = new System.Drawing.Size(243, 485); - this.splitContainer1.SplitterDistance = 242; - this.splitContainer1.TabIndex = 5; - // - // propertyGrid1 - // - this.propertyGrid1.Dock = System.Windows.Forms.DockStyle.Fill; - this.propertyGrid1.Location = new System.Drawing.Point(0, 0); - this.propertyGrid1.Name = "propertyGrid1"; - this.propertyGrid1.Size = new System.Drawing.Size(241, 237); - this.propertyGrid1.TabIndex = 0; - this.propertyGrid1.ToolbarVisible = false; - // - // splitContainer2 - // - this.splitContainer2.Dock = System.Windows.Forms.DockStyle.Fill; - this.splitContainer2.Location = new System.Drawing.Point(0, 24); - this.splitContainer2.Name = "splitContainer2"; - // - // splitContainer2.Panel1 - // - this.splitContainer2.Panel1.Controls.Add(this.splitContainer1); - // - // splitContainer2.Panel2 - // - this.splitContainer2.Panel2.Controls.Add(this.hexEditor); - this.splitContainer2.Size = new System.Drawing.Size(729, 485); - this.splitContainer2.SplitterDistance = 243; - this.splitContainer2.TabIndex = 7; - // - // hexEditor - // - this.hexEditor.BackColor = System.Drawing.Color.WhiteSmoke; - this.hexEditor.Dock = System.Windows.Forms.DockStyle.Fill; - this.hexEditor.Font = new System.Drawing.Font("Courier New", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.hexEditor.LineInfoForeColor = System.Drawing.Color.Empty; - this.hexEditor.LineInfoVisible = true; - this.hexEditor.Location = new System.Drawing.Point(0, 0); - this.hexEditor.Name = "hexEditor"; - this.hexEditor.ShadowSelectionColor = System.Drawing.Color.FromArgb(((int)(((byte)(100)))), ((int)(((byte)(60)))), ((int)(((byte)(188)))), ((int)(((byte)(255))))); - this.hexEditor.Size = new System.Drawing.Size(482, 485); - this.hexEditor.StringViewVisible = true; - this.hexEditor.TabIndex = 0; - this.hexEditor.UseFixedBytesPerLine = true; - this.hexEditor.VScrollBarVisible = true; - // - // MainForm - // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.ClientSize = new System.Drawing.Size(729, 531); - this.Controls.Add(this.splitContainer2); - this.Controls.Add(this.statusStrip1); - this.Controls.Add(this.menuStrip1); - this.MainMenuStrip = this.menuStrip1; - this.Name = "MainForm"; - this.Text = "Structured Storage eXplorer"; - this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_FormClosing); - this.contextMenuStrip1.ResumeLayout(false); - this.menuStrip1.ResumeLayout(false); - this.menuStrip1.PerformLayout(); - this.statusStrip1.ResumeLayout(false); - this.statusStrip1.PerformLayout(); - this.splitContainer1.Panel1.ResumeLayout(false); - this.splitContainer1.Panel2.ResumeLayout(false); - this.splitContainer1.ResumeLayout(false); - this.splitContainer2.Panel1.ResumeLayout(false); - this.splitContainer2.Panel2.ResumeLayout(false); - this.splitContainer2.ResumeLayout(false); - this.ResumeLayout(false); - this.PerformLayout(); - - } - - #endregion - - private System.Windows.Forms.OpenFileDialog openFileDialog1; - private System.Windows.Forms.TreeView treeView1; - private System.Windows.Forms.ContextMenuStrip contextMenuStrip1; - private System.Windows.Forms.ToolStripMenuItem exportDataToolStripMenuItem; - private System.Windows.Forms.SaveFileDialog saveFileDialog1; - private System.Windows.Forms.ToolStripMenuItem removeToolStripMenuItem; - private System.Windows.Forms.MenuStrip menuStrip1; - private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem; - private System.Windows.Forms.ToolStripMenuItem saveAsToolStripMenuItem; - private System.Windows.Forms.ToolStripMenuItem updateCurrentFileToolStripMenuItem; - private System.Windows.Forms.ToolStripMenuItem addStreamToolStripMenuItem; - private System.Windows.Forms.ToolStripMenuItem importDataStripMenuItem1; - private System.Windows.Forms.OpenFileDialog openDataFileDialog; - private System.Windows.Forms.ToolStripMenuItem addStorageStripMenuItem1; - private System.Windows.Forms.ToolStripMenuItem newStripMenuItem1; - private System.Windows.Forms.ToolStripMenuItem openFileMenuItem; - private System.Windows.Forms.ToolStripSeparator toolStripSeparator2; - private System.Windows.Forms.StatusStrip statusStrip1; - private System.Windows.Forms.ToolStripStatusLabel fileNameLabel; - private System.Windows.Forms.SplitContainer splitContainer1; - private System.Windows.Forms.PropertyGrid propertyGrid1; - private System.Windows.Forms.SplitContainer splitContainer2; - private Be.Windows.Forms.HexBox hexEditor; - private System.Windows.Forms.ToolStripMenuItem closeStripMenuItem1; - } -} - +namespace StructuredStorageExplorer +{ + partial class MainForm + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if (disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + this.components = new System.ComponentModel.Container(); + this.openFileDialog1 = new System.Windows.Forms.OpenFileDialog(); + this.treeView1 = new System.Windows.Forms.TreeView(); + this.contextMenuStrip1 = new System.Windows.Forms.ContextMenuStrip(this.components); + this.importDataStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); + this.exportDataToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.addStorageStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); + this.addStreamToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.removeToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.saveFileDialog1 = new System.Windows.Forms.SaveFileDialog(); + this.menuStrip1 = new System.Windows.Forms.MenuStrip(); + this.fileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.openFileMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.newStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); + this.closeStripMenuItem1 = new System.Windows.Forms.ToolStripMenuItem(); + this.toolStripSeparator2 = new System.Windows.Forms.ToolStripSeparator(); + this.updateCurrentFileToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.saveAsToolStripMenuItem = new System.Windows.Forms.ToolStripMenuItem(); + this.openDataFileDialog = new System.Windows.Forms.OpenFileDialog(); + this.statusStrip1 = new System.Windows.Forms.StatusStrip(); + this.fileNameLabel = new System.Windows.Forms.ToolStripStatusLabel(); + this.splitContainer1 = new System.Windows.Forms.SplitContainer(); + this.propertyGrid1 = new System.Windows.Forms.PropertyGrid(); + this.splitContainer2 = new System.Windows.Forms.SplitContainer(); + this.hexEditor = new Be.Windows.Forms.HexBox(); + this.tabControl1 = new System.Windows.Forms.TabControl(); + this.tabPage1 = new System.Windows.Forms.TabPage(); + this.tabPage2 = new System.Windows.Forms.TabPage(); + this.dgvOLEProps = new System.Windows.Forms.DataGridView(); + this.contextMenuStrip1.SuspendLayout(); + this.menuStrip1.SuspendLayout(); + this.statusStrip1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).BeginInit(); + this.splitContainer1.Panel1.SuspendLayout(); + this.splitContainer1.Panel2.SuspendLayout(); + this.splitContainer1.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer2)).BeginInit(); + this.splitContainer2.Panel1.SuspendLayout(); + this.splitContainer2.Panel2.SuspendLayout(); + this.splitContainer2.SuspendLayout(); + this.tabControl1.SuspendLayout(); + this.tabPage1.SuspendLayout(); + this.tabPage2.SuspendLayout(); + ((System.ComponentModel.ISupportInitialize)(this.dgvOLEProps)).BeginInit(); + this.SuspendLayout(); + // + // openFileDialog1 + // + this.openFileDialog1.Filter = "Office files (*.xls *.doc *.ppt)|*.xls;*.doc;*.ppt|Thumbs db files (Thumbs.db)|*." + + "db|MSI Setup files (*.msi)|*.msi|All files (*.*)|*.*"; + this.openFileDialog1.Title = "Open OLE Structured Storae file"; + // + // treeView1 + // + this.treeView1.ContextMenuStrip = this.contextMenuStrip1; + this.treeView1.Dock = System.Windows.Forms.DockStyle.Fill; + this.treeView1.HideSelection = false; + this.treeView1.Location = new System.Drawing.Point(0, 0); + this.treeView1.Name = "treeView1"; + this.treeView1.Size = new System.Drawing.Size(275, 264); + this.treeView1.TabIndex = 4; + this.treeView1.MouseUp += new System.Windows.Forms.MouseEventHandler(this.treeView1_MouseUp); + // + // contextMenuStrip1 + // + this.contextMenuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.importDataStripMenuItem1, + this.exportDataToolStripMenuItem, + this.addStorageStripMenuItem1, + this.addStreamToolStripMenuItem, + this.removeToolStripMenuItem}); + this.contextMenuStrip1.Name = "contextMenuStrip1"; + this.contextMenuStrip1.Size = new System.Drawing.Size(148, 114); + this.contextMenuStrip1.Opening += new System.ComponentModel.CancelEventHandler(this.contextMenuStrip1_Opening); + // + // importDataStripMenuItem1 + // + this.importDataStripMenuItem1.Name = "importDataStripMenuItem1"; + this.importDataStripMenuItem1.Size = new System.Drawing.Size(147, 22); + this.importDataStripMenuItem1.Text = "Import data..."; + this.importDataStripMenuItem1.Click += new System.EventHandler(this.importDataStripMenuItem1_Click); + // + // exportDataToolStripMenuItem + // + this.exportDataToolStripMenuItem.Name = "exportDataToolStripMenuItem"; + this.exportDataToolStripMenuItem.Size = new System.Drawing.Size(147, 22); + this.exportDataToolStripMenuItem.Text = "Export data..."; + this.exportDataToolStripMenuItem.Click += new System.EventHandler(this.exportDataToolStripMenuItem_Click); + // + // addStorageStripMenuItem1 + // + this.addStorageStripMenuItem1.Name = "addStorageStripMenuItem1"; + this.addStorageStripMenuItem1.Size = new System.Drawing.Size(147, 22); + this.addStorageStripMenuItem1.Text = "Add storage..."; + this.addStorageStripMenuItem1.Click += new System.EventHandler(this.addStorageStripMenuItem1_Click); + // + // addStreamToolStripMenuItem + // + this.addStreamToolStripMenuItem.Name = "addStreamToolStripMenuItem"; + this.addStreamToolStripMenuItem.Size = new System.Drawing.Size(147, 22); + this.addStreamToolStripMenuItem.Text = "Add stream..."; + this.addStreamToolStripMenuItem.Click += new System.EventHandler(this.addStreamToolStripMenuItem_Click); + // + // removeToolStripMenuItem + // + this.removeToolStripMenuItem.Name = "removeToolStripMenuItem"; + this.removeToolStripMenuItem.Size = new System.Drawing.Size(147, 22); + this.removeToolStripMenuItem.Text = "Remove"; + this.removeToolStripMenuItem.Click += new System.EventHandler(this.removeToolStripMenuItem_Click); + // + // saveFileDialog1 + // + this.saveFileDialog1.DefaultExt = "*.bin"; + this.saveFileDialog1.Filter = "Exported data files (*.bin)|*.bin|All files (*.*)|*.*"; + // + // menuStrip1 + // + this.menuStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.fileToolStripMenuItem}); + this.menuStrip1.Location = new System.Drawing.Point(0, 0); + this.menuStrip1.Name = "menuStrip1"; + this.menuStrip1.Size = new System.Drawing.Size(833, 24); + this.menuStrip1.TabIndex = 5; + this.menuStrip1.Text = "menuStrip1"; + // + // fileToolStripMenuItem + // + this.fileToolStripMenuItem.DropDownItems.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.openFileMenuItem, + this.newStripMenuItem1, + this.closeStripMenuItem1, + this.toolStripSeparator2, + this.updateCurrentFileToolStripMenuItem, + this.saveAsToolStripMenuItem}); + this.fileToolStripMenuItem.Name = "fileToolStripMenuItem"; + this.fileToolStripMenuItem.Size = new System.Drawing.Size(37, 20); + this.fileToolStripMenuItem.Text = "File"; + // + // openFileMenuItem + // + this.openFileMenuItem.Image = global::StructuredStorageExplorer.Properties.Resources.folder; + this.openFileMenuItem.Name = "openFileMenuItem"; + this.openFileMenuItem.Size = new System.Drawing.Size(183, 22); + this.openFileMenuItem.Text = "Open..."; + this.openFileMenuItem.Click += new System.EventHandler(this.openFileMenuItem_Click); + // + // newStripMenuItem1 + // + this.newStripMenuItem1.Image = global::StructuredStorageExplorer.Properties.Resources.page_white; + this.newStripMenuItem1.Name = "newStripMenuItem1"; + this.newStripMenuItem1.Size = new System.Drawing.Size(183, 22); + this.newStripMenuItem1.Text = "New Compound File"; + this.newStripMenuItem1.Click += new System.EventHandler(this.newStripMenuItem1_Click); + // + // closeStripMenuItem1 + // + this.closeStripMenuItem1.Name = "closeStripMenuItem1"; + this.closeStripMenuItem1.Size = new System.Drawing.Size(183, 22); + this.closeStripMenuItem1.Text = "Close file"; + this.closeStripMenuItem1.Click += new System.EventHandler(this.closeStripMenuItem1_Click); + // + // toolStripSeparator2 + // + this.toolStripSeparator2.Name = "toolStripSeparator2"; + this.toolStripSeparator2.Size = new System.Drawing.Size(180, 6); + // + // updateCurrentFileToolStripMenuItem + // + this.updateCurrentFileToolStripMenuItem.Image = global::StructuredStorageExplorer.Properties.Resources.disk; + this.updateCurrentFileToolStripMenuItem.Name = "updateCurrentFileToolStripMenuItem"; + this.updateCurrentFileToolStripMenuItem.Size = new System.Drawing.Size(183, 22); + this.updateCurrentFileToolStripMenuItem.Text = "Save"; + this.updateCurrentFileToolStripMenuItem.Click += new System.EventHandler(this.updateCurrentFileToolStripMenuItem_Click); + // + // saveAsToolStripMenuItem + // + this.saveAsToolStripMenuItem.Name = "saveAsToolStripMenuItem"; + this.saveAsToolStripMenuItem.Size = new System.Drawing.Size(183, 22); + this.saveAsToolStripMenuItem.Text = "Save As..."; + this.saveAsToolStripMenuItem.Click += new System.EventHandler(this.saveAsToolStripMenuItem_Click); + // + // statusStrip1 + // + this.statusStrip1.Items.AddRange(new System.Windows.Forms.ToolStripItem[] { + this.fileNameLabel}); + this.statusStrip1.Location = new System.Drawing.Point(0, 559); + this.statusStrip1.Name = "statusStrip1"; + this.statusStrip1.Size = new System.Drawing.Size(833, 22); + this.statusStrip1.TabIndex = 6; + this.statusStrip1.Text = "statusStrip1"; + // + // fileNameLabel + // + this.fileNameLabel.Name = "fileNameLabel"; + this.fileNameLabel.Size = new System.Drawing.Size(0, 17); + // + // splitContainer1 + // + this.splitContainer1.BorderStyle = System.Windows.Forms.BorderStyle.FixedSingle; + this.splitContainer1.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer1.Location = new System.Drawing.Point(0, 0); + this.splitContainer1.Name = "splitContainer1"; + this.splitContainer1.Orientation = System.Windows.Forms.Orientation.Horizontal; + // + // splitContainer1.Panel1 + // + this.splitContainer1.Panel1.Controls.Add(this.treeView1); + // + // splitContainer1.Panel2 + // + this.splitContainer1.Panel2.Controls.Add(this.propertyGrid1); + this.splitContainer1.Size = new System.Drawing.Size(277, 535); + this.splitContainer1.SplitterDistance = 266; + this.splitContainer1.TabIndex = 5; + // + // propertyGrid1 + // + this.propertyGrid1.Dock = System.Windows.Forms.DockStyle.Fill; + this.propertyGrid1.Location = new System.Drawing.Point(0, 0); + this.propertyGrid1.Name = "propertyGrid1"; + this.propertyGrid1.Size = new System.Drawing.Size(275, 263); + this.propertyGrid1.TabIndex = 0; + this.propertyGrid1.ToolbarVisible = false; + // + // splitContainer2 + // + this.splitContainer2.Dock = System.Windows.Forms.DockStyle.Fill; + this.splitContainer2.Location = new System.Drawing.Point(0, 24); + this.splitContainer2.Name = "splitContainer2"; + // + // splitContainer2.Panel1 + // + this.splitContainer2.Panel1.Controls.Add(this.splitContainer1); + // + // splitContainer2.Panel2 + // + this.splitContainer2.Panel2.Controls.Add(this.tabControl1); + this.splitContainer2.Size = new System.Drawing.Size(833, 535); + this.splitContainer2.SplitterDistance = 277; + this.splitContainer2.TabIndex = 7; + // + // hexEditor + // + this.hexEditor.BackColor = System.Drawing.Color.WhiteSmoke; + this.hexEditor.Dock = System.Windows.Forms.DockStyle.Fill; + this.hexEditor.Font = new System.Drawing.Font("Courier New", 9F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); + this.hexEditor.LineInfoForeColor = System.Drawing.Color.Empty; + this.hexEditor.LineInfoVisible = true; + this.hexEditor.Location = new System.Drawing.Point(3, 3); + this.hexEditor.Name = "hexEditor"; + this.hexEditor.ShadowSelectionColor = System.Drawing.Color.FromArgb(((int)(((byte)(100)))), ((int)(((byte)(60)))), ((int)(((byte)(188)))), ((int)(((byte)(255))))); + this.hexEditor.Size = new System.Drawing.Size(538, 503); + this.hexEditor.StringViewVisible = true; + this.hexEditor.TabIndex = 0; + this.hexEditor.UseFixedBytesPerLine = true; + this.hexEditor.VScrollBarVisible = true; + // + // tabControl1 + // + this.tabControl1.Controls.Add(this.tabPage1); + this.tabControl1.Controls.Add(this.tabPage2); + this.tabControl1.Dock = System.Windows.Forms.DockStyle.Fill; + this.tabControl1.Location = new System.Drawing.Point(0, 0); + this.tabControl1.Name = "tabControl1"; + this.tabControl1.SelectedIndex = 0; + this.tabControl1.Size = new System.Drawing.Size(552, 535); + this.tabControl1.TabIndex = 1; + // + // tabPage1 + // + this.tabPage1.Controls.Add(this.hexEditor); + this.tabPage1.Location = new System.Drawing.Point(4, 22); + this.tabPage1.Name = "tabPage1"; + this.tabPage1.Padding = new System.Windows.Forms.Padding(3); + this.tabPage1.Size = new System.Drawing.Size(544, 509); + this.tabPage1.TabIndex = 0; + this.tabPage1.Text = "Raw Data"; + this.tabPage1.UseVisualStyleBackColor = true; + // + // tabPage2 + // + this.tabPage2.Controls.Add(this.dgvOLEProps); + this.tabPage2.Location = new System.Drawing.Point(4, 22); + this.tabPage2.Name = "tabPage2"; + this.tabPage2.Padding = new System.Windows.Forms.Padding(3); + this.tabPage2.Size = new System.Drawing.Size(544, 509); + this.tabPage2.TabIndex = 1; + this.tabPage2.Text = "OLE Properties"; + this.tabPage2.UseVisualStyleBackColor = true; + // + // dgvOLEProps + // + this.dgvOLEProps.ColumnHeadersHeightSizeMode = System.Windows.Forms.DataGridViewColumnHeadersHeightSizeMode.AutoSize; + this.dgvOLEProps.Dock = System.Windows.Forms.DockStyle.Fill; + this.dgvOLEProps.Location = new System.Drawing.Point(3, 3); + this.dgvOLEProps.Name = "dgvOLEProps"; + this.dgvOLEProps.Size = new System.Drawing.Size(538, 503); + this.dgvOLEProps.TabIndex = 0; + // + // MainForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(833, 581); + this.Controls.Add(this.splitContainer2); + this.Controls.Add(this.statusStrip1); + this.Controls.Add(this.menuStrip1); + this.MainMenuStrip = this.menuStrip1; + this.Name = "MainForm"; + this.Text = "Structured Storage eXplorer"; + this.FormClosing += new System.Windows.Forms.FormClosingEventHandler(this.MainForm_FormClosing); + this.contextMenuStrip1.ResumeLayout(false); + this.menuStrip1.ResumeLayout(false); + this.menuStrip1.PerformLayout(); + this.statusStrip1.ResumeLayout(false); + this.statusStrip1.PerformLayout(); + this.splitContainer1.Panel1.ResumeLayout(false); + this.splitContainer1.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer1)).EndInit(); + this.splitContainer1.ResumeLayout(false); + this.splitContainer2.Panel1.ResumeLayout(false); + this.splitContainer2.Panel2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.splitContainer2)).EndInit(); + this.splitContainer2.ResumeLayout(false); + this.tabControl1.ResumeLayout(false); + this.tabPage1.ResumeLayout(false); + this.tabPage2.ResumeLayout(false); + ((System.ComponentModel.ISupportInitialize)(this.dgvOLEProps)).EndInit(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.OpenFileDialog openFileDialog1; + private System.Windows.Forms.TreeView treeView1; + private System.Windows.Forms.ContextMenuStrip contextMenuStrip1; + private System.Windows.Forms.ToolStripMenuItem exportDataToolStripMenuItem; + private System.Windows.Forms.SaveFileDialog saveFileDialog1; + private System.Windows.Forms.ToolStripMenuItem removeToolStripMenuItem; + private System.Windows.Forms.MenuStrip menuStrip1; + private System.Windows.Forms.ToolStripMenuItem fileToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem saveAsToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem updateCurrentFileToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem addStreamToolStripMenuItem; + private System.Windows.Forms.ToolStripMenuItem importDataStripMenuItem1; + private System.Windows.Forms.OpenFileDialog openDataFileDialog; + private System.Windows.Forms.ToolStripMenuItem addStorageStripMenuItem1; + private System.Windows.Forms.ToolStripMenuItem newStripMenuItem1; + private System.Windows.Forms.ToolStripMenuItem openFileMenuItem; + private System.Windows.Forms.ToolStripSeparator toolStripSeparator2; + private System.Windows.Forms.StatusStrip statusStrip1; + private System.Windows.Forms.ToolStripStatusLabel fileNameLabel; + private System.Windows.Forms.SplitContainer splitContainer1; + private System.Windows.Forms.PropertyGrid propertyGrid1; + private System.Windows.Forms.SplitContainer splitContainer2; + private Be.Windows.Forms.HexBox hexEditor; + private System.Windows.Forms.ToolStripMenuItem closeStripMenuItem1; + private System.Windows.Forms.TabControl tabControl1; + private System.Windows.Forms.TabPage tabPage1; + private System.Windows.Forms.TabPage tabPage2; + private System.Windows.Forms.DataGridView dgvOLEProps; + } +} + diff --git a/sources/Structured Storage Explorer/MainForm.cs b/sources/Structured Storage Explorer/MainForm.cs new file mode 100644 index 00000000..64ea3d6d --- /dev/null +++ b/sources/Structured Storage Explorer/MainForm.cs @@ -0,0 +1,485 @@ +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Text; +using System.Windows.Forms; +using OpenMcdf; +using System.IO; +using System.Resources; +using System.Globalization; +using StructuredStorageExplorer.Properties; +using Be.Windows.Forms; +using OpenMcdf.Extensions.OLEProperties; +using OpenMcdf.Extensions.OLEProperties.Interfaces; +using OpenMcdf.Extensions; + +// Author Federico Blaseotto + +namespace StructuredStorageExplorer +{ + + /// + /// Sample Structured Storage viewer to + /// demonstrate use of OpenMCDF + /// + public partial class MainForm : Form + { + private CompoundFile cf; + private FileStream fs; + + public MainForm() + { + InitializeComponent(); + + //Load images for icons from resx + Image folderImage = (Image)Properties.Resources.ResourceManager.GetObject("storage"); + Image streamImage = (Image)Properties.Resources.ResourceManager.GetObject("stream"); + Image olePropsImage = (Image)Properties.Resources.ResourceManager.GetObject("oleprops"); + + treeView1.ImageList = new ImageList(); + treeView1.ImageList.Images.Add(folderImage); + treeView1.ImageList.Images.Add(streamImage); + treeView1.ImageList.Images.Add(olePropsImage); + + saveAsToolStripMenuItem.Enabled = false; + updateCurrentFileToolStripMenuItem.Enabled = false; + + } + + + + private void OpenFile() + { + if (!String.IsNullOrEmpty(openFileDialog1.FileName)) + { + CloseCurrentFile(); + + treeView1.Nodes.Clear(); + fileNameLabel.Text = openFileDialog1.FileName; + LoadFile(openFileDialog1.FileName, true); + canUpdate = true; + saveAsToolStripMenuItem.Enabled = true; + updateCurrentFileToolStripMenuItem.Enabled = true; + } + } + + private void CloseCurrentFile() + { + if (cf != null) + cf.Close(); + + if (fs != null) + fs.Close(); + + treeView1.Nodes.Clear(); + fileNameLabel.Text = String.Empty; + saveAsToolStripMenuItem.Enabled = false; + updateCurrentFileToolStripMenuItem.Enabled = false; + + propertyGrid1.SelectedObject = null; + hexEditor.ByteProvider = null; + } + + private bool canUpdate = false; + + private void CreateNewFile() + { + CloseCurrentFile(); + + cf = new CompoundFile(); + canUpdate = false; + saveAsToolStripMenuItem.Enabled = true; + + updateCurrentFileToolStripMenuItem.Enabled = false; + + RefreshTree(); + } + + private void RefreshTree() + { + treeView1.Nodes.Clear(); + + TreeNode root = null; + root = treeView1.Nodes.Add("Root Entry", "Root"); + root.ImageIndex = 0; + root.Tag = cf.RootStorage; + + //Recursive function to get all storage and streams + AddNodes(root, cf.RootStorage); + } + + private void LoadFile(string fileName, bool enableCommit) + { + + fs = new FileStream( + fileName, + FileMode.Open, + enableCommit ? + FileAccess.ReadWrite + : FileAccess.Read + ); + + try + { + if (cf != null) + { + cf.Close(); + cf = null; + } + + //Load file + if (enableCommit) + { + cf = new CompoundFile(fs, CFSUpdateMode.Update, CFSConfiguration.SectorRecycle | CFSConfiguration.NoValidationException | CFSConfiguration.EraseFreeSectors); + } + else + { + cf = new CompoundFile(fs); + } + + RefreshTree(); + } + catch (Exception ex) + { + treeView1.Nodes.Clear(); + fileNameLabel.Text = String.Empty; + MessageBox.Show("Internal error: " + ex.Message, "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + /// + /// Recursive addition of tree nodes foreach child of current item in the storage + /// + /// Current TreeNode + /// Current storage associated with node + private void AddNodes(TreeNode node, CFStorage cfs) + { + Action va = delegate (CFItem target) + { + TreeNode temp = node.Nodes.Add( + target.Name, + target.Name + (target.IsStream ? " (" + target.Size + " bytes )" : "") + ); + + temp.Tag = target; + + if (target.IsStream) + { + + //Stream + temp.ImageIndex = 1; + temp.SelectedImageIndex = 1; + } + else + { + //Storage + temp.ImageIndex = 0; + temp.SelectedImageIndex = 0; + + //Recursion into the storage + AddNodes(temp, (CFStorage)target); + } + }; + + //Visit NON-recursively (first level only) + cfs.VisitEntries(va, false); + } + + + + private void exportDataToolStripMenuItem_Click(object sender, EventArgs e) + { + //No export if storage + if (treeView1.SelectedNode == null || !((CFItem)treeView1.SelectedNode.Tag).IsStream) + { + MessageBox.Show("Only stream data can be exported", "Warning", MessageBoxButtons.OK, MessageBoxIcon.Warning); + + return; + } + + CFStream target = (CFStream)treeView1.SelectedNode.Tag; + + // A lot of stream and storage have only non-printable characters. + // We need to sanitize filename. + + String sanitizedFileName = String.Empty; + + foreach (char c in target.Name) + { + if ( + Char.GetUnicodeCategory(c) == UnicodeCategory.LetterNumber + || Char.GetUnicodeCategory(c) == UnicodeCategory.LowercaseLetter + || Char.GetUnicodeCategory(c) == UnicodeCategory.UppercaseLetter + ) + + sanitizedFileName += c; + } + + if (String.IsNullOrEmpty(sanitizedFileName)) + { + sanitizedFileName = "tempFileName"; + } + + saveFileDialog1.FileName = sanitizedFileName + ".bin"; + + if (saveFileDialog1.ShowDialog() == DialogResult.OK) + { + FileStream fs = null; + + try + { + fs = new FileStream(saveFileDialog1.FileName, FileMode.CreateNew, FileAccess.ReadWrite); + fs.Write(target.GetData(), 0, (int)target.Size); + } + catch (Exception ex) + { + treeView1.Nodes.Clear(); + MessageBox.Show("Internal error: " + ex.Message, "ERROR", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + finally + { + if (fs != null) + { + fs.Flush(); + fs.Close(); + fs = null; + } + } + } + } + + private void removeToolStripMenuItem_Click(object sender, EventArgs e) + { + TreeNode n = treeView1.SelectedNode; + ((CFStorage)n.Parent.Tag).Delete(n.Name); + + RefreshTree(); + } + + private void saveAsToolStripMenuItem_Click(object sender, EventArgs e) + { + saveFileDialog1.FilterIndex = 2; + if (saveFileDialog1.ShowDialog() == DialogResult.OK) + { + cf.Save(saveFileDialog1.FileName); + } + } + + private void updateCurrentFileToolStripMenuItem_Click(object sender, EventArgs e) + { + if (canUpdate) + { + if (hexEditor.ByteProvider != null && hexEditor.ByteProvider.HasChanges()) + hexEditor.ByteProvider.ApplyChanges(); + cf.Commit(); + } + else + MessageBox.Show("Cannot update a compound document that is not based on a stream or on a file", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + + } + + private void addStreamToolStripMenuItem_Click(object sender, EventArgs e) + { + string streamName = String.Empty; + + if (Utils.InputBox("Add stream", "Insert stream name", ref streamName) == DialogResult.OK) + { + CFItem cfs = treeView1.SelectedNode.Tag as CFItem; + + if (cfs != null && (cfs.IsStorage || cfs.IsRoot)) + { + try + { + ((CFStorage)cfs).AddStream(streamName); + } + catch (CFDuplicatedItemException) + { + MessageBox.Show("Cannot insert a duplicated item", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + + RefreshTree(); + } + } + + private void addStorageStripMenuItem1_Click(object sender, EventArgs e) + { + string storage = String.Empty; + + if (Utils.InputBox("Add storage", "Insert storage name", ref storage) == DialogResult.OK) + { + CFItem cfs = treeView1.SelectedNode.Tag as CFItem; + + if (cfs != null && (cfs.IsStorage || cfs.IsRoot)) + { + try + { + ((CFStorage)cfs).AddStorage(storage); + } + catch (CFDuplicatedItemException) + { + MessageBox.Show("Cannot insert a duplicated item", "Error", MessageBoxButtons.OK, MessageBoxIcon.Error); + } + } + + RefreshTree(); + } + } + + private void importDataStripMenuItem1_Click(object sender, EventArgs e) + { + string fileName = String.Empty; + + if (openDataFileDialog.ShowDialog() == DialogResult.OK) + { + CFStream s = treeView1.SelectedNode.Tag as CFStream; + + if (s != null) + { + FileStream f = new FileStream(openDataFileDialog.FileName, FileMode.Open, FileAccess.Read, FileShare.Read); + byte[] data = new byte[f.Length]; + f.Read(data, 0, (int)f.Length); + f.Flush(); + f.Close(); + s.SetData(data); + + RefreshTree(); + } + } + } + + private void MainForm_FormClosing(object sender, FormClosingEventArgs e) + { + if (cf != null) + cf.Close(); + } + + private void contextMenuStrip1_Opening(object sender, CancelEventArgs e) + { + + } + + private void newStripMenuItem1_Click(object sender, EventArgs e) + { + + CreateNewFile(); + } + + private void openFileMenuItem_Click(object sender, EventArgs e) + { + if (openFileDialog1.ShowDialog() == DialogResult.OK) + { + try + { + OpenFile(); + } + catch + { + + } + } + } + + + private void treeView1_MouseUp(object sender, MouseEventArgs e) + { + // Get the node under the mouse cursor. + // We intercept both left and right mouse clicks + // and set the selected treenode according. + + TreeNode n = treeView1.GetNodeAt(e.X, e.Y); + + if (n != null) + { + if (this.hexEditor.ByteProvider != null && this.hexEditor.ByteProvider.HasChanges()) + { + if (MessageBox.Show("Do you want to save pending changes ?", "Save changes", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes) + { + this.hexEditor.ByteProvider.ApplyChanges(); + } + } + + treeView1.SelectedNode = n; + + + // The tag property contains the underlying CFItem. + CFItem target = (CFItem)n.Tag; + + if (target.IsStream) + { + addStorageStripMenuItem1.Enabled = false; + addStreamToolStripMenuItem.Enabled = false; + importDataStripMenuItem1.Enabled = true; + exportDataToolStripMenuItem.Enabled = true; + + if (target.Name == "\u0005SummaryInformation" || target.Name == "\u0005DocumentSummaryInformation") + { + PropertySetStream mgr = ((CFStream)target).AsOLEProperties(); + + DataTable ds = new DataTable(); + ds.Columns.Add("Name", typeof(String)); + ds.Columns.Add("Type", typeof(String)); + ds.Columns.Add("Value", typeof(String)); + + for (int i = 0; i < mgr.PropertySet0.NumProperties; i++) + { + ITypedPropertyValue p = mgr.PropertySet0.Properties[i]; + + DataRow dr = ds.NewRow(); + dr.ItemArray = new Object[] { mgr.PropertySet0.PropertyIdentifierAndOffsets[i].PropertyIdentifier.GetDescription(), p.VTType, p.PropertyValue }; + ds.Rows.Add(dr); + } + + ds.AcceptChanges(); + dgvOLEProps.DataSource = ds; + } + } + } + else + { + addStorageStripMenuItem1.Enabled = true; + addStreamToolStripMenuItem.Enabled = true; + importDataStripMenuItem1.Enabled = false; + exportDataToolStripMenuItem.Enabled = false; + } + + propertyGrid1.SelectedObject = n.Tag; + + + + CFStream targetStream = n.Tag as CFStream; + if (targetStream != null) + { + this.hexEditor.ByteProvider = new StreamDataProvider(targetStream); + } + else + { + this.hexEditor.ByteProvider = null; + } + + } + + void hexEditor_ByteProviderChanged(object sender, EventArgs e) + { + + } + + private void closeStripMenuItem1_Click(object sender, EventArgs e) + { + if (this.hexEditor.ByteProvider != null && this.hexEditor.ByteProvider.HasChanges()) + { + if (MessageBox.Show("Do you want to save pending changes ?", "Save changes", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) == DialogResult.Yes) + { + this.hexEditor.ByteProvider.ApplyChanges(); + } + } + + CloseCurrentFile(); + } + + + } +} diff --git a/Structured Storage Explorer/MainForm.resx b/sources/Structured Storage Explorer/MainForm.resx similarity index 90% rename from Structured Storage Explorer/MainForm.resx rename to sources/Structured Storage Explorer/MainForm.resx index c2d7ab61..38dc0d6f 100644 --- a/Structured Storage Explorer/MainForm.resx +++ b/sources/Structured Storage Explorer/MainForm.resx @@ -1,141 +1,141 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - 17, 17 - - - 147, 17 - - - 292, 17 - - - 420, 17 - - - 529, 17 - - - 676, 17 - - - 42 - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + + 147, 17 + + + 292, 17 + + + 420, 17 + + + 529, 17 + + + 676, 17 + + + 42 + \ No newline at end of file diff --git a/Structured Storage Explorer/Program.cs b/sources/Structured Storage Explorer/Program.cs similarity index 96% rename from Structured Storage Explorer/Program.cs rename to sources/Structured Storage Explorer/Program.cs index 87927eb4..be515da4 100644 --- a/Structured Storage Explorer/Program.cs +++ b/sources/Structured Storage Explorer/Program.cs @@ -1,20 +1,20 @@ -using System; -using System.Collections.Generic; -using System.Windows.Forms; - -namespace StructuredStorageExplorer -{ - static class Program - { - /// - /// The main entry point for the application. - /// - [STAThread] - static void Main() - { - Application.EnableVisualStyles(); - Application.SetCompatibleTextRenderingDefault(false); - Application.Run(new MainForm()); - } - } -} +using System; +using System.Collections.Generic; +using System.Windows.Forms; + +namespace StructuredStorageExplorer +{ + static class Program + { + /// + /// The main entry point for the application. + /// + [STAThread] + static void Main() + { + Application.EnableVisualStyles(); + Application.SetCompatibleTextRenderingDefault(false); + Application.Run(new MainForm()); + } + } +} diff --git a/sources/Structured Storage Explorer/Properties/AssemblyInfo.cs b/sources/Structured Storage Explorer/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..93e68e9b --- /dev/null +++ b/sources/Structured Storage Explorer/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("StucturedStorageExplorer")] +[assembly: AssemblyDescription("COM/OLE Stuctured Storage Viewer - Sample application for OpenMCDF component.")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("StucturedStorageExplorer")] +[assembly: AssemblyCopyright("Copyright © 2010-2015, Federico Blaseotto")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("aaf2d359-361e-47a3-883e-194bb63c7930")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("2.0.0.*")] diff --git a/sources/Structured Storage Explorer/Properties/Resources.Designer.cs b/sources/Structured Storage Explorer/Properties/Resources.Designer.cs new file mode 100644 index 00000000..fe5e034f --- /dev/null +++ b/sources/Structured Storage Explorer/Properties/Resources.Designer.cs @@ -0,0 +1,123 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace StructuredStorageExplorer.Properties { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("StructuredStorageExplorer.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap disk { + get { + object obj = ResourceManager.GetObject("disk", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap door_out { + get { + object obj = ResourceManager.GetObject("door_out", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap folder { + get { + object obj = ResourceManager.GetObject("folder", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap page_white { + get { + object obj = ResourceManager.GetObject("page_white", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap storage { + get { + object obj = ResourceManager.GetObject("storage", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + + /// + /// Looks up a localized resource of type System.Drawing.Bitmap. + /// + internal static System.Drawing.Bitmap stream { + get { + object obj = ResourceManager.GetObject("stream", resourceCulture); + return ((System.Drawing.Bitmap)(obj)); + } + } + } +} diff --git a/Structured Storage Explorer/Properties/Resources.resx b/sources/Structured Storage Explorer/Properties/Resources.resx similarity index 95% rename from Structured Storage Explorer/Properties/Resources.resx rename to sources/Structured Storage Explorer/Properties/Resources.resx index 24ff768b..4dccc070 100644 --- a/Structured Storage Explorer/Properties/Resources.resx +++ b/sources/Structured Storage Explorer/Properties/Resources.resx @@ -112,12 +112,12 @@ 2.0 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - + ..\img\disk.png;System.Drawing.Bitmap, System.Drawing, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a diff --git a/sources/Structured Storage Explorer/Properties/Settings.Designer.cs b/sources/Structured Storage Explorer/Properties/Settings.Designer.cs new file mode 100644 index 00000000..ae6feeb5 --- /dev/null +++ b/sources/Structured Storage Explorer/Properties/Settings.Designer.cs @@ -0,0 +1,38 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.296 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace StructuredStorageExplorer.Properties { + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "10.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default { + get { + return defaultInstance; + } + } + + [global::System.Configuration.UserScopedSettingAttribute()] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Configuration.DefaultSettingValueAttribute("True")] + public bool CommitEnabled { + get { + return ((bool)(this["CommitEnabled"])); + } + set { + this["CommitEnabled"] = value; + } + } + } +} diff --git a/Structured Storage Explorer/Properties/Settings.settings b/sources/Structured Storage Explorer/Properties/Settings.settings similarity index 98% rename from Structured Storage Explorer/Properties/Settings.settings rename to sources/Structured Storage Explorer/Properties/Settings.settings index 9dcf7035..8fa6102b 100644 --- a/Structured Storage Explorer/Properties/Settings.settings +++ b/sources/Structured Storage Explorer/Properties/Settings.settings @@ -1,9 +1,9 @@ - - - - - - True - - + + + + + + True + + \ No newline at end of file diff --git a/Structured Storage Explorer/StreamDataProvider.cs b/sources/Structured Storage Explorer/StreamDataProvider.cs similarity index 96% rename from Structured Storage Explorer/StreamDataProvider.cs rename to sources/Structured Storage Explorer/StreamDataProvider.cs index 1b5b25a3..59adb868 100644 --- a/Structured Storage Explorer/StreamDataProvider.cs +++ b/sources/Structured Storage Explorer/StreamDataProvider.cs @@ -1,178 +1,178 @@ -using System; -using System.Collections.Generic; -using System.Text; -using Be.Windows.Forms; -using OpenMcdf; - -namespace StructuredStorageExplorer -{ - public class StreamDataProvider : IByteProvider - { - /// - /// Modifying stream - /// - CFStream _modifiedStream; - - /// - /// Contains information about changes. - /// - bool _hasChanges; - - /// - /// Contains a byte collection. - /// - ByteCollection _bytes; - - - /// - /// Initializes a new instance of the DynamicByteProvider class. - /// - /// - public StreamDataProvider(CFStream modifiedStream) - { - _bytes = new ByteCollection(modifiedStream.GetData()); - _modifiedStream = modifiedStream; - } - - /// - /// Raises the Changed event. - /// - void OnChanged(EventArgs e) - { - _hasChanges = true; - - if (Changed != null) - Changed(this, e); - } - - /// - /// Raises the LengthChanged event. - /// - void OnLengthChanged(EventArgs e) - { - if (LengthChanged != null) - LengthChanged(this, e); - } - - /// - /// Gets the byte collection. - /// - public ByteCollection Bytes - { - get { return _bytes; } - } - - #region IByteProvider Members - /// - /// True, when changes are done. - /// - public bool HasChanges() - { - return _hasChanges; - } - - /// - /// Applies changes. - /// - public void ApplyChanges() - { - _hasChanges = false; - - _modifiedStream.SetData(this._bytes.ToArray()); - } - - /// - /// Occurs, when the write buffer contains new changes. - /// - public event EventHandler Changed; - - /// - /// Occurs, when InsertBytes or DeleteBytes method is called. - /// - public event EventHandler LengthChanged; - - - /// - /// Reads a byte from the byte collection. - /// - /// the index of the byte to read - /// the byte - public byte ReadByte(long index) - { return _bytes[(int)index]; } - - /// - /// Write a byte into the byte collection. - /// - /// the index of the byte to write. - /// the byte - public void WriteByte(long index, byte value) - { - _bytes[(int)index] = value; - OnChanged(EventArgs.Empty); - } - - /// - /// Deletes bytes from the byte collection. - /// - /// the start index of the bytes to delete. - /// the length of bytes to delete. - public void DeleteBytes(long index, long length) - { - int internal_index = (int)Math.Max(0, index); - int internal_length = (int)Math.Min((int)Length, length); - _bytes.RemoveRange(internal_index, internal_length); - - OnLengthChanged(EventArgs.Empty); - OnChanged(EventArgs.Empty); - } - - /// - /// Inserts byte into the byte collection. - /// - /// the start index of the bytes in the byte collection - /// the byte array to insert - public void InsertBytes(long index, byte[] bs) - { - _bytes.InsertRange((int)index, bs); - - OnLengthChanged(EventArgs.Empty); - OnChanged(EventArgs.Empty); - } - - /// - /// Gets the length of the bytes in the byte collection. - /// - public long Length - { - get - { - return _bytes.Count; - } - } - - /// - /// Returns true - /// - public bool SupportsWriteByte() - { - return true; - } - - /// - /// Returns true - /// - public bool SupportsInsertBytes() - { - return true; - } - - /// - /// Returns true - /// - public bool SupportsDeleteBytes() - { - return true; - } - #endregion - } -} +using System; +using System.Collections.Generic; +using System.Text; +using Be.Windows.Forms; +using OpenMcdf; + +namespace StructuredStorageExplorer +{ + public class StreamDataProvider : IByteProvider + { + /// + /// Modifying stream + /// + CFStream _modifiedStream; + + /// + /// Contains information about changes. + /// + bool _hasChanges; + + /// + /// Contains a byte collection. + /// + ByteCollection _bytes; + + + /// + /// Initializes a new instance of the DynamicByteProvider class. + /// + /// + public StreamDataProvider(CFStream modifiedStream) + { + _bytes = new ByteCollection(modifiedStream.GetData()); + _modifiedStream = modifiedStream; + } + + /// + /// Raises the Changed event. + /// + void OnChanged(EventArgs e) + { + _hasChanges = true; + + if (Changed != null) + Changed(this, e); + } + + /// + /// Raises the LengthChanged event. + /// + void OnLengthChanged(EventArgs e) + { + if (LengthChanged != null) + LengthChanged(this, e); + } + + /// + /// Gets the byte collection. + /// + public ByteCollection Bytes + { + get { return _bytes; } + } + + #region IByteProvider Members + /// + /// True, when changes are done. + /// + public bool HasChanges() + { + return _hasChanges; + } + + /// + /// Applies changes. + /// + public void ApplyChanges() + { + _hasChanges = false; + + _modifiedStream.SetData(this._bytes.ToArray()); + } + + /// + /// Occurs, when the write buffer contains new changes. + /// + public event EventHandler Changed; + + /// + /// Occurs, when InsertBytes or DeleteBytes method is called. + /// + public event EventHandler LengthChanged; + + + /// + /// Reads a byte from the byte collection. + /// + /// the index of the byte to read + /// the byte + public byte ReadByte(long index) + { return _bytes[(int)index]; } + + /// + /// Write a byte into the byte collection. + /// + /// the index of the byte to write. + /// the byte + public void WriteByte(long index, byte value) + { + _bytes[(int)index] = value; + OnChanged(EventArgs.Empty); + } + + /// + /// Deletes bytes from the byte collection. + /// + /// the start index of the bytes to delete. + /// the length of bytes to delete. + public void DeleteBytes(long index, long length) + { + int internal_index = (int)Math.Max(0, index); + int internal_length = (int)Math.Min((int)Length, length); + _bytes.RemoveRange(internal_index, internal_length); + + OnLengthChanged(EventArgs.Empty); + OnChanged(EventArgs.Empty); + } + + /// + /// Inserts byte into the byte collection. + /// + /// the start index of the bytes in the byte collection + /// the byte array to insert + public void InsertBytes(long index, byte[] bs) + { + _bytes.InsertRange((int)index, bs); + + OnLengthChanged(EventArgs.Empty); + OnChanged(EventArgs.Empty); + } + + /// + /// Gets the length of the bytes in the byte collection. + /// + public long Length + { + get + { + return _bytes.Count; + } + } + + /// + /// Returns true + /// + public bool SupportsWriteByte() + { + return true; + } + + /// + /// Returns true + /// + public bool SupportsInsertBytes() + { + return true; + } + + /// + /// Returns true + /// + public bool SupportsDeleteBytes() + { + return true; + } + #endregion + } +} diff --git a/sources/Structured Storage Explorer/StructuredStorageExplorer.csproj b/sources/Structured Storage Explorer/StructuredStorageExplorer.csproj new file mode 100644 index 00000000..92afc911 --- /dev/null +++ b/sources/Structured Storage Explorer/StructuredStorageExplorer.csproj @@ -0,0 +1,158 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {4F6323A8-9C06-4D94-808F-EBD69B8370D7} + WinExe + Properties + StructuredStorageExplorer + StucturedStorageExplorer + v4.0 + 512 + + + 3.5 + + http://localhost/StucturedStorageExplorer/ + true + Web + true + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + true + false + true + + + + true + full + false + ..\..\bin\Debug\StructuredStorageXplorer\ + DEBUG;TRACE + prompt + 4 + AllRules.ruleset + false + ..\..\bin\Debug\StructuredStorageXplorer\StucturedStorageExplorer.XML + + + pdbonly + true + ..\..\bin\Release\StructuredStorageXplorer\ + TRACE + prompt + 4 + AllRules.ruleset + ..\..\bin\Release\StructuredStorageXplorer\StucturedStorageExplorer.XML + + + false + + + + + + + + False + lib\Be.Windows.Forms.HexBox.dll + + + + + + + + + + + + + Form + + + MainForm.cs + + + + + MainForm.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + + + True + Resources.resx + True + + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + True + Settings.settings + True + + + + + + + + + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + + + + {db748c1d-d71c-442b-832d-2e33be816cbb} + OpenMcdf.Extensions + + + {56e15d4a-8a37-4c7c-bb44-fd59aff220c1} + OpenMcdf + + + + + \ No newline at end of file diff --git a/Structured Storage Explorer/Utils.cs b/sources/Structured Storage Explorer/Utils.cs similarity index 97% rename from Structured Storage Explorer/Utils.cs rename to sources/Structured Storage Explorer/Utils.cs index af4f289f..4c53406d 100644 --- a/Structured Storage Explorer/Utils.cs +++ b/sources/Structured Storage Explorer/Utils.cs @@ -1,63 +1,63 @@ -using System; -using System.Collections.Generic; -using System.Text; -using System.Windows.Forms; -using System.Drawing; -using OpenMcdf; - -namespace StructuredStorageExplorer -{ - class Utils - { - public static DialogResult InputBox(string title, string promptText, ref string value) - { - Form form = new Form(); - Label label = new Label(); - TextBox textBox = new TextBox(); - Button buttonOk = new Button(); - Button buttonCancel = new Button(); - - form.Text = title; - label.Text = promptText; - textBox.Text = value; - - buttonOk.Text = "OK"; - buttonCancel.Text = "Cancel"; - buttonOk.DialogResult = DialogResult.OK; - buttonCancel.DialogResult = DialogResult.Cancel; - - label.SetBounds(9, 20, 372, 13); - textBox.SetBounds(12, 36, 372, 20); - buttonOk.SetBounds(228, 72, 75, 23); - buttonCancel.SetBounds(309, 72, 75, 23); - - label.AutoSize = true; - textBox.Anchor = textBox.Anchor | AnchorStyles.Right; - buttonOk.Anchor = AnchorStyles.Bottom | AnchorStyles.Right; - buttonCancel.Anchor = AnchorStyles.Bottom | AnchorStyles.Right; - - form.ClientSize = new Size(396, 107); - form.Controls.AddRange(new Control[] { label, textBox, buttonOk, buttonCancel }); - form.ClientSize = new Size(Math.Max(300, label.Right + 10), form.ClientSize.Height); - form.FormBorderStyle = FormBorderStyle.FixedDialog; - form.StartPosition = FormStartPosition.CenterScreen; - form.MinimizeBox = false; - form.MaximizeBox = false; - form.AcceptButton = buttonOk; - form.CancelButton = buttonCancel; - - DialogResult dialogResult = form.ShowDialog(); - value = textBox.Text; - return dialogResult; - } - - public static bool IsStreamTreeNode(TreeNode node) - { - return ((CFItem)node.Tag).IsStream; - } - } - - - - -} +using System; +using System.Collections.Generic; +using System.Text; +using System.Windows.Forms; +using System.Drawing; +using OpenMcdf; + +namespace StructuredStorageExplorer +{ + class Utils + { + public static DialogResult InputBox(string title, string promptText, ref string value) + { + Form form = new Form(); + Label label = new Label(); + TextBox textBox = new TextBox(); + Button buttonOk = new Button(); + Button buttonCancel = new Button(); + + form.Text = title; + label.Text = promptText; + textBox.Text = value; + + buttonOk.Text = "OK"; + buttonCancel.Text = "Cancel"; + buttonOk.DialogResult = DialogResult.OK; + buttonCancel.DialogResult = DialogResult.Cancel; + + label.SetBounds(9, 20, 372, 13); + textBox.SetBounds(12, 36, 372, 20); + buttonOk.SetBounds(228, 72, 75, 23); + buttonCancel.SetBounds(309, 72, 75, 23); + + label.AutoSize = true; + textBox.Anchor = textBox.Anchor | AnchorStyles.Right; + buttonOk.Anchor = AnchorStyles.Bottom | AnchorStyles.Right; + buttonCancel.Anchor = AnchorStyles.Bottom | AnchorStyles.Right; + + form.ClientSize = new Size(396, 107); + form.Controls.AddRange(new Control[] { label, textBox, buttonOk, buttonCancel }); + form.ClientSize = new Size(Math.Max(300, label.Right + 10), form.ClientSize.Height); + form.FormBorderStyle = FormBorderStyle.FixedDialog; + form.StartPosition = FormStartPosition.CenterScreen; + form.MinimizeBox = false; + form.MaximizeBox = false; + form.AcceptButton = buttonOk; + form.CancelButton = buttonCancel; + + DialogResult dialogResult = form.ShowDialog(); + value = textBox.Text; + return dialogResult; + } + + public static bool IsStreamTreeNode(TreeNode node) + { + return ((CFItem)node.Tag).IsStream; + } + } + + + + +} diff --git a/sources/Structured Storage Explorer/app.config b/sources/Structured Storage Explorer/app.config new file mode 100644 index 00000000..150f7756 --- /dev/null +++ b/sources/Structured Storage Explorer/app.config @@ -0,0 +1,15 @@ + + + + +
+ + + + + + True + + + + diff --git a/Structured Storage Explorer/img/disk.png b/sources/Structured Storage Explorer/img/disk.png similarity index 100% rename from Structured Storage Explorer/img/disk.png rename to sources/Structured Storage Explorer/img/disk.png diff --git a/Structured Storage Explorer/img/door_out.png b/sources/Structured Storage Explorer/img/door_out.png similarity index 100% rename from Structured Storage Explorer/img/door_out.png rename to sources/Structured Storage Explorer/img/door_out.png diff --git a/Structured Storage Explorer/img/folder.png b/sources/Structured Storage Explorer/img/folder.png similarity index 100% rename from Structured Storage Explorer/img/folder.png rename to sources/Structured Storage Explorer/img/folder.png diff --git a/Structured Storage Explorer/img/page_white.png b/sources/Structured Storage Explorer/img/page_white.png similarity index 100% rename from Structured Storage Explorer/img/page_white.png rename to sources/Structured Storage Explorer/img/page_white.png diff --git a/Structured Storage Explorer/img/storage.png b/sources/Structured Storage Explorer/img/storage.png similarity index 100% rename from Structured Storage Explorer/img/storage.png rename to sources/Structured Storage Explorer/img/storage.png diff --git a/Structured Storage Explorer/img/stream.png b/sources/Structured Storage Explorer/img/stream.png similarity index 100% rename from Structured Storage Explorer/img/stream.png rename to sources/Structured Storage Explorer/img/stream.png diff --git a/sources/Structured Storage Explorer/lib/Be.Windows.Forms.HexBox.dll b/sources/Structured Storage Explorer/lib/Be.Windows.Forms.HexBox.dll new file mode 100644 index 00000000..1d4afbf9 Binary files /dev/null and b/sources/Structured Storage Explorer/lib/Be.Windows.Forms.HexBox.dll differ diff --git a/sources/Test/OpenMcdf.Extensions.Test/CFSStreamExtensionsTest.cs b/sources/Test/OpenMcdf.Extensions.Test/CFSStreamExtensionsTest.cs new file mode 100644 index 00000000..5ab0c428 --- /dev/null +++ b/sources/Test/OpenMcdf.Extensions.Test/CFSStreamExtensionsTest.cs @@ -0,0 +1,89 @@ +using System; +using System.IO; +using Microsoft.VisualStudio.TestTools.UnitTesting; + +namespace OpenMcdf.Extensions.Test +{ + /// + /// Summary description for UnitTest1 + /// + [TestClass] + public class CFSStreamExtensionsTest + { + public CFSStreamExtensionsTest() + { + + } + + + private TestContext testContextInstance; + + /// + ///Gets or sets the test context which provides + ///information about and functionality for the current test run. + /// + public TestContext TestContext + { + get + { + return testContextInstance; + } + set + { + testContextInstance = value; + } + } + + #region Additional test attributes + // + // You can use the following additional attributes as you write your tests: + // + // Use ClassInitialize to run code before running the first test in the class + // [ClassInitialize()] + // public static void MyClassInitialize(TestContext testContext) { } + // + // Use ClassCleanup to run code after all tests in a class have run + // [ClassCleanup()] + // public static void MyClassCleanup() { } + // + // Use TestInitialize to run code before running each test + // [TestInitialize()] + // public void MyTestInitialize() { } + // + // Use TestCleanup to run code after each test has run + // [TestCleanup()] + // public void MyTestCleanup() { } + // + #endregion + + [TestMethod] + public void Test_AS_IOSTREAM_READ() + { + CompoundFile cf = new CompoundFile("MultipleStorage.cfs"); + + Stream s = cf.RootStorage.GetStorage("MyStorage").GetStream("MyStream").AsIOStream(); + BinaryReader br = new BinaryReader(s); + byte[] result = br.ReadBytes(32); + Assert.IsTrue(Helpers.CompareBuffer(Helpers.GetBuffer(32, 1), result)); + } + + [TestMethod] + public void Test_AS_IOSTREAM_WRITE() + { + const String cmp = "Hello World of BinaryWriter !"; + + CompoundFile cf = new CompoundFile(); + Stream s = cf.RootStorage.AddStream("ANewStream").AsIOStream(); + BinaryWriter bw = new BinaryWriter(s); + bw.Write(cmp); + cf.Save("$ACFFile.cfs"); + cf.Close(); + + cf = new CompoundFile("$ACFFile.cfs"); + BinaryReader br = new BinaryReader(cf.RootStorage.GetStream("ANewStream").AsIOStream()); + String st = br.ReadString(); + Assert.IsTrue(st == cmp); + cf.Close(); + } + } +} diff --git a/sources/Test/OpenMcdf.Extensions.Test/Helpers.cs b/sources/Test/OpenMcdf.Extensions.Test/Helpers.cs new file mode 100644 index 00000000..8910e56e --- /dev/null +++ b/sources/Test/OpenMcdf.Extensions.Test/Helpers.cs @@ -0,0 +1,49 @@ +using System; + +namespace OpenMcdf.Extensions.Test +{ + public static class Helpers + { + public static byte[] GetBuffer(int count) + { + Random r = new Random(); + byte[] b = new byte[count]; + r.NextBytes(b); + return b; + } + + public static byte[] GetBuffer(int count, byte c) + { + byte[] b = new byte[count]; + for (int i = 0; i < b.Length; i++) + { + b[i] = c; + } + + return b; + } + + public static bool CompareBuffer(byte[] b, byte[] p) + { + if (b == null && p == null) + throw new Exception("Null buffers"); + + if (b == null && p != null) + return false; + + if (b != null && p == null) + return false; + + if (b.Length != p.Length) + return false; + + for (int i = 0; i < b.Length; i++) + { + if (b[i] != p[i]) + return false; + } + + return true; + } + } +} diff --git a/sources/Test/OpenMcdf.Extensions.Test/OpenMcdf.Extensions.Test.csproj b/sources/Test/OpenMcdf.Extensions.Test/OpenMcdf.Extensions.Test.csproj new file mode 100644 index 00000000..b42be158 --- /dev/null +++ b/sources/Test/OpenMcdf.Extensions.Test/OpenMcdf.Extensions.Test.csproj @@ -0,0 +1,70 @@ + + + + Debug + AnyCPU + + + 2.0 + {B9CB103F-0AA3-486D-9C9C-672924B6169C} + Library + Properties + OpenMcdf.Extensions.Test + OpenMcdf.Extensions.Test + v4.0 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + + + + + + 3.5 + + + + + False + + + + + + + + + + {db748c1d-d71c-442b-832d-2e33be816cbb} + OpenMcdf.Extensions + + + {56e15d4a-8a37-4c7c-bb44-fd59aff220c1} + OpenMcdf + + + + + \ No newline at end of file diff --git a/sources/Test/OpenMcdf.Extensions.Test/Properties/AssemblyInfo.cs b/sources/Test/OpenMcdf.Extensions.Test/Properties/AssemblyInfo.cs new file mode 100644 index 00000000..57080022 --- /dev/null +++ b/sources/Test/OpenMcdf.Extensions.Test/Properties/AssemblyInfo.cs @@ -0,0 +1,35 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("OpenMcdfExtensionsTest")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("-")] +[assembly: AssemblyProduct("OpenMcdfExtensionsTest")] +[assembly: AssemblyCopyright("Copyright © Federico Blaseotto - 2015")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("80a1f6c3-2533-4e2b-9c49-46dd8e3a1e33")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/sources/Test/OpenMcdf.MemTest/OpenMcdf.MemTest.csproj b/sources/Test/OpenMcdf.MemTest/OpenMcdf.MemTest.csproj new file mode 100644 index 00000000..b5552a61 --- /dev/null +++ b/sources/Test/OpenMcdf.MemTest/OpenMcdf.MemTest.csproj @@ -0,0 +1,99 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {E2BAD82D-3040-462B-BAA2-6E608A9054F4} + Exe + Properties + OpenMcdfMemTest + OpenMcdfMemTest + v4.0 + 512 + + + 3.5 + + http://localhost/OpenMcdfMemTest/ + true + Web + true + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + true + false + true + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + AllRules.ruleset + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + AllRules.ruleset + + + + + + + + + + + + + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + {56e15d4a-8a37-4c7c-bb44-fd59aff220c1} + OpenMcdf + + + + + \ No newline at end of file diff --git a/sources/Test/OpenMcdf.MemTest/Program.cs b/sources/Test/OpenMcdf.MemTest/Program.cs new file mode 100644 index 00000000..f1a2c072 --- /dev/null +++ b/sources/Test/OpenMcdf.MemTest/Program.cs @@ -0,0 +1,325 @@ +using System; +using System.IO; +using System.Diagnostics; + +using OpenMcdf; +//This project is used for profiling memory and performances of OpenMCDF . + +namespace OpenMcdf.MemTest +{ + class Program + { + static void Main(string[] args) + { + + //TestMultipleStreamCommit(); + TestCode(); + //StressMemory(); + //DummyFile(); + //Console.WriteLine("CLOSED"); + //Console.ReadKey(); + } + + private static void TestCode() + { + const int N_FACTOR = 1000; + + byte[] bA = GetBuffer(20 * 1024 * N_FACTOR, 0x0A); + byte[] bB = GetBuffer(5 * 1024, 0x0B); + byte[] bC = GetBuffer(5 * 1024, 0x0C); + byte[] bD = GetBuffer(5 * 1024, 0x0D); + byte[] bE = GetBuffer(8 * 1024 * N_FACTOR + 1, 0x1A); + byte[] bF = GetBuffer(16 * 1024 * N_FACTOR, 0x1B); + byte[] bG = GetBuffer(14 * 1024 * N_FACTOR, 0x1C); + byte[] bH = GetBuffer(12 * 1024 * N_FACTOR, 0x1D); + byte[] bE2 = GetBuffer(8 * 1024 * N_FACTOR, 0x2A); + byte[] bMini = GetBuffer(1027, 0xEE); + + Stopwatch sw = new Stopwatch(); + sw.Start(); + + var cf = new CompoundFile(CFSVersion.Ver_3, CFSConfiguration.SectorRecycle); + cf.RootStorage.AddStream("A").SetData(bA); + cf.Save("OneStream.cfs"); + + cf.Close(); + + cf = new CompoundFile("OneStream.cfs", CFSUpdateMode.ReadOnly, CFSConfiguration.SectorRecycle); + + cf.RootStorage.AddStream("B").SetData(bB); + cf.RootStorage.AddStream("C").SetData(bC); + cf.RootStorage.AddStream("D").SetData(bD); + cf.RootStorage.AddStream("E").SetData(bE); + cf.RootStorage.AddStream("F").SetData(bF); + cf.RootStorage.AddStream("G").SetData(bG); + cf.RootStorage.AddStream("H").SetData(bH); + + cf.Save("8_Streams.cfs"); + + cf.Close(); + + File.Copy("8_Streams.cfs", "6_Streams.cfs", true); + + cf = new CompoundFile("6_Streams.cfs", CFSUpdateMode.Update, CFSConfiguration.SectorRecycle|CFSConfiguration.EraseFreeSectors); + cf.RootStorage.Delete("D"); + cf.RootStorage.Delete("G"); + cf.Commit(); + + cf.Close(); + + File.Copy("6_Streams.cfs", "6_Streams_Shrinked.cfs", true); + + cf = new CompoundFile("6_Streams_Shrinked.cfs", CFSUpdateMode.Update, CFSConfiguration.SectorRecycle); + cf.RootStorage.AddStream("ZZZ").SetData(bF); + cf.RootStorage.GetStream("E").Append(bE2); + cf.Commit(); + cf.Close(); + + cf = new CompoundFile("6_Streams_Shrinked.cfs", CFSUpdateMode.Update, CFSConfiguration.SectorRecycle); + cf.RootStorage.CLSID = new Guid("EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE"); + cf.Commit(); + cf.Close(); + + cf = new CompoundFile("6_Streams_Shrinked.cfs", CFSUpdateMode.Update, CFSConfiguration.SectorRecycle); + cf.RootStorage.AddStorage("MyStorage").AddStream("ANS").Append(bE); + cf.Commit(); + cf.Close(); + + cf = new CompoundFile("6_Streams_Shrinked.cfs", CFSUpdateMode.Update, CFSConfiguration.SectorRecycle); + cf.RootStorage.AddStorage("AnotherStorage").AddStream("ANS").Append(bE); + cf.RootStorage.Delete("MyStorage"); + cf.Commit(); + cf.Close(); + + CompoundFile.ShrinkCompoundFile("6_Streams_Shrinked.cfs"); + + cf = new CompoundFile("6_Streams_Shrinked.cfs", CFSUpdateMode.Update, CFSConfiguration.SectorRecycle); + cf.RootStorage.AddStorage("MiniStorage").AddStream("miniSt").Append(bMini); + cf.RootStorage.GetStorage("MiniStorage").AddStream("miniSt2").Append(bMini); + cf.Commit(); + cf.Close(); + + cf = new CompoundFile("6_Streams_Shrinked.cfs", CFSUpdateMode.Update, CFSConfiguration.SectorRecycle); + cf.RootStorage.GetStorage("MiniStorage").Delete("miniSt"); + + + cf.RootStorage.GetStorage("MiniStorage").GetStream("miniSt2").Append(bE); + cf.Commit(); + cf.Close(); + + cf = new CompoundFile("6_Streams_Shrinked.cfs", CFSUpdateMode.ReadOnly, CFSConfiguration.SectorRecycle); + + var myStream = cf.RootStorage.GetStream("C"); + var data = myStream.GetData(); + Console.WriteLine(data[0] + " : " + data[data.Length - 1]); + + myStream = cf.RootStorage.GetStream("B"); + data = myStream.GetData(); + Console.WriteLine(data[0] + " : " + data[data.Length - 1]); + + cf.Close(); + + sw.Stop(); + Console.WriteLine(sw.ElapsedMilliseconds); + + Console.ReadKey(); + } + + private static void StressMemory() + { + const int N_LOOP = 20; + const int MB_SIZE = 10; + + byte[] b = GetBuffer(1024 * 1024 * MB_SIZE); //2GB buffer + byte[] cmp = new byte[] { 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7 }; + + CompoundFile cf = new CompoundFile(CFSVersion.Ver_4, CFSConfiguration.Default); + CFStream st = cf.RootStorage.AddStream("MySuperLargeStream"); + cf.Save("LARGE.cfs"); + cf.Close(); + + //Console.WriteLine("Closed save"); + //Console.ReadKey(); + + cf = new CompoundFile("LARGE.cfs", CFSUpdateMode.Update, CFSConfiguration.Default); + CFStream cfst = cf.RootStorage.GetStream("MySuperLargeStream"); + + Stopwatch sw = new Stopwatch(); + sw.Start(); + for (int i = 0; i < N_LOOP; i++) + { + + cfst.Append(b); + cf.Commit(true); + + Console.WriteLine(" Updated " + i.ToString()); + //Console.ReadKey(); + } + + cfst.Append(cmp); + cf.Commit(true); + sw.Stop(); + + + cf.Close(); + + Console.WriteLine(sw.Elapsed.TotalMilliseconds); + sw.Reset(); + + //Console.WriteLine(sw.Elapsed.TotalMilliseconds); + + //Console.WriteLine("Closed Transacted"); + //Console.ReadKey(); + + cf = new CompoundFile("LARGE.cfs"); + int count = 8; + sw.Reset(); + sw.Start(); + byte[] data = new byte[count]; + count = cf.RootStorage.GetStream("MySuperLargeStream").Read(data, b.Length * (long)N_LOOP, count); + sw.Stop(); + Console.Write(count); + cf.Close(); + + Console.WriteLine("Closed Final " + sw.ElapsedMilliseconds); + Console.ReadKey(); + + } + + private static void DummyFile() + { + Console.WriteLine("Start"); + FileStream fs = new FileStream("myDummyFile", FileMode.Create); + fs.Close(); + + Stopwatch sw = new Stopwatch(); + + byte[] b = GetBuffer(1024 * 1024 * 50); //2GB buffer + + fs = new FileStream("myDummyFile", FileMode.Open); + sw.Start(); + for (int i = 0; i < 42; i++) + { + + fs.Seek(b.Length * i, SeekOrigin.Begin); + fs.Write(b, 0, b.Length); + + } + + fs.Close(); + sw.Stop(); + Console.WriteLine("Stop - " + sw.ElapsedMilliseconds); + sw.Reset(); + + Console.ReadKey(); + } + + private static void AddNodes(String depth, CFStorage cfs) + { + + Action va = delegate(CFItem target) + { + + String temp = target.Name + (target is CFStorage ? "" : " (" + target.Size + " bytes )"); + + //Stream + + Console.WriteLine(depth + temp); + + if (target is CFStorage) + { //Storage + + String newDepth = depth + " "; + + //Recursion into the storage + AddNodes(newDepth, (CFStorage)target); + + } + }; + + //Visit NON-recursively (first level only) + cfs.VisitEntries(va, false); + } + + public static void TestMultipleStreamCommit() + { + String srcFilename = Directory.GetCurrentDirectory() + @"\testfile\report.xls"; + String dstFilename = Directory.GetCurrentDirectory() + @"\testfile\reportOverwriteMultiple.xls"; + //Console.WriteLine(Directory.GetCurrentDirectory()); + //Console.ReadKey(); + File.Copy(srcFilename, dstFilename, true); + + CompoundFile cf = new CompoundFile(dstFilename, CFSUpdateMode.Update, CFSConfiguration.SectorRecycle); + + Random r = new Random(); + + DateTime start = DateTime.Now; + + for (int i = 0; i < 1000; i++) + { + byte[] buffer = GetBuffer(r.Next(100, 3500), 0x0A); + + if (i > 0) + { + if (r.Next(0, 100) > 50) + { + cf.RootStorage.Delete("MyNewStream" + (i - 1).ToString()); + } + } + + CFStream addedStream = cf.RootStorage.AddStream("MyNewStream" + i.ToString()); + + addedStream.SetData(buffer); + + // Random commit, not on single addition + if (r.Next(0, 100) > 50) + cf.Commit(); + } + + cf.Close(); + + TimeSpan sp = (DateTime.Now - start); + Console.WriteLine(sp.TotalMilliseconds); + + } + + private static byte[] GetBuffer(int count) + { + Random r = new Random(); + byte[] b = new byte[count]; + r.NextBytes(b); + return b; + } + + private static byte[] GetBuffer(int count, byte c) + { + byte[] b = new byte[count]; + for (int i = 0; i < b.Length; i++) + { + b[i] = c; + } + + return b; + } + + private static bool CompareBuffer(byte[] b, byte[] p) + { + if (b == null && p == null) + throw new Exception("Null buffers"); + + if (b == null && p != null) return false; + if (b != null && p == null) return false; + + if (b.Length != p.Length) + return false; + + for (int i = 0; i < b.Length; i++) + { + if (b[i] != p[i]) + return false; + } + + return true; + } + } +} diff --git a/Memory Test/Properties/AssemblyInfo.cs b/sources/Test/OpenMcdf.MemTest/Properties/AssemblyInfo.cs similarity index 97% rename from Memory Test/Properties/AssemblyInfo.cs rename to sources/Test/OpenMcdf.MemTest/Properties/AssemblyInfo.cs index 7b97f2fa..f90c663e 100644 --- a/Memory Test/Properties/AssemblyInfo.cs +++ b/sources/Test/OpenMcdf.MemTest/Properties/AssemblyInfo.cs @@ -1,35 +1,35 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("OpenMcdfMemTest")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("Federico Blaseotto")] -[assembly: AssemblyProduct("OpenMcdfMemTest")] -[assembly: AssemblyCopyright("Copyright © 2010-2011, Federico Blaseotto")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("6be95c7d-5e29-4e84-b1d0-ac5a0de6bfe2")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.5.0.*")] +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("OpenMcdfMemTest")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Federico Blaseotto")] +[assembly: AssemblyProduct("OpenMcdfMemTest")] +[assembly: AssemblyCopyright("Copyright © 2010-2011, Federico Blaseotto")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("6be95c7d-5e29-4e84-b1d0-ac5a0de6bfe2")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.5.0.*")] diff --git a/sources/Test/OpenMcdf.MemTest/app.config b/sources/Test/OpenMcdf.MemTest/app.config new file mode 100644 index 00000000..e3656033 --- /dev/null +++ b/sources/Test/OpenMcdf.MemTest/app.config @@ -0,0 +1,3 @@ + + + diff --git a/Memory Test/testfile/report.xls b/sources/Test/OpenMcdf.MemTest/testfile/report.xls similarity index 100% rename from Memory Test/testfile/report.xls rename to sources/Test/OpenMcdf.MemTest/testfile/report.xls diff --git a/Memory Test/testfile/reportOverwriteMultiple.xls b/sources/Test/OpenMcdf.MemTest/testfile/reportOverwriteMultiple.xls similarity index 100% rename from Memory Test/testfile/reportOverwriteMultiple.xls rename to sources/Test/OpenMcdf.MemTest/testfile/reportOverwriteMultiple.xls diff --git a/Performance Test/Helpers.cs b/sources/Test/OpenMcdf.PerfTest/Helpers.cs similarity index 93% rename from Performance Test/Helpers.cs rename to sources/Test/OpenMcdf.PerfTest/Helpers.cs index 01bfd842..84bbed00 100644 --- a/Performance Test/Helpers.cs +++ b/sources/Test/OpenMcdf.PerfTest/Helpers.cs @@ -1,51 +1,51 @@ -using System; -using System.Collections.Generic; -using System.Text; - -namespace OpenMcdfPerfTest -{ - public static class Helpers - { - public static byte[] GetBuffer(int count) - { - Random r = new Random(); - byte[] b = new byte[count]; - r.NextBytes(b); - return b; - } - - public static byte[] GetBuffer(int count, byte c) - { - byte[] b = new byte[count]; - for (int i = 0; i < b.Length; i++) - { - b[i] = c; - } - - return b; - } - - public static bool CompareBuffer(byte[] b, byte[] p) - { - if (b == null && p == null) - throw new Exception("Null buffers"); - - if (b == null && p != null) - return false; - - if (b != null && p == null) - return false; - - if (b.Length != p.Length) - return false; - - for (int i = 0; i < b.Length; i++) - { - if (b[i] != p[i]) - return false; - } - - return true; - } - } -} +using System; +using System.Collections.Generic; +using System.Text; + +namespace OpenMcdf.PerfTest +{ + public static class Helpers + { + public static byte[] GetBuffer(int count) + { + Random r = new Random(); + byte[] b = new byte[count]; + r.NextBytes(b); + return b; + } + + public static byte[] GetBuffer(int count, byte c) + { + byte[] b = new byte[count]; + for (int i = 0; i < b.Length; i++) + { + b[i] = c; + } + + return b; + } + + public static bool CompareBuffer(byte[] b, byte[] p) + { + if (b == null && p == null) + throw new Exception("Null buffers"); + + if (b == null && p != null) + return false; + + if (b != null && p == null) + return false; + + if (b.Length != p.Length) + return false; + + for (int i = 0; i < b.Length; i++) + { + if (b[i] != p[i]) + return false; + } + + return true; + } + } +} diff --git a/sources/Test/OpenMcdf.PerfTest/OpenMcdf.PerfTest.csproj b/sources/Test/OpenMcdf.PerfTest/OpenMcdf.PerfTest.csproj new file mode 100644 index 00000000..c6f51fd4 --- /dev/null +++ b/sources/Test/OpenMcdf.PerfTest/OpenMcdf.PerfTest.csproj @@ -0,0 +1,99 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {7077508F-B313-4DF6-8855-4764911BE161} + Exe + Properties + OpenMcdf.PerfTest + OpenMcdf.PerfTest + v4.0 + 512 + + + 3.5 + + http://localhost/OpenMcdfPerfTest/ + true + Web + true + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + true + false + true + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + AllRules.ruleset + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + AllRules.ruleset + + + + + + + + + + + + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + true + + + False + Windows Installer 3.1 + true + + + + + + + + {56e15d4a-8a37-4c7c-bb44-fd59aff220c1} + OpenMcdf + + + + + \ No newline at end of file diff --git a/sources/Test/OpenMcdf.PerfTest/Program.cs b/sources/Test/OpenMcdf.PerfTest/Program.cs new file mode 100644 index 00000000..54586b68 --- /dev/null +++ b/sources/Test/OpenMcdf.PerfTest/Program.cs @@ -0,0 +1,41 @@ +using System; +using System.Collections.Generic; +using System.Text; +using OpenMcdf; +using System.IO; + +namespace OpenMcdf.PerfTest +{ + class Program + { + static int MAX_STREAM_COUNT = 5000; + static String fileName = "PerfLoad.cfs"; + + static void Main(string[] args) + { + File.Delete(fileName); + if (!File.Exists(fileName)) + { + CreateFile(fileName); + } + + CompoundFile cf = new CompoundFile(fileName); + DateTime dt = DateTime.Now; + CFStream s = cf.RootStorage.GetStream("Test1"); + TimeSpan ts = DateTime.Now.Subtract(dt); + Console.WriteLine(ts.TotalMilliseconds.ToString()); + Console.Read(); + } + + private static void CreateFile(String fn) + { + CompoundFile cf = new CompoundFile(); + for (int i = 0; i < MAX_STREAM_COUNT; i++) + { + cf.RootStorage.AddStream("Test" + i.ToString()).SetData(Helpers.GetBuffer(300)); + } + cf.Save(fileName); + cf.Close(); + } + } +} diff --git a/Performance Test/Properties/AssemblyInfo.cs b/sources/Test/OpenMcdf.PerfTest/Properties/AssemblyInfo.cs similarity index 97% rename from Performance Test/Properties/AssemblyInfo.cs rename to sources/Test/OpenMcdf.PerfTest/Properties/AssemblyInfo.cs index fd5bf289..de9d7003 100644 --- a/Performance Test/Properties/AssemblyInfo.cs +++ b/sources/Test/OpenMcdf.PerfTest/Properties/AssemblyInfo.cs @@ -1,36 +1,36 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("OpenMcdfPerfTest")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("-")] -[assembly: AssemblyProduct("OpenMcdfPerfTest")] -[assembly: AssemblyCopyright("Copyright © - 2012")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("c3a84d86-d1be-4fdc-8294-169e1f6747d8")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Build and Revision Numbers -// by using the '*' as shown below: -// [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("1.0.0.0")] -[assembly: AssemblyFileVersion("1.0.0.0")] +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("OpenMcdfPerfTest")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("-")] +[assembly: AssemblyProduct("OpenMcdfPerfTest")] +[assembly: AssemblyCopyright("Copyright © - 2012")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("c3a84d86-d1be-4fdc-8294-169e1f6747d8")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/sources/Test/OpenMcdf.PerfTest/app.config b/sources/Test/OpenMcdf.PerfTest/app.config new file mode 100644 index 00000000..e3656033 --- /dev/null +++ b/sources/Test/OpenMcdf.PerfTest/app.config @@ -0,0 +1,3 @@ + + + diff --git a/Unit Test/AuthoringTests.txt b/sources/Test/OpenMcdf.Test/AuthoringTests.txt similarity index 97% rename from Unit Test/AuthoringTests.txt rename to sources/Test/OpenMcdf.Test/AuthoringTests.txt index 64bab948..c94b3cfd 100644 --- a/Unit Test/AuthoringTests.txt +++ b/sources/Test/OpenMcdf.Test/AuthoringTests.txt @@ -1,136 +1,136 @@ -========================================================================== - Visual Studio Team System: Overview of Authoring and Running Tests -========================================================================== - -This overview describes the features for authoring and running tests in -Visual Studio Team System and Visual Studio Team Edition for Software Testers. - -Opening Tests -------------- -To open a test, open a test project or a test metadata file (a file with -extension .vsmdi) that contains the definition of the test. You can find -test projects and metadata files in Solution Explorer. - -Viewing Tests -------------- -To see which tests are available to you, open the Test View window. Or, -if you have installed Team Edition for Software Testers, you can also open -the Test List Editor window to view tests. - -To open the Test View window, click the Test menu, point to Windows, and -then click Test View. To open the Test List Editor window (if you have -installed Team Edition for Software Testers), click Test, point to Windows, -and then click Test List Editor. - -Running Tests -------------- -You can run tests from the Test View window and the Test List Editor window. -See Viewing Tests to learn how to open these windows. To run one or more -tests displayed in the Test View window, first select the tests in that -window; to select multiple tests, hold either the Shift or CTRL key while -clicking tests. Then click the Run Tests button in the Test View window -toolbar. - -If you have installed Visual Studio Team Edition for Software Testers, you can -also use the Test List Editor window to run tests. To run tests in Test List Editor, -select the check box next to each test that you want to run. Then click the -Run Tests button in the Test List Editor window toolbar. - -Viewing Test Results --------------------- -When you run a test or a series of tests, the results of the test run will be -shown in the Test Results window. Each individual test in the run is shown on -a separate line so that you can see its status. The window contains an -embedded status bar in the top half of the window that provides you with -summary details of the complete test run. - -To see more detailed results for a particular test result, double-click it in -the Test Results window. This opens a window that provides more information -about the particular test result, such as any specific error messages returned -by the test. - -Changing the way that tests are run ------------------------------------ -Each time you run one or more tests, a collection of settings is used to -determine how those tests are run. These settings are contained in a “test -run configuration” file. - -Here is a partial list of the changes you can make with a test run -configuration file: - - - Change the naming scheme for each test run. - - Change the test controller that the tests are run on so that you can run - tests remotely. - - Gather code coverage data for the code being tested so that you can see - which lines of code are covered by your tests. - - Enable and disable test deployment. - - Specify additional files to deploy before tests are run. - - Select a different host, ASP.NET, for running ASP.NET unit tests. - - Select a different host, the smart device test host, for running smart device unit tests. - - Set various properties for the test agents that run your tests. - - Run custom scripts at the start and end of each test run so that you can - set up the test environment exactly as required each time tests are run. - - Set time limits for tests and test runs. - - Set the browser mix and the number of times to repeat Web tests in the - test run. - -By default, a test run configuration file is created whenever you create a -new test project. You make changes to this file by double-clicking it in -Solution Explorer and then changing its settings. (Test run configuration -files have the extension .testrunconfig.) - -A solution can contain multiple test run configuration files. Only one of -those files, known as the “Active” test run configuration file, is used to -determine the settings that are currently used for test runs. You select -the active test run configuration by clicking Select Active Test Run -Configuration on the Test menu. - -------------------------------------------------------------------------------- - -Test Types ----------- -Using Visual Studio Team Edition for Software Testers, you can create a number -of different test types: - -Unit test: Use a unit test to create a programmatic test in C++, Visual C# or -Visual Basic that exercises source code. A unit test calls the methods of a -class, passing suitable parameters, and verifies that the returned value is -what you expect. -There are three specialized variants of unit tests: - - Data-driven unit tests are created when you configure a unit test to be - called repeatedly for each row of a data source. The data from each row - is used by the unit test as input data. - - ASP.NET unit tests are unit tests that exercise code in an ASP.NET Web - application. - - Smart device unit tests are unit tests that are deployed to a smart device - or emulator and then executed by the smart device test host. - -Web Test: Web tests consist of an ordered series of HTTP requests that you -record in a browser session using Microsoft Internet Explorer. You can have -the test report specific details about the pages or sites it requests, such -as whether a particular page contains a specified string. - -Load Test: You use a load test to encapsulate non-manual tests, such as -unit, Web, and generic tests, and then run them simultaneously by using -virtual users. Running these tests under load generates test results, -including performance and other counters, in tables and in graphs. - -Generic test: A generic test is an existing program wrapped to function as a -test in Visual Studio. The following are examples of tests or programs that -you can turn into generic tests: - - An existing test that uses process exit codes to communicate whether the - test passed or failed. 0 indicates passing and any other value indicates - a failure. - - A general program to obtain specific functionality during a test scenario. - - A test or program that uses a special XML file (called a “summary results - file”), to communicate detailed results. - -Manual test: The manual test type is used when the test tasks are to be -completed by a test engineer as opposed to an automated script. - -Ordered test: Use an ordered test to execute a set of tests in an order you -specify. - -------------------------------------------------------------------------------- - - +========================================================================== + Visual Studio Team System: Overview of Authoring and Running Tests +========================================================================== + +This overview describes the features for authoring and running tests in +Visual Studio Team System and Visual Studio Team Edition for Software Testers. + +Opening Tests +------------- +To open a test, open a test project or a test metadata file (a file with +extension .vsmdi) that contains the definition of the test. You can find +test projects and metadata files in Solution Explorer. + +Viewing Tests +------------- +To see which tests are available to you, open the Test View window. Or, +if you have installed Team Edition for Software Testers, you can also open +the Test List Editor window to view tests. + +To open the Test View window, click the Test menu, point to Windows, and +then click Test View. To open the Test List Editor window (if you have +installed Team Edition for Software Testers), click Test, point to Windows, +and then click Test List Editor. + +Running Tests +------------- +You can run tests from the Test View window and the Test List Editor window. +See Viewing Tests to learn how to open these windows. To run one or more +tests displayed in the Test View window, first select the tests in that +window; to select multiple tests, hold either the Shift or CTRL key while +clicking tests. Then click the Run Tests button in the Test View window +toolbar. + +If you have installed Visual Studio Team Edition for Software Testers, you can +also use the Test List Editor window to run tests. To run tests in Test List Editor, +select the check box next to each test that you want to run. Then click the +Run Tests button in the Test List Editor window toolbar. + +Viewing Test Results +-------------------- +When you run a test or a series of tests, the results of the test run will be +shown in the Test Results window. Each individual test in the run is shown on +a separate line so that you can see its status. The window contains an +embedded status bar in the top half of the window that provides you with +summary details of the complete test run. + +To see more detailed results for a particular test result, double-click it in +the Test Results window. This opens a window that provides more information +about the particular test result, such as any specific error messages returned +by the test. + +Changing the way that tests are run +----------------------------------- +Each time you run one or more tests, a collection of settings is used to +determine how those tests are run. These settings are contained in a “test +run configuration” file. + +Here is a partial list of the changes you can make with a test run +configuration file: + + - Change the naming scheme for each test run. + - Change the test controller that the tests are run on so that you can run + tests remotely. + - Gather code coverage data for the code being tested so that you can see + which lines of code are covered by your tests. + - Enable and disable test deployment. + - Specify additional files to deploy before tests are run. + - Select a different host, ASP.NET, for running ASP.NET unit tests. + - Select a different host, the smart device test host, for running smart device unit tests. + - Set various properties for the test agents that run your tests. + - Run custom scripts at the start and end of each test run so that you can + set up the test environment exactly as required each time tests are run. + - Set time limits for tests and test runs. + - Set the browser mix and the number of times to repeat Web tests in the + test run. + +By default, a test run configuration file is created whenever you create a +new test project. You make changes to this file by double-clicking it in +Solution Explorer and then changing its settings. (Test run configuration +files have the extension .testrunconfig.) + +A solution can contain multiple test run configuration files. Only one of +those files, known as the “Active” test run configuration file, is used to +determine the settings that are currently used for test runs. You select +the active test run configuration by clicking Select Active Test Run +Configuration on the Test menu. + +------------------------------------------------------------------------------- + +Test Types +---------- +Using Visual Studio Team Edition for Software Testers, you can create a number +of different test types: + +Unit test: Use a unit test to create a programmatic test in C++, Visual C# or +Visual Basic that exercises source code. A unit test calls the methods of a +class, passing suitable parameters, and verifies that the returned value is +what you expect. +There are three specialized variants of unit tests: + - Data-driven unit tests are created when you configure a unit test to be + called repeatedly for each row of a data source. The data from each row + is used by the unit test as input data. + - ASP.NET unit tests are unit tests that exercise code in an ASP.NET Web + application. + - Smart device unit tests are unit tests that are deployed to a smart device + or emulator and then executed by the smart device test host. + +Web Test: Web tests consist of an ordered series of HTTP requests that you +record in a browser session using Microsoft Internet Explorer. You can have +the test report specific details about the pages or sites it requests, such +as whether a particular page contains a specified string. + +Load Test: You use a load test to encapsulate non-manual tests, such as +unit, Web, and generic tests, and then run them simultaneously by using +virtual users. Running these tests under load generates test results, +including performance and other counters, in tables and in graphs. + +Generic test: A generic test is an existing program wrapped to function as a +test in Visual Studio. The following are examples of tests or programs that +you can turn into generic tests: + - An existing test that uses process exit codes to communicate whether the + test passed or failed. 0 indicates passing and any other value indicates + a failure. + - A general program to obtain specific functionality during a test scenario. + - A test or program that uses a special XML file (called a “summary results + file”), to communicate detailed results. + +Manual test: The manual test type is used when the test tasks are to be +completed by a test engineer as opposed to an automated script. + +Ordered test: Use an ordered test to execute a set of tests in an order you +specify. + +------------------------------------------------------------------------------- + + diff --git a/sources/Test/OpenMcdf.Test/CFSStreamTest.cs b/sources/Test/OpenMcdf.Test/CFSStreamTest.cs new file mode 100644 index 00000000..5e95bf8c --- /dev/null +++ b/sources/Test/OpenMcdf.Test/CFSStreamTest.cs @@ -0,0 +1,1086 @@ +using System; +using System.Text; +using System.Collections.Generic; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using OpenMcdf; +using System.IO; + +namespace OpenMcdf.Test +{ + /// + /// Summary description for UnitTest1 + /// + [TestClass] + public class CFSStreamTest + { + + //const String TestContext.TestDir = "C:\\TestOutputFiles\\"; + + public CFSStreamTest() + { + + } + + private TestContext testContextInstance; + + /// + ///Gets or sets the test context which provides + ///information about and functionality for the current test run. + /// + public TestContext TestContext + { + get + { + return testContextInstance; + } + set + { + testContextInstance = value; + } + } + + #region Additional test attributes + // + // You can use the following additional attributes as you write your tests: + // + // Use ClassInitialize to run code before running the first test in the class + // [ClassInitialize()] + // public static void MyClassInitialize(TestContext testContext) { } + // + // Use ClassCleanup to run code after all tests in a class have run + // [ClassCleanup()] + // public static void MyClassCleanup() { } + // + // Use TestInitialize to run code before running each test + // [TestInitialize()] + // public void MyTestInitialize() { } + // + // Use TestCleanup to run code after each test has run + // [TestCleanup()] + // public void MyTestCleanup() { } + // + #endregion + + [TestMethod] + public void Test_READ_STREAM() + { + String filename = "report.xls"; + + CompoundFile cf = new CompoundFile(filename); + CFStream foundStream = cf.RootStorage.GetStream("Workbook"); + + byte[] temp = foundStream.GetData(); + + Assert.IsNotNull(temp); + Assert.IsTrue(temp.Length > 0); + + cf.Close(); + } + + [TestMethod] + public void Test_WRITE_STREAM() + { + const int BUFFER_LENGTH = 10000; + + byte[] b = Helpers.GetBuffer(BUFFER_LENGTH); + + CompoundFile cf = new CompoundFile(); + CFStream myStream = cf.RootStorage.AddStream("MyStream"); + + Assert.IsNotNull(myStream); + Assert.IsTrue(myStream.Size == 0); + + myStream.SetData(b); + + Assert.IsTrue(myStream.Size == BUFFER_LENGTH, "Stream size differs from buffer size"); + + cf.Close(); + } + + [TestMethod] + public void Test_WRITE_MINI_STREAM() + { + const int BUFFER_LENGTH = 1023; // < 4096 + + byte[] b = Helpers.GetBuffer(BUFFER_LENGTH); + + CompoundFile cf = new CompoundFile(); + CFStream myStream = cf.RootStorage.AddStream("MyMiniStream"); + + Assert.IsNotNull(myStream); + Assert.IsTrue(myStream.Size == 0); + + myStream.SetData(b); + + Assert.IsTrue(myStream.Size == BUFFER_LENGTH, "Mini Stream size differs from buffer size"); + + cf.Close(); + } + + [TestMethod] + public void Test_ZERO_LENGTH_WRITE_STREAM() + { + byte[] b = new byte[0]; + + CompoundFile cf = new CompoundFile(); + CFStream myStream = cf.RootStorage.AddStream("MyStream"); + + Assert.IsNotNull(myStream); + + try + { + myStream.SetData(b); cf.Save("ZERO_LENGTH_STREAM.cfs"); + } + catch + { + Assert.Fail("Failed setting zero length stream"); + } + finally + { + if (cf != null) + cf.Close(); + } + + if (File.Exists("ZERO_LENGTH_STREAM.cfs")) + File.Delete("ZERO_LENGTH_STREAM.cfs"); + + } + + [TestMethod] + public void Test_ZERO_LENGTH_RE_WRITE_STREAM() + { + byte[] b = new byte[0]; + + CompoundFile cf = new CompoundFile(); + CFStream myStream = cf.RootStorage.AddStream("MyStream"); + + Assert.IsNotNull(myStream); + + try + { + myStream.SetData(b); + } + catch + { + Assert.Fail("Failed setting zero length stream"); + } + + cf.Save("ZERO_LENGTH_STREAM_RE.cfs"); + cf.Close(); + + CompoundFile cfo = new CompoundFile("ZERO_LENGTH_STREAM_RE.cfs"); + CFStream oStream = cfo.RootStorage.GetStream("MyStream"); + + Assert.IsNotNull(oStream); + Assert.IsTrue(oStream.Size == 0); + + try + { + oStream.SetData(Helpers.GetBuffer(30)); + cfo.Save("ZERO_LENGTH_STREAM_RE2.cfs"); + } + catch + { + Assert.Fail("Failed re-writing zero length stream"); + } + finally + { + cfo.Close(); + } + + if (File.Exists("ZERO_LENGTH_STREAM_RE.cfs")) + File.Delete("ZERO_LENGTH_STREAM_RE.cfs"); + + if (File.Exists("ZERO_LENGTH_STREAM_RE2.cfs")) + File.Delete("ZERO_LENGTH_STREAM_RE2.cfs"); + + } + + + [TestMethod] + public void Test_WRITE_STREAM_WITH_DIFAT() + { + //const int SIZE = 15388609; //Incredible condition of 'resonance' between FAT and DIFAT sec number + const int SIZE = 15345665; // 64 -> 65 NOT working (in the past ;-) ) + byte[] b = Helpers.GetBuffer(SIZE, 0); + + CompoundFile cf = new CompoundFile(); + CFStream myStream = cf.RootStorage.AddStream("MyStream"); + Assert.IsNotNull(myStream); + myStream.SetData(b); + + cf.Save("WRITE_STREAM_WITH_DIFAT.cfs"); + cf.Close(); + + + CompoundFile cf2 = new CompoundFile("WRITE_STREAM_WITH_DIFAT.cfs"); + CFStream st = cf2.RootStorage.GetStream("MyStream"); + + Assert.IsNotNull(cf2); + Assert.IsTrue(st.Size == SIZE); + + Assert.IsTrue(Helpers.CompareBuffer(b, st.GetData())); + + cf2.Close(); + + if (File.Exists("WRITE_STREAM_WITH_DIFAT.cfs")) + File.Delete("WRITE_STREAM_WITH_DIFAT.cfs"); + + } + + + [TestMethod] + public void Test_WRITE_MINISTREAM_READ_REWRITE_STREAM() + { + const int BIGGER_SIZE = 350; + //const int SMALLER_SIZE = 290; + const int MEGA_SIZE = 18000000; + + byte[] ba1 = Helpers.GetBuffer(BIGGER_SIZE, 1); + byte[] ba2 = Helpers.GetBuffer(BIGGER_SIZE, 2); + byte[] ba3 = Helpers.GetBuffer(BIGGER_SIZE, 3); + byte[] ba4 = Helpers.GetBuffer(BIGGER_SIZE, 4); + byte[] ba5 = Helpers.GetBuffer(BIGGER_SIZE, 5); + + //WRITE 5 (mini)streams in a compound file -- + + CompoundFile cfa = new CompoundFile(); + + CFStream myStream = cfa.RootStorage.AddStream("MyFirstStream"); + Assert.IsNotNull(myStream); + + myStream.SetData(ba1); + Assert.IsTrue(myStream.Size == BIGGER_SIZE); + + CFStream myStream2 = cfa.RootStorage.AddStream("MySecondStream"); + Assert.IsNotNull(myStream2); + + myStream2.SetData(ba2); + Assert.IsTrue(myStream2.Size == BIGGER_SIZE); + + CFStream myStream3 = cfa.RootStorage.AddStream("MyThirdStream"); + Assert.IsNotNull(myStream3); + + myStream3.SetData(ba3); + Assert.IsTrue(myStream3.Size == BIGGER_SIZE); + + CFStream myStream4 = cfa.RootStorage.AddStream("MyFourthStream"); + Assert.IsNotNull(myStream4); + + myStream4.SetData(ba4); + Assert.IsTrue(myStream4.Size == BIGGER_SIZE); + + CFStream myStream5 = cfa.RootStorage.AddStream("MyFifthStream"); + Assert.IsNotNull(myStream5); + + myStream5.SetData(ba5); + Assert.IsTrue(myStream5.Size == BIGGER_SIZE); + + cfa.Save("WRITE_MINISTREAM_READ_REWRITE_STREAM.cfs"); + + cfa.Close(); + + // Now get the second stream and rewrite it smaller + byte[] bb = Helpers.GetBuffer(MEGA_SIZE); + CompoundFile cfb = new CompoundFile("WRITE_MINISTREAM_READ_REWRITE_STREAM.cfs"); + CFStream myStreamB = cfb.RootStorage.GetStream("MySecondStream"); + Assert.IsNotNull(myStreamB); + myStreamB.SetData(bb); + Assert.IsTrue(myStreamB.Size == MEGA_SIZE); + + byte[] bufferB = myStreamB.GetData(); + cfb.Save("WRITE_MINISTREAM_READ_REWRITE_STREAM_2ND.cfs"); + cfb.Close(); + + CompoundFile cfc = new CompoundFile("WRITE_MINISTREAM_READ_REWRITE_STREAM_2ND.cfs"); + CFStream myStreamC = cfc.RootStorage.GetStream("MySecondStream"); + Assert.IsTrue(myStreamC.Size == MEGA_SIZE, "DATA SIZE FAILED"); + + byte[] bufferC = myStreamC.GetData(); + Assert.IsTrue(Helpers.CompareBuffer(bufferB, bufferC), "DATA INTEGRITY FAILED"); + + cfc.Close(); + + if (File.Exists("WRITE_MINISTREAM_READ_REWRITE_STREAM.cfs")) + File.Delete("WRITE_MINISTREAM_READ_REWRITE_STREAM.cfs"); + + + if (File.Exists("WRITE_MINISTREAM_READ_REWRITE_STREAM_2ND.cfs")) + File.Delete("WRITE_MINISTREAM_READ_REWRITE_STREAM_2ND.cfs"); + + } + + [TestMethod] + public void Test_RE_WRITE_SMALLER_STREAM() + { + const int BUFFER_LENGTH = 8000; + + String filename = "report.xls"; + + byte[] b = Helpers.GetBuffer(BUFFER_LENGTH); + + CompoundFile cf = new CompoundFile(filename); + CFStream foundStream = cf.RootStorage.GetStream("Workbook"); + foundStream.SetData(b); + cf.Save("reportRW_SMALL.xls"); + cf.Close(); + + cf = new CompoundFile("reportRW_SMALL.xls"); + byte[] c = cf.RootStorage.GetStream("Workbook").GetData(); + Assert.IsTrue(c.Length == BUFFER_LENGTH); + cf.Close(); + + if (File.Exists("reportRW_SMALL.xls")) + File.Delete("reportRW_SMALL.xls"); + + } + + [TestMethod] + public void Test_RE_WRITE_SMALLER_MINI_STREAM() + { + String filename = "report.xls"; + + CompoundFile cf = new CompoundFile(filename); + CFStream foundStream = cf.RootStorage.GetStream("\x05SummaryInformation"); + int TEST_LENGTH = (int)foundStream.Size - 20; + byte[] b = Helpers.GetBuffer(TEST_LENGTH); + foundStream.SetData(b); + + cf.Save("RE_WRITE_SMALLER_MINI_STREAM.xls"); + cf.Close(); + + cf = new CompoundFile("RE_WRITE_SMALLER_MINI_STREAM.xls"); + byte[] c = cf.RootStorage.GetStream("\x05SummaryInformation").GetData(); + Assert.IsTrue(c.Length == TEST_LENGTH); + Assert.IsTrue(Helpers.CompareBuffer(c, b)); + cf.Close(); + + if (File.Exists("RE_WRITE_SMALLER_MINI_STREAM.xls")) + File.Delete("RE_WRITE_SMALLER_MINI_STREAM.xls"); + + } + + [TestMethod] + public void Test_TRANSACTED_ADD_STREAM_TO_EXISTING_FILE() + { + String srcFilename = "report.xls"; + String dstFilename = "reportOverwrite.xls"; + + File.Copy(srcFilename, dstFilename, true); + + CompoundFile cf = new CompoundFile(dstFilename, CFSUpdateMode.Update, CFSConfiguration.Default); + + byte[] buffer = Helpers.GetBuffer(5000); + + CFStream addedStream = cf.RootStorage.AddStream("MyNewStream"); + addedStream.SetData(buffer); + + cf.Commit(); + cf.Close(); + + + if (File.Exists("reportOverwrite.xls")) + File.Delete("reportOverwrite.xls"); + + } + + [TestMethod] + public void Test_TRANSACTED_ADD_REMOVE_MULTIPLE_STREAM_TO_EXISTING_FILE() + { + String srcFilename = "report.xls"; + String dstFilename = "reportOverwriteMultiple.xls"; + + File.Copy(srcFilename, dstFilename, true); + + CompoundFile cf = new CompoundFile(dstFilename, CFSUpdateMode.ReadOnly, CFSConfiguration.SectorRecycle); + + //CompoundFile cf = new CompoundFile(); + + Random r = new Random(); + + for (int i = 0; i < 254; i++) + { + //byte[] buffer = Helpers.GetBuffer(r.Next(100, 3500), (byte)i); + byte[] buffer = Helpers.GetBuffer(1995, 1); + + //if (i > 0) + //{ + // if (r.Next(0, 100) > 50) + // { + // cf.RootStorage.Delete("MyNewStream" + (i - 1).ToString()); + // } + //} + + CFStream addedStream = cf.RootStorage.AddStream("MyNewStream" + i.ToString()); + Assert.IsNotNull(addedStream, "Stream not found"); + addedStream.SetData(buffer); + + Assert.IsTrue(Helpers.CompareBuffer(addedStream.GetData(), buffer), "Data buffer corrupted"); + + // Random commit, not on single addition + //if (r.Next(0, 100) > 50) + // cf.UpdateFile(); + + } + + cf.Save(dstFilename + "PP"); + cf.Close(); + + if (File.Exists("reportOverwriteMultiple.xls")) + File.Delete("reportOverwriteMultiple.xls"); + + if (File.Exists("reportOverwriteMultiple.xlsPP")) + File.Delete("reportOverwriteMultiple.xlsPP"); + + + } + + [TestMethod] + public void Test_TRANSACTED_ADD_MINISTREAM_TO_EXISTING_FILE() + { + String srcFilename = "report.xls"; + String dstFilename = "reportOverwriteMultiple.xls"; + + File.Copy(srcFilename, dstFilename, true); + + CompoundFile cf = new CompoundFile(dstFilename, CFSUpdateMode.Update, CFSConfiguration.SectorRecycle | CFSConfiguration.EraseFreeSectors); + + Random r = new Random(); + + byte[] buffer = Helpers.GetBuffer(31, 0x0A); + + cf.RootStorage.AddStream("MyStream").SetData(buffer); + cf.Commit(); + cf.Close(); + FileStream larger = new FileStream(dstFilename, FileMode.Open); + FileStream smaller = new FileStream(srcFilename, FileMode.Open); + + // Equal condition if minisector can be "allocated" + // within the existing standard sector border + Assert.IsTrue(larger.Length >= smaller.Length); + + larger.Close(); + smaller.Close(); + + if (File.Exists("reportOverwriteMultiple.xlsPP")) + File.Delete("reportOverwriteMultiple.xlsPP"); + + + } + + [TestMethod] + public void Test_TRANSACTED_REMOVE_MINI_STREAM_ADD_MINISTREAM_TO_EXISTING_FILE() + { + String srcFilename = "report.xls"; + String dstFilename = "reportOverwrite2.xls"; + + File.Copy(srcFilename, dstFilename, true); + + CompoundFile cf = new CompoundFile(dstFilename, CFSUpdateMode.Update, CFSConfiguration.SectorRecycle | CFSConfiguration.EraseFreeSectors); + + cf.RootStorage.Delete("\x05SummaryInformation"); + + byte[] buffer = Helpers.GetBuffer(2000); + + CFStream addedStream = cf.RootStorage.AddStream("MyNewStream"); + addedStream.SetData(buffer); + + cf.Commit(); + cf.Close(); + + if (File.Exists("reportOverwrite2.xlsPP")) + File.Delete("reportOverwrite2.xlsPP"); + + + } + + + + [TestMethod] + public void Test_DELETE_STREAM_1() + { + String filename = "MultipleStorage.cfs"; + + CompoundFile cf = new CompoundFile(filename); + CFStorage cfs = cf.RootStorage.GetStorage("MyStorage"); + cfs.Delete("MySecondStream"); + + cf.Save(TestContext.TestDir + "MultipleStorage_REMOVED_STREAM_1.cfs"); + cf.Close(); + } + + [TestMethod] + public void Test_DELETE_STREAM_2() + { + String filename = "MultipleStorage.cfs"; + + CompoundFile cf = new CompoundFile(filename); + CFStorage cfs = cf.RootStorage.GetStorage("MyStorage").GetStorage("AnotherStorage"); + + cfs.Delete("AnotherStream"); + + cf.Save(TestContext.TestDir + "MultipleStorage_REMOVED_STREAM_2.cfs"); + + cf.Close(); + } + + + [TestMethod] + public void Test_WRITE_AND_READ_CFS() + { + String filename = "WRITE_AND_READ_CFS.cfs"; + + CompoundFile cf = new CompoundFile(); + + CFStorage st = cf.RootStorage.AddStorage("MyStorage"); + CFStream sm = st.AddStream("MyStream"); + byte[] b = Helpers.GetBuffer(220, 0x0A); + sm.SetData(b); + + cf.Save(filename); + cf.Close(); + + CompoundFile cf2 = new CompoundFile(filename); + CFStorage st2 = cf2.RootStorage.GetStorage("MyStorage"); + CFStream sm2 = st2.GetStream("MyStream"); + cf2.Close(); + + Assert.IsNotNull(sm2); + Assert.IsTrue(sm2.Size == 220); + + + if (File.Exists(filename)) + File.Delete(filename); + + + } + + [TestMethod] + public void Test_INCREMENTAL_SIZE_MULTIPLE_WRITE_AND_READ_CFS() + { + + Random r = new Random(); + + for (int i = r.Next(1, 100); i < 1024 * 1024 * 70; i = i << 1) + { + SingleWriteReadMatching(i + r.Next(0, 3)); + } + + } + + + [TestMethod] + public void Test_INCREMENTAL_SIZE_MULTIPLE_WRITE_AND_READ_CFS_STREAM() + { + + Random r = new Random(); + + for (int i = r.Next(1, 100); i < 1024 * 1024 * 70; i = i << 1) + { + SingleWriteReadMatchingSTREAMED(i + r.Next(0, 3)); + } + + } + + [TestMethod] + public void Test_DELETE_ZERO_LENGTH_STREAM() + { + byte[] b = new byte[0]; + + CompoundFile cf = new CompoundFile(); + + string zeroLengthName = "MyZeroStream"; + CFStream myStream = cf.RootStorage.AddStream(zeroLengthName); + + Assert.IsNotNull(myStream); + + try + { + myStream.SetData(b); + } + catch + { + Assert.Fail("Failed setting zero length stream"); + } + + string filename = "DeleteZeroLengthStream.cfs"; + cf.Save(filename); + cf.Close(); + + CompoundFile cf2 = new CompoundFile(filename); + + // Execption in next line! + cf2.RootStorage.Delete(zeroLengthName); + + CFStream zeroStream2 = null; + + try + { + zeroStream2 = cf2.RootStorage.GetStream(zeroLengthName); + } + catch (Exception ex) + { + Assert.IsNull(zeroStream2); + Assert.IsInstanceOfType(ex, typeof(CFItemNotFound)); + } + + cf2.Save("MultipleDeleteMiniStream.cfs"); + cf2.Close(); + } + + //[TestMethod] + //public void Test_INCREMENTAL_TRANSACTED_CHANGE_CFS() + //{ + + // Random r = new Random(); + + // for (int i = r.Next(1, 100); i < 1024 * 1024 * 70; i = i << 1) + // { + // SingleTransactedChange(i + r.Next(0, 3)); + // } + + //} + + private void SingleTransactedChange(int size) + { + + String filename = "INCREMENTAL_SIZE_MULTIPLE_WRITE_AND_READ_CFS.cfs"; + + if (File.Exists(filename)) + File.Delete(filename); + + CompoundFile cf = new CompoundFile(); + CFStorage st = cf.RootStorage.AddStorage("MyStorage"); + CFStream sm = st.AddStream("MyStream"); + + byte[] b = Helpers.GetBuffer(size); + + sm.SetData(b); + cf.Save(filename); + cf.Close(); + + CompoundFile cf2 = new CompoundFile(filename); + CFStorage st2 = cf2.RootStorage.GetStorage("MyStorage"); + CFStream sm2 = st2.GetStream("MyStream"); + + Assert.IsNotNull(sm2); + Assert.IsTrue(sm2.Size == size); + Assert.IsTrue(Helpers.CompareBuffer(sm2.GetData(), b)); + + cf2.Close(); + } + + private void SingleWriteReadMatching(int size) + { + + String filename = "INCREMENTAL_SIZE_MULTIPLE_WRITE_AND_READ_CFS.cfs"; + + if (File.Exists(filename)) + File.Delete(filename); + + CompoundFile cf = new CompoundFile(); + CFStorage st = cf.RootStorage.AddStorage("MyStorage"); + CFStream sm = st.AddStream("MyStream"); + + byte[] b = Helpers.GetBuffer(size); + + sm.SetData(b); + cf.Save(filename); + cf.Close(); + + CompoundFile cf2 = new CompoundFile(filename); + CFStorage st2 = cf2.RootStorage.GetStorage("MyStorage"); + CFStream sm2 = st2.GetStream("MyStream"); + + Assert.IsNotNull(sm2); + Assert.IsTrue(sm2.Size == size); + Assert.IsTrue(Helpers.CompareBuffer(sm2.GetData(), b)); + + cf2.Close(); + } + + private void SingleWriteReadMatchingSTREAMED(int size) + { + MemoryStream ms = new MemoryStream(size); + + CompoundFile cf = new CompoundFile(); + CFStorage st = cf.RootStorage.AddStorage("MyStorage"); + CFStream sm = st.AddStream("MyStream"); + + byte[] b = Helpers.GetBuffer(size); + + sm.SetData(b); + cf.Save(ms); + cf.Close(); + + CompoundFile cf2 = new CompoundFile(ms); + CFStorage st2 = cf2.RootStorage.GetStorage("MyStorage"); + CFStream sm2 = st2.GetStream("MyStream"); + + Assert.IsNotNull(sm2); + Assert.IsTrue(sm2.Size == size); + Assert.IsTrue(Helpers.CompareBuffer(sm2.GetData(), b)); + + cf2.Close(); + } + + + [TestMethod] + public void Test_APPEND_DATA_TO_STREAM() + { + MemoryStream ms = new MemoryStream(); + + byte[] b = new byte[] { 0x0, 0x1, 0x2, 0x3 }; + byte[] b2 = new byte[] { 0x4, 0x5, 0x6, 0x7 }; + + CompoundFile cf = new CompoundFile(); + CFStream st = cf.RootStorage.AddStream("MyMiniStream"); + st.SetData(b); + st.Append(b2); + + cf.Save(ms); + cf.Close(); + + byte[] cmp = new byte[] { 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7 }; + cf = new CompoundFile(ms); + byte[] data = cf.RootStorage.GetStream("MyMiniStream").GetData(); + Assert.IsTrue(Helpers.CompareBuffer(cmp, data)); + + } + + [TestMethod] + public void Test_COPY_FROM_STREAM() + { + byte[] b = Helpers.GetBuffer(100); + MemoryStream ms = new MemoryStream(b); + + CompoundFile cf = new CompoundFile(); + CFStream st = cf.RootStorage.AddStream("MyImportedStream"); + st.CopyFrom(ms); + ms.Close(); + cf.Save("COPY_FROM_STREAM.cfs"); + cf.Close(); + + cf = new CompoundFile("COPY_FROM_STREAM.cfs"); + byte[] data = cf.RootStorage.GetStream("MyImportedStream").GetData(); + + Assert.IsTrue(Helpers.CompareBuffer(b, data)); + + } + + +#if LARGETEST + + [TestMethod] + public void Test_APPEND_DATA_TO_CREATE_LARGE_STREAM() + { + byte[] b = Helpers.GetBuffer(1024 * 1024 * 50); //2GB buffer + byte[] cmp = new byte[] { 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7 }; + + CompoundFile cf = new CompoundFile(CFSVersion.Ver_4, false, false); + CFStream st = cf.RootStorage.AddStream("MySuperLargeStream"); + cf.Save("MEGALARGESSIMUSFILE.cfs"); + cf.Close(); + + + cf = new CompoundFile("MEGALARGESSIMUSFILE.cfs", UpdateMode.Update, false, false); + CFStream cfst = cf.RootStorage.GetStream("MySuperLargeStream"); + for (int i = 0; i < 42; i++) + { + cfst.AppendData(b); + cf.Commit(true); + } + + cfst.AppendData(cmp); + cf.Commit(true); + + cf.Close(); + + + cf = new CompoundFile("MEGALARGESSIMUSFILE.cfs"); + int count = 8; + byte[] data = cf.RootStorage.GetStream("MySuperLargeStream").GetData((long)b.Length * 42L, ref count); + Assert.IsTrue(Helpers.CompareBuffer(cmp, data)); + cf.Close(); + + } +#endif + [TestMethod] + public void Test_RESIZE_STREAM_NO_TRANSITION() + { + CompoundFile cf = null; + //CFStream st = null; + byte[] b = Helpers.GetBuffer(1024 * 1024 * 2); //2MB buffer + + cf = new CompoundFile(CFSVersion.Ver_3, CFSConfiguration.Default); + cf.RootStorage.AddStream("AStream").SetData(b); + cf.Save("$Test_RESIZE_STREAM.cfs"); + cf.Close(); + + cf = new CompoundFile("$Test_RESIZE_STREAM.cfs", CFSUpdateMode.Update, CFSConfiguration.SectorRecycle); + CFStream item = cf.RootStorage.GetStream("AStream"); + item.Resize(item.Size / 2); + //cf.RootStorage.AddStream("BStream").SetData(b); + cf.Commit(true); + cf.Close(); + } + + [TestMethod] + public void Test_RESIZE_STREAM_TRANSITION_TO_MINI() + { + String FILE_NAME = "$Test_RESIZE_STREAM_TRANSITION_TO_MINI.cfs"; + CompoundFile cf = null; + + byte[] b = Helpers.GetBuffer(1024 * 1024 * 2); //2MB buffer + byte[] b100 = new byte[100]; + + for (int i = 0; i < 100; i++) + { + b100[i] = b[i]; + } + + cf = new CompoundFile(CFSVersion.Ver_3, CFSConfiguration.Default); + cf.RootStorage.AddStream("AStream").SetData(b); + cf.Save(FILE_NAME); + cf.Close(); + + cf = new CompoundFile(FILE_NAME, CFSUpdateMode.Update, CFSConfiguration.SectorRecycle); + CFStream item = cf.RootStorage.GetStream("AStream"); + item.Resize(100); + cf.Commit(); + cf.Close(); + + cf = new CompoundFile(FILE_NAME, CFSUpdateMode.ReadOnly, CFSConfiguration.Default); + Assert.IsTrue(Helpers.CompareBuffer(cf.RootStorage.GetStream("AStream").GetData(), b100)); + cf.Close(); + + if (File.Exists(FILE_NAME)) + File.Delete(FILE_NAME); + } + + [TestMethod] + public void Test_RESIZE_STREAM_TRANSITION_TO_NORMAL() + { + CompoundFile cf = null; + byte[] b = Helpers.GetBuffer(1024 * 2, 0xAA); //2MB buffer + + cf = new CompoundFile(CFSVersion.Ver_3, CFSConfiguration.Default); + cf.RootStorage.AddStream("AStream").SetData(b); + cf.Save("$Test_RESIZE_STREAM_TRANSITION_TO_NORMAL.cfs"); + cf.Save("$Test_RESIZE_STREAM_TRANSITION_TO_NORMAL2.cfs"); + cf.Close(); + + cf = new CompoundFile("$Test_RESIZE_STREAM_TRANSITION_TO_NORMAL.cfs", CFSUpdateMode.Update, CFSConfiguration.SectorRecycle | CFSConfiguration.EraseFreeSectors); + CFStream item = cf.RootStorage.GetStream("AStream"); + item.Resize(5000); + cf.Commit(); + cf.Close(); + + cf = new CompoundFile("$Test_RESIZE_STREAM_TRANSITION_TO_NORMAL.cfs", CFSUpdateMode.ReadOnly, CFSConfiguration.Default); + item = cf.RootStorage.GetStream("AStream"); + Assert.IsTrue(item != null); + Assert.IsTrue(item.Size == 5000); + + byte[] buffer = new byte[2048]; + item.Read(buffer, 0, 2048); + Assert.IsTrue(Helpers.CompareBuffer(b, buffer)); + + } + + [TestMethod] + public void Test_RESIZE_MINISTREAM_NO_TRANSITION() + { + CompoundFile cf = null; + + byte[] b = Helpers.GetBuffer(1024 * 2); + + cf = new CompoundFile(CFSVersion.Ver_3, CFSConfiguration.Default); + cf.RootStorage.AddStream("MiniStream").SetData(b); + cf.Save("$Test_RESIZE_MINISTREAM.cfs"); + cf.Close(); + + cf = new CompoundFile("$Test_RESIZE_MINISTREAM.cfs", CFSUpdateMode.Update, CFSConfiguration.SectorRecycle | CFSConfiguration.EraseFreeSectors); + CFStream item = cf.RootStorage.GetStream("MiniStream"); + item.Resize(item.Size / 2); + + cf.Commit(); + cf.Close(); + + cf = new CompoundFile("$Test_RESIZE_MINISTREAM.cfs", CFSUpdateMode.ReadOnly, CFSConfiguration.Default); + CFStream st = cf.RootStorage.GetStream("MiniStream"); + + Assert.IsNotNull(st); + Assert.IsTrue(st.Size == 1024); + + byte[] buffer = new byte[1024]; + st.Read(buffer, 0, 1024); + + Assert.IsTrue(Helpers.CompareBuffer(b, buffer, 1024)); + + cf.Close(); + } + + [TestMethod] + public void Test_RESIZE_MINISTREAM_SECTOR_RECYCLE() + { + CompoundFile cf = null; + + byte[] b = Helpers.GetBuffer(1024 * 2); + + cf = new CompoundFile(CFSVersion.Ver_3, CFSConfiguration.Default); + cf.RootStorage.AddStream("MiniStream").SetData(b); + cf.Save("$Test_RESIZE_MINISTREAM_RECYCLE.cfs"); + cf.Close(); + + cf = new CompoundFile("$Test_RESIZE_MINISTREAM_RECYCLE.cfs", CFSUpdateMode.Update, CFSConfiguration.SectorRecycle | CFSConfiguration.EraseFreeSectors); + CFStream item = cf.RootStorage.GetStream("MiniStream"); + item.Resize(item.Size / 2); + + cf.Commit(); + cf.Close(); + + cf = new CompoundFile("$Test_RESIZE_MINISTREAM_RECYCLE.cfs", CFSUpdateMode.ReadOnly, CFSConfiguration.SectorRecycle | CFSConfiguration.EraseFreeSectors); + CFStream st = cf.RootStorage.AddStream("ANewStream"); + st.SetData(Helpers.GetBuffer(400)); + cf.Save("$Test_RESIZE_MINISTREAM_RECYCLE2.cfs"); + cf.Close(); + + Assert.IsTrue( + new FileInfo("$Test_RESIZE_MINISTREAM_RECYCLE.cfs").Length + == new FileInfo("$Test_RESIZE_MINISTREAM_RECYCLE2.cfs").Length); + + } + + [TestMethod] + public void Test_DELETE_STREAM_SECTOR_REUSE() + { + CompoundFile cf = null; + CFStream st = null; + + byte[] b = Helpers.GetBuffer(1024 * 1024 * 2); //2MB buffer + byte[] cmp = new byte[] { 0x0, 0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7 }; + + cf = new CompoundFile(CFSVersion.Ver_4, CFSConfiguration.Default); + st = cf.RootStorage.AddStream("AStream"); + st.Append(b); + cf.Save("SectorRecycle.cfs"); + cf.Close(); + + + cf = new CompoundFile("SectorRecycle.cfs", CFSUpdateMode.Update, CFSConfiguration.SectorRecycle); + cf.RootStorage.Delete("AStream"); + cf.Commit(true); + cf.Close(); + + cf = new CompoundFile("SectorRecycle.cfs", CFSUpdateMode.ReadOnly, CFSConfiguration.Default); //No sector recycle + st = cf.RootStorage.AddStream("BStream"); + st.Append(Helpers.GetBuffer(1024 * 1024 * 1)); + cf.Save("SectorRecycleLarger.cfs"); + cf.Close(); + + Assert.IsFalse((new FileInfo("SectorRecycle.cfs").Length) >= (new FileInfo("SectorRecycleLarger.cfs").Length)); + + cf = new CompoundFile("SectorRecycle.cfs", CFSUpdateMode.ReadOnly, CFSConfiguration.SectorRecycle); + st = cf.RootStorage.AddStream("BStream"); + st.Append(Helpers.GetBuffer(1024 * 1024 * 1)); + cf.Save("SectorRecycleSmaller.cfs"); + cf.Close(); + long larger = (new FileInfo("SectorRecycle.cfs").Length); + long smaller = (new FileInfo("SectorRecycleSmaller.cfs").Length); + + Assert.IsTrue(larger >= smaller, "Larger size:" + larger.ToString() + " - Smaller size:" + smaller.ToString()); + + } + + + + [TestMethod] + public void TEST_STREAM_VIEW() + { + Stream a = null; + List temp = new List(); + Sector s = new Sector(512); + Buffer.BlockCopy(BitConverter.GetBytes((int)1), 0, s.GetData(), 0, 4); + temp.Add(s); + + StreamView sv = new StreamView(temp, 512, 0,null, a); + BinaryReader br = new BinaryReader(sv); + Int32 t = br.ReadInt32(); + + Assert.IsTrue(t == 1); + } + + + [TestMethod] + public void Test_STREAM_VIEW_2() + { + Stream b = null; + List temp = new List(); + + StreamView sv = new StreamView(temp, 512, b); + sv.Write(BitConverter.GetBytes(1), 0, 4); + sv.Seek(0, SeekOrigin.Begin); + BinaryReader br = new BinaryReader(sv); + Int32 t = br.ReadInt32(); + + Assert.IsTrue(t == 1); + } + + + /// + /// Write a sequence of Int32 greater than sector size, + /// read and compare. + /// + [TestMethod] + public void Test_STREAM_VIEW_3() + { + Stream b = null; + List temp = new List(); + + StreamView sv = new StreamView(temp, 512, b); + + for (int i = 0; i < 200; i++) + { + sv.Write(BitConverter.GetBytes(i), 0, 4); + } + + sv.Seek(0, SeekOrigin.Begin); + BinaryReader br = new BinaryReader(sv); + + for (int i = 0; i < 200; i++) + { + Assert.IsTrue(i == br.ReadInt32(), "Failed with " + i.ToString()); + } + } + + /// + /// Write a sequence of Int32 greater than sector size, + /// read and compare. + /// + [TestMethod] + public void Test_STREAM_VIEW_LARGE_DATA() + { + Stream b = null; + List temp = new List(); + + StreamView sv = new StreamView(temp, 512, b); + + for (int i = 0; i < 200; i++) + { + sv.Write(BitConverter.GetBytes(i), 0, 4); + } + + sv.Seek(0, SeekOrigin.Begin); + BinaryReader br = new BinaryReader(sv); + + for (int i = 0; i < 200; i++) + { + Assert.IsTrue(i == br.ReadInt32(), "Failed with " + i.ToString()); + } + } + + + + } +} diff --git a/sources/Test/OpenMcdf.Test/CFSTorageTest.cs b/sources/Test/OpenMcdf.Test/CFSTorageTest.cs new file mode 100644 index 00000000..599d7a4a --- /dev/null +++ b/sources/Test/OpenMcdf.Test/CFSTorageTest.cs @@ -0,0 +1,429 @@ +using System; +using System.Collections.Generic; +using System.IO; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using OpenMcdf; + +namespace OpenMcdf.Test +{ + /// + /// Summary description for CFTorageTest + /// + [TestClass] + public class CFSTorageTest + { + //const String OUTPUT_DIR = "C:\\TestOutputFiles\\"; + + public CFSTorageTest() + { + + } + + private TestContext testContextInstance; + + /// + ///Gets or sets the test context which provides + ///information about and functionality for the current test run. + /// + public TestContext TestContext + { + get + { + return testContextInstance; + } + set + { + testContextInstance = value; + } + } + + #region Additional test attributes + // + // You can use the following additional attributes as you write your tests: + // + // Use ClassInitialize to run code before running the first test in the class + // [ClassInitialize()] + //public static void MyClassInitialize(TestContext testContext) + + // + // Use ClassCleanup to run code after all tests in a class have run + // [ClassCleanup()] + // public static void MyClassCleanup() { } + // + // Use TestInitialize to run code before running each test + // [TestInitialize()] + // public void MyTestInitialize() { } + // + // Use TestCleanup to run code after each test has run + // [TestCleanup()] + // public void MyTestCleanup() { } + // + #endregion + + [TestMethod] + public void Test_CREATE_STORAGE() + { + const String STORAGE_NAME = "NewStorage"; + CompoundFile cf = new CompoundFile(); + + CFStorage st = cf.RootStorage.AddStorage(STORAGE_NAME); + + Assert.IsNotNull(st); + Assert.AreEqual(STORAGE_NAME, st.Name, false); + } + + [TestMethod] + public void Test_CREATE_STORAGE_WITH_CREATION_DATE() + { + const String STORAGE_NAME = "NewStorage1"; + CompoundFile cf = new CompoundFile(); + + CFStorage st = cf.RootStorage.AddStorage(STORAGE_NAME); + st.CreationDate = DateTime.Now; + + Assert.IsNotNull(st); + Assert.AreEqual(STORAGE_NAME, st.Name, false); + + cf.Save("ProvaData.cfs"); + cf.Close(); + } + + [TestMethod] + public void Test_VISIT_ENTRIES() + { + const String STORAGE_NAME = "report.xls"; + CompoundFile cf = new CompoundFile(STORAGE_NAME); + + FileStream output = new FileStream("LogEntries.txt", FileMode.Create); + TextWriter tw = new StreamWriter(output); + + Action va = delegate (CFItem item) + { + tw.WriteLine(item.Name); + }; + + cf.RootStorage.VisitEntries(va, true); + + tw.Close(); + + } + + + [TestMethod] + public void Test_TRY_GET_STREAM_STORAGE() + { + String FILENAME = "MultipleStorage.cfs"; + CompoundFile cf = new CompoundFile(FILENAME); + + CFStorage st = cf.RootStorage.TryGetStorage("MyStorage"); + Assert.IsNotNull(st); + + try + { + CFStorage nf = cf.RootStorage.TryGetStorage("IDONTEXIST"); + Assert.IsNull(nf); + } + catch (Exception) + { + Assert.Fail("Exception raised for try_get method"); + } + + try + { + CFStream s = st.TryGetStream("MyStream"); + Assert.IsNotNull(s); + CFStream ns = st.TryGetStream("IDONTEXIST2"); + Assert.IsNull(ns); + } + catch (Exception) + { + Assert.Fail("Exception raised for try_get method"); + } + } + + [TestMethod] + public void Test_VISIT_ENTRIES_CORRUPTED_FILE_VALIDATION_ON() + { + CompoundFile f = null; + + try + { + f = new CompoundFile("CorruptedDoc_bug3547815.doc", CFSUpdateMode.ReadOnly, CFSConfiguration.NoValidationException); + } + catch + { + Assert.Fail("No exception has to be fired on creation due to lazy loading"); + } + + FileStream output = null; + + try + { + output = new FileStream("LogEntriesCorrupted_1.txt", FileMode.Create); + + using (TextWriter tw = new StreamWriter(output)) + { + + Action va = delegate (CFItem item) + { + tw.WriteLine(item.Name); + }; + + f.RootStorage.VisitEntries(va, true); + tw.Flush(); + } + } + catch (Exception ex) + { + Assert.IsTrue(ex is CFCorruptedFileException); + Assert.IsTrue(f != null && f.IsClosed); + + } + finally + { + if (output != null) + output.Close(); + + + + } + } + + [TestMethod] + public void Test_VISIT_ENTRIES_CORRUPTED_FILE_VALIDATION_OFF_BUT_CAN_LOAD() + { + CompoundFile f = null; + + try + { + //Corrupted file has invalid children item sid reference + f = new CompoundFile("CorruptedDoc_bug3547815_B.doc", CFSUpdateMode.ReadOnly, CFSConfiguration.NoValidationException); + } + catch + { + Assert.Fail("No exception has to be fired on creation due to lazy loading"); + } + + FileStream output = null; + + try + { + output = new FileStream("LogEntriesCorrupted_2.txt", FileMode.Create); + + + using (TextWriter tw = new StreamWriter(output)) + { + + Action va = delegate (CFItem item) + { + tw.WriteLine(item.Name); + }; + + f.RootStorage.VisitEntries(va, true); + tw.Flush(); + } + } + catch + { + Assert.Fail("Fail is corrupted but it has to be loaded anyway by test design"); + } + finally + { + if (output != null) + output.Close(); + + + } + } + + + [TestMethod] + public void Test_VISIT_STORAGE() + { + String FILENAME = "testVisiting.xls"; + + // Remove... + if (File.Exists(FILENAME)) + File.Delete(FILENAME); + + //Create... + + CompoundFile ncf = new CompoundFile(); + + CFStorage l1 = ncf.RootStorage.AddStorage("Storage Level 1"); + l1.AddStream("l1ns1"); + l1.AddStream("l1ns2"); + l1.AddStream("l1ns3"); + + CFStorage l2 = l1.AddStorage("Storage Level 2"); + l2.AddStream("l2ns1"); + l2.AddStream("l2ns2"); + + ncf.Save(FILENAME); + ncf.Close(); + + + // Read... + + CompoundFile cf = new CompoundFile(FILENAME); + + FileStream output = new FileStream("reportVisit.txt", FileMode.Create); + TextWriter sw = new StreamWriter(output); + + Console.SetOut(sw); + + Action va = delegate (CFItem target) + { + sw.WriteLine(target.Name); + }; + + cf.RootStorage.VisitEntries(va, true); + + cf.Close(); + sw.Close(); + } + + [TestMethod] + public void Test_DELETE_DIRECTORY() + { + String FILENAME = "MultipleStorage2.cfs"; + CompoundFile cf = new CompoundFile(FILENAME, CFSUpdateMode.ReadOnly, CFSConfiguration.Default); + + CFStorage st = cf.RootStorage.GetStorage("MyStorage"); + + Assert.IsNotNull(st); + + st.Delete("AnotherStorage"); + + cf.Save("MultipleStorage_Delete.cfs"); + + cf.Close(); + } + + [TestMethod] + public void Test_DELETE_MINISTREAM_STREAM() + { + String FILENAME = "MultipleStorage2.cfs"; + CompoundFile cf = new CompoundFile(FILENAME); + + CFStorage found = null; + Action action = delegate (CFItem item) { if (item.Name == "AnotherStorage") found = item as CFStorage; }; + cf.RootStorage.VisitEntries(action, true); + + Assert.IsNotNull(found); + + found.Delete("AnotherStream"); + + cf.Save("MultipleDeleteMiniStream"); + cf.Close(); + } + + [TestMethod] + public void Test_DELETE_STREAM() + { + String FILENAME = "MultipleStorage3.cfs"; + CompoundFile cf = new CompoundFile(FILENAME); + + CFStorage found = null; + Action action = delegate (CFItem item) + { + if (item.Name == "AnotherStorage") + found = item as CFStorage; + }; + + cf.RootStorage.VisitEntries(action, true); + + Assert.IsNotNull(found); + + found.Delete("Another2Stream"); + + cf.Save("MultipleDeleteStream"); + cf.Close(); + } + + [TestMethod] + public void Test_CHECK_DISPOSED_() + { + const String FILENAME = "MultipleStorage.cfs"; + CompoundFile cf = new CompoundFile(FILENAME); + + CFStorage st = cf.RootStorage.GetStorage("MyStorage"); + cf.Close(); + + try + { + byte[] temp = st.GetStream("MyStream").GetData(); + Assert.Fail("Stream without media"); + } + catch (Exception ex) + { + Assert.IsTrue(ex is CFDisposedException); + } + } + + [TestMethod] + public void Test_LAZY_LOAD_CHILDREN_() + { + CompoundFile cf = new CompoundFile(); + cf.RootStorage.AddStorage("Level_1") + .AddStorage("Level_2") + .AddStream("Level2Stream") + .SetData(Helpers.GetBuffer(100)); + + cf.Save("$Hel1"); + + cf.Close(); + + cf = new CompoundFile("$Hel1"); + IList i = cf.GetAllNamedEntries("Level2Stream"); + Assert.IsNotNull(i[0]); + Assert.IsTrue(i[0] is CFStream); + Assert.IsTrue((i[0] as CFStream).GetData().Length == 100); + cf.Save("$Hel2"); + cf.Close(); + + if (File.Exists("$Hel1")) + { + File.Delete("$Hel1"); + } + if (File.Exists("$Hel2")) + { + File.Delete("$Hel2"); + } + } + + [TestMethod] + public void Test_FIX_BUG_31() + { + CompoundFile cf = new CompoundFile(); + cf.RootStorage.AddStorage("Level_1") + + .AddStream("Level2Stream") + .SetData(Helpers.GetBuffer(100)); + + cf.Save("$Hel1"); + + cf.Close(); + + CompoundFile cf1 = new CompoundFile("$Hel1"); + try + { + CFStream cs = cf1.RootStorage.GetStorage("Level_1").AddStream("Level2Stream"); + } + catch (Exception ex) + { + Assert.IsTrue(ex.GetType() == typeof(CFDuplicatedItemException)); + } + + } + + [TestMethod] + [ExpectedException(typeof(OpenMcdf.CFCorruptedFileException))] + public void Test_CORRUPTEDDOC_BUG36_SHOULD_THROW_CORRUPTED_FILE_EXCEPTION() + { + using (CompoundFile file = new CompoundFile("CorruptedDoc_bug36.doc", CFSUpdateMode.ReadOnly, CFSConfiguration.NoValidationException) ) { + //Many thanks to theseus for bug reporting + } + } + } +} diff --git a/sources/Test/OpenMcdf.Test/CompoundFileTest.cs b/sources/Test/OpenMcdf.Test/CompoundFileTest.cs new file mode 100644 index 00000000..fd63af88 --- /dev/null +++ b/sources/Test/OpenMcdf.Test/CompoundFileTest.cs @@ -0,0 +1,821 @@ +using System; +using System.Text; +using System.Collections.Generic; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using OpenMcdf; +using System.IO; +using System.Diagnostics; + +namespace OpenMcdf.Test +{ + /// + /// Summary description for CompoundFileTest + /// + [TestClass] + public class CompoundFileTest + { + public CompoundFileTest() + { + + } + + private TestContext testContextInstance; + + /// + ///Gets or sets the test context which provides + ///information about and functionality for the current test run. + /// + public TestContext TestContext + { + get + { + return testContextInstance; + } + set + { + testContextInstance = value; + } + } + + #region Additional test attributes + // + // You can use the following additional attributes as you write your tests: + // + // Use ClassInitialize to run code before running the first test in the class + // [ClassInitialize()] + // public static void MyClassInitialize(TestContext testContext) { } + // + // Use ClassCleanup to run code after all tests in a class have run + // [ClassCleanup()] + // public static void MyClassCleanup() { } + // + // Use TestInitialize to run code before running each test + // [TestInitialize()] + // public void MyTestInitialize() { } + // + // Use TestCleanup to run code after each test has run + // [TestCleanup()] + // public void MyTestCleanup() { } + // + #endregion + + [TestMethod] + public void Test_COMPRESS_SPACE() + { + String FILENAME = "MultipleStorage3.cfs"; // 22Kb + + FileInfo srcFile = new FileInfo(FILENAME); + + File.Copy(FILENAME, "MultipleStorage_Deleted_Compress.cfs", true); + + CompoundFile cf = new CompoundFile("MultipleStorage_Deleted_Compress.cfs", CFSUpdateMode.Update, CFSConfiguration.SectorRecycle | CFSConfiguration.EraseFreeSectors); + + CFStorage st = cf.RootStorage.GetStorage("MyStorage"); + st = st.GetStorage("AnotherStorage"); + + Assert.IsNotNull(st); + st.Delete("Another2Stream"); + cf.Commit(); + cf.Close(); + + CompoundFile.ShrinkCompoundFile("MultipleStorage_Deleted_Compress.cfs"); // -> 7Kb + + FileInfo dstFile = new FileInfo("MultipleStorage_Deleted_Compress.cfs"); + + Assert.IsTrue(srcFile.Length > dstFile.Length); + + } + + [TestMethod] + public void Test_ENTRY_NAME_LENGTH() + { + //Thanks to Mark Bosold for bug fix and unit + + CompoundFile cf = new CompoundFile(); + + // Cannot be equal. + string maxCharactersStreamName = "1234567890123456789A12345678901"; // 31 chars + string maxCharactersStorageName = "1234567890123456789012345678901"; // 31 chars + + // Try Storage entry name with max characters. + Assert.IsNotNull(cf.RootStorage.AddStorage(maxCharactersStorageName)); + CFStorage strg = cf.RootStorage.GetStorage(maxCharactersStorageName); + Assert.IsNotNull(strg); + Assert.IsTrue(strg.Name == maxCharactersStorageName); + + + // Try Stream entry name with max characters. + Assert.IsNotNull(cf.RootStorage.AddStream(maxCharactersStreamName)); + CFStream strm = cf.RootStorage.GetStream(maxCharactersStreamName); + Assert.IsNotNull(strm); + Assert.IsTrue(strm.Name == maxCharactersStreamName); + + string tooManyCharactersEntryName = "12345678901234567890123456789012"; // 32 chars + + try + { + // Try Storage entry name with too many characters. + cf.RootStorage.AddStorage(tooManyCharactersEntryName); + Assert.Fail(); + } + catch (Exception ex) + { + Assert.IsTrue(ex is CFException); + } + + try + { + // Try Stream entry name with too many characters. + cf.RootStorage.AddStream(tooManyCharactersEntryName); + Assert.Fail(); + } + catch (Exception ex) + { + Assert.IsTrue(ex is CFException); + } + + cf.Save("EntryNameLength"); + cf.Close(); + } + + [TestMethod] + public void Test_DELETE_WITHOUT_COMPRESSION() + { + String FILENAME = "MultipleStorage3.cfs"; + + FileInfo srcFile = new FileInfo(FILENAME); + + CompoundFile cf = new CompoundFile(FILENAME); + + CFStorage st = cf.RootStorage.GetStorage("MyStorage"); + st = st.GetStorage("AnotherStorage"); + + Assert.IsNotNull(st); + + st.Delete("Another2Stream"); //17Kb + + //cf.CompressFreeSpace(); + cf.Save("MultipleStorage_Deleted_Compress.cfs"); + + cf.Close(); + FileInfo dstFile = new FileInfo("MultipleStorage_Deleted_Compress.cfs"); + + Assert.IsFalse(srcFile.Length > dstFile.Length); + + } + + [TestMethod] + public void Test_WRITE_AND_READ_CFS_VERSION_4() + { + String filename = "WRITE_AND_READ_CFS_V4.cfs"; + + CompoundFile cf = new CompoundFile(CFSVersion.Ver_4, CFSConfiguration.EraseFreeSectors | CFSConfiguration.SectorRecycle); + + CFStorage st = cf.RootStorage.AddStorage("MyStorage"); + CFStream sm = st.AddStream("MyStream"); + byte[] b = new byte[220]; + sm.SetData(b); + + cf.Save(filename); + cf.Close(); + + CompoundFile cf2 = new CompoundFile(filename); + CFStorage st2 = cf2.RootStorage.GetStorage("MyStorage"); + CFStream sm2 = st2.GetStream("MyStream"); + + Assert.IsNotNull(sm2); + Assert.IsTrue(sm2.Size == 220); + + cf2.Close(); + } + + [TestMethod] + public void Test_WRITE_READ_CFS_VERSION_4_STREAM() + { + String filename = "WRITE_COMMIT_READ_CFS_V4.cfs"; + + CompoundFile cf = new CompoundFile(CFSVersion.Ver_4, CFSConfiguration.SectorRecycle | CFSConfiguration.EraseFreeSectors); + + CFStorage st = cf.RootStorage.AddStorage("MyStorage"); + CFStream sm = st.AddStream("MyStream"); + byte[] b = Helpers.GetBuffer(227); + sm.SetData(b); + + cf.Save(filename); + cf.Close(); + + CompoundFile cf2 = new CompoundFile(filename); + CFStorage st2 = cf2.RootStorage.GetStorage("MyStorage"); + CFStream sm2 = st2.GetStream("MyStream"); + + Assert.IsNotNull(sm2); + Assert.IsTrue(sm2.Size == b.Length); + + cf2.Close(); + } + + [TestMethod] + public void Test_OPEN_FROM_STREAM() + { + String filename = "reportREAD.xls"; + File.Copy(filename, "reportOPENFROMSTREAM.xls"); + FileStream fs = new FileStream(filename, FileMode.Open); + CompoundFile cf = new CompoundFile(fs); + CFStream foundStream = cf.RootStorage.GetStream("Workbook"); + + byte[] temp = foundStream.GetData(); + + Assert.IsNotNull(temp); + + cf.Close(); + } + + [TestMethod] + public void Test_MULTIPLE_SAVE() + { + var file = new CompoundFile(); + + file.Save("test.mdf"); + + var meta = file. + RootStorage. + AddStream("meta"); + + meta.Append(BitConverter.GetBytes(DateTime.Now.ToBinary())); + meta.Append(BitConverter.GetBytes(DateTime.Now.ToBinary())); + + file.Save("test.mdf"); + } + + [TestMethod] + public void Test_OPEN_COMPOUND_BUG_FIX_133() + { + var f = new CompoundFile("testbad.ole"); + CFStream cfs = f.RootStorage.GetStream("\x01Ole10Native"); + byte[] data = cfs.GetData(); + Assert.IsTrue(data.Length == 18140); + } + + [TestMethod] + public void Test_COMPARE_DIR_ENTRY_NAME_BUG_FIX_ID_3487353() + { + var f = new CompoundFile("report_name_fix.xls", CFSUpdateMode.Update, CFSConfiguration.SectorRecycle | CFSConfiguration.EraseFreeSectors); + CFStream cfs = f.RootStorage.AddStream("Poorbook"); + cfs.Append(Helpers.GetBuffer(20)); + f.Commit(); + f.Close(); + + f = new CompoundFile("report_name_fix.xls", CFSUpdateMode.Update, CFSConfiguration.SectorRecycle | CFSConfiguration.EraseFreeSectors); + cfs = f.RootStorage.GetStream("Workbook"); + Assert.IsTrue(cfs.Name == "Workbook"); + f.RootStorage.Delete("PoorBook"); + f.Commit(); + f.Close(); + + } + + [TestMethod] + public void Test_GET_COMPOUND_VERSION() + { + var f = new CompoundFile("report_name_fix.xls"); + CFSVersion ver = f.Version; + + Assert.IsTrue(ver == CFSVersion.Ver_3); + + f.Close(); + } + + [TestMethod] + public void Test_FUNCTIONAL_BEHAVIOUR() + { + //System.Diagnostics.Trace.Listeners.Add(new ConsoleTraceListener()); + + const int N_FACTOR = 1; + + byte[] bA = Helpers.GetBuffer(20 * 1024 * N_FACTOR, 0x0A); + byte[] bB = Helpers.GetBuffer(5 * 1024, 0x0B); + byte[] bC = Helpers.GetBuffer(5 * 1024, 0x0C); + byte[] bD = Helpers.GetBuffer(5 * 1024, 0x0D); + byte[] bE = Helpers.GetBuffer(8 * 1024 * N_FACTOR + 1, 0x1A); + byte[] bF = Helpers.GetBuffer(16 * 1024 * N_FACTOR, 0x1B); + byte[] bG = Helpers.GetBuffer(14 * 1024 * N_FACTOR, 0x1C); + byte[] bH = Helpers.GetBuffer(12 * 1024 * N_FACTOR, 0x1D); + byte[] bE2 = Helpers.GetBuffer(8 * 1024 * N_FACTOR, 0x2A); + byte[] bMini = Helpers.GetBuffer(1027, 0xEE); + + Stopwatch sw = new Stopwatch(); + sw.Start(); + + //############ + + // Phase 1 + var cf = new CompoundFile(CFSVersion.Ver_3, CFSConfiguration.SectorRecycle); + cf.RootStorage.AddStream("A").SetData(bA); + cf.Save("OneStream.cfs"); + cf.Close(); + + // Test Phase 1 + var cfTest = new CompoundFile("OneStream.cfs"); + CFStream testSt = cfTest.RootStorage.GetStream("A"); + + Assert.IsNotNull(testSt); + Assert.IsTrue(testSt.Size == bA.Length); + Assert.IsTrue(Helpers.CompareBuffer(bA, testSt.GetData())); + + cfTest.Close(); + + //########### + + //Phase 2 + cf = new CompoundFile("OneStream.cfs", CFSUpdateMode.ReadOnly, CFSConfiguration.SectorRecycle); + + cf.RootStorage.AddStream("B").SetData(bB); + cf.RootStorage.AddStream("C").SetData(bC); + cf.RootStorage.AddStream("D").SetData(bD); + cf.RootStorage.AddStream("E").SetData(bE); + cf.RootStorage.AddStream("F").SetData(bF); + cf.RootStorage.AddStream("G").SetData(bG); + cf.RootStorage.AddStream("H").SetData(bH); + + cf.Save("8_Streams.cfs"); + cf.Close(); + + // Test Phase 2 + + + cfTest = new CompoundFile("8_Streams.cfs"); + + testSt = cfTest.RootStorage.GetStream("B"); + Assert.IsNotNull(testSt); + Assert.IsTrue(testSt.Size == bB.Length); + Assert.IsTrue(Helpers.CompareBuffer(bB, testSt.GetData())); + + testSt = cfTest.RootStorage.GetStream("C"); + Assert.IsNotNull(testSt); + Assert.IsTrue(testSt.Size == bC.Length); + Assert.IsTrue(Helpers.CompareBuffer(bC, testSt.GetData())); + + testSt = cfTest.RootStorage.GetStream("D"); + Assert.IsNotNull(testSt); + Assert.IsTrue(testSt.Size == bD.Length); + Assert.IsTrue(Helpers.CompareBuffer(bD, testSt.GetData())); + + testSt = cfTest.RootStorage.GetStream("E"); + Assert.IsNotNull(testSt); + Assert.IsTrue(testSt.Size == bE.Length); + Assert.IsTrue(Helpers.CompareBuffer(bE, testSt.GetData())); + + testSt = cfTest.RootStorage.GetStream("F"); + Assert.IsNotNull(testSt); + Assert.IsTrue(testSt.Size == bF.Length); + Assert.IsTrue(Helpers.CompareBuffer(bF, testSt.GetData())); + + testSt = cfTest.RootStorage.GetStream("G"); + Assert.IsNotNull(testSt); + Assert.IsTrue(testSt.Size == bG.Length); + Assert.IsTrue(Helpers.CompareBuffer(bG, testSt.GetData())); + + testSt = cfTest.RootStorage.GetStream("H"); + Assert.IsNotNull(testSt); + Assert.IsTrue(testSt.Size == bH.Length); + Assert.IsTrue(Helpers.CompareBuffer(bH, testSt.GetData())); + + cfTest.Close(); + + + File.Copy("8_Streams.cfs", "6_Streams.cfs", true); + File.Delete("8_Streams.cfs"); + + //########### + + Trace.Listeners.Add(new ConsoleTraceListener()); + // Phase 3 + cf = new CompoundFile("6_Streams.cfs", CFSUpdateMode.Update, CFSConfiguration.SectorRecycle | CFSConfiguration.EraseFreeSectors); + cf.RootStorage.Delete("D"); + cf.RootStorage.Delete("G"); + cf.Commit(); + + cf.Close(); + + //Test Phase 3 + + + cfTest = new CompoundFile("6_Streams.cfs"); + + + bool catched = false; + + try + { + testSt = cfTest.RootStorage.GetStream("D"); + + } + catch (Exception ex) + { + if (ex is CFItemNotFound) + catched = true; + } + + Assert.IsTrue(catched); + + catched = false; + + try + { + testSt = cfTest.RootStorage.GetStream("G"); + } + catch (Exception ex) + { + if (ex is CFItemNotFound) + catched = true; + } + + Assert.IsTrue(catched); + + cfTest.Close(); + + //########## + + // Phase 4 + + File.Copy("6_Streams.cfs", "6_Streams_Shrinked.cfs", true); + CompoundFile.ShrinkCompoundFile("6_Streams_Shrinked.cfs"); + + // Test Phase 4 + + Assert.IsTrue(new FileInfo("6_Streams_Shrinked.cfs").Length < new FileInfo("6_Streams.cfs").Length); + + cfTest = new CompoundFile("6_Streams_Shrinked.cfs"); + Action va = delegate (CFItem item) + { + if (item.IsStream) + { + CFStream ia = item as CFStream; + Assert.IsNotNull(ia); + Assert.IsTrue(ia.Size > 0); + byte[] d = ia.GetData(); + Assert.IsNotNull(d); + Assert.IsTrue(d.Length > 0); + Assert.IsTrue(d.Length == ia.Size); + } + }; + + cfTest.RootStorage.VisitEntries(va, true); + cfTest.Close(); + + //########## + + //Phase 5 + + cf = new CompoundFile("6_Streams_Shrinked.cfs", CFSUpdateMode.Update, CFSConfiguration.SectorRecycle); + cf.RootStorage.AddStream("ZZZ").SetData(bF); + cf.RootStorage.GetStream("E").Append(bE2); + cf.Commit(); + cf.Close(); + + + cf = new CompoundFile("6_Streams_Shrinked.cfs", CFSUpdateMode.Update, CFSConfiguration.SectorRecycle); + cf.RootStorage.CLSID = new Guid("EEEEEEEEEEEEEEEEEEEEEEEEEEEEEEEE"); + cf.Commit(); + cf.Close(); + + cf = new CompoundFile("6_Streams_Shrinked.cfs", CFSUpdateMode.Update, CFSConfiguration.SectorRecycle); + cf.RootStorage.AddStorage("MyStorage").AddStream("ZIP").Append(bE); + cf.Commit(); + cf.Close(); + + cf = new CompoundFile("6_Streams_Shrinked.cfs", CFSUpdateMode.Update, CFSConfiguration.SectorRecycle); + cf.RootStorage.AddStorage("AnotherStorage").AddStream("ANS").Append(bE); + cf.RootStorage.Delete("MyStorage"); + + + cf.Commit(); + cf.Close(); + + //Test Phase 5 + + //##### + + //Phase 6 + + cf = new CompoundFile("6_Streams_Shrinked.cfs", CFSUpdateMode.Update, CFSConfiguration.SectorRecycle); + CFStorage root = cf.RootStorage; + + root.AddStorage("MiniStorage").AddStream("miniSt").Append(bMini); + + cf.RootStorage.GetStorage("MiniStorage").AddStream("miniSt2").Append(bMini); + cf.Commit(); + cf.Close(); + + cf = new CompoundFile("6_Streams_Shrinked.cfs", CFSUpdateMode.Update, CFSConfiguration.SectorRecycle); + cf.RootStorage.GetStorage("MiniStorage").Delete("miniSt"); + + + cf.RootStorage.GetStorage("MiniStorage").GetStream("miniSt2").Append(bE); + + cf.Commit(); + cf.Close(); + + //Test Phase 6 + + cfTest = new CompoundFile("6_Streams_Shrinked.cfs"); + byte[] d2 = cfTest.RootStorage.GetStorage("MiniStorage").GetStream("miniSt2").GetData(); + Assert.IsTrue(d2.Length == (bE.Length + bMini.Length)); + + int cnt = 1; + byte[] buf = new byte[cnt]; + cnt = cfTest.RootStorage.GetStorage("MiniStorage").GetStream("miniSt2").Read(buf, bMini.Length, cnt); + + Assert.IsTrue(cnt == 1); + Assert.IsTrue(buf[0] == 0x1A); + + cnt = 1; + cnt = cfTest.RootStorage.GetStorage("MiniStorage").GetStream("miniSt2").Read(buf, bMini.Length - 1, cnt); + Assert.IsTrue(cnt == 1); + Assert.IsTrue(buf[0] == 0xEE); + + try + { + cfTest.RootStorage.GetStorage("MiniStorage").GetStream("miniSt"); + } + catch (Exception ex) + { + Assert.IsTrue(ex is CFItemNotFound); + } + + cfTest.Close(); + + //############## + + //Phase 7 + + cf = new CompoundFile("6_Streams_Shrinked.cfs", CFSUpdateMode.Update, CFSConfiguration.SectorRecycle); + + cf.RootStorage.GetStorage("MiniStorage").GetStream("miniSt2").SetData(bA); + cf.Commit(); + cf.Close(); + + + //Test Phase 7 + + cf = new CompoundFile("6_Streams_Shrinked.cfs", CFSUpdateMode.Update, CFSConfiguration.SectorRecycle); + d2 = cf.RootStorage.GetStorage("MiniStorage").GetStream("miniSt2").GetData(); + Assert.IsNotNull(d2); + Assert.IsTrue(d2.Length == bA.Length); + + cf.Close(); + + //############## + + cf = new CompoundFile("6_Streams_Shrinked.cfs", CFSUpdateMode.ReadOnly, CFSConfiguration.SectorRecycle); + + var myStream = cf.RootStorage.GetStream("C"); + var data = myStream.GetData(); + Console.WriteLine(data[0] + " : " + data[data.Length - 1]); + + myStream = cf.RootStorage.GetStream("B"); + data = myStream.GetData(); + Console.WriteLine(data[0] + " : " + data[data.Length - 1]); + + cf.Close(); + + sw.Stop(); + Console.WriteLine(sw.ElapsedMilliseconds); + + } + + [TestMethod] + public void Test_RETRIVE_ALL_NAMED_ENTRIES() + { + var f = new CompoundFile("MultipleStorage4.cfs"); + IList result = f.GetAllNamedEntries("MyStream"); + + Assert.IsTrue(result.Count == 3); + } + + + [TestMethod] + public void Test_CORRUPTED_CYCLIC_FAT_CHECK() + { + CompoundFile f = null; + try + { + f = new CompoundFile("CyclicFAT.cfs"); + + } + catch (Exception ex) + { + Assert.IsTrue(ex is CFCorruptedFileException); + } + finally + { + if (f != null) + f.Close(); + } + } + + [TestMethod] + public void Test_DIFAT_CHECK() + { + CompoundFile f = null; + try + { + f = new CompoundFile(); + CFStream st = f.RootStorage.AddStream("LargeStream"); + st.Append(Helpers.GetBuffer(20000000, 0x0A)); //Forcing creation of two DIFAT sectors + byte[] b1 = Helpers.GetBuffer(3, 0x0B); + st.Append(b1); //Forcing creation of two DIFAT sectors + + f.Save("$OpenMcdf$LargeFile.cfs"); + + f.Close(); + + int cnt = 3; + f = new CompoundFile("$OpenMcdf$LargeFile.cfs"); + + byte[] b2 = new byte[cnt]; + cnt = f.RootStorage.GetStream("LargeStream").Read(b2, 20000000, cnt); + f.Close(); + Assert.IsTrue(Helpers.CompareBuffer(b1, b2)); + } + finally + { + if (f != null) + f.Close(); + + if (File.Exists("$OpenMcdf$LargeFile.cfs")) + File.Delete("$OpenMcdf$LargeFile.cfs"); + } + + } + + [TestMethod] + public void Test_ADD_LARGE_NUMBER_OF_ITEMS() + { + int ITEM_NUMBER = 10000; + + CompoundFile f = null; + byte[] buffer = Helpers.GetBuffer(10, 0x0A); + try + { + f = new CompoundFile(); + + for (int i = 0; i < ITEM_NUMBER; i++) + { + CFStream st = f.RootStorage.AddStream("Stream" + i.ToString()); + st.Append(buffer); + } + + + if (File.Exists("$ItemsLargeNumber.cfs")) + File.Delete("$ItemsLargeNumber.cfs"); + + f.Save("$ItemsLargeNumber.cfs"); + f.Close(); + + f = new CompoundFile("$ItemsLargeNumber.cfs"); + CFStream cfs = f.RootStorage.GetStream("Stream" + (ITEM_NUMBER / 2).ToString()); + + Assert.IsTrue(cfs != null, "Item is null"); + Assert.IsTrue(Helpers.CompareBuffer(cfs.GetData(), buffer), "Items are different"); + f.Close(); + } + catch (Exception ex) + { + Assert.Fail(ex.Message); + } + finally + { + //if (File.Exists("$ItemsLargeNumber.cfs")) + // File.Delete("$ItemsLargeNumber.cfs"); + } + + } + + [TestMethod] + public void Test_FIX_BUG_16_CORRUPTED_AFTER_RESIZE() + { + + const string FILE_PATH = @"BUG_16_.xls"; + + CompoundFile cf = new CompoundFile(FILE_PATH); + + CFStream dirStream = cf.RootStorage.GetStorage("_VBA_PROJECT_CUR").GetStorage("VBA").GetStream("dir"); + + byte[] currentData = dirStream.GetData(); + + Array.Resize(ref currentData, currentData.Length - 50); + + dirStream.SetData(currentData); + + cf.Save(FILE_PATH + ".edited"); + cf.Close(); + } + + + [TestMethod] + public void Test_FIX_BUG_17_CORRUPTED_PPT_FILE() + { + + const string FILE_PATH = @"2_MB-W.ppt"; + + using (CompoundFile file = new CompoundFile(FILE_PATH)) + { + //CFStorage dataSpaceInfo = file.RootStorage.GetStorage("\u0006DataSpaces").GetStorage("DataSpaceInfo"); + CFItem dsiItem = file.GetAllNamedEntries("DataSpaceInfo").FirstOrDefault(); + } + } + + [TestMethod] + public void Test_FIX_BUG_24_CORRUPTED_THUMBS_DB_FILE() + { + try + { + using (var cf = new CompoundFile("_thumbs_bug_24.db")) + { + cf.RootStorage.VisitEntries(item => Console.WriteLine(item.Name), recursive: false); + } + } + catch (Exception exc) + { + Assert.IsInstanceOfType(exc, typeof(CFCorruptedFileException)); + } + + using (var cf = new CompoundFile("report.xls")) + { + cf.RootStorage.VisitEntries(item => Console.WriteLine(item.Name), recursive: false); + } + + } + + + [TestMethod] + public void Test_FIX_BUG_28_CompoundFile_Delete_ChildElementMaintainsFiles() + { + using (var compoundFile = new CompoundFile()) + { + var storage1 = compoundFile.RootStorage.AddStorage("A"); + var storage2 = compoundFile.RootStorage.AddStorage("B"); + var storage3 = compoundFile.RootStorage.AddStorage("C"); + storage1.AddStream("A.1"); + compoundFile.RootStorage.Delete("B"); + storage1 = compoundFile.RootStorage.GetStorage("A"); + storage1.GetStream("A.1"); + } + } + + [TestMethod] + public void Test_CORRUPTEDDOC_BUG36_SHOULD_THROW_CORRUPTED_FILE_EXCEPTION() + { + FileStream fs = null; + try + { + fs = new FileStream("CorruptedDoc_bug36.doc", FileMode.Open, FileAccess.ReadWrite, FileShare.ReadWrite); + CompoundFile file = new CompoundFile(fs, CFSUpdateMode.ReadOnly, CFSConfiguration.LeaveOpen); + + } + catch (Exception ex) + { + Assert.IsTrue(fs.CanRead && fs.CanSeek && fs.CanWrite); + } + + + } + + //[TestMethod] + //public void Test_CORRUPTED_CYCLIC_DIFAT_VALIDATION_CHECK() + //{ + + // CompoundFile cf = null; + // try + // { + // cf = new CompoundFile("CiclycDFAT.cfs"); + // CFStorage s = cf.RootStorage.GetStorage("MyStorage"); + // CFStream st = s.GetStream("MyStream"); + // Assert.IsTrue(st.Size > 0); + // } + // catch (Exception ex) + // { + // Assert.IsTrue(ex is CFCorruptedFileException); + // } + // finally + // { + // if (cf != null) + // { + // cf.Close(); + // } + // } + //} + //[TestMethod] + //public void Test_REM() + //{ + // var f = new CompoundFile(); + + // byte[] bB = Helpers.GetBuffer(5 * 1024, 0x0B); + // f.RootStorage.AddStream("Test").AppendData(bB); + // f.Save("Astorage.cfs"); + //} + + } +} diff --git a/sources/Test/OpenMcdf.Test/Helpers.cs b/sources/Test/OpenMcdf.Test/Helpers.cs new file mode 100644 index 00000000..58fbfb38 --- /dev/null +++ b/sources/Test/OpenMcdf.Test/Helpers.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace OpenMcdf.Test +{ + public static class Helpers + { + public static byte[] GetBuffer(int count) + { + Random r = new Random(); + byte[] b = new byte[count]; + r.NextBytes(b); + return b; + } + + public static byte[] GetBuffer(int count, byte c) + { + byte[] b = new byte[count]; + for (int i = 0; i < b.Length; i++) + { + b[i] = c; + } + + return b; + } + + public static bool CompareBuffer(byte[] b, byte[] p) + { + bool res = CompareBuffer(b, p, b.Length); + return res && (b.Length == p.Length); + } + + public static bool CompareBuffer(byte[] b, byte[] p, int count) + { + if (b == null && p == null) + throw new Exception("Null buffers"); + + if (b == null && p != null) + return false; + + if (b != null && p == null) + return false; + + + for (int i = 0; i < count; i++) + { + if (b[i] != p[i]) + return false; + } + + return true; + } + } +} diff --git a/sources/Test/OpenMcdf.Test/OpenMcdf.Test.csproj b/sources/Test/OpenMcdf.Test/OpenMcdf.Test.csproj new file mode 100644 index 00000000..55b48849 --- /dev/null +++ b/sources/Test/OpenMcdf.Test/OpenMcdf.Test.csproj @@ -0,0 +1,120 @@ + + + + Debug + AnyCPU + 9.0.30729 + 2.0 + {FD339266-8842-40B4-9230-F8E84FC42AC2} + Library + Properties + OpenMcdf.Test + OpenMcdf.Test + v4.0 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + + + 3.5 + + http://localhost/OpenMcdfTest/ + true + Web + true + Foreground + 7 + Days + false + false + true + 0 + 1.0.0.%2a + true + false + true + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + AllRules.ruleset + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + AllRules.ruleset + + + false + + + + + + + + + + 3.5 + + + + + + + + + + + + + + + + + + + + False + Microsoft .NET Framework 4 %28x86 and x64%29 + true + + + False + .NET Framework 3.5 SP1 Client Profile + false + + + False + .NET Framework 3.5 SP1 + false + + + False + Windows Installer 3.1 + true + + + + + {56e15d4a-8a37-4c7c-bb44-fd59aff220c1} + OpenMcdf + + + + + \ No newline at end of file diff --git a/Unit Test/Properties/AssemblyInfo.cs b/sources/Test/OpenMcdf.Test/Properties/AssemblyInfo.cs similarity index 97% rename from Unit Test/Properties/AssemblyInfo.cs rename to sources/Test/OpenMcdf.Test/Properties/AssemblyInfo.cs index e7bf1361..995ba9af 100644 --- a/Unit Test/Properties/AssemblyInfo.cs +++ b/sources/Test/OpenMcdf.Test/Properties/AssemblyInfo.cs @@ -1,34 +1,34 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("OpenMcdfTest")] -[assembly: AssemblyDescription("")] -[assembly: AssemblyConfiguration("")] -[assembly: AssemblyCompany("-")] -[assembly: AssemblyProduct("OpenMcdfTest")] -[assembly: AssemblyCopyright("Copyright © 2010-2011, Federico Blaseotto")] -[assembly: AssemblyTrademark("")] -[assembly: AssemblyCulture("")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM componenets. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("7b918910-a8be-4e22-85d6-d927eae56003")] - -// Version information for an assembly consists of the following four values: -// -// Major Version -// Minor Version -// Build Number -// Revision -// -// You can specify all the values or you can default the Revision and Build Numbers -// by using the '*' as shown below: -[assembly: AssemblyVersion("1.5.1.*")] +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("OpenMcdfTest")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("-")] +[assembly: AssemblyProduct("OpenMcdfTest")] +[assembly: AssemblyCopyright("Copyright © 2010-2011, Federico Blaseotto")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM componenets. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("7b918910-a8be-4e22-85d6-d927eae56003")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: +[assembly: AssemblyVersion("1.5.1.*")] diff --git a/sources/Test/OpenMcdf.Test/RBTreeTest.cs b/sources/Test/OpenMcdf.Test/RBTreeTest.cs new file mode 100644 index 00000000..a6d7fc26 --- /dev/null +++ b/sources/Test/OpenMcdf.Test/RBTreeTest.cs @@ -0,0 +1,233 @@ +using System; +using System.Text; +using System.Collections.Generic; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using OpenMcdf; +using RedBlackTree; + +namespace OpenMcdf.Test +{ + /// + /// Summary description for RBTreeTest + /// + [TestClass] + public class RBTreeTest + { + public RBTreeTest() + { + + } + + private TestContext testContextInstance; + + /// + ///Gets or sets the test context which provides + ///information about and functionality for the current test run. + /// + public TestContext TestContext + { + get + { + return testContextInstance; + } + set + { + testContextInstance = value; + } + } + + #region Additional test attributes + // + // You can use the following additional attributes as you write your tests: + // + // Use ClassInitialize to run code before running the first test in the class + // [ClassInitialize()] + // public static void MyClassInitialize(TestContext testContext) { } + // + // Use ClassCleanup to run code after all tests in a class have run + // [ClassCleanup()] + // public static void MyClassCleanup() { } + // + // Use TestInitialize to run code before running each test + // [TestInitialize()] + // public void MyTestInitialize() { } + // + // Use TestCleanup to run code after each test has run + // [TestCleanup()] + // public void MyTestCleanup() { } + // + #endregion + + internal IList GetDirectoryRepository(int count) + { + List repo = new List(); + for (int i = 0; i < count; i++) + { + IDirectoryEntry de = DirectoryEntry.New(i.ToString(), StgType.StgInvalid, repo); + } + + return repo; + } + + [TestMethod] + public void Test_RBTREE_INSERT() + { + RBTree rbTree = new RBTree(); + System.Collections.Generic.IList repo = GetDirectoryRepository(25); + + foreach (var item in repo) + { + rbTree.Insert(item); + } + + for (int i = 0; i < repo.Count; i++) + { + IRBNode c; + rbTree.TryLookup(DirectoryEntry.Mock(i.ToString(), StgType.StgInvalid), out c); + Assert.IsTrue(c is IDirectoryEntry); + Assert.IsTrue(((IDirectoryEntry)c).Name == i.ToString()); + //Assert.IsTrue(c.IsStream); + } + } + + + [TestMethod] + public void Test_RBTREE_DELETE() + { + RBTree rbTree = new RBTree(); + System.Collections.Generic.IList repo = GetDirectoryRepository(25); + + + foreach (var item in repo) + { + rbTree.Insert(item); + } + + try + { + IRBNode n; + rbTree.Delete(DirectoryEntry.Mock("5", StgType.StgInvalid),out n); + rbTree.Delete(DirectoryEntry.Mock("24", StgType.StgInvalid), out n); + rbTree.Delete(DirectoryEntry.Mock("7", StgType.StgInvalid), out n); + } + catch (Exception ex) + { + Assert.Fail("Item removal failed: " + ex.Message); + } + + + + // CFItem c; + // bool s = rbTree.TryLookup(new CFMock("7", StgType.StgStream), out c); + + + // Assert.IsFalse(s); + + // c = null; + + // Assert.IsTrue(rbTree.TryLookup(new CFMock("6", StgType.StgStream), out c)); + // Assert.IsTrue(c.IsStream); + // Assert.IsTrue(rbTree.TryLookup(new CFMock("12", StgType.StgStream), out c)); + // Assert.IsTrue(c.Name == "12"); + + + //} + + + } + + private static void VerifyProperties(RBTree t) + { + VerifyProperty1(t.Root); + VerifyProperty2(t.Root); + // Property 3 is implicit + VerifyProperty4(t.Root); + VerifyProperty5(t.Root); + } + + private static Color NodeColor(IRBNode n) + { + return n == null ? Color.BLACK : n.Color; + } + + private static void VerifyProperty1(IRBNode n) + { + + Assert.IsTrue(NodeColor(n) == Color.RED || NodeColor(n) == Color.BLACK); + + if (n == null) return; + VerifyProperty1(n.Left); + VerifyProperty1(n.Right); + } + + private static void VerifyProperty2(IRBNode root) + { + Assert.IsTrue(NodeColor(root) == Color.BLACK); + } + + private static void VerifyProperty4(IRBNode n) + { + + if (NodeColor(n) == Color.RED) + { + Assert.IsTrue((NodeColor(n.Left) == Color.BLACK)); + Assert.IsTrue((NodeColor(n.Right) == Color.BLACK)); + Assert.IsTrue((NodeColor(n.Parent) == Color.BLACK)); + } + + if (n == null) return; + VerifyProperty4(n.Left); + VerifyProperty4(n.Right); + } + + private static void VerifyProperty5(IRBNode root) + { + VerifyProperty5Helper(root, 0, -1); + } + + private static int VerifyProperty5Helper(IRBNode n, int blackCount, int pathBlackCount) + { + if (NodeColor(n) == Color.BLACK) + { + blackCount++; + } + if (n == null) + { + if (pathBlackCount == -1) + { + pathBlackCount = blackCount; + } + else + { + + Assert.IsTrue(blackCount == pathBlackCount); + + } + return pathBlackCount; + } + + pathBlackCount = VerifyProperty5Helper(n.Left, blackCount, pathBlackCount); + pathBlackCount = VerifyProperty5Helper(n.Right, blackCount, pathBlackCount); + + return pathBlackCount; + } + + + + [TestMethod] + public void Test_RBTREE_ENUMERATE() + { + RBTree rbTree = new RBTree(); + System.Collections.Generic.IList repo = GetDirectoryRepository(10000); + + foreach (var item in repo) + { + rbTree.Insert(item); + } + + VerifyProperties(rbTree); + //rbTree.Print(); + } + } +} diff --git a/Unit Test/SectorCollectionTest.cs b/sources/Test/OpenMcdf.Test/SectorCollectionTest.cs similarity index 95% rename from Unit Test/SectorCollectionTest.cs rename to sources/Test/OpenMcdf.Test/SectorCollectionTest.cs index 8c120145..a64fcaca 100644 --- a/Unit Test/SectorCollectionTest.cs +++ b/sources/Test/OpenMcdf.Test/SectorCollectionTest.cs @@ -1,198 +1,198 @@ -using OpenMcdf; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System.Collections.Generic; -using System; - -namespace OpenMcdfTest -{ - - - /// - ///This is a test class for SectorCollectionTest and is intended - ///to contain all SectorCollectionTest Unit Tests - /// - [TestClass()] - public class SectorCollectionTest - { - - - private TestContext testContextInstance; - - /// - ///Gets or sets the test context which provides - ///information about and functionality for the current test run. - /// - public TestContext TestContext - { - get - { - return testContextInstance; - } - set - { - testContextInstance = value; - } - } - - #region Additional test attributes - // - //You can use the following additional attributes as you write your tests: - // - //Use ClassInitialize to run code before running the first test in the class - //[ClassInitialize()] - //public static void MyClassInitialize(TestContext testContext) - //{ - //} - // - //Use ClassCleanup to run code after all tests in a class have run - //[ClassCleanup()] - //public static void MyClassCleanup() - //{ - //} - // - //Use TestInitialize to run code before running each test - //[TestInitialize()] - //public void MyTestInitialize() - //{ - //} - // - //Use TestCleanup to run code after each test has run - //[TestCleanup()] - //public void MyTestCleanup() - //{ - //} - // - #endregion - - - /// - ///A test for Count - /// - [TestMethod()] - public void CountTest() - { - - int count = 0; - - SectorCollection target = new SectorCollection(); // TODO: Initialize to an appropriate value - int actual; - actual = target.Count; - - Assert.IsTrue(actual == count); - Sector s = new Sector(4096); - - target.Add(s); - Assert.IsTrue(target.Count == actual + 1); - - - for (int i = 0; i < 5000; i++) - target.Add(s); - - Assert.IsTrue(target.Count == actual + 1 + 5000); - } - - /// - ///A test for Item - /// - [TestMethod()] - public void ItemTest() - { - int count = 37; - - SectorCollection target = new SectorCollection(); - int index = 0; - - Sector expected = new Sector(4096); - target.Add(null); - - Sector actual; - target[index] = expected; - actual = target[index]; - - Assert.AreEqual(expected, actual); - Assert.IsNotNull(actual); - Assert.IsTrue(actual.Id == expected.Id); - - actual = null; - - try - { - actual = target[count + 100]; - } - catch (Exception ex) - { - Assert.IsTrue(ex is ArgumentOutOfRangeException); - } - - try - { - actual = target[-1]; - } - catch (Exception ex) - { - Assert.IsTrue(ex is ArgumentOutOfRangeException); - } - } - - /// - ///A test for SectorCollection Constructor - /// - [TestMethod()] - public void SectorCollectionConstructorTest() - { - - SectorCollection target = new SectorCollection(); - - Assert.IsNotNull(target); - Assert.IsTrue(target.Count == 0); - - Sector s = new Sector(4096); - target.Add(s); - Assert.IsTrue(target.Count == 1); - } - - /// - ///A test for Add - /// - [TestMethod()] - public void AddTest() - { - SectorCollection target = new SectorCollection(); - for (int i = 0; i < 579; i++) - { - target.Add(null); - } - - - Sector item = new Sector(4096); - target.Add(item); - Assert.IsTrue(target.Count == 580); - } - - /// - ///A test for GetEnumerator - /// - [TestMethod()] - public void GetEnumeratorTest() - { - SectorCollection target = new SectorCollection(); - for (int i = 0; i < 579; i++) - { - target.Add(null); - } - - - Sector item = new Sector(4096); - target.Add(item); - Assert.IsTrue(target.Count == 580); - - int cnt = 0; - foreach (Sector s in target) - { - cnt++; - } - - Assert.IsTrue(cnt == target.Count); - } - } -} +using OpenMcdf; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Collections.Generic; +using System; + +namespace OpenMcdf.Test +{ + + + /// + ///This is a test class for SectorCollectionTest and is intended + ///to contain all SectorCollectionTest Unit Tests + /// + [TestClass()] + public class SectorCollectionTest + { + + + private TestContext testContextInstance; + + /// + ///Gets or sets the test context which provides + ///information about and functionality for the current test run. + /// + public TestContext TestContext + { + get + { + return testContextInstance; + } + set + { + testContextInstance = value; + } + } + + #region Additional test attributes + // + //You can use the following additional attributes as you write your tests: + // + //Use ClassInitialize to run code before running the first test in the class + //[ClassInitialize()] + //public static void MyClassInitialize(TestContext testContext) + //{ + //} + // + //Use ClassCleanup to run code after all tests in a class have run + //[ClassCleanup()] + //public static void MyClassCleanup() + //{ + //} + // + //Use TestInitialize to run code before running each test + //[TestInitialize()] + //public void MyTestInitialize() + //{ + //} + // + //Use TestCleanup to run code after each test has run + //[TestCleanup()] + //public void MyTestCleanup() + //{ + //} + // + #endregion + + + /// + ///A test for Count + /// + [TestMethod()] + public void CountTest() + { + + int count = 0; + + SectorCollection target = new SectorCollection(); // TODO: Initialize to an appropriate value + int actual; + actual = target.Count; + + Assert.IsTrue(actual == count); + Sector s = new Sector(4096); + + target.Add(s); + Assert.IsTrue(target.Count == actual + 1); + + + for (int i = 0; i < 5000; i++) + target.Add(s); + + Assert.IsTrue(target.Count == actual + 1 + 5000); + } + + /// + ///A test for Item + /// + [TestMethod()] + public void ItemTest() + { + int count = 37; + + SectorCollection target = new SectorCollection(); + int index = 0; + + Sector expected = new Sector(4096); + target.Add(null); + + Sector actual; + target[index] = expected; + actual = target[index]; + + Assert.AreEqual(expected, actual); + Assert.IsNotNull(actual); + Assert.IsTrue(actual.Id == expected.Id); + + actual = null; + + try + { + actual = target[count + 100]; + } + catch (Exception ex) + { + Assert.IsTrue(ex is ArgumentOutOfRangeException); + } + + try + { + actual = target[-1]; + } + catch (Exception ex) + { + Assert.IsTrue(ex is ArgumentOutOfRangeException); + } + } + + /// + ///A test for SectorCollection Constructor + /// + [TestMethod()] + public void SectorCollectionConstructorTest() + { + + SectorCollection target = new SectorCollection(); + + Assert.IsNotNull(target); + Assert.IsTrue(target.Count == 0); + + Sector s = new Sector(4096); + target.Add(s); + Assert.IsTrue(target.Count == 1); + } + + /// + ///A test for Add + /// + [TestMethod()] + public void AddTest() + { + SectorCollection target = new SectorCollection(); + for (int i = 0; i < 579; i++) + { + target.Add(null); + } + + + Sector item = new Sector(4096); + target.Add(item); + Assert.IsTrue(target.Count == 580); + } + + /// + ///A test for GetEnumerator + /// + [TestMethod()] + public void GetEnumeratorTest() + { + SectorCollection target = new SectorCollection(); + for (int i = 0; i < 579; i++) + { + target.Add(null); + } + + + Sector item = new Sector(4096); + target.Add(item); + Assert.IsTrue(target.Count == 580); + + int cnt = 0; + foreach (Sector s in target) + { + cnt++; + } + + Assert.IsTrue(cnt == target.Count); + } + } +} diff --git a/sources/Test/OpenMcdf.Test/StreamRWTest.cs b/sources/Test/OpenMcdf.Test/StreamRWTest.cs new file mode 100644 index 00000000..6d3ac7b2 --- /dev/null +++ b/sources/Test/OpenMcdf.Test/StreamRWTest.cs @@ -0,0 +1,52 @@ +using System; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.IO; + +namespace OpenMcdf.Test +{ + [TestClass] + public class StreamRWTest + { + [TestMethod] + public void ReadInt64_MaxSizeRead() + { + Int64 input = Int64.MaxValue; + byte[] bytes = BitConverter.GetBytes(input); + long actual = 0; + using (MemoryStream memStream = new MemoryStream(bytes)) + { + OpenMcdf.StreamRW reader = new OpenMcdf.StreamRW(memStream); + actual = reader.ReadInt64(); + } + Assert.AreEqual((long)input, actual); + } + + [TestMethod] + public void ReadInt64_SmallNumber() + { + Int64 input = 1234; + byte[] bytes = BitConverter.GetBytes(input); + long actual = 0; + using (MemoryStream memStream = new MemoryStream(bytes)) + { + OpenMcdf.StreamRW reader = new OpenMcdf.StreamRW(memStream); + actual = reader.ReadInt64(); + } + Assert.AreEqual((long)input, actual); + } + + [TestMethod] + public void ReadInt64_Int32MaxPlusTen() + { + Int64 input = (Int64)Int32.MaxValue + 10; + byte[] bytes = BitConverter.GetBytes(input); + long actual = 0; + using (MemoryStream memStream = new MemoryStream(bytes)) + { + OpenMcdf.StreamRW reader = new OpenMcdf.StreamRW(memStream); + actual = reader.ReadInt64(); + } + Assert.AreEqual((long)input, actual); + } + } +} diff --git a/sources/Test/TestFiles/2_MB-W.ppt b/sources/Test/TestFiles/2_MB-W.ppt new file mode 100644 index 00000000..79852522 Binary files /dev/null and b/sources/Test/TestFiles/2_MB-W.ppt differ diff --git a/sources/Test/TestFiles/BUG_16_.xls b/sources/Test/TestFiles/BUG_16_.xls new file mode 100644 index 00000000..be2553a7 Binary files /dev/null and b/sources/Test/TestFiles/BUG_16_.xls differ diff --git a/TestFiles/CorruptedDoc_bug3547815.doc b/sources/Test/TestFiles/CorruptedDoc_bug3547815.doc similarity index 100% rename from TestFiles/CorruptedDoc_bug3547815.doc rename to sources/Test/TestFiles/CorruptedDoc_bug3547815.doc diff --git a/TestFiles/CorruptedDoc_bug3547815_B.doc b/sources/Test/TestFiles/CorruptedDoc_bug3547815_B.doc similarity index 100% rename from TestFiles/CorruptedDoc_bug3547815_B.doc rename to sources/Test/TestFiles/CorruptedDoc_bug3547815_B.doc diff --git a/sources/Test/TestFiles/CorruptedDoc_bug36.doc b/sources/Test/TestFiles/CorruptedDoc_bug36.doc new file mode 100644 index 00000000..ab9ed4a9 Binary files /dev/null and b/sources/Test/TestFiles/CorruptedDoc_bug36.doc differ diff --git a/TestFiles/CyclicFAT.cfs b/sources/Test/TestFiles/CyclicFAT.cfs similarity index 100% rename from TestFiles/CyclicFAT.cfs rename to sources/Test/TestFiles/CyclicFAT.cfs diff --git a/TestFiles/MultipleStorage.cfs b/sources/Test/TestFiles/MultipleStorage.cfs similarity index 100% rename from TestFiles/MultipleStorage.cfs rename to sources/Test/TestFiles/MultipleStorage.cfs diff --git a/TestFiles/MultipleStorage2.cfs b/sources/Test/TestFiles/MultipleStorage2.cfs similarity index 100% rename from TestFiles/MultipleStorage2.cfs rename to sources/Test/TestFiles/MultipleStorage2.cfs diff --git a/TestFiles/MultipleStorage3.cfs b/sources/Test/TestFiles/MultipleStorage3.cfs similarity index 100% rename from TestFiles/MultipleStorage3.cfs rename to sources/Test/TestFiles/MultipleStorage3.cfs diff --git a/TestFiles/MultipleStorage4.cfs b/sources/Test/TestFiles/MultipleStorage4.cfs similarity index 100% rename from TestFiles/MultipleStorage4.cfs rename to sources/Test/TestFiles/MultipleStorage4.cfs diff --git a/sources/Test/TestFiles/_thumbs_bug_24.db b/sources/Test/TestFiles/_thumbs_bug_24.db new file mode 100644 index 00000000..ffe469ff Binary files /dev/null and b/sources/Test/TestFiles/_thumbs_bug_24.db differ diff --git a/TestFiles/report.xls b/sources/Test/TestFiles/report.xls similarity index 100% rename from TestFiles/report.xls rename to sources/Test/TestFiles/report.xls diff --git a/TestFiles/reportREAD.xls b/sources/Test/TestFiles/reportREAD.xls similarity index 100% rename from TestFiles/reportREAD.xls rename to sources/Test/TestFiles/reportREAD.xls diff --git a/TestFiles/report_name_fix.xls b/sources/Test/TestFiles/report_name_fix.xls similarity index 100% rename from TestFiles/report_name_fix.xls rename to sources/Test/TestFiles/report_name_fix.xls diff --git a/TestFiles/testbad.ole b/sources/Test/TestFiles/testbad.ole similarity index 100% rename from TestFiles/testbad.ole rename to sources/Test/TestFiles/testbad.ole