Do not set the iSubItem part of the struct for LVM_HITTEST,
[wine] / dlls / ole32 / storage32.c
1 /*
2  * Compound Storage (32 bit version)
3  * Storage implementation
4  *
5  * This file contains the compound file implementation
6  * of the storage interface.
7  *
8  * Copyright 1999 Francis Beaudet
9  * Copyright 1999 Sylvain St-Germain
10  * Copyright 1999 Thuy Nguyen
11  */
12
13 #include <assert.h>
14 #include <stdio.h>
15 #include <stdlib.h>
16 #include <string.h>
17
18 #include "winbase.h" /* for lstrlenW() and the likes */
19 #include "winnls.h"
20 #include "wine/unicode.h"
21 #include "wine/winestring.h"
22 #include "debugtools.h"
23
24 #include "storage32.h"
25 #include "ole2.h"      /* For Write/ReadClassStm */
26
27 #include "winreg.h"
28 #include "wine/wingdi16.h"
29
30 DEFAULT_DEBUG_CHANNEL(storage);
31
32 #define FILE_BEGIN 0
33
34
35 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
36 #define OLESTREAM_ID 0x501
37 #define OLESTREAM_MAX_STR_LEN 255
38
39 static const char rootPropertyName[] = "Root Entry";
40
41
42 /* OLESTREAM memory structure to use for Get and Put Routines */
43 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
44 typedef struct 
45 {
46     DWORD dwOleID;
47     DWORD dwTypeID;
48     DWORD dwOleTypeNameLength;
49     CHAR  strOleTypeName[OLESTREAM_MAX_STR_LEN];
50     CHAR  *pstrOleObjFileName;
51     DWORD dwOleObjFileNameLength;
52     DWORD dwMetaFileWidth;
53     DWORD dwMetaFileHeight;
54     CHAR  strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
55     DWORD dwDataLength;
56     BYTE *pData;
57 }OLECONVERT_OLESTREAM_DATA;
58
59 /* CompObj Stream structure */
60 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
61 typedef struct
62 {
63     BYTE byUnknown1[12];
64     CLSID clsid;
65     DWORD dwCLSIDNameLength;
66     CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
67     DWORD dwOleTypeNameLength;
68     CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
69     DWORD dwProgIDNameLength;
70     CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
71     BYTE byUnknown2[16];
72 }OLECONVERT_ISTORAGE_COMPOBJ;
73
74
75 /* Ole Presention Stream structure */
76 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
77 typedef struct
78 {
79     BYTE byUnknown1[28];
80     DWORD dwExtentX;
81     DWORD dwExtentY;
82     DWORD dwSize;  
83     BYTE *pData;
84 }OLECONVERT_ISTORAGE_OLEPRES;
85
86
87
88 /***********************************************************************
89  * Forward declaration of internal functions used by the method DestroyElement
90  */
91 static HRESULT deleteStorageProperty(
92   StorageImpl *parentStorage,
93   ULONG        foundPropertyIndexToDelete,
94   StgProperty  propertyToDelete);
95
96 static HRESULT deleteStreamProperty(
97   StorageImpl *parentStorage,
98   ULONG         foundPropertyIndexToDelete,
99   StgProperty   propertyToDelete);
100
101 static HRESULT findPlaceholder(
102   StorageImpl *storage,
103   ULONG         propertyIndexToStore,
104   ULONG         storagePropertyIndex,
105   INT         typeOfRelation);
106
107 static HRESULT adjustPropertyChain( 
108   StorageImpl *This,
109   StgProperty   propertyToDelete,
110   StgProperty   parentProperty,
111   ULONG         parentPropertyId,
112   INT         typeOfRelation);
113
114 /***********************************************************************
115  * Declaration of the functions used to manipulate StgProperty
116  */
117
118 static ULONG getFreeProperty(
119   StorageImpl *storage);
120
121 static void updatePropertyChain(
122   StorageImpl *storage,
123   ULONG       newPropertyIndex,
124   StgProperty newProperty);
125
126 static LONG propertyNameCmp(
127   OLECHAR *newProperty,
128   OLECHAR *currentProperty);
129
130
131 /***********************************************************************
132  * Declaration of miscellaneous functions...
133  */
134 static HRESULT validateSTGM(DWORD stgmValue); 
135
136 static DWORD GetShareModeFromSTGM(DWORD stgm);
137 static DWORD GetAccessModeFromSTGM(DWORD stgm);
138 static DWORD GetCreationModeFromSTGM(DWORD stgm);
139
140 /*
141  * Virtual function table for the IStorage32Impl class.
142  */
143 static ICOM_VTABLE(IStorage) Storage32Impl_Vtbl =
144 {
145     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
146     StorageBaseImpl_QueryInterface,
147     StorageBaseImpl_AddRef,
148     StorageBaseImpl_Release,
149     StorageBaseImpl_CreateStream,
150     StorageBaseImpl_OpenStream,
151     StorageImpl_CreateStorage,
152     StorageBaseImpl_OpenStorage,
153     StorageImpl_CopyTo,
154     StorageImpl_MoveElementTo,
155     StorageImpl_Commit,
156     StorageImpl_Revert,
157     StorageBaseImpl_EnumElements,
158     StorageImpl_DestroyElement,
159     StorageBaseImpl_RenameElement,
160     StorageImpl_SetElementTimes,
161     StorageBaseImpl_SetClass,
162     StorageImpl_SetStateBits,
163     StorageBaseImpl_Stat
164 };
165
166 /*
167  * Virtual function table for the Storage32InternalImpl class.
168  */
169 static ICOM_VTABLE(IStorage) Storage32InternalImpl_Vtbl =
170   {
171     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
172     StorageBaseImpl_QueryInterface,
173     StorageBaseImpl_AddRef,
174     StorageBaseImpl_Release,
175     StorageBaseImpl_CreateStream,
176     StorageBaseImpl_OpenStream,
177     StorageImpl_CreateStorage,
178     StorageBaseImpl_OpenStorage,
179     StorageImpl_CopyTo,
180     StorageImpl_MoveElementTo,
181     StorageInternalImpl_Commit,
182     StorageInternalImpl_Revert,
183     StorageBaseImpl_EnumElements,
184     StorageImpl_DestroyElement,
185     StorageBaseImpl_RenameElement,
186     StorageImpl_SetElementTimes,
187     StorageBaseImpl_SetClass,
188     StorageImpl_SetStateBits,
189     StorageBaseImpl_Stat
190 };
191
192 /*
193  * Virtual function table for the IEnumSTATSTGImpl class.
194  */
195 static ICOM_VTABLE(IEnumSTATSTG) IEnumSTATSTGImpl_Vtbl =
196 {
197     ICOM_MSVTABLE_COMPAT_DummyRTTIVALUE
198     IEnumSTATSTGImpl_QueryInterface,
199     IEnumSTATSTGImpl_AddRef,
200     IEnumSTATSTGImpl_Release,
201     IEnumSTATSTGImpl_Next,
202     IEnumSTATSTGImpl_Skip,
203     IEnumSTATSTGImpl_Reset,
204     IEnumSTATSTGImpl_Clone
205 };
206
207
208
209
210
211 /************************************************************************
212 ** Storage32BaseImpl implementatiion
213 */
214
215 /************************************************************************
216  * Storage32BaseImpl_QueryInterface (IUnknown)
217  *
218  * This method implements the common QueryInterface for all IStorage32
219  * implementations contained in this file.
220  * 
221  * See Windows documentation for more details on IUnknown methods.
222  */
223 HRESULT WINAPI StorageBaseImpl_QueryInterface(
224   IStorage*        iface,
225   REFIID             riid,
226   void**             ppvObject)
227 {
228   ICOM_THIS(StorageBaseImpl,iface);
229   /*
230    * Perform a sanity check on the parameters.
231    */
232   if ( (This==0) || (ppvObject==0) )
233     return E_INVALIDARG;
234   
235   /*
236    * Initialize the return parameter.
237    */
238   *ppvObject = 0;
239   
240   /*
241    * Compare the riid with the interface IDs implemented by this object.
242    */
243   if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0) 
244   {
245     *ppvObject = (IStorage*)This;
246   }
247   else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStorage)) == 0) 
248   {
249     *ppvObject = (IStorage*)This;
250   }
251   
252   /*
253    * Check that we obtained an interface.
254    */
255   if ((*ppvObject)==0)
256     return E_NOINTERFACE;
257   
258   /*
259    * Query Interface always increases the reference count by one when it is
260    * successful
261    */
262   StorageBaseImpl_AddRef(iface);
263
264   return S_OK;
265 }
266         
267 /************************************************************************
268  * Storage32BaseImpl_AddRef (IUnknown)
269  *
270  * This method implements the common AddRef for all IStorage32
271  * implementations contained in this file.
272  * 
273  * See Windows documentation for more details on IUnknown methods.
274  */
275 ULONG WINAPI StorageBaseImpl_AddRef( 
276             IStorage* iface)
277 {
278   ICOM_THIS(StorageBaseImpl,iface);
279   This->ref++;
280
281   return This->ref;
282 }
283         
284 /************************************************************************
285  * Storage32BaseImpl_Release (IUnknown)
286  *
287  * This method implements the common Release for all IStorage32
288  * implementations contained in this file.
289  * 
290  * See Windows documentation for more details on IUnknown methods.
291  */
292 ULONG WINAPI StorageBaseImpl_Release( 
293       IStorage* iface)
294 {
295   ICOM_THIS(StorageBaseImpl,iface);
296   /*
297    * Decrease the reference count on this object.
298    */
299   This->ref--;
300
301   /*
302    * If the reference count goes down to 0, perform suicide.
303    */
304   if (This->ref==0)
305   {
306     /*
307      * Since we are using a system of base-classes, we want to call the 
308      * destructor of the appropriate derived class. To do this, we are 
309      * using virtual functions to implement the destructor.
310      */
311     This->v_destructor(This);
312
313     return 0;
314   }
315   
316   return This->ref;
317 }
318
319 /************************************************************************
320  * Storage32BaseImpl_OpenStream (IStorage)
321  *
322  * This method will open the specified stream object from the current storage.
323  *
324  * See Windows documentation for more details on IStorage methods.
325  */
326 HRESULT WINAPI StorageBaseImpl_OpenStream( 
327   IStorage*        iface,
328   const OLECHAR*   pwcsName,  /* [string][in] */
329   void*            reserved1, /* [unique][in] */
330   DWORD            grfMode,   /* [in]  */        
331   DWORD            reserved2, /* [in]  */        
332   IStream**        ppstm)     /* [out] */       
333 {
334   ICOM_THIS(StorageBaseImpl,iface);
335   IEnumSTATSTGImpl* propertyEnumeration;
336   StgStreamImpl*    newStream;
337   StgProperty       currentProperty;
338   ULONG             foundPropertyIndex;
339
340   TRACE("(%p, %s, %p, %lx, %ld, %p)\n", 
341         iface, debugstr_w(pwcsName), reserved1, grfMode, reserved2, ppstm);
342
343   /*
344    * Perform a sanity check on the parameters.
345    */
346   if ( (pwcsName==NULL) || (ppstm==0) )
347     return E_INVALIDARG;
348   
349   /*
350    * Initialize the out parameter
351    */
352   *ppstm = 0;
353   
354   /*
355    * Validate the STGM flags
356    */
357   if ( FAILED( validateSTGM(grfMode) ))
358     return STG_E_INVALIDFLAG;
359
360   /*
361    * As documented.
362    */
363   if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
364         (grfMode & STGM_DELETEONRELEASE) ||
365         (grfMode & STGM_TRANSACTED) )
366     return STG_E_INVALIDFUNCTION;
367
368   /*
369    * Create a property enumeration to search the properties
370    */
371   propertyEnumeration = IEnumSTATSTGImpl_Construct(
372     This->ancestorStorage, 
373     This->rootPropertySetIndex);
374   
375   /*
376    * Search the enumeration for the property with the given name
377    */
378   foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
379     propertyEnumeration,
380     pwcsName,
381     &currentProperty);
382   
383   /*
384    * Delete the property enumeration since we don't need it anymore
385    */
386   IEnumSTATSTGImpl_Destroy(propertyEnumeration);
387   
388   /*
389    * If it was found, construct the stream object and return a pointer to it.
390    */
391   if ( (foundPropertyIndex!=PROPERTY_NULL) && 
392        (currentProperty.propertyType==PROPTYPE_STREAM) )
393   {
394     newStream = StgStreamImpl_Construct(This, grfMode, foundPropertyIndex);
395     
396     if (newStream!=0)
397     {
398       newStream->grfMode = grfMode;
399       *ppstm = (IStream*)newStream;
400
401       /*
402        * Since we are returning a pointer to the interface, we have to 
403        * nail down the reference.
404        */
405       StgStreamImpl_AddRef(*ppstm);
406       
407       return S_OK;
408     }
409     
410     return E_OUTOFMEMORY;
411   }
412   
413   return STG_E_FILENOTFOUND;
414 }
415
416 /************************************************************************
417  * Storage32BaseImpl_OpenStorage (IStorage)
418  *
419  * This method will open a new storage object from the current storage.
420  * 
421  * See Windows documentation for more details on IStorage methods.
422  */        
423 HRESULT WINAPI StorageBaseImpl_OpenStorage( 
424   IStorage*        iface,
425   const OLECHAR*   pwcsName,      /* [string][unique][in] */ 
426   IStorage*        pstgPriority,  /* [unique][in] */         
427   DWORD            grfMode,       /* [in] */                 
428   SNB              snbExclude,    /* [unique][in] */         
429   DWORD            reserved,      /* [in] */                 
430   IStorage**       ppstg)         /* [out] */                        
431 {
432   ICOM_THIS(StorageBaseImpl,iface);
433   StorageInternalImpl* newStorage;
434   IEnumSTATSTGImpl*      propertyEnumeration;
435   StgProperty            currentProperty;
436   ULONG                  foundPropertyIndex;
437
438   TRACE("(%p, %s, %p, %lx, %p, %ld, %p)\n", 
439         iface, debugstr_w(pwcsName), pstgPriority, 
440         grfMode, snbExclude, reserved, ppstg);
441   
442   /*
443    * Perform a sanity check on the parameters.
444    */
445   if ( (This==0) || (pwcsName==NULL) || (ppstg==0) )
446     return E_INVALIDARG;
447   
448   /*
449    * Validate the STGM flags
450    */
451   if ( FAILED( validateSTGM(grfMode) ))
452     return STG_E_INVALIDFLAG;
453
454   /*
455    * As documented.
456    */
457   if ( !(grfMode & STGM_SHARE_EXCLUSIVE) || 
458         (grfMode & STGM_DELETEONRELEASE) ||
459         (grfMode & STGM_PRIORITY) )
460     return STG_E_INVALIDFUNCTION;
461
462   /*
463    * Initialize the out parameter
464    */
465   *ppstg = 0;
466   
467   /*
468    * Create a property enumeration to search the properties
469    */
470   propertyEnumeration = IEnumSTATSTGImpl_Construct(
471                           This->ancestorStorage, 
472                           This->rootPropertySetIndex);
473   
474   /*
475    * Search the enumeration for the property with the given name
476    */
477   foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(
478                          propertyEnumeration,
479                          pwcsName,
480                          &currentProperty);
481   
482   /*
483    * Delete the property enumeration since we don't need it anymore
484    */
485   IEnumSTATSTGImpl_Destroy(propertyEnumeration);
486   
487   /*
488    * If it was found, construct the stream object and return a pointer to it.
489    */
490   if ( (foundPropertyIndex!=PROPERTY_NULL) && 
491        (currentProperty.propertyType==PROPTYPE_STORAGE) )
492   {
493     /*
494      * Construct a new Storage object
495      */
496     newStorage = StorageInternalImpl_Construct(
497                    This->ancestorStorage,
498                    foundPropertyIndex);
499     
500     if (newStorage != 0)
501     {
502       *ppstg = (IStorage*)newStorage;
503
504       /*
505        * Since we are returning a pointer to the interface, 
506        * we have to nail down the reference.
507        */
508       StorageBaseImpl_AddRef(*ppstg);
509       
510       return S_OK;
511     }
512     
513     return STG_E_INSUFFICIENTMEMORY;
514   }
515   
516   return STG_E_FILENOTFOUND;
517 }
518
519 /************************************************************************
520  * Storage32BaseImpl_EnumElements (IStorage)
521  *
522  * This method will create an enumerator object that can be used to 
523  * retrieve informatino about all the properties in the storage object.
524  * 
525  * See Windows documentation for more details on IStorage methods.
526  */        
527 HRESULT WINAPI StorageBaseImpl_EnumElements( 
528   IStorage*       iface,
529   DWORD           reserved1, /* [in] */                  
530   void*           reserved2, /* [size_is][unique][in] */ 
531   DWORD           reserved3, /* [in] */                  
532   IEnumSTATSTG**  ppenum)    /* [out] */                 
533 {
534   ICOM_THIS(StorageBaseImpl,iface);
535   IEnumSTATSTGImpl* newEnum;
536
537   TRACE("(%p, %ld, %p, %ld, %p)\n", 
538         iface, reserved1, reserved2, reserved3, ppenum);
539
540   /*
541    * Perform a sanity check on the parameters.
542    */
543   if ( (This==0) || (ppenum==0))
544     return E_INVALIDARG;
545   
546   /*
547    * Construct the enumerator.
548    */
549   newEnum = IEnumSTATSTGImpl_Construct(
550               This->ancestorStorage,
551               This->rootPropertySetIndex);
552
553   if (newEnum!=0)
554   {
555     *ppenum = (IEnumSTATSTG*)newEnum;
556
557     /*
558      * Don't forget to nail down a reference to the new object before
559      * returning it.
560      */
561     IEnumSTATSTGImpl_AddRef(*ppenum);
562     
563     return S_OK;
564   }
565
566   return E_OUTOFMEMORY;
567 }
568
569 /************************************************************************
570  * Storage32BaseImpl_Stat (IStorage)
571  *
572  * This method will retrieve information about this storage object.
573  * 
574  * See Windows documentation for more details on IStorage methods.
575  */        
576 HRESULT WINAPI StorageBaseImpl_Stat( 
577   IStorage*        iface,
578   STATSTG*         pstatstg,     /* [out] */ 
579   DWORD            grfStatFlag)  /* [in] */  
580 {
581   ICOM_THIS(StorageBaseImpl,iface);
582   StgProperty    curProperty;
583   BOOL         readSuccessful;
584
585   TRACE("(%p, %p, %lx)\n", 
586         iface, pstatstg, grfStatFlag);
587
588   /*
589    * Perform a sanity check on the parameters.
590    */
591   if ( (This==0) || (pstatstg==0))
592     return E_INVALIDARG;
593
594   /*
595    * Read the information from the property.
596    */
597   readSuccessful = StorageImpl_ReadProperty(
598                     This->ancestorStorage,
599                     This->rootPropertySetIndex,
600                     &curProperty);
601
602   if (readSuccessful)
603   {
604     StorageUtl_CopyPropertyToSTATSTG(
605       pstatstg, 
606       &curProperty, 
607       grfStatFlag);
608     
609     return S_OK;
610   }
611   
612   return E_FAIL;
613 }
614
615 /************************************************************************
616  * Storage32BaseImpl_RenameElement (IStorage)
617  *
618  * This method will rename the specified element. 
619  *
620  * See Windows documentation for more details on IStorage methods.
621  * 
622  * Implementation notes: The method used to rename consists of creating a clone 
623  *    of the deleted StgProperty object setting it with the new name and to 
624  *    perform a DestroyElement of the old StgProperty.
625  */
626 HRESULT WINAPI StorageBaseImpl_RenameElement(
627             IStorage*        iface,
628             const OLECHAR*   pwcsOldName,  /* [in] */
629             const OLECHAR*   pwcsNewName)  /* [in] */
630 {
631   ICOM_THIS(StorageBaseImpl,iface);
632   IEnumSTATSTGImpl* propertyEnumeration;
633   StgProperty       currentProperty;
634   ULONG             foundPropertyIndex;
635
636   TRACE("(%p, %s, %s)\n", 
637         iface, debugstr_w(pwcsOldName), debugstr_w(pwcsNewName));
638
639   /*
640    * Create a property enumeration to search the properties
641    */
642   propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
643                                                    This->rootPropertySetIndex);
644
645   /*
646    * Search the enumeration for the new property name
647    */
648   foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
649                                                      pwcsNewName,
650                                                      &currentProperty);
651
652   if (foundPropertyIndex != PROPERTY_NULL)
653   {
654     /*
655      * There is already a property with the new name
656      */
657     IEnumSTATSTGImpl_Destroy(propertyEnumeration);
658     return STG_E_FILEALREADYEXISTS;
659   }
660
661   IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)propertyEnumeration);
662
663   /*
664    * Search the enumeration for the old property name
665    */
666   foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
667                                                      pwcsOldName,
668                                                      &currentProperty);
669
670   /*
671    * Delete the property enumeration since we don't need it anymore
672    */
673   IEnumSTATSTGImpl_Destroy(propertyEnumeration);
674
675   if (foundPropertyIndex != PROPERTY_NULL)
676   {
677     StgProperty renamedProperty;
678     ULONG       renamedPropertyIndex;
679
680     /*
681      * Setup a new property for the renamed property
682      */
683     renamedProperty.sizeOfNameString = 
684       ( lstrlenW(pwcsNewName)+1 ) * sizeof(WCHAR);
685   
686     if (renamedProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
687       return STG_E_INVALIDNAME;
688   
689     strcpyW(renamedProperty.name, pwcsNewName);
690  
691     renamedProperty.propertyType  = currentProperty.propertyType;
692     renamedProperty.startingBlock = currentProperty.startingBlock;
693     renamedProperty.size.s.LowPart  = currentProperty.size.s.LowPart;
694     renamedProperty.size.s.HighPart = currentProperty.size.s.HighPart;
695   
696     renamedProperty.previousProperty = PROPERTY_NULL;
697     renamedProperty.nextProperty     = PROPERTY_NULL;
698   
699     /*
700      * Bring the dirProperty link in case it is a storage and in which
701      * case the renamed storage elements don't require to be reorganized.
702      */
703     renamedProperty.dirProperty = currentProperty.dirProperty;
704   
705     /* call CoFileTime to get the current time 
706     renamedProperty.timeStampS1
707     renamedProperty.timeStampD1
708     renamedProperty.timeStampS2
709     renamedProperty.timeStampD2
710     renamedProperty.propertyUniqueID 
711     */
712   
713     /* 
714      * Obtain a free property in the property chain
715      */
716     renamedPropertyIndex = getFreeProperty(This->ancestorStorage);
717   
718     /*
719      * Save the new property into the new property spot
720      */  
721     StorageImpl_WriteProperty(
722       This->ancestorStorage,
723       renamedPropertyIndex, 
724       &renamedProperty);
725   
726     /* 
727      * Find a spot in the property chain for our newly created property.
728      */
729     updatePropertyChain(
730       (StorageImpl*)This,
731       renamedPropertyIndex, 
732       renamedProperty);
733
734     /*
735      * At this point the renamed property has been inserted in the tree, 
736      * now, before to Destroy the old property we must zeroed it's dirProperty 
737      * otherwise the DestroyProperty below will zap it all and we do not want 
738      * this to happen.
739      * Also, we fake that the old property is a storage so the DestroyProperty
740      * will not do a SetSize(0) on the stream data.
741      * 
742      * This means that we need to tweek the StgProperty if it is a stream or a
743      * non empty storage.
744      */
745     StorageImpl_ReadProperty(This->ancestorStorage,
746                              foundPropertyIndex,
747                              &currentProperty);
748
749     currentProperty.dirProperty  = PROPERTY_NULL;
750     currentProperty.propertyType = PROPTYPE_STORAGE;
751     StorageImpl_WriteProperty(
752       This->ancestorStorage,
753       foundPropertyIndex, 
754       &currentProperty);
755
756     /* 
757      * Invoke Destroy to get rid of the ole property and automatically redo 
758      * the linking of it's previous and next members... 
759      */ 
760     StorageImpl_DestroyElement((IStorage*)This->ancestorStorage, pwcsOldName); 
761
762   }
763   else
764   {
765     /*
766      * There is no property with the old name
767      */
768     return STG_E_FILENOTFOUND;
769   }
770
771   return S_OK;
772 }
773
774 /************************************************************************
775  * Storage32BaseImpl_CreateStream (IStorage)
776  *
777  * This method will create a stream object within this storage 
778  *
779  * See Windows documentation for more details on IStorage methods.
780  */
781 HRESULT WINAPI StorageBaseImpl_CreateStream(
782             IStorage*        iface,
783             const OLECHAR*   pwcsName,  /* [string][in] */
784             DWORD            grfMode,   /* [in] */
785             DWORD            reserved1, /* [in] */
786             DWORD            reserved2, /* [in] */
787             IStream**        ppstm)     /* [out] */
788 {
789   ICOM_THIS(StorageBaseImpl,iface);
790   IEnumSTATSTGImpl* propertyEnumeration;
791   StgStreamImpl*    newStream;
792   StgProperty       currentProperty, newStreamProperty;
793   ULONG             foundPropertyIndex, newPropertyIndex;
794
795   TRACE("(%p, %s, %lx, %ld, %ld, %p)\n", 
796         iface, debugstr_w(pwcsName), grfMode, 
797         reserved1, reserved2, ppstm);
798
799   /*
800    * Validate parameters
801    */
802   if (ppstm == 0)
803     return STG_E_INVALIDPOINTER;
804
805   if (pwcsName == 0)
806     return STG_E_INVALIDNAME;
807
808   /*
809    * Validate the STGM flags
810    */
811   if ( FAILED( validateSTGM(grfMode) ))
812     return STG_E_INVALIDFLAG;
813
814   /*
815    * As documented.
816    */
817   if ( !(grfMode & STGM_SHARE_EXCLUSIVE) ||
818         (grfMode & STGM_DELETEONRELEASE) ||
819         (grfMode & STGM_TRANSACTED) )
820     return STG_E_INVALIDFUNCTION;
821
822   /*
823    * Initialize the out parameter
824    */
825   *ppstm = 0;
826
827   /*
828    * Create a property enumeration to search the properties
829    */
830   propertyEnumeration = IEnumSTATSTGImpl_Construct(This->ancestorStorage,
831                                                    This->rootPropertySetIndex);
832
833   foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
834                                                      pwcsName,
835                                                      &currentProperty);
836
837   IEnumSTATSTGImpl_Destroy(propertyEnumeration);
838
839   if (foundPropertyIndex != PROPERTY_NULL)
840   {
841     /*
842      * An element with this name already exists 
843      */
844     if (grfMode & STGM_CREATE)
845     {
846       IStorage_DestroyElement(iface, pwcsName); 
847     }
848     else 
849       return STG_E_FILEALREADYEXISTS;
850   }
851
852   /* 
853    * memset the empty property 
854    */
855   memset(&newStreamProperty, 0, sizeof(StgProperty));
856
857   newStreamProperty.sizeOfNameString =
858       ( lstrlenW(pwcsName)+1 ) * sizeof(WCHAR);
859
860   if (newStreamProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
861     return STG_E_INVALIDNAME;
862
863   strcpyW(newStreamProperty.name, pwcsName);
864
865   newStreamProperty.propertyType  = PROPTYPE_STREAM;
866   newStreamProperty.startingBlock = BLOCK_END_OF_CHAIN;
867   newStreamProperty.size.s.LowPart  = 0;
868   newStreamProperty.size.s.HighPart = 0;
869
870   newStreamProperty.previousProperty = PROPERTY_NULL;
871   newStreamProperty.nextProperty     = PROPERTY_NULL;
872   newStreamProperty.dirProperty      = PROPERTY_NULL;
873
874   /* call CoFileTime to get the current time 
875   newStreamProperty.timeStampS1
876   newStreamProperty.timeStampD1
877   newStreamProperty.timeStampS2
878   newStreamProperty.timeStampD2
879   */
880
881   /*  newStreamProperty.propertyUniqueID */
882
883   /*
884    * Get a free property or create a new one 
885    */
886   newPropertyIndex = getFreeProperty(This->ancestorStorage);
887
888   /*
889    * Save the new property into the new property spot
890    */  
891   StorageImpl_WriteProperty(
892     This->ancestorStorage,
893     newPropertyIndex, 
894     &newStreamProperty);
895
896   /* 
897    * Find a spot in the property chain for our newly created property.
898    */
899   updatePropertyChain(
900     (StorageImpl*)This,
901     newPropertyIndex, 
902     newStreamProperty);
903
904   /* 
905    * Open the stream to return it.
906    */
907   newStream = StgStreamImpl_Construct(This, grfMode, newPropertyIndex);
908
909   if (newStream != 0)
910   {
911     *ppstm = (IStream*)newStream;
912
913     /*
914      * Since we are returning a pointer to the interface, we have to nail down
915      * the reference.
916      */
917     StgStreamImpl_AddRef(*ppstm);
918   }
919   else
920   {
921     return STG_E_INSUFFICIENTMEMORY;
922   }
923
924   return S_OK;
925 }
926
927 /************************************************************************
928  * Storage32BaseImpl_SetClass (IStorage)
929  *
930  * This method will write the specified CLSID in the property of this 
931  * storage.
932  *
933  * See Windows documentation for more details on IStorage methods.
934  */
935 HRESULT WINAPI StorageBaseImpl_SetClass(
936   IStorage*        iface,
937   REFCLSID         clsid) /* [in] */
938 {
939   ICOM_THIS(StorageBaseImpl,iface);
940   HRESULT hRes = E_FAIL;
941   StgProperty curProperty;
942   BOOL success;
943
944   TRACE("(%p, %p)\n", iface, clsid);
945   
946   success = StorageImpl_ReadProperty(This->ancestorStorage,
947                                        This->rootPropertySetIndex,
948                                        &curProperty);
949   if (success)
950   {
951     curProperty.propertyUniqueID = *clsid;
952
953     success =  StorageImpl_WriteProperty(This->ancestorStorage,
954                                            This->rootPropertySetIndex,
955                                            &curProperty);
956     if (success)
957       hRes = S_OK;
958   }
959
960   return hRes;
961 }
962
963 /************************************************************************
964 ** Storage32Impl implementation
965 */
966         
967 /************************************************************************
968  * Storage32Impl_CreateStorage (IStorage)
969  *
970  * This method will create the storage object within the provided storage.
971  *
972  * See Windows documentation for more details on IStorage methods.
973  */
974 HRESULT WINAPI StorageImpl_CreateStorage( 
975   IStorage*      iface,
976   const OLECHAR  *pwcsName, /* [string][in] */ 
977   DWORD            grfMode,   /* [in] */ 
978   DWORD            reserved1, /* [in] */ 
979   DWORD            reserved2, /* [in] */ 
980   IStorage       **ppstg)   /* [out] */ 
981 {
982   StorageImpl* const This=(StorageImpl*)iface;
983
984   IEnumSTATSTGImpl *propertyEnumeration;
985   StgProperty      currentProperty;
986   StgProperty      newProperty;
987   ULONG            foundPropertyIndex;
988   ULONG            newPropertyIndex;
989   HRESULT          hr;
990
991   TRACE("(%p, %s, %lx, %ld, %ld, %p)\n", 
992         iface, debugstr_w(pwcsName), grfMode, 
993         reserved1, reserved2, ppstg);
994   
995   /*
996    * Validate parameters
997    */
998   if (ppstg == 0)
999     return STG_E_INVALIDPOINTER;
1000
1001   if (pwcsName == 0)
1002     return STG_E_INVALIDNAME;
1003
1004   /*
1005    * Validate the STGM flags
1006    */
1007   if ( FAILED( validateSTGM(grfMode) ) ||
1008        (grfMode & STGM_DELETEONRELEASE) )
1009     return STG_E_INVALIDFLAG;
1010
1011   /*
1012    * Initialize the out parameter
1013    */
1014   *ppstg = 0;
1015
1016   /*
1017    * Create a property enumeration and search the properties
1018    */
1019   propertyEnumeration = IEnumSTATSTGImpl_Construct( This->ancestorStorage,
1020                                                     This->rootPropertySetIndex);
1021
1022   foundPropertyIndex = IEnumSTATSTGImpl_FindProperty(propertyEnumeration,
1023                                                      pwcsName,
1024                                                      &currentProperty);
1025   IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1026
1027   if (foundPropertyIndex != PROPERTY_NULL)
1028   {
1029     /*
1030      * An element with this name already exists 
1031      */
1032     if (grfMode & STGM_CREATE)
1033       IStorage_DestroyElement(iface, pwcsName); 
1034     else 
1035       return STG_E_FILEALREADYEXISTS;
1036   }
1037
1038   /* 
1039    * memset the empty property 
1040    */
1041   memset(&newProperty, 0, sizeof(StgProperty));
1042
1043   newProperty.sizeOfNameString = (lstrlenW(pwcsName)+1)*sizeof(WCHAR);
1044
1045   if (newProperty.sizeOfNameString > PROPERTY_NAME_BUFFER_LEN)
1046     return STG_E_INVALIDNAME;
1047
1048   strcpyW(newProperty.name, pwcsName);
1049
1050   newProperty.propertyType  = PROPTYPE_STORAGE;
1051   newProperty.startingBlock = BLOCK_END_OF_CHAIN;
1052   newProperty.size.s.LowPart  = 0;
1053   newProperty.size.s.HighPart = 0;
1054
1055   newProperty.previousProperty = PROPERTY_NULL;
1056   newProperty.nextProperty     = PROPERTY_NULL;
1057   newProperty.dirProperty      = PROPERTY_NULL;
1058
1059   /* call CoFileTime to get the current time 
1060   newProperty.timeStampS1
1061   newProperty.timeStampD1
1062   newProperty.timeStampS2
1063   newProperty.timeStampD2
1064   */
1065
1066   /*  newStorageProperty.propertyUniqueID */
1067
1068   /* 
1069    * Obtain a free property in the property chain
1070    */
1071   newPropertyIndex = getFreeProperty(This->ancestorStorage);
1072
1073   /*
1074    * Save the new property into the new property spot
1075    */  
1076   StorageImpl_WriteProperty(
1077     This->ancestorStorage,
1078     newPropertyIndex, 
1079     &newProperty);
1080
1081   /* 
1082    * Find a spot in the property chain for our newly created property.
1083    */
1084   updatePropertyChain(
1085     This,
1086     newPropertyIndex, 
1087     newProperty);
1088
1089   /* 
1090    * Open it to get a pointer to return.
1091    */
1092   hr = IStorage_OpenStorage(
1093          iface,
1094          (OLECHAR*)pwcsName,
1095          0,
1096          grfMode,
1097          0,
1098          0,
1099          ppstg);
1100
1101   if( (hr != S_OK) || (*ppstg == NULL))
1102   {
1103     return hr;
1104   }
1105
1106   
1107   return S_OK;
1108 }
1109
1110
1111 /***************************************************************************
1112  *
1113  * Internal Method
1114  *
1115  * Get a free property or create a new one.
1116  */
1117 static ULONG getFreeProperty(
1118   StorageImpl *storage)
1119 {
1120   ULONG       currentPropertyIndex = 0;
1121   ULONG       newPropertyIndex     = PROPERTY_NULL;
1122   BOOL      readSuccessful        = TRUE;
1123   StgProperty currentProperty;
1124
1125   do
1126   {
1127     /*
1128      * Start by reading the root property
1129      */
1130     readSuccessful = StorageImpl_ReadProperty(storage->ancestorStorage,
1131                                                currentPropertyIndex,
1132                                                &currentProperty);
1133     if (readSuccessful)
1134     {
1135       if (currentProperty.sizeOfNameString == 0)
1136       {
1137         /* 
1138          * The property existis and is available, we found it.
1139          */
1140         newPropertyIndex = currentPropertyIndex;
1141       }
1142     }
1143     else
1144     {
1145       /*
1146        * We exhausted the property list, we will create more space below
1147        */
1148       newPropertyIndex = currentPropertyIndex;
1149     }
1150     currentPropertyIndex++;
1151
1152   } while (newPropertyIndex == PROPERTY_NULL);
1153
1154   /* 
1155    * grow the property chain 
1156    */
1157   if (! readSuccessful)
1158   {
1159     StgProperty    emptyProperty;
1160     ULARGE_INTEGER newSize;
1161     ULONG          propertyIndex;
1162     ULONG          lastProperty  = 0;
1163     ULONG          blockCount    = 0;
1164
1165     /* 
1166      * obtain the new count of property blocks 
1167      */
1168     blockCount = BlockChainStream_GetCount(
1169                    storage->ancestorStorage->rootBlockChain)+1;
1170
1171     /* 
1172      * initialize the size used by the property stream 
1173      */
1174     newSize.s.HighPart = 0;
1175     newSize.s.LowPart  = storage->bigBlockSize * blockCount;
1176
1177     /* 
1178      * add a property block to the property chain 
1179      */
1180     BlockChainStream_SetSize(storage->ancestorStorage->rootBlockChain, newSize);
1181
1182     /* 
1183      * memset the empty property in order to initialize the unused newly 
1184      * created property
1185      */
1186     memset(&emptyProperty, 0, sizeof(StgProperty));
1187
1188     /* 
1189      * initialize them
1190      */
1191     lastProperty = storage->bigBlockSize / PROPSET_BLOCK_SIZE * blockCount; 
1192     
1193     for(
1194       propertyIndex = newPropertyIndex;
1195       propertyIndex < lastProperty;
1196       propertyIndex++)
1197     {
1198       StorageImpl_WriteProperty(
1199         storage->ancestorStorage,
1200         propertyIndex, 
1201         &emptyProperty);
1202     }
1203   }
1204
1205   return newPropertyIndex;
1206 }
1207
1208 /****************************************************************************
1209  *
1210  * Internal Method
1211  *
1212  * Case insensitive comparaison of StgProperty.name by first considering 
1213  * their size.
1214  *
1215  * Returns <0 when newPrpoerty < currentProperty
1216  *         >0 when newPrpoerty > currentProperty
1217  *          0 when newPrpoerty == currentProperty
1218  */
1219 static LONG propertyNameCmp(
1220   OLECHAR *newProperty,
1221   OLECHAR *currentProperty)
1222 {
1223   LONG diff      = lstrlenW(newProperty) - lstrlenW(currentProperty);
1224
1225   if (diff == 0) 
1226   {
1227     /* 
1228      * We compare the string themselves only when they are of the same lenght
1229      */
1230     diff = lstrcmpiW( newProperty, currentProperty);
1231   }
1232
1233   return diff;  
1234 }
1235
1236 /****************************************************************************
1237  *
1238  * Internal Method
1239  *
1240  * Properly link this new element in the property chain.
1241  */
1242 static void updatePropertyChain(
1243   StorageImpl *storage,
1244   ULONG         newPropertyIndex,
1245   StgProperty   newProperty) 
1246 {
1247   StgProperty currentProperty;
1248
1249   /*
1250    * Read the root property
1251    */
1252   StorageImpl_ReadProperty(storage->ancestorStorage,
1253                              storage->rootPropertySetIndex,
1254                              &currentProperty);
1255
1256   if (currentProperty.dirProperty != PROPERTY_NULL)
1257   {
1258     /* 
1259      * The root storage contains some element, therefore, start the research
1260      * for the appropriate location.
1261      */
1262     BOOL found = 0;
1263     ULONG  current, next, previous, currentPropertyId;
1264
1265     /*
1266      * Keep the StgProperty sequence number of the storage first property
1267      */
1268     currentPropertyId = currentProperty.dirProperty;
1269
1270     /*
1271      * Read 
1272      */
1273     StorageImpl_ReadProperty(storage->ancestorStorage,
1274                                currentProperty.dirProperty,
1275                                &currentProperty);
1276
1277     previous = currentProperty.previousProperty;
1278     next     = currentProperty.nextProperty;
1279     current  = currentPropertyId;
1280
1281     while (found == 0)
1282     {
1283       LONG diff = propertyNameCmp( newProperty.name, currentProperty.name);
1284   
1285       if (diff < 0)
1286       {
1287         if (previous != PROPERTY_NULL)
1288         {
1289           StorageImpl_ReadProperty(storage->ancestorStorage,
1290                                      previous,
1291                                      &currentProperty);
1292           current = previous;
1293         }
1294         else
1295         {
1296           currentProperty.previousProperty = newPropertyIndex;
1297           StorageImpl_WriteProperty(storage->ancestorStorage,
1298                                       current,
1299                                       &currentProperty);
1300           found = 1;
1301         }
1302       }
1303       else if (diff > 0)
1304       {
1305         if (next != PROPERTY_NULL)
1306         {
1307           StorageImpl_ReadProperty(storage->ancestorStorage,
1308                                      next,
1309                                      &currentProperty);
1310           current = next;
1311         }
1312         else
1313         {
1314           currentProperty.nextProperty = newPropertyIndex;
1315           StorageImpl_WriteProperty(storage->ancestorStorage,
1316                                       current,
1317                                       &currentProperty);
1318           found = 1;
1319         }
1320       }
1321       else
1322       {
1323         /*
1324          * Trying to insert an item with the same name in the
1325          * subtree structure.
1326          */
1327         assert(FALSE);
1328       }
1329
1330       previous = currentProperty.previousProperty;
1331       next     = currentProperty.nextProperty;
1332     }
1333   }
1334   else
1335   {
1336     /* 
1337      * The root storage is empty, link the new property to it's dir property
1338      */
1339     currentProperty.dirProperty = newPropertyIndex;
1340     StorageImpl_WriteProperty(storage->ancestorStorage,
1341                                 storage->rootPropertySetIndex,
1342                                 &currentProperty);
1343   }
1344 }
1345
1346       
1347 /*************************************************************************
1348  * CopyTo (IStorage)
1349  */
1350 HRESULT WINAPI StorageImpl_CopyTo( 
1351   IStorage*   iface,
1352   DWORD       ciidExclude,  /* [in] */ 
1353   const IID*  rgiidExclude, /* [size_is][unique][in] */ 
1354   SNB         snbExclude,   /* [unique][in] */ 
1355   IStorage*   pstgDest)     /* [unique][in] */ 
1356 {
1357   IEnumSTATSTG *elements     = 0;
1358   STATSTG      curElement, strStat;
1359   HRESULT      hr;
1360   IStorage     *pstgTmp, *pstgChild;
1361   IStream      *pstrTmp, *pstrChild;
1362
1363   if ((ciidExclude != 0) || (rgiidExclude != NULL) || (snbExclude != NULL))
1364     FIXME("Exclude option not implemented\n");
1365
1366   TRACE("(%p, %ld, %p, %p, %p)\n", 
1367         iface, ciidExclude, rgiidExclude, 
1368         snbExclude, pstgDest);
1369
1370   /*
1371    * Perform a sanity check
1372    */
1373   if ( pstgDest == 0 )
1374     return STG_E_INVALIDPOINTER;
1375
1376   /* 
1377    * Enumerate the elements
1378    */
1379   hr = IStorage_EnumElements( iface, 0, 0, 0, &elements );
1380
1381   if ( hr != S_OK )
1382     return hr;
1383
1384   /*
1385    * set the class ID
1386    */
1387   IStorage_Stat( iface, &curElement, STATFLAG_NONAME);
1388   IStorage_SetClass( pstgDest, &curElement.clsid );
1389   
1390   do
1391   {
1392     /*
1393      * Obtain the next element
1394      */
1395     hr = IEnumSTATSTG_Next( elements, 1, &curElement, NULL );
1396
1397     if ( hr == S_FALSE )
1398     {
1399       hr = S_OK;   /* done, every element has been copied */
1400       break;
1401     }
1402
1403     if (curElement.type == STGTY_STORAGE)
1404     {
1405       /*
1406        * open child source storage
1407        */
1408       hr = IStorage_OpenStorage( iface, curElement.pwcsName, NULL,
1409                                  STGM_READ|STGM_SHARE_EXCLUSIVE,
1410                                  NULL, 0, &pstgChild );
1411
1412       if (hr != S_OK)
1413         break;
1414
1415       /*
1416        * Check if destination storage is not a child of the source
1417        * storage, which will cause an infinite loop
1418        */
1419       if (pstgChild == pstgDest)
1420       {
1421         IEnumSTATSTG_Release(elements);
1422
1423         return STG_E_ACCESSDENIED;
1424       }
1425             
1426       /*
1427        * create a new storage in destination storage
1428        */
1429       hr = IStorage_CreateStorage( pstgDest, curElement.pwcsName,
1430                                    STGM_FAILIFTHERE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1431                                    0, 0,
1432                                    &pstgTmp );
1433       /*
1434        * if it already exist, don't create a new one use this one
1435        */
1436       if (hr == STG_E_FILEALREADYEXISTS)
1437       {
1438         hr = IStorage_OpenStorage( pstgDest, curElement.pwcsName, NULL,
1439                                    STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1440                                    NULL, 0, &pstgTmp );
1441       }
1442         
1443       if (hr != S_OK)
1444         break;
1445
1446         
1447       /*
1448        * do the copy recursively
1449        */
1450       hr = IStorage_CopyTo( pstgChild, ciidExclude, rgiidExclude,
1451                                snbExclude, pstgTmp );
1452                                 
1453       IStorage_Release( pstgTmp );
1454       IStorage_Release( pstgChild );
1455     }
1456     else if (curElement.type == STGTY_STREAM)
1457     {
1458       /*
1459        * create a new stream in destination storage. If the stream already
1460        * exist, it will be deleted and a new one will be created.
1461        */
1462       hr = IStorage_CreateStream( pstgDest, curElement.pwcsName,
1463                                   STGM_CREATE|STGM_WRITE|STGM_SHARE_EXCLUSIVE,
1464                                   0, 0, &pstrTmp );
1465
1466       if (hr != S_OK)
1467         break;
1468
1469       /*
1470        * open child stream storage
1471        */
1472       hr = IStorage_OpenStream( iface, curElement.pwcsName, NULL,
1473                                 STGM_READ|STGM_SHARE_EXCLUSIVE,
1474                                 0, &pstrChild );
1475
1476       if (hr != S_OK)
1477         break;
1478
1479       /*
1480        * Get the size of the source stream
1481        */
1482       IStream_Stat( pstrChild, &strStat, STATFLAG_NONAME );
1483
1484       /*
1485        * Set the size of the destination stream.
1486        */
1487       IStream_SetSize(pstrTmp, strStat.cbSize);
1488       
1489       /*
1490        * do the copy
1491        */
1492       hr = IStream_CopyTo( pstrChild, pstrTmp, strStat.cbSize,
1493                            NULL, NULL );
1494                                 
1495       IStream_Release( pstrTmp );
1496       IStream_Release( pstrChild );
1497     }
1498     else
1499     {
1500       WARN("unknown element type: %ld\n", curElement.type);
1501     }
1502
1503   } while (hr == S_OK);
1504
1505   /*
1506    * Clean-up
1507    */
1508   IEnumSTATSTG_Release(elements);
1509   
1510   return hr;
1511 }
1512         
1513 /*************************************************************************
1514  * MoveElementTo (IStorage)
1515  */
1516 HRESULT WINAPI StorageImpl_MoveElementTo( 
1517   IStorage*     iface,
1518   const OLECHAR *pwcsName,   /* [string][in] */ 
1519   IStorage      *pstgDest,   /* [unique][in] */ 
1520   const OLECHAR *pwcsNewName,/* [string][in] */ 
1521   DWORD           grfFlags)    /* [in] */ 
1522 {
1523   FIXME("not implemented!\n");
1524   return E_NOTIMPL;
1525 }
1526         
1527 /*************************************************************************
1528  * Commit (IStorage)
1529  */
1530 HRESULT WINAPI StorageImpl_Commit( 
1531   IStorage*   iface,
1532   DWORD         grfCommitFlags)/* [in] */ 
1533 {
1534   FIXME("(%ld): stub!\n", grfCommitFlags);
1535   return S_OK;
1536 }
1537         
1538 /*************************************************************************
1539  * Revert (IStorage)
1540  */
1541 HRESULT WINAPI StorageImpl_Revert( 
1542   IStorage* iface)
1543 {
1544   FIXME("not implemented!\n");
1545   return E_NOTIMPL;
1546 }
1547
1548 /*************************************************************************
1549  * DestroyElement (IStorage)
1550  *
1551  * Stategy: This implementation is build this way for simplicity not for speed. 
1552  *          I always delete the top most element of the enumeration and adjust
1553  *          the deleted element pointer all the time.  This takes longer to 
1554  *          do but allow to reinvoke DestroyElement whenever we encounter a 
1555  *          storage object.  The optimisation reside in the usage of another
1556  *          enumeration stategy that would give all the leaves of a storage 
1557  *          first. (postfix order)
1558  */
1559 HRESULT WINAPI StorageImpl_DestroyElement( 
1560   IStorage*     iface,
1561   const OLECHAR *pwcsName)/* [string][in] */ 
1562 {
1563   StorageImpl* const This=(StorageImpl*)iface;
1564
1565   IEnumSTATSTGImpl* propertyEnumeration;
1566   HRESULT           hr = S_OK;
1567   BOOL            res;
1568   StgProperty       propertyToDelete;
1569   StgProperty       parentProperty;
1570   ULONG             foundPropertyIndexToDelete;
1571   ULONG             typeOfRelation;
1572   ULONG             parentPropertyId;
1573
1574   TRACE("(%p, %s)\n", 
1575         iface, debugstr_w(pwcsName));
1576
1577   /*
1578    * Perform a sanity check on the parameters.
1579    */
1580   if (pwcsName==NULL) 
1581     return STG_E_INVALIDPOINTER;
1582   
1583   /*
1584    * Create a property enumeration to search the property with the given name
1585    */
1586   propertyEnumeration = IEnumSTATSTGImpl_Construct(
1587     This->ancestorStorage, 
1588     This->rootPropertySetIndex);
1589   
1590   foundPropertyIndexToDelete = IEnumSTATSTGImpl_FindProperty(
1591     propertyEnumeration,
1592     pwcsName,
1593     &propertyToDelete);
1594
1595   IEnumSTATSTGImpl_Destroy(propertyEnumeration);
1596
1597   if ( foundPropertyIndexToDelete == PROPERTY_NULL )  
1598   {
1599     return STG_E_FILENOTFOUND;
1600   }
1601
1602   /* 
1603    * Find the parent property of the property to delete (the one that 
1604    * link to it).  If This->dirProperty == foundPropertyIndexToDelete, 
1605    * the parent is This. Otherwise, the parent is one of it's sibling...
1606    */
1607
1608   /* 
1609    * First, read This's StgProperty..
1610    */
1611   res = StorageImpl_ReadProperty( 
1612           This->ancestorStorage,
1613           This->rootPropertySetIndex,
1614           &parentProperty);
1615
1616   assert(res==TRUE);
1617
1618   /* 
1619    * Second, check to see if by any chance the actual storage (This) is not
1620    * the parent of the property to delete... We never know...
1621    */
1622   if ( parentProperty.dirProperty == foundPropertyIndexToDelete )
1623   {
1624     /* 
1625      * Set data as it would have been done in the else part...
1626      */
1627     typeOfRelation   = PROPERTY_RELATION_DIR;
1628     parentPropertyId = This->rootPropertySetIndex;
1629   }
1630   else 
1631   { 
1632     /*
1633      * Create a property enumeration to search the parent properties, and 
1634      * delete it once done.
1635      */
1636     IEnumSTATSTGImpl* propertyEnumeration2;
1637
1638     propertyEnumeration2 = IEnumSTATSTGImpl_Construct(
1639       This->ancestorStorage, 
1640       This->rootPropertySetIndex);
1641   
1642     typeOfRelation = IEnumSTATSTGImpl_FindParentProperty(
1643       propertyEnumeration2,
1644       foundPropertyIndexToDelete,
1645       &parentProperty,
1646       &parentPropertyId);
1647
1648     IEnumSTATSTGImpl_Destroy(propertyEnumeration2);
1649   }
1650
1651   if ( propertyToDelete.propertyType == PROPTYPE_STORAGE ) 
1652   {
1653     hr = deleteStorageProperty(
1654            This, 
1655            foundPropertyIndexToDelete,
1656            propertyToDelete);
1657   } 
1658   else if ( propertyToDelete.propertyType == PROPTYPE_STREAM )
1659   {
1660     hr = deleteStreamProperty(
1661            This, 
1662            foundPropertyIndexToDelete,
1663            propertyToDelete);
1664   }
1665
1666   if (hr!=S_OK) 
1667     return hr;
1668
1669   /*
1670    * Adjust the property chain
1671    */
1672   hr = adjustPropertyChain(
1673         This,
1674         propertyToDelete, 
1675         parentProperty,
1676         parentPropertyId,
1677         typeOfRelation);
1678
1679   return hr;
1680 }
1681
1682
1683 /*********************************************************************
1684  *
1685  * Internal Method
1686  *
1687  * Perform the deletion of a complete storage node
1688  *
1689  */
1690 static HRESULT deleteStorageProperty(
1691   StorageImpl *parentStorage,
1692   ULONG        indexOfPropertyToDelete,
1693   StgProperty  propertyToDelete)
1694 {
1695   IEnumSTATSTG *elements     = 0;
1696   IStorage   *childStorage = 0;
1697   STATSTG      currentElement;
1698   HRESULT      hr;
1699   HRESULT      destroyHr = S_OK;
1700
1701   /*
1702    * Open the storage and enumerate it
1703    */
1704   hr = StorageBaseImpl_OpenStorage(
1705         (IStorage*)parentStorage,
1706         propertyToDelete.name,
1707         0,
1708         STGM_SHARE_EXCLUSIVE,
1709         0,
1710         0,
1711         &childStorage);
1712
1713   if (hr != S_OK)
1714   {
1715     return hr;
1716   }
1717
1718   /* 
1719    * Enumerate the elements
1720    */
1721   IStorage_EnumElements( childStorage, 0, 0, 0, &elements);
1722
1723   do
1724   {
1725     /*
1726      * Obtain the next element
1727      */
1728     hr = IEnumSTATSTG_Next(elements, 1, &currentElement, NULL);
1729     if (hr==S_OK)
1730     {
1731       destroyHr = StorageImpl_DestroyElement(
1732                     (IStorage*)childStorage, 
1733                     (OLECHAR*)currentElement.pwcsName);
1734
1735       CoTaskMemFree(currentElement.pwcsName);
1736     }
1737
1738     /*
1739      * We need to Reset the enumeration every time because we delete elements
1740      * and the enumeration could be invalid
1741      */
1742     IEnumSTATSTG_Reset(elements);
1743
1744   } while ((hr == S_OK) && (destroyHr == S_OK));
1745
1746   /*
1747    * Invalidate the property by zeroing it's name member.
1748    */
1749   propertyToDelete.sizeOfNameString = 0;
1750
1751   StorageImpl_WriteProperty(parentStorage->ancestorStorage,
1752                             indexOfPropertyToDelete,
1753                             &propertyToDelete);
1754
1755   IStorage_Release(childStorage);
1756   IEnumSTATSTG_Release(elements);
1757     
1758   return destroyHr;
1759 }
1760
1761 /*********************************************************************
1762  *
1763  * Internal Method
1764  *
1765  * Perform the deletion of a stream node
1766  *
1767  */
1768 static HRESULT deleteStreamProperty(
1769   StorageImpl *parentStorage,
1770   ULONG         indexOfPropertyToDelete,
1771   StgProperty   propertyToDelete)
1772 {
1773   IStream      *pis;
1774   HRESULT        hr;
1775   ULARGE_INTEGER size;
1776
1777   size.s.HighPart = 0;
1778   size.s.LowPart = 0;
1779
1780   hr = StorageBaseImpl_OpenStream(
1781          (IStorage*)parentStorage,
1782          (OLECHAR*)propertyToDelete.name,
1783          NULL,
1784          STGM_WRITE | STGM_SHARE_EXCLUSIVE,
1785          0,
1786          &pis);
1787     
1788   if (hr!=S_OK)
1789   {
1790     return(hr);
1791   }
1792
1793   /* 
1794    * Zap the stream 
1795    */ 
1796   hr = IStream_SetSize(pis, size); 
1797
1798   if(hr != S_OK)
1799   {
1800     return hr;
1801   }
1802
1803   /*
1804    * Release the stream object.
1805    */
1806   IStream_Release(pis);
1807
1808   /* 
1809    * Invalidate the property by zeroing it's name member.
1810    */
1811   propertyToDelete.sizeOfNameString = 0;
1812
1813   /* 
1814    * Here we should re-read the property so we get the updated pointer
1815    * but since we are here to zap it, I don't do it...
1816    */
1817   StorageImpl_WriteProperty(
1818     parentStorage->ancestorStorage, 
1819     indexOfPropertyToDelete,
1820     &propertyToDelete);
1821
1822   return S_OK;
1823 }
1824
1825 /*********************************************************************
1826  *
1827  * Internal Method
1828  *
1829  * Finds a placeholder for the StgProperty within the Storage
1830  *
1831  */
1832 static HRESULT findPlaceholder(
1833   StorageImpl *storage,
1834   ULONG         propertyIndexToStore,
1835   ULONG         storePropertyIndex,
1836   INT         typeOfRelation)
1837 {
1838   StgProperty storeProperty;
1839   HRESULT     hr = S_OK;
1840   BOOL      res = TRUE;
1841
1842   /*
1843    * Read the storage property
1844    */
1845   res = StorageImpl_ReadProperty(
1846           storage->ancestorStorage,
1847           storePropertyIndex, 
1848           &storeProperty);
1849
1850   if(! res)
1851   {
1852     return E_FAIL;
1853   }
1854
1855   if (typeOfRelation == PROPERTY_RELATION_PREVIOUS)
1856   {
1857     if (storeProperty.previousProperty != PROPERTY_NULL)
1858     {
1859       return findPlaceholder(
1860                storage,
1861                propertyIndexToStore, 
1862                storeProperty.previousProperty,
1863                typeOfRelation);
1864     }
1865     else
1866     {
1867       storeProperty.previousProperty = propertyIndexToStore;
1868     }
1869   }
1870   else if (typeOfRelation == PROPERTY_RELATION_NEXT)
1871   {
1872     if (storeProperty.nextProperty != PROPERTY_NULL)
1873     {
1874       return findPlaceholder(
1875                storage,
1876                propertyIndexToStore, 
1877                storeProperty.nextProperty,
1878                typeOfRelation);
1879     }
1880     else
1881     {
1882       storeProperty.nextProperty = propertyIndexToStore;
1883     }
1884   } 
1885   else if (typeOfRelation == PROPERTY_RELATION_DIR)
1886   {
1887     if (storeProperty.dirProperty != PROPERTY_NULL)
1888     {
1889       return findPlaceholder(
1890                storage,
1891                propertyIndexToStore, 
1892                storeProperty.dirProperty,
1893                typeOfRelation);
1894     }
1895     else
1896     {
1897       storeProperty.dirProperty = propertyIndexToStore;
1898     }
1899   }
1900
1901   hr = StorageImpl_WriteProperty(
1902          storage->ancestorStorage,
1903          storePropertyIndex, 
1904          &storeProperty);
1905
1906   if(! hr)
1907   {
1908     return E_FAIL;
1909   }
1910
1911   return S_OK;
1912 }
1913
1914 /*************************************************************************
1915  *
1916  * Internal Method
1917  *
1918  * This method takes the previous and the next property link of a property 
1919  * to be deleted and find them a place in the Storage.
1920  */
1921 static HRESULT adjustPropertyChain( 
1922   StorageImpl *This,
1923   StgProperty   propertyToDelete,
1924   StgProperty   parentProperty,
1925   ULONG         parentPropertyId,
1926   INT         typeOfRelation)
1927 {
1928   ULONG   newLinkProperty        = PROPERTY_NULL;
1929   BOOL  needToFindAPlaceholder = FALSE;
1930   ULONG   storeNode              = PROPERTY_NULL;
1931   ULONG   toStoreNode            = PROPERTY_NULL;
1932   INT   relationType           = 0;
1933   HRESULT hr                     = S_OK;
1934   BOOL  res                    = TRUE;
1935   
1936   if (typeOfRelation == PROPERTY_RELATION_PREVIOUS) 
1937   {
1938     if (propertyToDelete.previousProperty != PROPERTY_NULL)  
1939     {
1940       /* 
1941        * Set the parent previous to the property to delete previous
1942        */
1943       newLinkProperty = propertyToDelete.previousProperty;
1944
1945       if (propertyToDelete.nextProperty != PROPERTY_NULL)  
1946       {
1947         /*
1948          * We also need to find a storage for the other link, setup variables 
1949          * to do this at the end...
1950          */      
1951         needToFindAPlaceholder = TRUE;
1952         storeNode              = propertyToDelete.previousProperty;
1953         toStoreNode            = propertyToDelete.nextProperty;
1954         relationType           = PROPERTY_RELATION_NEXT;
1955       }
1956     } 
1957     else if (propertyToDelete.nextProperty != PROPERTY_NULL)  
1958     {
1959       /* 
1960        * Set the parent previous to the property to delete next
1961        */
1962       newLinkProperty = propertyToDelete.nextProperty;
1963     }
1964    
1965     /* 
1966      * Link it for real...
1967      */ 
1968     parentProperty.previousProperty = newLinkProperty;
1969   
1970   } 
1971   else if (typeOfRelation == PROPERTY_RELATION_NEXT) 
1972   {
1973     if (propertyToDelete.previousProperty != PROPERTY_NULL)  
1974     {
1975       /* 
1976        * Set the parent next to the property to delete next previous
1977        */
1978       newLinkProperty = propertyToDelete.previousProperty;
1979       
1980       if (propertyToDelete.nextProperty != PROPERTY_NULL)  
1981       {
1982         /*
1983          * We also need to find a storage for the other link, setup variables 
1984          * to do this at the end...
1985          */      
1986         needToFindAPlaceholder = TRUE;
1987         storeNode              = propertyToDelete.previousProperty;
1988         toStoreNode            = propertyToDelete.nextProperty;
1989         relationType           = PROPERTY_RELATION_NEXT;
1990       }
1991     } 
1992     else if (propertyToDelete.nextProperty != PROPERTY_NULL)  
1993     {
1994       /* 
1995        * Set the parent next to the property to delete next
1996        */
1997       newLinkProperty = propertyToDelete.nextProperty;
1998     }
1999
2000     /* 
2001      * Link it for real...
2002      */ 
2003     parentProperty.nextProperty = newLinkProperty;
2004   } 
2005   else /* (typeOfRelation == PROPERTY_RELATION_DIR) */
2006   {
2007     if (propertyToDelete.previousProperty != PROPERTY_NULL) 
2008     {
2009       /* 
2010        * Set the parent dir to the property to delete previous
2011        */
2012       newLinkProperty = propertyToDelete.previousProperty;
2013
2014       if (propertyToDelete.nextProperty != PROPERTY_NULL)  
2015       {
2016         /*
2017          * We also need to find a storage for the other link, setup variables 
2018          * to do this at the end...
2019          */      
2020         needToFindAPlaceholder = TRUE;
2021         storeNode              = propertyToDelete.previousProperty;
2022         toStoreNode            = propertyToDelete.nextProperty;
2023         relationType           = PROPERTY_RELATION_NEXT;
2024       }
2025     } 
2026     else if (propertyToDelete.nextProperty != PROPERTY_NULL)  
2027     {
2028       /* 
2029        * Set the parent dir to the property to delete next
2030        */
2031       newLinkProperty = propertyToDelete.nextProperty;
2032     }
2033
2034     /* 
2035      * Link it for real...
2036      */ 
2037     parentProperty.dirProperty = newLinkProperty;
2038   }
2039
2040   /* 
2041    * Write back the parent property    
2042    */
2043   res = StorageImpl_WriteProperty(
2044           This->ancestorStorage, 
2045           parentPropertyId,
2046           &parentProperty);
2047   if(! res)
2048   {
2049     return E_FAIL;
2050   }
2051
2052   /*
2053    * If a placeholder is required for the other link, then, find one and 
2054    * get out of here...
2055    */
2056   if (needToFindAPlaceholder) 
2057   {
2058     hr = findPlaceholder(
2059            This, 
2060            toStoreNode, 
2061            storeNode,
2062            relationType);
2063   }
2064
2065   return hr;
2066 }
2067
2068
2069 /******************************************************************************
2070  * SetElementTimes (IStorage)
2071  */
2072 HRESULT WINAPI StorageImpl_SetElementTimes( 
2073   IStorage*     iface,
2074   const OLECHAR *pwcsName,/* [string][in] */ 
2075   const FILETIME  *pctime,  /* [in] */ 
2076   const FILETIME  *patime,  /* [in] */ 
2077   const FILETIME  *pmtime)  /* [in] */ 
2078 {
2079   FIXME("not implemented!\n");
2080   return E_NOTIMPL;
2081 }
2082
2083 /******************************************************************************
2084  * SetStateBits (IStorage)
2085  */
2086 HRESULT WINAPI StorageImpl_SetStateBits( 
2087   IStorage*   iface,
2088   DWORD         grfStateBits,/* [in] */ 
2089   DWORD         grfMask)     /* [in] */ 
2090 {
2091   FIXME("not implemented!\n");
2092   return E_NOTIMPL;
2093 }
2094
2095 HRESULT StorageImpl_Construct(
2096   StorageImpl* This,
2097   HANDLE       hFile,
2098   ILockBytes*  pLkbyt,
2099   DWORD        openFlags,
2100   BOOL         fileBased,
2101   BOOL         fileCreate)
2102 {
2103   HRESULT     hr = S_OK;
2104   StgProperty currentProperty;
2105   BOOL      readSuccessful;
2106   ULONG       currentPropertyIndex;
2107   
2108   if ( FAILED( validateSTGM(openFlags) ))
2109     return STG_E_INVALIDFLAG;
2110
2111   memset(This, 0, sizeof(StorageImpl));
2112   
2113   /*
2114    * Initialize the virtual fgunction table.
2115    */
2116   ICOM_VTBL(This)    = &Storage32Impl_Vtbl;
2117   This->v_destructor = &StorageImpl_Destroy;
2118   
2119   /*
2120    * This is the top-level storage so initialize the ancester pointer
2121    * to this.
2122    */
2123   This->ancestorStorage = This;
2124   
2125   /*
2126    * Initialize the physical support of the storage.
2127    */
2128   This->hFile = hFile;
2129   
2130   /*
2131    * Initialize the big block cache.
2132    */
2133   This->bigBlockSize   = DEF_BIG_BLOCK_SIZE;
2134   This->smallBlockSize = DEF_SMALL_BLOCK_SIZE;
2135   This->bigBlockFile   = BIGBLOCKFILE_Construct(hFile,
2136                                                 pLkbyt,
2137                                                 openFlags,
2138                                                 This->bigBlockSize,
2139                                                 fileBased);
2140
2141   if (This->bigBlockFile == 0)
2142     return E_FAIL;
2143  
2144   if (fileCreate)
2145   {
2146     ULARGE_INTEGER size;
2147     BYTE* bigBlockBuffer;
2148
2149     /*
2150      * Initialize all header variables:
2151      * - The big block depot consists of one block and it is at block 0
2152      * - The properties start at block 1
2153      * - There is no small block depot
2154      */
2155     memset( This->bigBlockDepotStart,     
2156             BLOCK_UNUSED, 
2157             sizeof(This->bigBlockDepotStart));
2158
2159     This->bigBlockDepotCount    = 1;
2160     This->bigBlockDepotStart[0] = 0;
2161     This->rootStartBlock        = 1;
2162     This->smallBlockDepotStart  = BLOCK_END_OF_CHAIN;
2163     This->bigBlockSizeBits      = DEF_BIG_BLOCK_SIZE_BITS;
2164     This->smallBlockSizeBits    = DEF_SMALL_BLOCK_SIZE_BITS;
2165     This->extBigBlockDepotStart = BLOCK_END_OF_CHAIN;
2166     This->extBigBlockDepotCount = 0;
2167
2168     StorageImpl_SaveFileHeader(This);
2169
2170     /*
2171      * Add one block for the big block depot and one block for the properties
2172      */
2173     size.s.HighPart = 0;
2174     size.s.LowPart  = This->bigBlockSize * 3;
2175     BIGBLOCKFILE_SetSize(This->bigBlockFile, size);
2176
2177     /*
2178      * Initialize the big block depot
2179      */
2180     bigBlockBuffer = StorageImpl_GetBigBlock(This, 0);
2181     memset(bigBlockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2182     StorageUtl_WriteDWord(bigBlockBuffer, 0, BLOCK_SPECIAL);
2183     StorageUtl_WriteDWord(bigBlockBuffer, sizeof(ULONG), BLOCK_END_OF_CHAIN);
2184     StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
2185   }
2186   else
2187   {
2188     /*
2189      * Load the header for the file.
2190      */
2191     hr = StorageImpl_LoadFileHeader(This);
2192
2193     if (FAILED(hr))
2194     {
2195       BIGBLOCKFILE_Destructor(This->bigBlockFile);
2196
2197       return hr;
2198     }
2199   }
2200
2201   /*
2202    * There is no block depot cached yet.
2203    */
2204   This->indexBlockDepotCached = 0xFFFFFFFF;
2205
2206   /*
2207    * Start searching for free blocks with block 0.
2208    */
2209   This->prevFreeBlock = 0;
2210   
2211   /*
2212    * Create the block chain abstractions.
2213    */
2214   This->rootBlockChain = 
2215     BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL);
2216
2217   This->smallBlockDepotChain = BlockChainStream_Construct(
2218                                  This, 
2219                                  &This->smallBlockDepotStart, 
2220                                  PROPERTY_NULL);
2221
2222   /*
2223    * Write the root property 
2224    */
2225   if (fileCreate)
2226   {
2227     StgProperty rootProp;
2228     /*
2229      * Initialize the property chain
2230      */
2231     memset(&rootProp, 0, sizeof(rootProp));
2232     lstrcpyAtoW(rootProp.name, rootPropertyName);
2233
2234     rootProp.sizeOfNameString = (lstrlenW(rootProp.name)+1) * sizeof(WCHAR);
2235     rootProp.propertyType     = PROPTYPE_ROOT;
2236     rootProp.previousProperty = PROPERTY_NULL;
2237     rootProp.nextProperty     = PROPERTY_NULL;
2238     rootProp.dirProperty      = PROPERTY_NULL;
2239     rootProp.startingBlock    = BLOCK_END_OF_CHAIN;
2240     rootProp.size.s.HighPart    = 0;
2241     rootProp.size.s.LowPart     = 0;
2242
2243     StorageImpl_WriteProperty(This, 0, &rootProp);
2244   }
2245
2246   /*
2247    * Find the ID of the root int he property sets.
2248    */
2249   currentPropertyIndex = 0;
2250   
2251   do
2252   {
2253     readSuccessful = StorageImpl_ReadProperty(
2254                       This, 
2255                       currentPropertyIndex, 
2256                       &currentProperty);
2257     
2258     if (readSuccessful)
2259     {
2260       if ( (currentProperty.sizeOfNameString != 0 ) &&
2261            (currentProperty.propertyType     == PROPTYPE_ROOT) )
2262       {
2263         This->rootPropertySetIndex = currentPropertyIndex;
2264       }
2265     }
2266
2267     currentPropertyIndex++;
2268     
2269   } while (readSuccessful && (This->rootPropertySetIndex == PROPERTY_NULL) );
2270   
2271   if (!readSuccessful)
2272   {
2273     /* TODO CLEANUP */
2274     return E_FAIL;
2275   }
2276
2277   /*
2278    * Create the block chain abstraction for the small block root chain.
2279    */
2280   This->smallBlockRootChain = BlockChainStream_Construct(
2281                                 This, 
2282                                 NULL, 
2283                                 This->rootPropertySetIndex);
2284   
2285   return hr;
2286 }
2287
2288 void StorageImpl_Destroy(
2289   StorageImpl* This)
2290 {
2291   TRACE("(%p)\n", This);
2292
2293   BlockChainStream_Destroy(This->smallBlockRootChain);
2294   BlockChainStream_Destroy(This->rootBlockChain);
2295   BlockChainStream_Destroy(This->smallBlockDepotChain);
2296
2297   BIGBLOCKFILE_Destructor(This->bigBlockFile);
2298   return;
2299 }
2300
2301 /******************************************************************************
2302  *      Storage32Impl_GetNextFreeBigBlock
2303  *
2304  * Returns the index of the next free big block.
2305  * If the big block depot is filled, this method will enlarge it.
2306  *
2307  */
2308 ULONG StorageImpl_GetNextFreeBigBlock(
2309   StorageImpl* This)
2310 {
2311   ULONG depotBlockIndexPos;
2312   void  *depotBuffer;
2313   ULONG depotBlockOffset;
2314   ULONG blocksPerDepot    = This->bigBlockSize / sizeof(ULONG);
2315   ULONG nextBlockIndex    = BLOCK_SPECIAL;
2316   int   depotIndex        = 0;
2317   ULONG freeBlock         = BLOCK_UNUSED;
2318
2319   depotIndex = This->prevFreeBlock / blocksPerDepot;
2320   depotBlockOffset = (This->prevFreeBlock % blocksPerDepot) * sizeof(ULONG);
2321
2322   /*
2323    * Scan the entire big block depot until we find a block marked free
2324    */
2325   while (nextBlockIndex != BLOCK_UNUSED)
2326   {
2327     if (depotIndex < COUNT_BBDEPOTINHEADER)
2328     {
2329       depotBlockIndexPos = This->bigBlockDepotStart[depotIndex];
2330
2331       /*
2332        * Grow the primary depot.
2333        */
2334       if (depotBlockIndexPos == BLOCK_UNUSED)
2335       {
2336         depotBlockIndexPos = depotIndex*blocksPerDepot;
2337
2338         /*
2339          * Add a block depot.
2340          */
2341         Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2342         This->bigBlockDepotCount++;
2343         This->bigBlockDepotStart[depotIndex] = depotBlockIndexPos;
2344
2345         /*
2346          * Flag it as a block depot.
2347          */
2348         StorageImpl_SetNextBlockInChain(This,
2349                                           depotBlockIndexPos,
2350                                           BLOCK_SPECIAL);
2351
2352         /* Save new header information.
2353          */
2354         StorageImpl_SaveFileHeader(This);
2355       }
2356     }
2357     else
2358     {
2359       depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotIndex);
2360
2361       if (depotBlockIndexPos == BLOCK_UNUSED)
2362       {
2363         /*
2364          * Grow the extended depot.
2365          */
2366         ULONG extIndex       = BLOCK_UNUSED;
2367         ULONG numExtBlocks   = depotIndex - COUNT_BBDEPOTINHEADER;
2368         ULONG extBlockOffset = numExtBlocks % (blocksPerDepot - 1);
2369
2370         if (extBlockOffset == 0)
2371         {
2372           /* We need an extended block.
2373            */
2374           extIndex = Storage32Impl_AddExtBlockDepot(This);
2375           This->extBigBlockDepotCount++;
2376           depotBlockIndexPos = extIndex + 1;
2377         }
2378         else
2379           depotBlockIndexPos = depotIndex * blocksPerDepot;
2380
2381         /*
2382          * Add a block depot and mark it in the extended block.
2383          */
2384         Storage32Impl_AddBlockDepot(This, depotBlockIndexPos);
2385         This->bigBlockDepotCount++;
2386         Storage32Impl_SetExtDepotBlock(This, depotIndex, depotBlockIndexPos);
2387
2388         /* Flag the block depot.
2389          */
2390         StorageImpl_SetNextBlockInChain(This,
2391                                           depotBlockIndexPos,
2392                                           BLOCK_SPECIAL);
2393
2394         /* If necessary, flag the extended depot block.
2395          */
2396         if (extIndex != BLOCK_UNUSED)
2397           StorageImpl_SetNextBlockInChain(This, extIndex, BLOCK_EXTBBDEPOT);
2398
2399         /* Save header information.
2400          */
2401         StorageImpl_SaveFileHeader(This);
2402       }
2403     }
2404
2405     depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2406
2407     if (depotBuffer != 0)
2408     {
2409       while ( ( (depotBlockOffset/sizeof(ULONG) ) < blocksPerDepot) &&
2410               ( nextBlockIndex != BLOCK_UNUSED))
2411       {
2412         StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2413
2414         if (nextBlockIndex == BLOCK_UNUSED)
2415         {
2416           freeBlock = (depotIndex * blocksPerDepot) +
2417                       (depotBlockOffset/sizeof(ULONG));
2418         }
2419
2420         depotBlockOffset += sizeof(ULONG);
2421       }
2422
2423       StorageImpl_ReleaseBigBlock(This, depotBuffer);
2424     }
2425
2426     depotIndex++;
2427     depotBlockOffset = 0;
2428   }
2429
2430   This->prevFreeBlock = freeBlock;
2431
2432   return freeBlock;
2433 }
2434
2435 /******************************************************************************
2436  *      Storage32Impl_AddBlockDepot
2437  *
2438  * This will create a depot block, essentially it is a block initialized
2439  * to BLOCK_UNUSEDs.
2440  */
2441 void Storage32Impl_AddBlockDepot(StorageImpl* This, ULONG blockIndex)
2442 {
2443   BYTE* blockBuffer;
2444
2445   blockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
2446
2447   /*
2448    * Initialize blocks as free
2449    */
2450   memset(blockBuffer, BLOCK_UNUSED, This->bigBlockSize);
2451
2452   StorageImpl_ReleaseBigBlock(This, blockBuffer);
2453 }
2454
2455 /******************************************************************************
2456  *      Storage32Impl_GetExtDepotBlock
2457  *
2458  * Returns the index of the block that corresponds to the specified depot
2459  * index. This method is only for depot indexes equal or greater than
2460  * COUNT_BBDEPOTINHEADER.
2461  */
2462 ULONG Storage32Impl_GetExtDepotBlock(StorageImpl* This, ULONG depotIndex)
2463 {
2464   ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2465   ULONG numExtBlocks           = depotIndex - COUNT_BBDEPOTINHEADER;
2466   ULONG extBlockCount          = numExtBlocks / depotBlocksPerExtBlock;
2467   ULONG extBlockOffset         = numExtBlocks % depotBlocksPerExtBlock;
2468   ULONG blockIndex             = BLOCK_UNUSED;
2469   ULONG extBlockIndex          = This->extBigBlockDepotStart;
2470
2471   assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2472
2473   if (This->extBigBlockDepotStart == BLOCK_END_OF_CHAIN)
2474     return BLOCK_UNUSED;
2475
2476   while (extBlockCount > 0)
2477   {
2478     extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2479     extBlockCount--;
2480   }
2481
2482   if (extBlockIndex != BLOCK_UNUSED)
2483   {
2484     BYTE* depotBuffer;
2485
2486     depotBuffer = StorageImpl_GetROBigBlock(This, extBlockIndex);
2487
2488     if (depotBuffer != 0)
2489     {
2490       StorageUtl_ReadDWord(depotBuffer,
2491                            extBlockOffset * sizeof(ULONG),
2492                            &blockIndex);
2493
2494       StorageImpl_ReleaseBigBlock(This, depotBuffer);
2495     }
2496   }
2497
2498   return blockIndex;
2499 }
2500
2501 /******************************************************************************
2502  *      Storage32Impl_SetExtDepotBlock
2503  *
2504  * Associates the specified block index to the specified depot index.
2505  * This method is only for depot indexes equal or greater than
2506  * COUNT_BBDEPOTINHEADER.
2507  */
2508 void Storage32Impl_SetExtDepotBlock(StorageImpl* This,
2509                                     ULONG depotIndex,
2510                                     ULONG blockIndex)
2511 {
2512   ULONG depotBlocksPerExtBlock = (This->bigBlockSize / sizeof(ULONG)) - 1;
2513   ULONG numExtBlocks           = depotIndex - COUNT_BBDEPOTINHEADER;
2514   ULONG extBlockCount          = numExtBlocks / depotBlocksPerExtBlock;
2515   ULONG extBlockOffset         = numExtBlocks % depotBlocksPerExtBlock;
2516   ULONG extBlockIndex          = This->extBigBlockDepotStart;
2517
2518   assert(depotIndex >= COUNT_BBDEPOTINHEADER);
2519
2520   while (extBlockCount > 0)
2521   {
2522     extBlockIndex = Storage32Impl_GetNextExtendedBlock(This, extBlockIndex);
2523     extBlockCount--;
2524   }
2525
2526   if (extBlockIndex != BLOCK_UNUSED)
2527   {
2528     BYTE* depotBuffer;
2529
2530     depotBuffer = StorageImpl_GetBigBlock(This, extBlockIndex);
2531
2532     if (depotBuffer != 0)
2533     {
2534       StorageUtl_WriteDWord(depotBuffer,
2535                             extBlockOffset * sizeof(ULONG),
2536                             blockIndex);
2537
2538       StorageImpl_ReleaseBigBlock(This, depotBuffer);
2539     }
2540   }
2541 }
2542
2543 /******************************************************************************
2544  *      Storage32Impl_AddExtBlockDepot
2545  *
2546  * Creates an extended depot block.
2547  */
2548 ULONG Storage32Impl_AddExtBlockDepot(StorageImpl* This)
2549 {
2550   ULONG numExtBlocks           = This->extBigBlockDepotCount;
2551   ULONG nextExtBlock           = This->extBigBlockDepotStart;
2552   BYTE* depotBuffer            = NULL;
2553   ULONG index                  = BLOCK_UNUSED;
2554   ULONG nextBlockOffset        = This->bigBlockSize - sizeof(ULONG);
2555   ULONG blocksPerDepotBlock    = This->bigBlockSize / sizeof(ULONG);
2556   ULONG depotBlocksPerExtBlock = blocksPerDepotBlock - 1;
2557
2558   index = (COUNT_BBDEPOTINHEADER + (numExtBlocks * depotBlocksPerExtBlock)) *
2559           blocksPerDepotBlock;
2560
2561   if ((numExtBlocks == 0) && (nextExtBlock == BLOCK_END_OF_CHAIN))
2562   {
2563     /*
2564      * The first extended block.
2565      */
2566     This->extBigBlockDepotStart = index;
2567   }
2568   else
2569   {
2570     int i;
2571     /*
2572      * Follow the chain to the last one.
2573      */
2574     for (i = 0; i < (numExtBlocks - 1); i++)
2575     {
2576       nextExtBlock = Storage32Impl_GetNextExtendedBlock(This, nextExtBlock);
2577     }
2578
2579     /*
2580      * Add the new extended block to the chain.
2581      */
2582     depotBuffer = StorageImpl_GetBigBlock(This, nextExtBlock);
2583     StorageUtl_WriteDWord(depotBuffer, nextBlockOffset, index);
2584     StorageImpl_ReleaseBigBlock(This, depotBuffer);
2585   }
2586
2587   /*
2588    * Initialize this block.
2589    */
2590   depotBuffer = StorageImpl_GetBigBlock(This, index);
2591   memset(depotBuffer, BLOCK_UNUSED, This->bigBlockSize);
2592   StorageImpl_ReleaseBigBlock(This, depotBuffer);
2593
2594   return index;
2595 }
2596
2597 /******************************************************************************
2598  *      Storage32Impl_FreeBigBlock
2599  *
2600  * This method will flag the specified block as free in the big block depot.
2601  */
2602 void  StorageImpl_FreeBigBlock(
2603   StorageImpl* This,
2604   ULONG          blockIndex)
2605 {
2606   StorageImpl_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
2607
2608   if (blockIndex < This->prevFreeBlock)
2609     This->prevFreeBlock = blockIndex;
2610 }
2611
2612 /************************************************************************
2613  * Storage32Impl_GetNextBlockInChain
2614  *
2615  * This method will retrieve the block index of the next big block in
2616  * in the chain.
2617  *
2618  * Params:  This       - Pointer to the Storage object.
2619  *          blockIndex - Index of the block to retrieve the chain
2620  *                       for.
2621  *
2622  * Returns: This method returns the index of the next block in the chain.
2623  *          It will return the constants:
2624  *              BLOCK_SPECIAL - If the block given was not part of a
2625  *                              chain.
2626  *              BLOCK_END_OF_CHAIN - If the block given was the last in
2627  *                                   a chain.
2628  *              BLOCK_UNUSED - If the block given was not past of a chain
2629  *                             and is available.
2630  *              BLOCK_EXTBBDEPOT - This block is part of the extended
2631  *                                 big block depot.
2632  *
2633  * See Windows documentation for more details on IStorage methods.
2634  */
2635 ULONG StorageImpl_GetNextBlockInChain(
2636   StorageImpl* This,
2637   ULONG          blockIndex)
2638 {
2639   ULONG offsetInDepot    = blockIndex * sizeof (ULONG);
2640   ULONG depotBlockCount  = offsetInDepot / This->bigBlockSize;
2641   ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2642   ULONG nextBlockIndex   = BLOCK_SPECIAL;
2643   void* depotBuffer;
2644   ULONG depotBlockIndexPos;
2645
2646   assert(depotBlockCount < This->bigBlockDepotCount);
2647
2648   /*
2649    * Cache the currently accessed depot block.
2650    */
2651   if (depotBlockCount != This->indexBlockDepotCached)
2652   {
2653     This->indexBlockDepotCached = depotBlockCount;
2654
2655     if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2656     {
2657       depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2658     }
2659     else
2660     {
2661       /*
2662        * We have to look in the extended depot.
2663        */
2664       depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2665     }
2666
2667     depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2668
2669     if (depotBuffer!=0)
2670     {
2671       int index;
2672
2673       for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2674       {
2675         StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), &nextBlockIndex);
2676         This->blockDepotCached[index] = nextBlockIndex;
2677       }
2678
2679       StorageImpl_ReleaseBigBlock(This, depotBuffer);
2680     }
2681   }
2682
2683   nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2684
2685   return nextBlockIndex;
2686 }
2687
2688 /******************************************************************************
2689  *      Storage32Impl_GetNextExtendedBlock
2690  *
2691  * Given an extended block this method will return the next extended block.
2692  *
2693  * NOTES:
2694  * The last ULONG of an extended block is the block index of the next
2695  * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2696  * depot.
2697  *
2698  * Return values:
2699  *    - The index of the next extended block
2700  *    - BLOCK_UNUSED: there is no next extended block.
2701  *    - Any other return values denotes failure.
2702  */
2703 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2704 {
2705   ULONG nextBlockIndex   = BLOCK_SPECIAL;
2706   ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2707   void* depotBuffer;
2708
2709   depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2710
2711   if (depotBuffer!=0)
2712   {
2713     StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2714
2715     StorageImpl_ReleaseBigBlock(This, depotBuffer);
2716   }
2717
2718   return nextBlockIndex;
2719 }
2720
2721 /******************************************************************************
2722  *      Storage32Impl_SetNextBlockInChain
2723  *
2724  * This method will write the index of the specified block's next block
2725  * in the big block depot.
2726  *
2727  * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2728  *              do the following
2729  *
2730  * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2731  * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2732  * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2733  *
2734  */
2735 void  StorageImpl_SetNextBlockInChain(
2736           StorageImpl* This,
2737           ULONG          blockIndex,
2738           ULONG          nextBlock)
2739 {
2740   ULONG offsetInDepot    = blockIndex * sizeof (ULONG);
2741   ULONG depotBlockCount  = offsetInDepot / This->bigBlockSize;
2742   ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2743   ULONG depotBlockIndexPos;
2744   void* depotBuffer;
2745
2746   assert(depotBlockCount < This->bigBlockDepotCount);
2747   assert(blockIndex != nextBlock);
2748
2749   if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2750   {
2751     depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2752   }
2753   else
2754   {
2755     /*
2756      * We have to look in the extended depot.
2757      */
2758     depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2759   }
2760
2761   depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2762
2763   if (depotBuffer!=0)
2764   {
2765     StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2766     StorageImpl_ReleaseBigBlock(This, depotBuffer);
2767   }
2768
2769   /*
2770    * Update the cached block depot, if necessary.
2771    */
2772   if (depotBlockCount == This->indexBlockDepotCached)
2773   {
2774     This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2775   }
2776 }
2777
2778 /******************************************************************************
2779  *      Storage32Impl_LoadFileHeader
2780  *
2781  * This method will read in the file header, i.e. big block index -1.
2782  */
2783 HRESULT StorageImpl_LoadFileHeader(
2784           StorageImpl* This)
2785 {
2786   HRESULT hr = STG_E_FILENOTFOUND;
2787   void*   headerBigBlock = NULL;
2788   int     index;
2789
2790   /*
2791    * Get a pointer to the big block of data containing the header.
2792    */
2793   headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2794
2795   /*
2796    * Extract the information from the header.
2797    */
2798   if (headerBigBlock!=0)
2799   {
2800     /*
2801      * Check for the "magic number" signature and return an error if it is not
2802      * found.
2803      */
2804     if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2805     {
2806       StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2807       return STG_E_OLDFORMAT;
2808     }
2809
2810     if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2811     {
2812       StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2813       return STG_E_INVALIDHEADER;
2814     }
2815
2816     StorageUtl_ReadWord(
2817       headerBigBlock, 
2818       OFFSET_BIGBLOCKSIZEBITS,   
2819       &This->bigBlockSizeBits);
2820
2821     StorageUtl_ReadWord(
2822       headerBigBlock, 
2823       OFFSET_SMALLBLOCKSIZEBITS, 
2824       &This->smallBlockSizeBits);
2825
2826     StorageUtl_ReadDWord(
2827       headerBigBlock, 
2828       OFFSET_BBDEPOTCOUNT,      
2829       &This->bigBlockDepotCount);
2830
2831     StorageUtl_ReadDWord(
2832       headerBigBlock, 
2833       OFFSET_ROOTSTARTBLOCK,    
2834       &This->rootStartBlock);
2835
2836     StorageUtl_ReadDWord(
2837       headerBigBlock, 
2838       OFFSET_SBDEPOTSTART,      
2839       &This->smallBlockDepotStart);
2840
2841     StorageUtl_ReadDWord( 
2842       headerBigBlock, 
2843       OFFSET_EXTBBDEPOTSTART,   
2844       &This->extBigBlockDepotStart);
2845
2846     StorageUtl_ReadDWord(
2847       headerBigBlock, 
2848       OFFSET_EXTBBDEPOTCOUNT,   
2849       &This->extBigBlockDepotCount);
2850     
2851     for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2852     {
2853       StorageUtl_ReadDWord(
2854         headerBigBlock, 
2855         OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2856         &(This->bigBlockDepotStart[index]));
2857     }
2858     
2859     /*
2860      * Make the bitwise arithmetic to get the size of the blocks in bytes.
2861      */
2862     if ((1 << 2) == 4)
2863     {
2864       This->bigBlockSize   = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2865       This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2866     }
2867     else
2868     {
2869       This->bigBlockSize   = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
2870       This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
2871     }
2872     
2873     /*
2874      * Right now, the code is making some assumptions about the size of the 
2875      * blocks, just make sure they are what we're expecting.
2876      */
2877     assert( (This->bigBlockSize==DEF_BIG_BLOCK_SIZE) && 
2878             (This->smallBlockSize==DEF_SMALL_BLOCK_SIZE));
2879     
2880     /*
2881      * Release the block.
2882      */
2883     StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2884
2885     hr = S_OK;
2886   }
2887   
2888   return hr;
2889 }
2890
2891 /******************************************************************************
2892  *      Storage32Impl_SaveFileHeader
2893  *
2894  * This method will save to the file the header, i.e. big block -1.
2895  */
2896 void StorageImpl_SaveFileHeader(
2897           StorageImpl* This)
2898 {
2899   BYTE   headerBigBlock[BIG_BLOCK_SIZE];
2900   int    index;
2901   BOOL success;
2902
2903   /*
2904    * Get a pointer to the big block of data containing the header.
2905    */
2906   success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
2907   
2908   /*
2909    * If the block read failed, the file is probably new.
2910    */
2911   if (!success)
2912   {
2913     /*
2914      * Initialize for all unknown fields.
2915      */
2916     memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
2917     
2918     /*
2919      * Initialize the magic number.
2920      */
2921     memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
2922     
2923     /*
2924      * And a bunch of things we don't know what they mean
2925      */
2926     StorageUtl_WriteWord(headerBigBlock,  0x18, 0x3b);
2927     StorageUtl_WriteWord(headerBigBlock,  0x1a, 0x3);
2928     StorageUtl_WriteWord(headerBigBlock,  0x1c, (WORD)-2);
2929     StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
2930     StorageUtl_WriteDWord(headerBigBlock, 0x40, (DWORD)0x0001);
2931   }
2932   
2933   /*
2934    * Write the information to the header.
2935    */
2936   if (headerBigBlock!=0)
2937   {
2938     StorageUtl_WriteWord(
2939       headerBigBlock, 
2940       OFFSET_BIGBLOCKSIZEBITS,   
2941       This->bigBlockSizeBits);
2942
2943     StorageUtl_WriteWord(
2944       headerBigBlock, 
2945       OFFSET_SMALLBLOCKSIZEBITS, 
2946       This->smallBlockSizeBits);
2947
2948     StorageUtl_WriteDWord(
2949       headerBigBlock, 
2950       OFFSET_BBDEPOTCOUNT,      
2951       This->bigBlockDepotCount);
2952
2953     StorageUtl_WriteDWord(
2954       headerBigBlock, 
2955       OFFSET_ROOTSTARTBLOCK,    
2956       This->rootStartBlock);
2957
2958     StorageUtl_WriteDWord(
2959       headerBigBlock, 
2960       OFFSET_SBDEPOTSTART,      
2961       This->smallBlockDepotStart);
2962
2963     StorageUtl_WriteDWord(
2964       headerBigBlock, 
2965       OFFSET_EXTBBDEPOTSTART,   
2966       This->extBigBlockDepotStart);
2967
2968     StorageUtl_WriteDWord(
2969       headerBigBlock, 
2970       OFFSET_EXTBBDEPOTCOUNT,   
2971       This->extBigBlockDepotCount);
2972
2973     for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2974     {
2975       StorageUtl_WriteDWord(
2976         headerBigBlock, 
2977         OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2978         (This->bigBlockDepotStart[index]));
2979     }
2980   }
2981   
2982   /*
2983    * Write the big block back to the file.
2984    */
2985   StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
2986 }
2987
2988 /******************************************************************************
2989  *      Storage32Impl_ReadProperty
2990  *
2991  * This method will read the specified property from the property chain.
2992  */
2993 BOOL StorageImpl_ReadProperty(
2994   StorageImpl* This,
2995   ULONG          index,
2996   StgProperty*   buffer)
2997 {
2998   BYTE           currentProperty[PROPSET_BLOCK_SIZE];
2999   ULARGE_INTEGER offsetInPropSet;
3000   BOOL         readSuccessful;
3001   ULONG          bytesRead;
3002
3003   offsetInPropSet.s.HighPart = 0;
3004   offsetInPropSet.s.LowPart  = index * PROPSET_BLOCK_SIZE;
3005   
3006   readSuccessful = BlockChainStream_ReadAt(
3007                     This->rootBlockChain,
3008                     offsetInPropSet,
3009                     PROPSET_BLOCK_SIZE,
3010                     currentProperty,
3011                     &bytesRead);
3012   
3013   if (readSuccessful)
3014   {
3015     memset(buffer->name, 0, sizeof(buffer->name));
3016     memcpy(
3017       buffer->name, 
3018       currentProperty+OFFSET_PS_NAME, 
3019       PROPERTY_NAME_BUFFER_LEN );
3020
3021     memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3022     
3023     StorageUtl_ReadWord(
3024       currentProperty,  
3025       OFFSET_PS_NAMELENGTH,  
3026       &buffer->sizeOfNameString);
3027
3028     StorageUtl_ReadDWord(
3029       currentProperty, 
3030       OFFSET_PS_PREVIOUSPROP, 
3031       &buffer->previousProperty);
3032
3033     StorageUtl_ReadDWord(
3034       currentProperty, 
3035       OFFSET_PS_NEXTPROP,     
3036       &buffer->nextProperty);
3037
3038     StorageUtl_ReadDWord(
3039       currentProperty, 
3040       OFFSET_PS_DIRPROP,      
3041       &buffer->dirProperty);
3042
3043     StorageUtl_ReadGUID(
3044       currentProperty,  
3045       OFFSET_PS_GUID,        
3046       &buffer->propertyUniqueID);
3047
3048     StorageUtl_ReadDWord(
3049       currentProperty, 
3050       OFFSET_PS_TSS1,         
3051       &buffer->timeStampS1);
3052
3053     StorageUtl_ReadDWord(
3054       currentProperty, 
3055       OFFSET_PS_TSD1,         
3056       &buffer->timeStampD1);
3057
3058     StorageUtl_ReadDWord(
3059       currentProperty, 
3060       OFFSET_PS_TSS2,         
3061       &buffer->timeStampS2);
3062
3063     StorageUtl_ReadDWord(
3064       currentProperty, 
3065       OFFSET_PS_TSD2,         
3066       &buffer->timeStampD2);
3067
3068     StorageUtl_ReadDWord(
3069       currentProperty, 
3070       OFFSET_PS_STARTBLOCK,   
3071       &buffer->startingBlock);
3072
3073     StorageUtl_ReadDWord(
3074       currentProperty, 
3075       OFFSET_PS_SIZE,         
3076       &buffer->size.s.LowPart);
3077
3078     buffer->size.s.HighPart = 0;
3079   }
3080
3081   return readSuccessful;
3082 }
3083
3084 /*********************************************************************
3085  * Write the specified property into the property chain
3086  */
3087 BOOL StorageImpl_WriteProperty(
3088   StorageImpl* This,
3089   ULONG          index,
3090   StgProperty*   buffer)
3091 {
3092   BYTE           currentProperty[PROPSET_BLOCK_SIZE];
3093   ULARGE_INTEGER offsetInPropSet;
3094   BOOL         writeSuccessful;
3095   ULONG          bytesWritten;
3096
3097   offsetInPropSet.s.HighPart = 0;
3098   offsetInPropSet.s.LowPart  = index * PROPSET_BLOCK_SIZE;
3099
3100   memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3101
3102   memcpy(
3103     currentProperty + OFFSET_PS_NAME, 
3104     buffer->name, 
3105     PROPERTY_NAME_BUFFER_LEN );
3106
3107   memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3108
3109   StorageUtl_WriteWord(
3110     currentProperty,  
3111       OFFSET_PS_NAMELENGTH,   
3112       buffer->sizeOfNameString);
3113
3114   StorageUtl_WriteDWord(
3115     currentProperty, 
3116       OFFSET_PS_PREVIOUSPROP, 
3117       buffer->previousProperty);
3118
3119   StorageUtl_WriteDWord(
3120     currentProperty, 
3121       OFFSET_PS_NEXTPROP,     
3122       buffer->nextProperty);
3123
3124   StorageUtl_WriteDWord(
3125     currentProperty, 
3126       OFFSET_PS_DIRPROP,      
3127       buffer->dirProperty);
3128
3129   StorageUtl_WriteGUID(
3130     currentProperty,  
3131       OFFSET_PS_GUID,        
3132       &buffer->propertyUniqueID);
3133
3134   StorageUtl_WriteDWord(
3135     currentProperty, 
3136       OFFSET_PS_TSS1,         
3137       buffer->timeStampS1);
3138
3139   StorageUtl_WriteDWord(
3140     currentProperty, 
3141       OFFSET_PS_TSD1,         
3142       buffer->timeStampD1);
3143
3144   StorageUtl_WriteDWord(
3145     currentProperty, 
3146       OFFSET_PS_TSS2,         
3147       buffer->timeStampS2);
3148
3149   StorageUtl_WriteDWord(
3150     currentProperty, 
3151       OFFSET_PS_TSD2,         
3152       buffer->timeStampD2);
3153
3154   StorageUtl_WriteDWord(
3155     currentProperty, 
3156       OFFSET_PS_STARTBLOCK,   
3157       buffer->startingBlock);
3158
3159   StorageUtl_WriteDWord(
3160     currentProperty, 
3161       OFFSET_PS_SIZE,         
3162       buffer->size.s.LowPart);
3163
3164   writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3165                                             offsetInPropSet,
3166                                             PROPSET_BLOCK_SIZE,
3167                                             currentProperty,
3168                                             &bytesWritten);
3169   return writeSuccessful;
3170 }
3171
3172 BOOL StorageImpl_ReadBigBlock(
3173   StorageImpl* This,
3174   ULONG          blockIndex,
3175   void*          buffer)
3176 {
3177   void* bigBlockBuffer;
3178
3179   bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3180
3181   if (bigBlockBuffer!=0)
3182   {
3183     memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3184
3185     StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3186
3187     return TRUE;
3188   }
3189
3190   return FALSE;
3191 }
3192
3193 BOOL StorageImpl_WriteBigBlock(
3194   StorageImpl* This,
3195   ULONG          blockIndex,
3196   void*          buffer)
3197 {
3198   void* bigBlockBuffer;
3199
3200   bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3201
3202   if (bigBlockBuffer!=0)
3203   {
3204     memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3205
3206     StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3207     
3208     return TRUE;
3209   }
3210   
3211   return FALSE;
3212 }
3213
3214 void* StorageImpl_GetROBigBlock(
3215   StorageImpl* This,
3216   ULONG          blockIndex)
3217 {
3218   return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3219 }
3220
3221 void* StorageImpl_GetBigBlock(
3222   StorageImpl* This,
3223   ULONG          blockIndex)
3224 {
3225   return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3226 }
3227
3228 void StorageImpl_ReleaseBigBlock(
3229   StorageImpl* This,
3230   void*          pBigBlock)
3231 {
3232   BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3233 }
3234
3235 /******************************************************************************
3236  *              Storage32Impl_SmallBlocksToBigBlocks
3237  *
3238  * This method will convert a small block chain to a big block chain.
3239  * The small block chain will be destroyed.
3240  */
3241 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3242                       StorageImpl* This,
3243                       SmallBlockChainStream** ppsbChain)
3244 {
3245   ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3246   ULARGE_INTEGER size, offset;
3247   ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3248   ULONG propertyIndex;
3249   BOOL successRead, successWrite;
3250   StgProperty chainProperty;
3251   BYTE *buffer;
3252   BlockChainStream *bbTempChain = NULL;
3253   BlockChainStream *bigBlockChain = NULL;
3254
3255   /*
3256    * Create a temporary big block chain that doesn't have
3257    * an associated property. This temporary chain will be
3258    * used to copy data from small blocks to big blocks.
3259    */
3260   bbTempChain = BlockChainStream_Construct(This,
3261                                            &bbHeadOfChain,
3262                                            PROPERTY_NULL);
3263
3264   /*
3265    * Grow the big block chain.
3266    */
3267   size = SmallBlockChainStream_GetSize(*ppsbChain);
3268   BlockChainStream_SetSize(bbTempChain, size);
3269
3270   /*
3271    * Copy the contents of the small block chain to the big block chain
3272    * by small block size increments.
3273    */
3274   offset.s.LowPart = 0;
3275   offset.s.HighPart = 0;
3276   cbTotalRead = 0;
3277   cbTotalWritten = 0;
3278
3279   buffer = (BYTE *) HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3280   do
3281   {
3282     successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3283                                                offset,
3284                                                DEF_SMALL_BLOCK_SIZE,
3285                                                buffer,
3286                                                &cbRead);
3287     cbTotalRead += cbRead;
3288
3289     successWrite = BlockChainStream_WriteAt(bbTempChain,
3290                                             offset,
3291                                             cbRead,
3292                                             buffer,
3293                                             &cbWritten);
3294     cbTotalWritten += cbWritten;
3295
3296     offset.s.LowPart += This->smallBlockSize;
3297
3298   } while (successRead && successWrite);
3299   HeapFree(GetProcessHeap(),0,buffer);
3300
3301   assert(cbTotalRead == cbTotalWritten);
3302
3303   /*
3304    * Destroy the small block chain.
3305    */
3306   propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3307   size.s.HighPart = 0;
3308   size.s.LowPart  = 0;
3309   SmallBlockChainStream_SetSize(*ppsbChain, size);
3310   SmallBlockChainStream_Destroy(*ppsbChain);
3311   *ppsbChain = 0;
3312
3313   /*
3314    * Change the property information. This chain is now a big block chain
3315    * and it doesn't reside in the small blocks chain anymore.
3316    */
3317   StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3318
3319   chainProperty.startingBlock = bbHeadOfChain;
3320
3321   StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3322
3323   /*
3324    * Destroy the temporary propertyless big block chain.
3325    * Create a new big block chain associated with this property.
3326    */
3327   BlockChainStream_Destroy(bbTempChain);
3328   bigBlockChain = BlockChainStream_Construct(This,
3329                                              NULL,
3330                                              propertyIndex);
3331
3332   return bigBlockChain;
3333 }
3334
3335 /******************************************************************************
3336 ** Storage32InternalImpl implementation
3337 */
3338
3339 StorageInternalImpl* StorageInternalImpl_Construct(
3340   StorageImpl* ancestorStorage,  
3341   ULONG          rootPropertyIndex)
3342 {
3343   StorageInternalImpl* newStorage;
3344
3345   /*
3346    * Allocate space for the new storage object
3347    */
3348   newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
3349
3350   if (newStorage!=0)
3351   {
3352     memset(newStorage, 0, sizeof(StorageInternalImpl));
3353
3354     /*
3355      * Initialize the virtual function table.
3356      */
3357     ICOM_VTBL(newStorage)    = &Storage32InternalImpl_Vtbl;
3358     newStorage->v_destructor = &StorageInternalImpl_Destroy;
3359
3360     /*
3361      * Keep the ancestor storage pointer and nail a reference to it.
3362      */
3363     newStorage->ancestorStorage = ancestorStorage;
3364     StorageBaseImpl_AddRef((IStorage*)(newStorage->ancestorStorage));
3365
3366     /*
3367      * Keep the index of the root property set for this storage,
3368      */
3369     newStorage->rootPropertySetIndex = rootPropertyIndex;
3370
3371     return newStorage;
3372   }
3373
3374   return 0;
3375 }
3376
3377 void StorageInternalImpl_Destroy(
3378   StorageInternalImpl* This)
3379 {
3380   StorageBaseImpl_Release((IStorage*)This->ancestorStorage);
3381   HeapFree(GetProcessHeap(), 0, This);
3382 }
3383
3384 /******************************************************************************
3385 **
3386 ** Storage32InternalImpl_Commit
3387 **
3388 ** The non-root storages cannot be opened in transacted mode thus this function
3389 ** does nothing.
3390 */
3391 HRESULT WINAPI StorageInternalImpl_Commit( 
3392   IStorage*            iface,
3393   DWORD                  grfCommitFlags)  /* [in] */ 
3394 {
3395   return S_OK;
3396 }
3397         
3398 /******************************************************************************
3399 **
3400 ** Storage32InternalImpl_Revert
3401 **
3402 ** The non-root storages cannot be opened in transacted mode thus this function
3403 ** does nothing.
3404 */
3405 HRESULT WINAPI StorageInternalImpl_Revert( 
3406   IStorage*            iface)
3407 {
3408   return S_OK;
3409 }
3410
3411 /******************************************************************************
3412 ** IEnumSTATSTGImpl implementation
3413 */
3414
3415 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3416   StorageImpl* parentStorage,
3417   ULONG          firstPropertyNode)
3418 {
3419   IEnumSTATSTGImpl* newEnumeration;
3420
3421   newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
3422   
3423   if (newEnumeration!=0)
3424   {
3425     /*
3426      * Set-up the virtual function table and reference count.
3427      */
3428     ICOM_VTBL(newEnumeration) = &IEnumSTATSTGImpl_Vtbl;
3429     newEnumeration->ref       = 0;
3430     
3431     /*
3432      * We want to nail-down the reference to the storage in case the
3433      * enumeration out-lives the storage in the client application.
3434      */
3435     newEnumeration->parentStorage = parentStorage;
3436     IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
3437     
3438     newEnumeration->firstPropertyNode   = firstPropertyNode;
3439     
3440     /*
3441      * Initialize the search stack
3442      */
3443     newEnumeration->stackSize    = 0;
3444     newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
3445     newEnumeration->stackToVisit = 
3446       HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
3447     
3448     /*
3449      * Make sure the current node of the iterator is the first one.
3450      */
3451     IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
3452   }
3453   
3454   return newEnumeration;
3455 }
3456
3457 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3458 {
3459   IStorage_Release((IStorage*)This->parentStorage);
3460   HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3461   HeapFree(GetProcessHeap(), 0, This);
3462 }
3463
3464 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3465   IEnumSTATSTG*     iface,
3466   REFIID            riid,
3467   void**            ppvObject)
3468 {
3469   IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3470
3471   /*
3472    * Perform a sanity check on the parameters.
3473    */
3474   if (ppvObject==0)
3475     return E_INVALIDARG;
3476
3477   /*
3478    * Initialize the return parameter.
3479    */
3480   *ppvObject = 0;
3481
3482   /*
3483    * Compare the riid with the interface IDs implemented by this object.
3484    */
3485   if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0) 
3486   {
3487     *ppvObject = (IEnumSTATSTG*)This;
3488   }
3489   else if (memcmp(&IID_IStorage, riid, sizeof(IID_IEnumSTATSTG)) == 0) 
3490   {
3491     *ppvObject = (IEnumSTATSTG*)This;
3492   }
3493
3494   /*
3495    * Check that we obtained an interface.
3496    */
3497   if ((*ppvObject)==0)
3498     return E_NOINTERFACE;
3499
3500   /*
3501    * Query Interface always increases the reference count by one when it is
3502    * successful
3503    */
3504   IEnumSTATSTGImpl_AddRef((IEnumSTATSTG*)This);
3505
3506   return S_OK;
3507 }
3508         
3509 ULONG   WINAPI IEnumSTATSTGImpl_AddRef(
3510   IEnumSTATSTG* iface)
3511 {
3512   IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3513
3514   This->ref++;
3515   return This->ref;
3516 }
3517         
3518 ULONG   WINAPI IEnumSTATSTGImpl_Release(
3519   IEnumSTATSTG* iface)
3520 {
3521   IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3522
3523   ULONG newRef;
3524
3525   This->ref--;
3526   newRef = This->ref;
3527
3528   /*
3529    * If the reference count goes down to 0, perform suicide.
3530    */
3531   if (newRef==0)
3532   {
3533     IEnumSTATSTGImpl_Destroy(This);
3534   }
3535
3536   return newRef;;
3537 }
3538
3539 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3540   IEnumSTATSTG* iface,
3541   ULONG             celt,
3542   STATSTG*          rgelt,
3543   ULONG*            pceltFetched)
3544 {
3545   IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3546
3547   StgProperty currentProperty;
3548   STATSTG*    currentReturnStruct = rgelt;
3549   ULONG       objectFetched       = 0;
3550   ULONG      currentSearchNode;
3551
3552   /*
3553    * Perform a sanity check on the parameters.
3554    */
3555   if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3556     return E_INVALIDARG;  
3557   
3558   /*
3559    * To avoid the special case, get another pointer to a ULONG value if
3560    * the caller didn't supply one.
3561    */
3562   if (pceltFetched==0)
3563     pceltFetched = &objectFetched;
3564   
3565   /*
3566    * Start the iteration, we will iterate until we hit the end of the
3567    * linked list or until we hit the number of items to iterate through
3568    */
3569   *pceltFetched = 0;
3570
3571   /*
3572    * Start with the node at the top of the stack.
3573    */
3574   currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3575
3576   while ( ( *pceltFetched < celt) && 
3577           ( currentSearchNode!=PROPERTY_NULL) )
3578   {
3579     /* 
3580      * Remove the top node from the stack
3581      */
3582     IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3583
3584     /*
3585      * Read the property from the storage.
3586      */
3587     StorageImpl_ReadProperty(This->parentStorage,
3588       currentSearchNode, 
3589       &currentProperty);
3590
3591     /*
3592      * Copy the information to the return buffer.
3593      */
3594     StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3595       &currentProperty,
3596       STATFLAG_DEFAULT);
3597         
3598     /*
3599      * Step to the next item in the iteration
3600      */
3601     (*pceltFetched)++;
3602     currentReturnStruct++;
3603
3604     /*
3605      * Push the next search node in the search stack.
3606      */
3607     IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3608
3609     /*
3610      * continue the iteration.
3611      */
3612     currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3613   }
3614
3615   if (*pceltFetched == celt)
3616     return S_OK;
3617
3618   return S_FALSE;
3619 }
3620
3621         
3622 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3623   IEnumSTATSTG* iface,
3624   ULONG             celt)
3625 {
3626   IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3627
3628   StgProperty currentProperty;
3629   ULONG       objectFetched       = 0;
3630   ULONG       currentSearchNode;
3631
3632   /*
3633    * Start with the node at the top of the stack.
3634    */
3635   currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3636
3637   while ( (objectFetched < celt) && 
3638           (currentSearchNode!=PROPERTY_NULL) )
3639   {
3640     /* 
3641      * Remove the top node from the stack
3642      */
3643     IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3644
3645     /*
3646      * Read the property from the storage.
3647      */
3648     StorageImpl_ReadProperty(This->parentStorage,
3649       currentSearchNode, 
3650       &currentProperty);
3651     
3652     /*
3653      * Step to the next item in the iteration
3654      */
3655     objectFetched++;
3656
3657     /*
3658      * Push the next search node in the search stack.
3659      */
3660     IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3661
3662     /*
3663      * continue the iteration.
3664      */
3665     currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3666   }
3667
3668   if (objectFetched == celt)
3669     return S_OK;
3670
3671   return S_FALSE;
3672 }
3673         
3674 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3675   IEnumSTATSTG* iface)
3676 {
3677   IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3678
3679   StgProperty rootProperty;
3680   BOOL      readSuccessful;
3681
3682   /*
3683    * Re-initialize the search stack to an empty stack
3684    */
3685   This->stackSize = 0;
3686
3687   /*
3688    * Read the root property from the storage.
3689    */
3690   readSuccessful = StorageImpl_ReadProperty(
3691                     This->parentStorage,
3692                     This->firstPropertyNode, 
3693                     &rootProperty);
3694
3695   if (readSuccessful)
3696   {
3697     assert(rootProperty.sizeOfNameString!=0);
3698
3699     /*
3700      * Push the search node in the search stack.
3701      */
3702     IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3703   }
3704
3705   return S_OK;
3706 }
3707         
3708 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3709   IEnumSTATSTG* iface,
3710   IEnumSTATSTG**    ppenum)
3711 {
3712   IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3713
3714   IEnumSTATSTGImpl* newClone;
3715
3716   /*
3717    * Perform a sanity check on the parameters.
3718    */
3719   if (ppenum==0)
3720     return E_INVALIDARG;
3721   
3722   newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3723                This->firstPropertyNode);
3724
3725   
3726   /*
3727    * The new clone enumeration must point to the same current node as
3728    * the ole one.
3729    */
3730   newClone->stackSize    = This->stackSize    ;
3731   newClone->stackMaxSize = This->stackMaxSize ;
3732   newClone->stackToVisit = 
3733     HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3734
3735   memcpy(
3736     newClone->stackToVisit, 
3737     This->stackToVisit, 
3738     sizeof(ULONG) * newClone->stackSize);
3739
3740   *ppenum = (IEnumSTATSTG*)newClone;
3741
3742   /*
3743    * Don't forget to nail down a reference to the clone before
3744    * returning it.
3745    */
3746   IEnumSTATSTGImpl_AddRef(*ppenum);
3747
3748   return S_OK;
3749 }
3750
3751 INT IEnumSTATSTGImpl_FindParentProperty(
3752   IEnumSTATSTGImpl *This,
3753   ULONG             childProperty, 
3754   StgProperty      *currentProperty,
3755   ULONG            *thisNodeId)
3756 {
3757   ULONG currentSearchNode;
3758   ULONG foundNode;
3759
3760   /*
3761    * To avoid the special case, get another pointer to a ULONG value if
3762    * the caller didn't supply one.
3763    */
3764   if (thisNodeId==0)
3765     thisNodeId = &foundNode;
3766
3767   /*
3768    * Start with the node at the top of the stack.
3769    */
3770   currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3771   
3772
3773   while (currentSearchNode!=PROPERTY_NULL)
3774   {
3775     /*
3776      * Store the current node in the returned parameters
3777      */
3778     *thisNodeId = currentSearchNode;
3779
3780     /* 
3781      * Remove the top node from the stack
3782      */
3783     IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3784
3785     /*
3786      * Read the property from the storage.
3787      */
3788     StorageImpl_ReadProperty(
3789       This->parentStorage,
3790       currentSearchNode, 
3791       currentProperty);
3792       
3793     if (currentProperty->previousProperty == childProperty)
3794       return PROPERTY_RELATION_PREVIOUS;
3795
3796     else if (currentProperty->nextProperty == childProperty)  
3797       return PROPERTY_RELATION_NEXT;
3798   
3799     else if (currentProperty->dirProperty == childProperty)
3800       return PROPERTY_RELATION_DIR;
3801        
3802     /*
3803      * Push the next search node in the search stack.
3804      */
3805     IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3806
3807     /*
3808      * continue the iteration.
3809      */
3810     currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3811   }
3812
3813   return PROPERTY_NULL;
3814 }
3815
3816 ULONG IEnumSTATSTGImpl_FindProperty(
3817   IEnumSTATSTGImpl* This,
3818   const OLECHAR*  lpszPropName,
3819   StgProperty*      currentProperty)
3820 {
3821   ULONG currentSearchNode;
3822
3823   /*
3824    * Start with the node at the top of the stack.
3825    */
3826   currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3827
3828   while (currentSearchNode!=PROPERTY_NULL)
3829   {
3830     /* 
3831      * Remove the top node from the stack
3832      */
3833     IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3834
3835     /*
3836      * Read the property from the storage.
3837      */
3838     StorageImpl_ReadProperty(This->parentStorage,
3839       currentSearchNode, 
3840       currentProperty);
3841
3842     if ( propertyNameCmp(
3843           (OLECHAR*)currentProperty->name, 
3844           (OLECHAR*)lpszPropName) == 0)
3845       return currentSearchNode;
3846
3847     /*
3848      * Push the next search node in the search stack.
3849      */
3850     IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3851
3852     /*
3853      * continue the iteration.
3854      */
3855     currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3856   }
3857
3858   return PROPERTY_NULL;
3859 }
3860
3861 void IEnumSTATSTGImpl_PushSearchNode(
3862   IEnumSTATSTGImpl* This,
3863   ULONG             nodeToPush)
3864 {
3865   StgProperty rootProperty;
3866   BOOL      readSuccessful;
3867
3868   /*
3869    * First, make sure we're not trying to push an unexisting node.
3870    */
3871   if (nodeToPush==PROPERTY_NULL)
3872     return;
3873
3874   /*
3875    * First push the node to the stack
3876    */
3877   if (This->stackSize == This->stackMaxSize)
3878   {
3879     This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
3880
3881     This->stackToVisit = HeapReAlloc(
3882                            GetProcessHeap(), 
3883                            0,
3884                            This->stackToVisit,
3885                            sizeof(ULONG) * This->stackMaxSize);
3886   }
3887
3888   This->stackToVisit[This->stackSize] = nodeToPush;
3889   This->stackSize++;
3890
3891   /*
3892    * Read the root property from the storage.
3893    */
3894   readSuccessful = StorageImpl_ReadProperty(
3895                     This->parentStorage,
3896                     nodeToPush, 
3897                     &rootProperty);
3898
3899   if (readSuccessful)
3900   {
3901     assert(rootProperty.sizeOfNameString!=0);
3902
3903     /*
3904      * Push the previous search node in the search stack.
3905      */
3906     IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
3907   }
3908 }
3909
3910 ULONG IEnumSTATSTGImpl_PopSearchNode(
3911   IEnumSTATSTGImpl* This,
3912   BOOL            remove)
3913 {
3914   ULONG topNode;
3915
3916   if (This->stackSize == 0)
3917     return PROPERTY_NULL;
3918
3919   topNode = This->stackToVisit[This->stackSize-1];
3920
3921   if (remove)
3922     This->stackSize--;
3923
3924   return topNode;
3925 }
3926
3927 /******************************************************************************
3928 ** StorageUtl implementation
3929 */
3930
3931 void StorageUtl_ReadWord(void* buffer, ULONG offset, WORD* value)
3932 {
3933   memcpy(value, (BYTE*)buffer+offset, sizeof(WORD));
3934 }
3935
3936 void StorageUtl_WriteWord(void* buffer, ULONG offset, WORD value)
3937 {
3938   memcpy((BYTE*)buffer+offset, &value, sizeof(WORD));
3939 }
3940
3941 void StorageUtl_ReadDWord(void* buffer, ULONG offset, DWORD* value)
3942 {
3943   memcpy(value, (BYTE*)buffer+offset, sizeof(DWORD));
3944 }
3945
3946 void StorageUtl_WriteDWord(void* buffer, ULONG offset, DWORD value)
3947 {
3948   memcpy((BYTE*)buffer+offset, &value, sizeof(DWORD));
3949 }
3950
3951 void StorageUtl_ReadGUID(void* buffer, ULONG offset, GUID* value)
3952 {
3953   StorageUtl_ReadDWord(buffer, offset,   &(value->Data1));
3954   StorageUtl_ReadWord(buffer,  offset+4, &(value->Data2));
3955   StorageUtl_ReadWord(buffer,  offset+6, &(value->Data3));
3956
3957   memcpy(value->Data4, (BYTE*)buffer+offset+8, sizeof(value->Data4));
3958 }
3959
3960 void StorageUtl_WriteGUID(void* buffer, ULONG offset, GUID* value)
3961 {
3962   StorageUtl_WriteDWord(buffer, offset,   value->Data1);
3963   StorageUtl_WriteWord(buffer,  offset+4, value->Data2);
3964   StorageUtl_WriteWord(buffer,  offset+6, value->Data3);
3965
3966   memcpy((BYTE*)buffer+offset+8, value->Data4, sizeof(value->Data4));
3967 }
3968
3969 void StorageUtl_CopyPropertyToSTATSTG(
3970   STATSTG*     destination,
3971   StgProperty* source,
3972   int          statFlags)
3973 {
3974   /*
3975    * The copy of the string occurs only when the flag is not set
3976    */
3977   if ((statFlags & STATFLAG_NONAME) != 0)
3978   {
3979     destination->pwcsName = 0;
3980   }
3981   else
3982   {
3983     destination->pwcsName = 
3984       CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
3985
3986     strcpyW((LPWSTR)destination->pwcsName, source->name);
3987   }
3988   
3989   switch (source->propertyType)
3990   {
3991     case PROPTYPE_STORAGE:
3992     case PROPTYPE_ROOT:
3993       destination->type = STGTY_STORAGE;
3994       break;
3995     case PROPTYPE_STREAM:
3996       destination->type = STGTY_STREAM;
3997       break;
3998     default:
3999       destination->type = STGTY_STREAM;
4000       break;        
4001   }
4002
4003   destination->cbSize            = source->size;
4004 /*    
4005   currentReturnStruct->mtime     = {0}; TODO
4006   currentReturnStruct->ctime     = {0};
4007   currentReturnStruct->atime     = {0}; 
4008 */
4009   destination->grfMode           = 0;
4010   destination->grfLocksSupported = 0; 
4011   destination->clsid             = source->propertyUniqueID;
4012   destination->grfStateBits      = 0; 
4013   destination->reserved          = 0; 
4014 }
4015
4016 /******************************************************************************
4017 ** BlockChainStream implementation
4018 */
4019
4020 BlockChainStream* BlockChainStream_Construct(
4021   StorageImpl* parentStorage,  
4022   ULONG*         headOfStreamPlaceHolder,
4023   ULONG          propertyIndex)
4024 {
4025   BlockChainStream* newStream;
4026   ULONG blockIndex;
4027
4028   newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4029
4030   newStream->parentStorage           = parentStorage;
4031   newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4032   newStream->ownerPropertyIndex      = propertyIndex;
4033   newStream->lastBlockNoInSequence   = 0xFFFFFFFF;
4034   newStream->tailIndex               = BLOCK_END_OF_CHAIN;
4035   newStream->numBlocks               = 0;
4036
4037   blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4038
4039   while (blockIndex != BLOCK_END_OF_CHAIN)
4040   {
4041     newStream->numBlocks++;
4042     newStream->tailIndex = blockIndex;
4043
4044     blockIndex = StorageImpl_GetNextBlockInChain(
4045                    parentStorage,
4046                    blockIndex);
4047   }
4048
4049   return newStream;
4050 }
4051
4052 void BlockChainStream_Destroy(BlockChainStream* This)
4053 {
4054   HeapFree(GetProcessHeap(), 0, This);
4055 }
4056
4057 /******************************************************************************
4058  *      BlockChainStream_GetHeadOfChain
4059  *
4060  * Returns the head of this stream chain.
4061  * Some special chains don't have properties, their heads are kept in
4062  * This->headOfStreamPlaceHolder.
4063  *
4064  */
4065 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4066 {
4067   StgProperty chainProperty;
4068   BOOL      readSuccessful;
4069
4070   if (This->headOfStreamPlaceHolder != 0)
4071     return *(This->headOfStreamPlaceHolder);
4072
4073   if (This->ownerPropertyIndex != PROPERTY_NULL)
4074   {
4075     readSuccessful = StorageImpl_ReadProperty(
4076                       This->parentStorage,
4077                       This->ownerPropertyIndex,
4078                       &chainProperty);
4079
4080     if (readSuccessful)
4081     {
4082       return chainProperty.startingBlock;
4083     }
4084   }
4085
4086   return BLOCK_END_OF_CHAIN;
4087 }
4088
4089 /******************************************************************************
4090  *       BlockChainStream_GetCount
4091  *
4092  * Returns the number of blocks that comprises this chain.
4093  * This is not the size of the stream as the last block may not be full!
4094  * 
4095  */
4096 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4097 {
4098   ULONG blockIndex;
4099   ULONG count = 0;
4100
4101   blockIndex = BlockChainStream_GetHeadOfChain(This);
4102
4103   while (blockIndex != BLOCK_END_OF_CHAIN)
4104   {
4105     count++;
4106
4107     blockIndex = StorageImpl_GetNextBlockInChain(
4108                    This->parentStorage, 
4109                    blockIndex);
4110   }
4111
4112   return count;
4113 }
4114
4115 /******************************************************************************
4116  *      BlockChainStream_ReadAt 
4117  *
4118  * Reads a specified number of bytes from this chain at the specified offset.
4119  * bytesRead may be NULL.
4120  * Failure will be returned if the specified number of bytes has not been read.
4121  */
4122 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4123   ULARGE_INTEGER offset,
4124   ULONG          size,
4125   void*          buffer,
4126   ULONG*         bytesRead)
4127 {
4128   ULONG blockNoInSequence = offset.s.LowPart / This->parentStorage->bigBlockSize;
4129   ULONG offsetInBlock     = offset.s.LowPart % This->parentStorage->bigBlockSize;
4130   ULONG bytesToReadInBuffer;
4131   ULONG blockIndex;
4132   BYTE* bufferWalker;
4133   BYTE* bigBlockBuffer;
4134
4135   /*
4136    * Find the first block in the stream that contains part of the buffer.
4137    */
4138   if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4139        (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4140        (blockNoInSequence < This->lastBlockNoInSequence) )
4141   {
4142     blockIndex = BlockChainStream_GetHeadOfChain(This);
4143     This->lastBlockNoInSequence = blockNoInSequence;
4144   }
4145   else
4146   {
4147     ULONG temp = blockNoInSequence;
4148
4149     blockIndex = This->lastBlockNoInSequenceIndex;
4150     blockNoInSequence -= This->lastBlockNoInSequence;
4151     This->lastBlockNoInSequence = temp;
4152   }
4153
4154   while ( (blockNoInSequence > 0) &&  (blockIndex != BLOCK_END_OF_CHAIN))
4155   {
4156     blockIndex = 
4157       StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4158     
4159     blockNoInSequence--;
4160   }
4161
4162   This->lastBlockNoInSequenceIndex = blockIndex;
4163
4164   /*
4165    * Start reading the buffer.
4166    */
4167   *bytesRead   = 0;
4168   bufferWalker = buffer;
4169   
4170   while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4171   {
4172     /*
4173      * Calculate how many bytes we can copy from this big block.
4174      */
4175     bytesToReadInBuffer = 
4176       min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4177     
4178     /*
4179      * Copy those bytes to the buffer
4180      */
4181     bigBlockBuffer = 
4182       StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4183     
4184     memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4185     
4186     StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4187     
4188     /*
4189      * Step to the next big block.
4190      */
4191     blockIndex    = 
4192       StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4193
4194     bufferWalker += bytesToReadInBuffer;
4195     size         -= bytesToReadInBuffer;
4196     *bytesRead   += bytesToReadInBuffer;
4197     offsetInBlock = 0;  /* There is no offset on the next block */
4198
4199   }
4200   
4201   return (size == 0);
4202 }
4203
4204 /******************************************************************************
4205  *      BlockChainStream_WriteAt
4206  *
4207  * Writes the specified number of bytes to this chain at the specified offset.
4208  * bytesWritten may be NULL.
4209  * Will fail if not all specified number of bytes have been written.
4210  */
4211 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4212   ULARGE_INTEGER    offset,
4213   ULONG             size,
4214   const void*       buffer,
4215   ULONG*            bytesWritten)
4216 {
4217   ULONG blockNoInSequence = offset.s.LowPart / This->parentStorage->bigBlockSize;
4218   ULONG offsetInBlock     = offset.s.LowPart % This->parentStorage->bigBlockSize;
4219   ULONG bytesToWrite;
4220   ULONG blockIndex;
4221   BYTE* bufferWalker;
4222   BYTE* bigBlockBuffer;
4223
4224   /*
4225    * Find the first block in the stream that contains part of the buffer.
4226    */
4227   if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4228        (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4229        (blockNoInSequence < This->lastBlockNoInSequence) )
4230   {
4231     blockIndex = BlockChainStream_GetHeadOfChain(This);
4232     This->lastBlockNoInSequence = blockNoInSequence;
4233   }
4234   else
4235   {
4236     ULONG temp = blockNoInSequence;
4237
4238     blockIndex = This->lastBlockNoInSequenceIndex;
4239     blockNoInSequence -= This->lastBlockNoInSequence;
4240     This->lastBlockNoInSequence = temp;
4241   }
4242
4243   while ( (blockNoInSequence > 0) &&  (blockIndex != BLOCK_END_OF_CHAIN))
4244   {
4245     blockIndex = 
4246       StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4247     
4248     blockNoInSequence--;
4249   }
4250
4251   This->lastBlockNoInSequenceIndex = blockIndex;
4252
4253   /*
4254    * Here, I'm casting away the constness on the buffer variable
4255    * This is OK since we don't intend to modify that buffer.
4256    */
4257   *bytesWritten   = 0;
4258   bufferWalker = (BYTE*)buffer;
4259
4260   while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4261   {
4262     /*
4263      * Calculate how many bytes we can copy from this big block.
4264      */
4265     bytesToWrite = 
4266       min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4267     
4268     /*
4269      * Copy those bytes to the buffer
4270      */
4271     bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4272     
4273     memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4274     
4275     StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4276     
4277     /*
4278      * Step to the next big block.
4279      */
4280     blockIndex    = 
4281       StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4282
4283     bufferWalker  += bytesToWrite;
4284     size          -= bytesToWrite;
4285     *bytesWritten += bytesToWrite;
4286     offsetInBlock  = 0;      /* There is no offset on the next block */
4287   }
4288   
4289   return (size == 0);
4290 }
4291
4292 /******************************************************************************
4293  *      BlockChainStream_Shrink
4294  *
4295  * Shrinks this chain in the big block depot.
4296  */
4297 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4298                                ULARGE_INTEGER    newSize)
4299 {
4300   ULONG blockIndex, extraBlock;
4301   ULONG numBlocks;
4302   ULONG count = 1;
4303
4304   /*
4305    * Reset the last accessed block cache.
4306    */
4307   This->lastBlockNoInSequence = 0xFFFFFFFF;
4308   This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4309
4310   /*
4311    * Figure out how many blocks are needed to contain the new size
4312    */
4313   numBlocks = newSize.s.LowPart / This->parentStorage->bigBlockSize;
4314
4315   if ((newSize.s.LowPart % This->parentStorage->bigBlockSize) != 0)
4316     numBlocks++;
4317
4318   blockIndex = BlockChainStream_GetHeadOfChain(This);
4319
4320   /*
4321    * Go to the new end of chain
4322    */
4323   while (count < numBlocks)
4324   {
4325     blockIndex = 
4326       StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4327
4328     count++;
4329   }
4330
4331   /* Get the next block before marking the new end */
4332   extraBlock = 
4333     StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex);
4334
4335   /* Mark the new end of chain */
4336   StorageImpl_SetNextBlockInChain(
4337     This->parentStorage, 
4338     blockIndex, 
4339     BLOCK_END_OF_CHAIN);
4340
4341   This->tailIndex = blockIndex;
4342   This->numBlocks = numBlocks;
4343
4344   /*
4345    * Mark the extra blocks as free
4346    */
4347   while (extraBlock != BLOCK_END_OF_CHAIN)
4348   {
4349     blockIndex = 
4350       StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock);
4351
4352     StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4353     extraBlock = blockIndex;
4354   }
4355
4356   return TRUE;
4357 }
4358
4359 /******************************************************************************
4360  *      BlockChainStream_Enlarge
4361  *
4362  * Grows this chain in the big block depot.
4363  */
4364 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4365                                 ULARGE_INTEGER    newSize)
4366 {
4367   ULONG blockIndex, currentBlock;
4368   ULONG newNumBlocks;
4369   ULONG oldNumBlocks = 0;
4370
4371   blockIndex = BlockChainStream_GetHeadOfChain(This);
4372
4373   /*
4374    * Empty chain. Create the head.
4375    */
4376   if (blockIndex == BLOCK_END_OF_CHAIN)
4377   {
4378     blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4379     StorageImpl_SetNextBlockInChain(This->parentStorage,
4380                                       blockIndex,
4381                                       BLOCK_END_OF_CHAIN);
4382
4383     if (This->headOfStreamPlaceHolder != 0)
4384     {
4385       *(This->headOfStreamPlaceHolder) = blockIndex;
4386     }
4387     else
4388     {
4389       StgProperty chainProp;
4390       assert(This->ownerPropertyIndex != PROPERTY_NULL);
4391
4392       StorageImpl_ReadProperty(
4393         This->parentStorage, 
4394         This->ownerPropertyIndex,
4395         &chainProp);
4396
4397       chainProp.startingBlock = blockIndex; 
4398
4399       StorageImpl_WriteProperty(
4400         This->parentStorage, 
4401         This->ownerPropertyIndex,
4402         &chainProp);
4403     }
4404
4405     This->tailIndex = blockIndex;
4406     This->numBlocks = 1;
4407   }
4408
4409   /*
4410    * Figure out how many blocks are needed to contain this stream
4411    */
4412   newNumBlocks = newSize.s.LowPart / This->parentStorage->bigBlockSize;
4413
4414   if ((newSize.s.LowPart % This->parentStorage->bigBlockSize) != 0)
4415     newNumBlocks++;
4416
4417   /*
4418    * Go to the current end of chain
4419    */
4420   if (This->tailIndex == BLOCK_END_OF_CHAIN)
4421   {
4422     currentBlock = blockIndex;
4423
4424     while (blockIndex != BLOCK_END_OF_CHAIN)
4425     {
4426       This->numBlocks++;
4427       currentBlock = blockIndex;
4428
4429       blockIndex =
4430         StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock);
4431     }
4432
4433     This->tailIndex = currentBlock;
4434   }
4435
4436   currentBlock = This->tailIndex;
4437   oldNumBlocks = This->numBlocks;
4438
4439   /*
4440    * Add new blocks to the chain
4441    */
4442   if (oldNumBlocks < newNumBlocks)
4443   {
4444     while (oldNumBlocks < newNumBlocks)
4445     {
4446       blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4447
4448       StorageImpl_SetNextBlockInChain(
4449         This->parentStorage, 
4450         currentBlock, 
4451         blockIndex);
4452
4453       StorageImpl_SetNextBlockInChain(
4454         This->parentStorage, 
4455         blockIndex, 
4456         BLOCK_END_OF_CHAIN);
4457
4458       currentBlock = blockIndex;
4459       oldNumBlocks++;
4460     }
4461
4462     This->tailIndex = blockIndex;
4463     This->numBlocks = newNumBlocks;
4464   }
4465
4466   return TRUE;
4467 }
4468
4469 /******************************************************************************
4470  *      BlockChainStream_SetSize
4471  *
4472  * Sets the size of this stream. The big block depot will be updated.
4473  * The file will grow if we grow the chain.
4474  *
4475  * TODO: Free the actual blocks in the file when we shrink the chain.
4476  *       Currently, the blocks are still in the file. So the file size
4477  *       doesn't shrink even if we shrink streams. 
4478  */
4479 BOOL BlockChainStream_SetSize(
4480   BlockChainStream* This,
4481   ULARGE_INTEGER    newSize)
4482 {
4483   ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4484
4485   if (newSize.s.LowPart == size.s.LowPart)
4486     return TRUE;
4487
4488   if (newSize.s.LowPart < size.s.LowPart)
4489   {
4490     BlockChainStream_Shrink(This, newSize);
4491   }
4492   else
4493   {
4494     ULARGE_INTEGER fileSize = 
4495       BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4496
4497     ULONG diff = newSize.s.LowPart - size.s.LowPart;
4498
4499     /*
4500      * Make sure the file stays a multiple of blocksize
4501      */
4502     if ((diff % This->parentStorage->bigBlockSize) != 0)
4503       diff += (This->parentStorage->bigBlockSize - 
4504                 (diff % This->parentStorage->bigBlockSize) );
4505
4506     fileSize.s.LowPart += diff;
4507     BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4508
4509     BlockChainStream_Enlarge(This, newSize);
4510   }
4511
4512   return TRUE;
4513 }
4514
4515 /******************************************************************************
4516  *      BlockChainStream_GetSize
4517  *
4518  * Returns the size of this chain.
4519  * Will return the block count if this chain doesn't have a property.
4520  */
4521 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4522 {
4523   StgProperty chainProperty;
4524
4525   if(This->headOfStreamPlaceHolder == NULL)
4526   {
4527     /* 
4528      * This chain is a data stream read the property and return 
4529      * the appropriate size
4530      */
4531     StorageImpl_ReadProperty(
4532       This->parentStorage,
4533       This->ownerPropertyIndex,
4534       &chainProperty);
4535
4536     return chainProperty.size;
4537   }
4538   else
4539   {
4540     /*
4541      * this chain is a chain that does not have a property, figure out the 
4542      * size by making the product number of used blocks times the 
4543      * size of them
4544      */
4545     ULARGE_INTEGER result;
4546     result.s.HighPart = 0;
4547
4548     result.s.LowPart  = 
4549       BlockChainStream_GetCount(This) * 
4550       This->parentStorage->bigBlockSize;
4551
4552     return result;
4553   }
4554 }
4555
4556 /******************************************************************************
4557 ** SmallBlockChainStream implementation
4558 */
4559
4560 SmallBlockChainStream* SmallBlockChainStream_Construct(
4561   StorageImpl* parentStorage,  
4562   ULONG          propertyIndex)
4563 {
4564   SmallBlockChainStream* newStream;
4565
4566   newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4567
4568   newStream->parentStorage      = parentStorage;
4569   newStream->ownerPropertyIndex = propertyIndex;
4570
4571   return newStream;
4572 }
4573
4574 void SmallBlockChainStream_Destroy(
4575   SmallBlockChainStream* This)
4576 {
4577   HeapFree(GetProcessHeap(), 0, This);
4578 }
4579
4580 /******************************************************************************
4581  *      SmallBlockChainStream_GetHeadOfChain
4582  *
4583  * Returns the head of this chain of small blocks.
4584  */
4585 ULONG SmallBlockChainStream_GetHeadOfChain(
4586   SmallBlockChainStream* This)
4587 {
4588   StgProperty chainProperty;
4589   BOOL      readSuccessful;
4590
4591   if (This->ownerPropertyIndex)
4592   {
4593     readSuccessful = StorageImpl_ReadProperty(
4594                       This->parentStorage,
4595                       This->ownerPropertyIndex,
4596                       &chainProperty);
4597
4598     if (readSuccessful)
4599     {
4600       return chainProperty.startingBlock;
4601     }
4602
4603   }
4604
4605   return BLOCK_END_OF_CHAIN;
4606 }
4607
4608 /******************************************************************************
4609  *      SmallBlockChainStream_GetNextBlockInChain
4610  *
4611  * Returns the index of the next small block in this chain.
4612  * 
4613  * Return Values:
4614  *    - BLOCK_END_OF_CHAIN: end of this chain
4615  *    - BLOCK_UNUSED: small block 'blockIndex' is free
4616  */
4617 ULONG SmallBlockChainStream_GetNextBlockInChain(
4618   SmallBlockChainStream* This,
4619   ULONG                  blockIndex)
4620 {
4621   ULARGE_INTEGER offsetOfBlockInDepot;
4622   DWORD  buffer;
4623   ULONG  nextBlockInChain = BLOCK_END_OF_CHAIN;
4624   ULONG  bytesRead;
4625   BOOL success;
4626
4627   offsetOfBlockInDepot.s.HighPart = 0;
4628   offsetOfBlockInDepot.s.LowPart  = blockIndex * sizeof(ULONG);
4629
4630   /*
4631    * Read those bytes in the buffer from the small block file.
4632    */
4633   success = BlockChainStream_ReadAt(
4634               This->parentStorage->smallBlockDepotChain,
4635               offsetOfBlockInDepot,
4636               sizeof(DWORD),
4637               &buffer,
4638               &bytesRead);
4639
4640   if (success)
4641   {
4642     StorageUtl_ReadDWord(&buffer, 0, &nextBlockInChain);
4643   }
4644
4645   return nextBlockInChain;
4646 }
4647
4648 /******************************************************************************
4649  *       SmallBlockChainStream_SetNextBlockInChain
4650  *
4651  * Writes the index of the next block of the specified block in the small
4652  * block depot.
4653  * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4654  * To flag a block as free use BLOCK_UNUSED as nextBlock.
4655  */
4656 void SmallBlockChainStream_SetNextBlockInChain(
4657   SmallBlockChainStream* This,
4658   ULONG                  blockIndex,
4659   ULONG                  nextBlock)
4660 {
4661   ULARGE_INTEGER offsetOfBlockInDepot;
4662   DWORD  buffer;
4663   ULONG  bytesWritten;
4664
4665   offsetOfBlockInDepot.s.HighPart = 0;
4666   offsetOfBlockInDepot.s.LowPart  = blockIndex * sizeof(ULONG);
4667
4668   StorageUtl_WriteDWord(&buffer, 0, nextBlock);
4669
4670   /*
4671    * Read those bytes in the buffer from the small block file.
4672    */
4673   BlockChainStream_WriteAt(
4674     This->parentStorage->smallBlockDepotChain,
4675     offsetOfBlockInDepot,
4676     sizeof(DWORD),
4677     &buffer,
4678     &bytesWritten);
4679 }
4680
4681 /******************************************************************************
4682  *      SmallBlockChainStream_FreeBlock
4683  *
4684  * Flag small block 'blockIndex' as free in the small block depot.
4685  */
4686 void SmallBlockChainStream_FreeBlock(
4687   SmallBlockChainStream* This,
4688   ULONG                  blockIndex)
4689 {
4690   SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4691 }
4692
4693 /******************************************************************************
4694  *      SmallBlockChainStream_GetNextFreeBlock
4695  *
4696  * Returns the index of a free small block. The small block depot will be
4697  * enlarged if necessary. The small block chain will also be enlarged if
4698  * necessary.
4699  */
4700 ULONG SmallBlockChainStream_GetNextFreeBlock(
4701   SmallBlockChainStream* This)
4702 {
4703   ULARGE_INTEGER offsetOfBlockInDepot;
4704   DWORD buffer;
4705   ULONG bytesRead;
4706   ULONG blockIndex = 0;
4707   ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4708   BOOL success = TRUE;
4709   ULONG smallBlocksPerBigBlock;
4710
4711   offsetOfBlockInDepot.s.HighPart = 0;
4712
4713   /*
4714    * Scan the small block depot for a free block
4715    */
4716   while (nextBlockIndex != BLOCK_UNUSED)
4717   {
4718     offsetOfBlockInDepot.s.LowPart = blockIndex * sizeof(ULONG);
4719
4720     success = BlockChainStream_ReadAt(
4721                 This->parentStorage->smallBlockDepotChain,
4722                 offsetOfBlockInDepot,
4723                 sizeof(DWORD),
4724                 &buffer,
4725                 &bytesRead);
4726
4727     /*
4728      * If we run out of space for the small block depot, enlarge it
4729      */
4730     if (success)
4731     {
4732       StorageUtl_ReadDWord(&buffer, 0, &nextBlockIndex);
4733
4734       if (nextBlockIndex != BLOCK_UNUSED)
4735         blockIndex++;
4736     }
4737     else
4738     {
4739       ULONG count = 
4740         BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4741
4742       ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4743       ULONG nextBlock, newsbdIndex;
4744       BYTE* smallBlockDepot;
4745
4746       nextBlock = sbdIndex;
4747       while (nextBlock != BLOCK_END_OF_CHAIN)
4748       {
4749         sbdIndex = nextBlock;
4750         nextBlock = 
4751           StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex);
4752       }
4753
4754       newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4755       if (sbdIndex != BLOCK_END_OF_CHAIN)
4756         StorageImpl_SetNextBlockInChain(
4757           This->parentStorage, 
4758           sbdIndex, 
4759           newsbdIndex);
4760
4761       StorageImpl_SetNextBlockInChain(
4762         This->parentStorage, 
4763         newsbdIndex, 
4764         BLOCK_END_OF_CHAIN);
4765
4766       /*
4767        * Initialize all the small blocks to free
4768        */
4769       smallBlockDepot = 
4770         StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
4771
4772       memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
4773       StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
4774
4775       if (count == 0)
4776       {
4777         /*
4778          * We have just created the small block depot.
4779          */
4780         StgProperty rootProp;
4781         ULONG sbStartIndex; 
4782
4783         /*
4784          * Save it in the header
4785          */
4786         This->parentStorage->smallBlockDepotStart = newsbdIndex;
4787         StorageImpl_SaveFileHeader(This->parentStorage);
4788
4789         /*
4790          * And allocate the first big block that will contain small blocks 
4791          */
4792         sbStartIndex = 
4793           StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4794
4795         StorageImpl_SetNextBlockInChain(
4796           This->parentStorage, 
4797           sbStartIndex, 
4798           BLOCK_END_OF_CHAIN);
4799
4800         StorageImpl_ReadProperty(
4801           This->parentStorage, 
4802           This->parentStorage->rootPropertySetIndex, 
4803           &rootProp);
4804
4805         rootProp.startingBlock = sbStartIndex;
4806         rootProp.size.s.HighPart = 0;
4807         rootProp.size.s.LowPart  = This->parentStorage->bigBlockSize;
4808
4809         StorageImpl_WriteProperty(
4810           This->parentStorage, 
4811           This->parentStorage->rootPropertySetIndex, 
4812           &rootProp);
4813       }
4814     }
4815   }
4816
4817   smallBlocksPerBigBlock = 
4818     This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
4819
4820   /*
4821    * Verify if we have to allocate big blocks to contain small blocks
4822    */
4823   if (blockIndex % smallBlocksPerBigBlock == 0)
4824   {
4825     StgProperty rootProp;
4826     ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
4827
4828     StorageImpl_ReadProperty(
4829       This->parentStorage, 
4830       This->parentStorage->rootPropertySetIndex, 
4831       &rootProp);
4832
4833     if (rootProp.size.s.LowPart < 
4834        (blocksRequired * This->parentStorage->bigBlockSize))
4835     {
4836       rootProp.size.s.LowPart += This->parentStorage->bigBlockSize;
4837
4838       BlockChainStream_SetSize(
4839         This->parentStorage->smallBlockRootChain, 
4840         rootProp.size);
4841
4842       StorageImpl_WriteProperty(
4843         This->parentStorage, 
4844         This->parentStorage->rootPropertySetIndex, 
4845         &rootProp);
4846     }
4847   }
4848
4849   return blockIndex;
4850 }
4851
4852 /******************************************************************************
4853  *      SmallBlockChainStream_ReadAt
4854  *
4855  * Reads a specified number of bytes from this chain at the specified offset.
4856  * bytesRead may be NULL.
4857  * Failure will be returned if the specified number of bytes has not been read. 
4858  */
4859 BOOL SmallBlockChainStream_ReadAt(
4860   SmallBlockChainStream* This,
4861   ULARGE_INTEGER         offset,
4862   ULONG                  size,
4863   void*                  buffer,
4864   ULONG*                 bytesRead)
4865 {
4866   ULARGE_INTEGER offsetInBigBlockFile;
4867   ULONG blockNoInSequence = 
4868     offset.s.LowPart / This->parentStorage->smallBlockSize;
4869
4870   ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->smallBlockSize;
4871   ULONG bytesToReadInBuffer;
4872   ULONG blockIndex;
4873   ULONG bytesReadFromBigBlockFile;
4874   BYTE* bufferWalker;
4875
4876   /*
4877    * This should never happen on a small block file.
4878    */
4879   assert(offset.s.HighPart==0);
4880
4881   /*
4882    * Find the first block in the stream that contains part of the buffer.
4883    */
4884   blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
4885
4886   while ( (blockNoInSequence > 0) &&  (blockIndex != BLOCK_END_OF_CHAIN))
4887   {
4888     blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4889
4890     blockNoInSequence--;
4891   }
4892
4893   /*
4894    * Start reading the buffer.
4895    */
4896   *bytesRead   = 0;
4897   bufferWalker = buffer;
4898
4899   while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4900   {
4901     /*
4902      * Calculate how many bytes we can copy from this small block.
4903      */
4904     bytesToReadInBuffer = 
4905       min(This->parentStorage->smallBlockSize - offsetInBlock, size);
4906
4907     /*
4908      * Calculate the offset of the small block in the small block file.
4909      */
4910     offsetInBigBlockFile.s.HighPart  = 0;
4911     offsetInBigBlockFile.s.LowPart   = 
4912       blockIndex * This->parentStorage->smallBlockSize;
4913
4914     offsetInBigBlockFile.s.LowPart  += offsetInBlock;
4915
4916     /*
4917      * Read those bytes in the buffer from the small block file.
4918      */
4919     BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
4920       offsetInBigBlockFile,
4921       bytesToReadInBuffer,
4922       bufferWalker,
4923       &bytesReadFromBigBlockFile);
4924
4925     assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
4926
4927     /*
4928      * Step to the next big block.
4929      */
4930     blockIndex    = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4931     bufferWalker += bytesToReadInBuffer;
4932     size         -= bytesToReadInBuffer;
4933     *bytesRead   += bytesToReadInBuffer;
4934     offsetInBlock = 0;  /* There is no offset on the next block */
4935   }
4936
4937   return (size == 0);
4938 }
4939
4940 /******************************************************************************
4941  *       SmallBlockChainStream_WriteAt
4942  *
4943  * Writes the specified number of bytes to this chain at the specified offset.
4944  * bytesWritten may be NULL.
4945  * Will fail if not all specified number of bytes have been written.
4946  */
4947 BOOL SmallBlockChainStream_WriteAt(
4948   SmallBlockChainStream* This,
4949   ULARGE_INTEGER offset,
4950   ULONG          size,
4951   const void*    buffer,
4952   ULONG*         bytesWritten)
4953 {
4954   ULARGE_INTEGER offsetInBigBlockFile;
4955   ULONG blockNoInSequence = 
4956     offset.s.LowPart / This->parentStorage->smallBlockSize;
4957
4958   ULONG offsetInBlock = offset.s.LowPart % This->parentStorage->smallBlockSize;
4959   ULONG bytesToWriteInBuffer;
4960   ULONG blockIndex;
4961   ULONG bytesWrittenFromBigBlockFile;
4962   BYTE* bufferWalker;
4963   
4964   /*
4965    * This should never happen on a small block file.
4966    */
4967   assert(offset.s.HighPart==0);
4968   
4969   /*
4970    * Find the first block in the stream that contains part of the buffer.
4971    */
4972   blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
4973   
4974   while ( (blockNoInSequence > 0) &&  (blockIndex != BLOCK_END_OF_CHAIN))
4975   {
4976     blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
4977     
4978     blockNoInSequence--;
4979   }
4980   
4981   /*
4982    * Start writing the buffer.
4983    *
4984    * Here, I'm casting away the constness on the buffer variable
4985    * This is OK since we don't intend to modify that buffer.
4986    */
4987   *bytesWritten   = 0;
4988   bufferWalker = (BYTE*)buffer;
4989   while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4990   {
4991     /*
4992      * Calculate how many bytes we can copy to this small block.
4993      */
4994     bytesToWriteInBuffer = 
4995       min(This->parentStorage->smallBlockSize - offsetInBlock, size);
4996     
4997     /*
4998      * Calculate the offset of the small block in the small block file.
4999      */
5000     offsetInBigBlockFile.s.HighPart  = 0;
5001     offsetInBigBlockFile.s.LowPart   = 
5002       blockIndex * This->parentStorage->smallBlockSize;
5003
5004     offsetInBigBlockFile.s.LowPart  += offsetInBlock;
5005     
5006     /*
5007      * Write those bytes in the buffer to the small block file.
5008      */
5009     BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
5010       offsetInBigBlockFile,
5011       bytesToWriteInBuffer,
5012       bufferWalker,
5013       &bytesWrittenFromBigBlockFile);
5014     
5015     assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
5016     
5017     /*
5018      * Step to the next big block.
5019      */
5020     blockIndex    = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5021     bufferWalker  += bytesToWriteInBuffer;
5022     size          -= bytesToWriteInBuffer;
5023     *bytesWritten += bytesToWriteInBuffer;
5024     offsetInBlock  = 0;     /* There is no offset on the next block */
5025   }
5026   
5027   return (size == 0);
5028 }
5029
5030 /******************************************************************************
5031  *       SmallBlockChainStream_Shrink
5032  *
5033  * Shrinks this chain in the small block depot. 
5034  */
5035 BOOL SmallBlockChainStream_Shrink(
5036   SmallBlockChainStream* This,
5037   ULARGE_INTEGER newSize)
5038 {
5039   ULONG blockIndex, extraBlock;
5040   ULONG numBlocks;
5041   ULONG count = 0;
5042
5043   numBlocks = newSize.s.LowPart / This->parentStorage->smallBlockSize;
5044
5045   if ((newSize.s.LowPart % This->parentStorage->smallBlockSize) != 0)
5046     numBlocks++;
5047
5048   blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5049
5050   /*
5051    * Go to the new end of chain
5052    */
5053   while (count < numBlocks)
5054   {
5055     blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5056     count++;
5057   }
5058
5059   /*
5060    * If the count is 0, we have a special case, the head of the chain was
5061    * just freed. 
5062    */
5063   if (count == 0)
5064   {
5065     StgProperty chainProp;
5066
5067     StorageImpl_ReadProperty(This->parentStorage, 
5068                              This->ownerPropertyIndex,
5069                              &chainProp);
5070
5071     chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5072
5073     StorageImpl_WriteProperty(This->parentStorage,
5074                               This->ownerPropertyIndex,
5075                               &chainProp);
5076
5077     /*
5078      * We start freeing the chain at the head block.
5079      */
5080     extraBlock = blockIndex;
5081   }
5082   else
5083   {
5084     /* Get the next block before marking the new end */
5085     extraBlock = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5086
5087     /* Mark the new end of chain */
5088     SmallBlockChainStream_SetNextBlockInChain(
5089       This, 
5090       blockIndex, 
5091       BLOCK_END_OF_CHAIN);
5092   }
5093
5094   /*
5095    * Mark the extra blocks as free
5096    */
5097   while (extraBlock != BLOCK_END_OF_CHAIN)
5098   {
5099     blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, extraBlock);
5100     SmallBlockChainStream_FreeBlock(This, extraBlock);
5101     extraBlock = blockIndex;
5102   }
5103
5104   return TRUE;  
5105 }
5106
5107 /******************************************************************************
5108  *      SmallBlockChainStream_Enlarge
5109  *
5110  * Grows this chain in the small block depot.
5111  */
5112 BOOL SmallBlockChainStream_Enlarge(
5113   SmallBlockChainStream* This,
5114   ULARGE_INTEGER newSize)
5115 {
5116   ULONG blockIndex, currentBlock;
5117   ULONG newNumBlocks;
5118   ULONG oldNumBlocks = 0;
5119
5120   blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5121
5122   /*
5123    * Empty chain
5124    */
5125   if (blockIndex == BLOCK_END_OF_CHAIN)
5126   {
5127
5128     StgProperty chainProp;
5129
5130     StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5131                                &chainProp);
5132
5133     chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5134
5135     StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5136                                 &chainProp);
5137
5138     blockIndex = chainProp.startingBlock;
5139     SmallBlockChainStream_SetNextBlockInChain(
5140       This, 
5141       blockIndex, 
5142       BLOCK_END_OF_CHAIN);
5143   }
5144
5145   currentBlock = blockIndex;
5146
5147   /*
5148    * Figure out how many blocks are needed to contain this stream
5149    */
5150   newNumBlocks = newSize.s.LowPart / This->parentStorage->smallBlockSize;
5151
5152   if ((newSize.s.LowPart % This->parentStorage->smallBlockSize) != 0)
5153     newNumBlocks++;
5154
5155   /*
5156    * Go to the current end of chain
5157    */
5158   while (blockIndex != BLOCK_END_OF_CHAIN)
5159   {
5160     oldNumBlocks++;
5161     currentBlock = blockIndex;
5162     blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, currentBlock);
5163   }
5164
5165   /*
5166    * Add new blocks to the chain
5167    */
5168   while (oldNumBlocks < newNumBlocks)
5169   {
5170     blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5171     SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5172
5173     SmallBlockChainStream_SetNextBlockInChain(
5174       This, 
5175       blockIndex, 
5176       BLOCK_END_OF_CHAIN);
5177
5178     currentBlock = blockIndex;
5179     oldNumBlocks++;
5180   }
5181
5182   return TRUE;
5183 }
5184
5185 /******************************************************************************
5186  *      SmallBlockChainStream_GetCount
5187  *
5188  * Returns the number of blocks that comprises this chain.
5189  * This is not the size of this chain as the last block may not be full!
5190  */
5191 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5192 {
5193   ULONG blockIndex;
5194   ULONG count = 0;
5195
5196   blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5197
5198   while (blockIndex != BLOCK_END_OF_CHAIN)
5199   {
5200     count++;
5201
5202     blockIndex = SmallBlockChainStream_GetNextBlockInChain(This, blockIndex);
5203   }
5204
5205   return count;
5206 }
5207
5208 /******************************************************************************
5209  *      SmallBlockChainStream_SetSize
5210  *
5211  * Sets the size of this stream.
5212  * The file will grow if we grow the chain.
5213  *
5214  * TODO: Free the actual blocks in the file when we shrink the chain.
5215  *       Currently, the blocks are still in the file. So the file size
5216  *       doesn't shrink even if we shrink streams. 
5217  */
5218 BOOL SmallBlockChainStream_SetSize(
5219                 SmallBlockChainStream* This,
5220                 ULARGE_INTEGER    newSize)
5221 {
5222   ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5223
5224   if (newSize.s.LowPart == size.s.LowPart)
5225     return TRUE;
5226
5227   if (newSize.s.LowPart < size.s.LowPart)
5228   {
5229     SmallBlockChainStream_Shrink(This, newSize);
5230   }
5231   else
5232   {
5233     SmallBlockChainStream_Enlarge(This, newSize);
5234   }
5235
5236   return TRUE;
5237 }
5238
5239 /******************************************************************************
5240  *      SmallBlockChainStream_GetSize
5241  *
5242  * Returns the size of this chain.
5243  */
5244 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5245 {
5246   StgProperty chainProperty;
5247
5248   StorageImpl_ReadProperty(
5249     This->parentStorage,
5250     This->ownerPropertyIndex,
5251     &chainProperty);
5252
5253   return chainProperty.size;
5254 }
5255
5256 /******************************************************************************
5257  *    StgCreateDocfile  [OLE32.144]
5258  */
5259 HRESULT WINAPI StgCreateDocfile(
5260   LPCOLESTR pwcsName,
5261   DWORD       grfMode,
5262   DWORD       reserved,
5263   IStorage  **ppstgOpen)
5264 {
5265   StorageImpl* newStorage = 0;
5266   HANDLE       hFile      = INVALID_HANDLE_VALUE;
5267   HRESULT        hr         = S_OK;
5268   DWORD          shareMode;
5269   DWORD          accessMode;
5270   DWORD          creationMode;
5271   DWORD          fileAttributes;
5272   WCHAR          tempFileName[MAX_PATH];
5273
5274   TRACE("(%s, %lx, %ld, %p)\n", 
5275         debugstr_w(pwcsName), grfMode, 
5276         reserved, ppstgOpen);
5277
5278   /*
5279    * Validate the parameters
5280    */
5281   if (ppstgOpen == 0)
5282     return STG_E_INVALIDPOINTER;
5283
5284   /*
5285    * Validate the STGM flags
5286    */
5287   if ( FAILED( validateSTGM(grfMode) ))
5288     return STG_E_INVALIDFLAG;
5289
5290   /*
5291    * Generate a unique name.
5292    */
5293   if (pwcsName == 0)
5294   {
5295     WCHAR tempPath[MAX_PATH];
5296     WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5297
5298     memset(tempPath, 0, sizeof(tempPath));
5299     memset(tempFileName, 0, sizeof(tempFileName));
5300
5301     if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5302       tempPath[0] = '.';
5303
5304     if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5305       pwcsName = tempFileName;
5306     else
5307       return STG_E_INSUFFICIENTMEMORY;
5308   }
5309
5310   /*
5311    * Interpret the STGM value grfMode 
5312    */
5313   shareMode    = GetShareModeFromSTGM(grfMode);
5314   accessMode   = GetAccessModeFromSTGM(grfMode);
5315   creationMode = GetCreationModeFromSTGM(grfMode);
5316
5317   if (grfMode & STGM_DELETEONRELEASE)
5318     fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5319   else
5320     fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5321
5322   if (grfMode & STGM_TRANSACTED)
5323     FIXME("Transacted mode not implemented.\n");
5324
5325   /*
5326    * Initialize the "out" parameter.
5327    */
5328   *ppstgOpen = 0;
5329
5330   hFile = CreateFileW(pwcsName,
5331                         accessMode,
5332                         shareMode,
5333             NULL,
5334                         creationMode,
5335                         fileAttributes,
5336             0);
5337  
5338   if (hFile == INVALID_HANDLE_VALUE)
5339   {
5340     return E_FAIL;
5341   }
5342
5343   /*
5344    * Allocate and initialize the new IStorage32object.
5345    */
5346   newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5347  
5348   if (newStorage == 0)
5349     return STG_E_INSUFFICIENTMEMORY;
5350
5351   hr = StorageImpl_Construct(
5352          newStorage,
5353          hFile,
5354          NULL,
5355          grfMode,
5356          TRUE,
5357          TRUE);
5358  
5359   if (FAILED(hr))
5360   {
5361     HeapFree(GetProcessHeap(), 0, newStorage);
5362     return hr;
5363   }
5364
5365   /*
5366    * Get an "out" pointer for the caller.
5367    */
5368   hr = StorageBaseImpl_QueryInterface(
5369          (IStorage*)newStorage,
5370          (REFIID)&IID_IStorage,
5371          (void**)ppstgOpen);
5372
5373   return hr;
5374 }
5375
5376 /******************************************************************************
5377  *              StgOpenStorage        [OLE32.148]
5378  */
5379 HRESULT WINAPI StgOpenStorage(
5380   const OLECHAR *pwcsName,
5381   IStorage      *pstgPriority,
5382   DWORD           grfMode,
5383   SNB           snbExclude,
5384   DWORD           reserved, 
5385   IStorage      **ppstgOpen)
5386 {
5387   StorageImpl* newStorage = 0;
5388   HRESULT        hr = S_OK;
5389   HANDLE       hFile = 0;
5390   DWORD          shareMode;
5391   DWORD          accessMode;
5392
5393   TRACE("(%s, %p, %lx, %p, %ld, %p)\n", 
5394         debugstr_w(pwcsName), pstgPriority, grfMode,
5395         snbExclude, reserved, ppstgOpen);
5396
5397   /*
5398    * Perform a sanity check
5399    */
5400   if (( pwcsName == 0) || (ppstgOpen == 0) )
5401     return STG_E_INVALIDPOINTER;
5402
5403   /*
5404    * Validate the STGM flags
5405    */
5406   if ( FAILED( validateSTGM(grfMode) ))
5407     return STG_E_INVALIDFLAG;
5408
5409   /*
5410    * Interpret the STGM value grfMode
5411    */
5412   shareMode    = GetShareModeFromSTGM(grfMode);
5413   accessMode   = GetAccessModeFromSTGM(grfMode);
5414
5415   /*
5416    * Initialize the "out" parameter.
5417    */
5418   *ppstgOpen = 0;
5419   
5420   hFile = CreateFileW( pwcsName, 
5421                        accessMode,
5422                        shareMode,
5423                        NULL,
5424                        OPEN_EXISTING,
5425                        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5426                        0);
5427   
5428   
5429   if (hFile==INVALID_HANDLE_VALUE)
5430   {
5431     HRESULT hr = E_FAIL;
5432     DWORD last_error = GetLastError();
5433
5434     switch (last_error)
5435     {
5436       case ERROR_FILE_NOT_FOUND:
5437         hr = STG_E_FILENOTFOUND;
5438         break;
5439
5440       case ERROR_PATH_NOT_FOUND:
5441         hr = STG_E_PATHNOTFOUND;
5442         break;
5443
5444       case ERROR_ACCESS_DENIED:
5445       case ERROR_WRITE_PROTECT:
5446         hr = STG_E_ACCESSDENIED;
5447         break;
5448
5449       case ERROR_SHARING_VIOLATION:
5450         hr = STG_E_SHAREVIOLATION;
5451         break;
5452
5453       default:
5454         hr = E_FAIL;
5455     }
5456
5457     return hr;
5458   }
5459
5460   /*
5461    * Allocate and initialize the new IStorage32object.
5462    */
5463   newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5464   
5465   if (newStorage == 0)
5466     return STG_E_INSUFFICIENTMEMORY;
5467
5468   hr = StorageImpl_Construct(
5469          newStorage,
5470          hFile,
5471          NULL,
5472          grfMode,
5473          TRUE,
5474          FALSE);
5475   
5476   if (FAILED(hr))
5477   {
5478     HeapFree(GetProcessHeap(), 0, newStorage);
5479     return hr;
5480   }
5481   
5482   /*
5483    * Get an "out" pointer for the caller.
5484    */
5485   hr = StorageBaseImpl_QueryInterface(
5486          (IStorage*)newStorage,
5487          (REFIID)&IID_IStorage,
5488          (void**)ppstgOpen);
5489   
5490   return hr;
5491 }
5492
5493 /******************************************************************************
5494  *    StgCreateDocfileOnILockBytes    [OLE32.145]
5495  */
5496 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5497       ILockBytes *plkbyt,
5498       DWORD grfMode,
5499       DWORD reserved,
5500       IStorage** ppstgOpen)
5501 {
5502   StorageImpl*   newStorage = 0;
5503   HRESULT        hr         = S_OK;
5504
5505   /*
5506    * Validate the parameters
5507    */
5508   if ((ppstgOpen == 0) || (plkbyt == 0))
5509     return STG_E_INVALIDPOINTER;
5510
5511   /*
5512    * Allocate and initialize the new IStorage object.
5513    */
5514   newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5515
5516   if (newStorage == 0)
5517     return STG_E_INSUFFICIENTMEMORY;
5518
5519   hr = StorageImpl_Construct(
5520          newStorage,
5521          0,
5522          plkbyt,
5523          grfMode,
5524          FALSE,
5525          TRUE);
5526
5527   if (FAILED(hr))
5528   {
5529     HeapFree(GetProcessHeap(), 0, newStorage);
5530     return hr;
5531   }
5532
5533   /*
5534    * Get an "out" pointer for the caller.
5535    */
5536   hr = StorageBaseImpl_QueryInterface(
5537          (IStorage*)newStorage,
5538          (REFIID)&IID_IStorage,
5539          (void**)ppstgOpen);
5540
5541   return hr;  
5542 }
5543
5544 /******************************************************************************
5545  *    StgOpenStorageOnILockBytes    [OLE32.149]
5546  */
5547 HRESULT WINAPI StgOpenStorageOnILockBytes(
5548       ILockBytes *plkbyt,
5549       IStorage *pstgPriority,
5550       DWORD grfMode,
5551       SNB snbExclude,
5552       DWORD reserved,
5553       IStorage **ppstgOpen)
5554 {
5555   StorageImpl* newStorage = 0;
5556   HRESULT        hr = S_OK;
5557
5558   /*
5559    * Perform a sanity check
5560    */
5561   if ((plkbyt == 0) || (ppstgOpen == 0))
5562     return STG_E_INVALIDPOINTER;
5563
5564   /*
5565    * Validate the STGM flags
5566    */
5567   if ( FAILED( validateSTGM(grfMode) ))
5568     return STG_E_INVALIDFLAG;
5569
5570   /*
5571    * Initialize the "out" parameter.
5572    */
5573   *ppstgOpen = 0;
5574
5575   /*
5576    * Allocate and initialize the new IStorage object.
5577    */
5578   newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5579  
5580   if (newStorage == 0)
5581     return STG_E_INSUFFICIENTMEMORY;
5582
5583   hr = StorageImpl_Construct(
5584          newStorage,
5585          0,
5586          plkbyt,
5587          grfMode,
5588          FALSE,
5589          FALSE);
5590
5591   if (FAILED(hr))
5592   {
5593     HeapFree(GetProcessHeap(), 0, newStorage);
5594     return hr;
5595   }
5596
5597   /*
5598    * Get an "out" pointer for the caller.
5599    */
5600   hr = StorageBaseImpl_QueryInterface(
5601          (IStorage*)newStorage,
5602          (REFIID)&IID_IStorage,
5603          (void**)ppstgOpen);
5604
5605   return hr;
5606 }
5607
5608 /******************************************************************************
5609  *              StgSetTimes [ole32.150]
5610  *
5611  *
5612  */
5613 HRESULT WINAPI StgSetTimes(WCHAR * str, FILETIME * a, FILETIME * b, FILETIME *c )
5614 {
5615  
5616   FIXME("(%p, %p, %p, %p),stub!\n", str, a, b, c);
5617   return FALSE;
5618 }
5619
5620 /******************************************************************************
5621  *              StgIsStorageILockBytes        [OLE32.147]
5622  *
5623  * Determines if the ILockBytes contains a storage object.
5624  */
5625 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
5626 {
5627   BYTE sig[8];
5628   ULARGE_INTEGER offset;
5629
5630   offset.s.HighPart = 0;
5631   offset.s.LowPart  = 0;
5632
5633   ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
5634
5635   if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
5636     return S_OK;
5637
5638   return S_FALSE;
5639 }
5640
5641 /******************************************************************************
5642  *              WriteClassStg        [OLE32.158]
5643  *
5644  * This method will store the specified CLSID in the specified storage object
5645  */
5646 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
5647 {
5648   HRESULT hRes;
5649
5650   assert(pStg != 0);
5651
5652   hRes = IStorage_SetClass(pStg, rclsid);
5653
5654   return hRes;
5655 }
5656
5657 /***********************************************************************
5658  *    ReadClassStg
5659  *
5660  * This method reads the CLSID previously written to a storage object with the WriteClassStg.
5661  */
5662 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
5663
5664     STATSTG pstatstg;
5665     HRESULT hRes;
5666     
5667     TRACE("()\n");
5668
5669     if(pclsid==NULL)
5670         return E_POINTER;
5671    /*
5672     * read a STATSTG structure (contains the clsid) from the storage
5673     */
5674     hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
5675
5676     if(SUCCEEDED(hRes))
5677         *pclsid=pstatstg.clsid;
5678
5679     return hRes;
5680 }
5681
5682 /***********************************************************************
5683  *    OleLoadFromStream
5684  *
5685  * This function loads an object from stream
5686  */
5687 HRESULT  WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
5688 {
5689     CLSID clsid;
5690     HRESULT res;
5691
5692     FIXME("(),stub!\n");
5693
5694     res=ReadClassStm(pStm,&clsid);
5695
5696     if (SUCCEEDED(res)){
5697         
5698         res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
5699
5700         if (SUCCEEDED(res))
5701
5702             res=IPersistStream_Load((IPersistStream*)ppvObj,pStm);
5703     }
5704
5705     return res;
5706 }
5707
5708 /***********************************************************************
5709  *    OleSaveToStream
5710  *
5711  * This function saves an object with the IPersistStream interface on it
5712  * to the specified stream.
5713  */
5714 HRESULT  WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
5715 {
5716
5717     CLSID clsid;
5718     HRESULT res;
5719     
5720     TRACE("(%p,%p)\n",pPStm,pStm);
5721
5722     res=IPersistStream_GetClassID(pPStm,&clsid);
5723
5724     if (SUCCEEDED(res)){
5725         
5726         res=WriteClassStm(pStm,&clsid);
5727
5728         if (SUCCEEDED(res))
5729
5730             res=IPersistStream_Save(pPStm,pStm,TRUE);
5731     }
5732
5733     TRACE("Finished Save\n");
5734     return res;
5735 }
5736
5737 /****************************************************************************
5738  * This method validate a STGM parameter that can contain the values below
5739  *
5740  * STGM_DIRECT               0x00000000
5741  * STGM_TRANSACTED           0x00010000
5742  * STGM_SIMPLE               0x08000000
5743  * 
5744  * STGM_READ                 0x00000000
5745  * STGM_WRITE                0x00000001
5746  * STGM_READWRITE            0x00000002
5747  * 
5748  * STGM_SHARE_DENY_NONE      0x00000040
5749  * STGM_SHARE_DENY_READ      0x00000030
5750  * STGM_SHARE_DENY_WRITE     0x00000020
5751  * STGM_SHARE_EXCLUSIVE      0x00000010
5752  * 
5753  * STGM_PRIORITY             0x00040000
5754  * STGM_DELETEONRELEASE      0x04000000
5755  *
5756  * STGM_CREATE               0x00001000
5757  * STGM_CONVERT              0x00020000
5758  * STGM_FAILIFTHERE          0x00000000
5759  *
5760  * STGM_NOSCRATCH            0x00100000
5761  * STGM_NOSNAPSHOT           0x00200000
5762  */
5763 static HRESULT validateSTGM(DWORD stgm)
5764 {
5765   BOOL bSTGM_TRANSACTED       = ((stgm & STGM_TRANSACTED) == STGM_TRANSACTED);
5766   BOOL bSTGM_SIMPLE           = ((stgm & STGM_SIMPLE) == STGM_SIMPLE);
5767   BOOL bSTGM_DIRECT           = ! (bSTGM_TRANSACTED || bSTGM_SIMPLE);
5768    
5769   BOOL bSTGM_WRITE            = ((stgm & STGM_WRITE) == STGM_WRITE);
5770   BOOL bSTGM_READWRITE        = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5771   BOOL bSTGM_READ             = ! (bSTGM_WRITE || bSTGM_READWRITE);
5772    
5773   BOOL bSTGM_SHARE_DENY_NONE  =
5774                      ((stgm & STGM_SHARE_DENY_NONE)  == STGM_SHARE_DENY_NONE);
5775
5776   BOOL bSTGM_SHARE_DENY_READ  =
5777                      ((stgm & STGM_SHARE_DENY_READ)  == STGM_SHARE_DENY_READ);
5778
5779   BOOL bSTGM_SHARE_DENY_WRITE =
5780                      ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5781
5782   BOOL bSTGM_SHARE_EXCLUSIVE  =
5783                      ((stgm & STGM_SHARE_EXCLUSIVE)  == STGM_SHARE_EXCLUSIVE);
5784
5785   BOOL bSTGM_CREATE           = ((stgm & STGM_CREATE) == STGM_CREATE);
5786   BOOL bSTGM_CONVERT          = ((stgm & STGM_CONVERT) == STGM_CONVERT);
5787    
5788   BOOL bSTGM_NOSCRATCH        = ((stgm & STGM_NOSCRATCH) == STGM_NOSCRATCH);
5789   BOOL bSTGM_NOSNAPSHOT       = ((stgm & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT);
5790
5791   /* 
5792    * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
5793    */
5794   if ( ! bSTGM_DIRECT )
5795     if( bSTGM_TRANSACTED && bSTGM_SIMPLE )
5796       return E_FAIL;
5797
5798   /* 
5799    * STGM_WRITE |  STGM_READWRITE | STGM_READ
5800    */
5801   if ( ! bSTGM_READ )
5802     if( bSTGM_WRITE && bSTGM_READWRITE )
5803       return E_FAIL;
5804
5805   /*
5806    * STGM_SHARE_DENY_NONE | others 
5807    * (I assume here that DENY_READ implies DENY_WRITE)
5808    */
5809   if ( bSTGM_SHARE_DENY_NONE )
5810     if ( bSTGM_SHARE_DENY_READ ||
5811          bSTGM_SHARE_DENY_WRITE || 
5812          bSTGM_SHARE_EXCLUSIVE) 
5813       return E_FAIL;
5814
5815   /*
5816    * STGM_CREATE | STGM_CONVERT
5817    * if both are false, STGM_FAILIFTHERE is set to TRUE
5818    */
5819   if ( bSTGM_CREATE && bSTGM_CONVERT )
5820     return E_FAIL;
5821
5822   /*
5823    * STGM_NOSCRATCH requires STGM_TRANSACTED
5824    */
5825   if ( bSTGM_NOSCRATCH && ! bSTGM_TRANSACTED )
5826     return E_FAIL;
5827   
5828   /*
5829    * STGM_NOSNAPSHOT requires STGM_TRANSACTED and 
5830    * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
5831    */
5832   if (bSTGM_NOSNAPSHOT)
5833   {
5834     if ( ! ( bSTGM_TRANSACTED && 
5835            !(bSTGM_SHARE_EXCLUSIVE || bSTGM_SHARE_DENY_WRITE)) )
5836     return E_FAIL;
5837   }
5838
5839   return S_OK;
5840 }
5841
5842 /****************************************************************************
5843  *      GetShareModeFromSTGM
5844  *
5845  * This method will return a share mode flag from a STGM value.
5846  * The STGM value is assumed valid. 
5847  */
5848 static DWORD GetShareModeFromSTGM(DWORD stgm)
5849 {
5850   DWORD dwShareMode = 0;
5851   BOOL bSTGM_SHARE_DENY_NONE  =
5852                      ((stgm & STGM_SHARE_DENY_NONE)  == STGM_SHARE_DENY_NONE);
5853
5854   BOOL bSTGM_SHARE_DENY_READ  =
5855                      ((stgm & STGM_SHARE_DENY_READ)  == STGM_SHARE_DENY_READ);
5856
5857   BOOL bSTGM_SHARE_DENY_WRITE =
5858                      ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5859
5860   BOOL bSTGM_SHARE_EXCLUSIVE  =
5861                      ((stgm & STGM_SHARE_EXCLUSIVE)  == STGM_SHARE_EXCLUSIVE);
5862
5863   if ((bSTGM_SHARE_EXCLUSIVE) || (bSTGM_SHARE_DENY_READ))
5864     dwShareMode = 0;
5865
5866   if (bSTGM_SHARE_DENY_NONE)
5867     dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
5868
5869   if (bSTGM_SHARE_DENY_WRITE)
5870     dwShareMode = FILE_SHARE_READ;
5871
5872   return dwShareMode;
5873 }
5874
5875 /****************************************************************************
5876  *      GetAccessModeFromSTGM
5877  *
5878  * This method will return an access mode flag from a STGM value.
5879  * The STGM value is assumed valid.
5880  */
5881 static DWORD GetAccessModeFromSTGM(DWORD stgm)
5882 {
5883   DWORD dwDesiredAccess = GENERIC_READ;
5884   BOOL bSTGM_WRITE     = ((stgm & STGM_WRITE) == STGM_WRITE);
5885   BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5886   BOOL bSTGM_READ      = ! (bSTGM_WRITE || bSTGM_READWRITE);
5887
5888   if (bSTGM_READ)
5889     dwDesiredAccess = GENERIC_READ;
5890
5891   if (bSTGM_WRITE)
5892     dwDesiredAccess |= GENERIC_WRITE;
5893
5894   if (bSTGM_READWRITE)
5895     dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
5896
5897   return dwDesiredAccess;
5898 }
5899
5900 /****************************************************************************
5901  *      GetCreationModeFromSTGM
5902  *
5903  * This method will return a creation mode flag from a STGM value.
5904  * The STGM value is assumed valid.
5905  */
5906 static DWORD GetCreationModeFromSTGM(DWORD stgm)
5907 {
5908   if ( stgm & STGM_CREATE)
5909     return CREATE_ALWAYS;
5910   if (stgm & STGM_CONVERT) {
5911     FIXME("STGM_CONVERT not implemented!\n");
5912     return CREATE_NEW;
5913   }
5914   /* All other cases */
5915   if (stgm & ~ (STGM_CREATE|STGM_CONVERT))
5916         FIXME("unhandled storage mode : 0x%08lx\n",stgm & ~ (STGM_CREATE|STGM_CONVERT));
5917   return CREATE_NEW;
5918 }
5919
5920
5921 /*************************************************************************
5922  * OLECONVERT_LoadOLE10 [Internal] 
5923  *
5924  * Loads the OLE10 STREAM to memory 
5925  *
5926  * PARAMS
5927  *     pOleStream   [I] The OLESTREAM
5928  *     pData        [I] Data Structure for the OLESTREAM Data
5929  *
5930  * RETURNS
5931  *     Success:  S_OK
5932  *     Failure:  CONVERT10_E_OLESTREAM_GET for invalid Get
5933  *               CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
5934  *
5935  * NOTES
5936  *     This function is used by OleConvertOLESTREAMToIStorage only.
5937  *     
5938  *     Memory allocated for pData must be freed by the caller
5939  */
5940 HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
5941 {
5942         DWORD dwSize;
5943         HRESULT hRes = S_OK;
5944         int nTryCnt=0;
5945         int max_try = 6;
5946
5947         pData->pData = NULL;
5948         pData->pstrOleObjFileName = (CHAR *) NULL;
5949
5950         for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
5951         {
5952         /* Get the OleID */
5953         dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
5954         if(dwSize != sizeof(pData->dwOleID))
5955         {
5956                 hRes = CONVERT10_E_OLESTREAM_GET;
5957         }
5958         else if(pData->dwOleID != OLESTREAM_ID)
5959         {
5960                 hRes = CONVERT10_E_OLESTREAM_FMT;
5961         }
5962                 else
5963                 {
5964                         hRes = S_OK;
5965                         break;
5966                 }
5967         }
5968
5969         if(hRes == S_OK)
5970         {
5971                 /* Get the TypeID...more info needed for this field */
5972                 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
5973                 if(dwSize != sizeof(pData->dwTypeID))
5974                 {
5975                         hRes = CONVERT10_E_OLESTREAM_GET;
5976                 }
5977         }
5978         if(hRes == S_OK)
5979         {
5980                 if(pData->dwTypeID != 0)
5981                 {
5982                         /* Get the lenght of the OleTypeName */
5983                         dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
5984                         if(dwSize != sizeof(pData->dwOleTypeNameLength))
5985                         {
5986                                 hRes = CONVERT10_E_OLESTREAM_GET;
5987                         }
5988
5989                         if(hRes == S_OK)
5990                         {
5991                                 if(pData->dwOleTypeNameLength > 0)
5992                                 {
5993                                         /* Get the OleTypeName */
5994                                         dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
5995                                         if(dwSize != pData->dwOleTypeNameLength)
5996                                         {
5997                                                 hRes = CONVERT10_E_OLESTREAM_GET;
5998                                         }
5999                                 }
6000                         }
6001                         if(bStrem1)
6002                         {
6003                                 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6004                                 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6005                                 {
6006                                         hRes = CONVERT10_E_OLESTREAM_GET;
6007                                 }
6008                         if(hRes == S_OK)
6009                         {
6010                                         if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6011                                                 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6012                                         pData->pstrOleObjFileName = (CHAR *)malloc(pData->dwOleObjFileNameLength);
6013                                         if(pData->pstrOleObjFileName)
6014                                         {
6015                                                 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6016                                                 if(dwSize != pData->dwOleObjFileNameLength)
6017                                                 {
6018                                                         hRes = CONVERT10_E_OLESTREAM_GET;
6019                                                 }
6020                                         }
6021                                         else
6022                                                 hRes = CONVERT10_E_OLESTREAM_GET;
6023                                 }
6024                         }
6025                         else
6026                         {
6027                                 /* Get the Width of the Metafile */
6028                                 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6029                                 if(dwSize != sizeof(pData->dwMetaFileWidth))
6030                                 {
6031                                         hRes = CONVERT10_E_OLESTREAM_GET;
6032                                 }
6033                         if(hRes == S_OK)
6034                         {
6035                                 /* Get the Height of the Metafile */
6036                                 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6037                                 if(dwSize != sizeof(pData->dwMetaFileHeight))
6038                                 {
6039                                         hRes = CONVERT10_E_OLESTREAM_GET;
6040                                 }
6041                         }
6042                         }
6043                         if(hRes == S_OK)
6044                         {
6045                                 /* Get the Lenght of the Data */
6046                                 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6047                                 if(dwSize != sizeof(pData->dwDataLength))
6048                                 {
6049                                         hRes = CONVERT10_E_OLESTREAM_GET;
6050                                 }
6051                         }
6052
6053                         if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6054                         {
6055                                 if(!bStrem1) /* if it is a second OLE stream data */
6056                                 {
6057                                         pData->dwDataLength -= 8;
6058                                         dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6059                                         if(dwSize != sizeof(pData->strUnknown))
6060                                         {
6061                                                 hRes = CONVERT10_E_OLESTREAM_GET;
6062                                         }
6063                                 }
6064                         }
6065                         if(hRes == S_OK)
6066                         {
6067                                 if(pData->dwDataLength > 0)
6068                                 {
6069                                         pData->pData = (BYTE *)HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6070
6071                                         /* Get Data (ex. IStorage, Metafile, or BMP) */
6072                                         if(pData->pData)
6073                                         {
6074                                                 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6075                                                 if(dwSize != pData->dwDataLength)
6076                                                 {
6077                                                         hRes = CONVERT10_E_OLESTREAM_GET;
6078                                                 }
6079                                         }
6080                                         else
6081                                         {
6082                                                 hRes = CONVERT10_E_OLESTREAM_GET;
6083                                         }
6084                                 }
6085                         }
6086                 }
6087         }
6088         return hRes;
6089 }
6090
6091 /*************************************************************************
6092  * OLECONVERT_SaveOLE10 [Internal] 
6093  *
6094  * Saves the OLE10 STREAM From memory 
6095  *
6096  * PARAMS
6097  *     pData        [I] Data Structure for the OLESTREAM Data
6098  *     pOleStream   [I] The OLESTREAM to save
6099  *
6100  * RETURNS
6101  *     Success:  S_OK
6102  *     Failure:  CONVERT10_E_OLESTREAM_PUT for invalid Put
6103  *
6104  * NOTES
6105  *     This function is used by OleConvertIStorageToOLESTREAM only.
6106  *     
6107  */
6108 HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6109 {
6110     DWORD dwSize;
6111     HRESULT hRes = S_OK;
6112
6113
6114    /* Set the OleID */
6115     dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6116     if(dwSize != sizeof(pData->dwOleID))
6117     {
6118         hRes = CONVERT10_E_OLESTREAM_PUT;
6119     }
6120
6121     if(hRes == S_OK)
6122     {
6123         /* Set the TypeID */
6124         dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6125         if(dwSize != sizeof(pData->dwTypeID))
6126         {
6127             hRes = CONVERT10_E_OLESTREAM_PUT;
6128         }
6129     }
6130
6131     if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6132     {
6133         /* Set the Lenght of the OleTypeName */
6134         dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6135         if(dwSize != sizeof(pData->dwOleTypeNameLength))
6136         {
6137             hRes = CONVERT10_E_OLESTREAM_PUT;
6138         }
6139
6140         if(hRes == S_OK)
6141         {
6142             if(pData->dwOleTypeNameLength > 0)
6143             {
6144                 /* Set the OleTypeName */
6145                 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)  pData->strOleTypeName, pData->dwOleTypeNameLength);
6146                 if(dwSize != pData->dwOleTypeNameLength)
6147                 {
6148                     hRes = CONVERT10_E_OLESTREAM_PUT;
6149                 }
6150             }
6151         }
6152
6153         if(hRes == S_OK)
6154         {
6155             /* Set the width of the Metafile */
6156             dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6157             if(dwSize != sizeof(pData->dwMetaFileWidth))
6158             {
6159                 hRes = CONVERT10_E_OLESTREAM_PUT;
6160             }
6161         }
6162
6163         if(hRes == S_OK)
6164         {
6165             /* Set the height of the Metafile */
6166             dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6167             if(dwSize != sizeof(pData->dwMetaFileHeight))
6168             {
6169                 hRes = CONVERT10_E_OLESTREAM_PUT;
6170             }
6171         }
6172
6173         if(hRes == S_OK)
6174         {
6175             /* Set the lenght of the Data */
6176             dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6177             if(dwSize != sizeof(pData->dwDataLength))
6178             {
6179                 hRes = CONVERT10_E_OLESTREAM_PUT;
6180             }
6181         }
6182
6183         if(hRes == S_OK)
6184         {
6185             if(pData->dwDataLength > 0)
6186             {
6187                 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6188                 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)  pData->pData, pData->dwDataLength);
6189                 if(dwSize != pData->dwDataLength)
6190                 {
6191                     hRes = CONVERT10_E_OLESTREAM_PUT;
6192                 }
6193             }
6194         }
6195     }
6196     return hRes;
6197 }
6198
6199 /*************************************************************************
6200  * OLECONVERT_GetOLE20FromOLE10[Internal] 
6201  *
6202  * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6203  * opens it, and copies the content to the dest IStorage for 
6204  * OleConvertOLESTREAMToIStorage
6205  * 
6206  *
6207  * PARAMS
6208  *     pDestStorage  [I] The IStorage to copy the data to
6209  *     pBuffer       [I] Buffer that contains the IStorage from the OLESTREAM
6210  *     nBufferLength [I] The size of the buffer
6211  *
6212  * RETURNS
6213  *     Nothing
6214  *
6215  * NOTES
6216  *     
6217  *     
6218  */
6219 void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6220 {
6221     HRESULT hRes;
6222     HANDLE hFile;
6223     IStorage *pTempStorage;
6224     DWORD dwNumOfBytesWritten;
6225     WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6226     WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6227
6228     /* Create a temp File */
6229     GetTempPathW(MAX_PATH, wstrTempDir);
6230     GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6231     hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6232
6233     if(hFile != INVALID_HANDLE_VALUE)
6234     {
6235         /* Write IStorage Data to File */
6236         WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6237         CloseHandle(hFile);
6238
6239         /* Open and copy temp storage to the Dest Storage */
6240         hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6241         if(hRes == S_OK)
6242         {
6243             hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6244             StorageBaseImpl_Release(pTempStorage);
6245         }
6246         DeleteFileW(wstrTempFile);
6247     }
6248 }
6249
6250
6251 /*************************************************************************
6252  * OLECONVERT_WriteOLE20ToBuffer [Internal] 
6253  *
6254  * Saves the OLE10 STREAM From memory 
6255  *
6256  * PARAMS
6257  *     pStorage  [I] The Src IStorage to copy
6258  *     pData     [I] The Dest Memory to write to.
6259  *
6260  * RETURNS
6261  *     The size in bytes allocated for pData
6262  *
6263  * NOTES
6264  *     Memory allocated for pData must be freed by the caller
6265  *
6266  *     Used by OleConvertIStorageToOLESTREAM only.
6267  *     
6268  */
6269 DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6270 {
6271     HANDLE hFile;
6272     HRESULT hRes;
6273     DWORD nDataLength = 0;
6274     IStorage *pTempStorage;
6275     WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6276     WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6277
6278     *pData = NULL;
6279     
6280     /* Create temp Storage */
6281     GetTempPathW(MAX_PATH, wstrTempDir);
6282     GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6283     hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6284
6285     if(hRes == S_OK)
6286     {
6287         /* Copy Src Storage to the Temp Storage */
6288         StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6289         StorageBaseImpl_Release(pTempStorage);
6290
6291         /* Open Temp Storage as a file and copy to memory */
6292         hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6293         if(hFile != INVALID_HANDLE_VALUE)
6294         {
6295             nDataLength = GetFileSize(hFile, NULL);
6296             *pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,nDataLength);
6297             ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6298             CloseHandle(hFile);
6299         }
6300         DeleteFileW(wstrTempFile);
6301     }
6302     return nDataLength;
6303 }
6304
6305 /*************************************************************************
6306  * OLECONVERT_CreateOleStream [Internal] 
6307  *
6308  * Creates the "\001OLE" stream in the IStorage if neccessary.
6309  *
6310  * PARAMS
6311  *     pStorage     [I] Dest storage to create the stream in
6312  *
6313  * RETURNS
6314  *     Nothing
6315  *
6316  * NOTES
6317  *     This function is used by OleConvertOLESTREAMToIStorage only.
6318  *
6319  *     This stream is still unknown, MS Word seems to have extra data
6320  *     but since the data is stored in the OLESTREAM there should be
6321  *     no need to recreate the stream.  If the stream is manually 
6322  *     deleted it will create it with this default data.
6323  *     
6324  */
6325 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6326 {
6327     HRESULT hRes;
6328     IStream *pStream;
6329     WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6330     BYTE pOleStreamHeader [] = 
6331     {
6332         0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 
6333         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
6334         0x00, 0x00, 0x00, 0x00 
6335     };
6336     
6337     /* Create stream if not present */
6338     hRes = IStorage_CreateStream(pStorage, wstrStreamName, 
6339         STGM_WRITE  | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6340
6341     if(hRes == S_OK)
6342     {
6343         /* Write default Data */
6344         hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6345         IStream_Release(pStream);
6346     }
6347 }
6348
6349
6350 /*************************************************************************
6351  * OLECONVERT_CreateCompObjStream [Internal] 
6352  *
6353  * Creates a "\001CompObj" is the destination IStorage if necessary.
6354  *
6355  * PARAMS
6356  *     pStorage       [I] The dest IStorage to create the CompObj Stream 
6357  *                        if necessary.
6358  *     strOleTypeName [I] The ProgID
6359  *
6360  * RETURNS
6361  *     Success:  S_OK
6362  *     Failure:  REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6363  *
6364  * NOTES
6365  *     This function is used by OleConvertOLESTREAMToIStorage only.
6366  *
6367  *     The stream data is stored in the OLESTREAM and there should be
6368  *     no need to recreate the stream.  If the stream is manually 
6369  *     deleted it will attempt to create it by querying the registry.
6370  *
6371  *     
6372  */
6373 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
6374 {
6375     IStream *pStream;
6376     HRESULT hStorageRes, hRes = S_OK;
6377     OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
6378     WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6379
6380     BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
6381     BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
6382
6383     /* Initialize the CompObj structure */
6384     memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
6385     memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
6386     memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
6387
6388
6389     /*  Create a CompObj stream if it doesn't exist */
6390     hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName, 
6391         STGM_WRITE  | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6392     if(hStorageRes == S_OK)
6393     {
6394         /* copy the OleTypeName to the compobj struct */
6395         IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
6396         strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
6397
6398         /* copy the OleTypeName to the compobj struct */
6399         /* Note: in the test made, these where Identical      */
6400         IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
6401         strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
6402
6403         /* Get the CLSID */
6404         hRes = CLSIDFromProgID16(IStorageCompObj.strProgIDName, &(IStorageCompObj.clsid));
6405
6406         if(hRes == S_OK)
6407         {
6408             HKEY hKey;
6409             LONG hErr;
6410             /* Get the CLSID Default Name from the Registry */
6411             hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey); 
6412             if(hErr == ERROR_SUCCESS)
6413             {
6414                 char strTemp[OLESTREAM_MAX_STR_LEN];
6415                 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
6416                 hErr = RegQueryValueA(hKey, NULL, strTemp, &(IStorageCompObj.dwCLSIDNameLength));
6417                 if(hErr == ERROR_SUCCESS)
6418                 {
6419                     strcpy(IStorageCompObj.strCLSIDName, strTemp);
6420                 }
6421                 RegCloseKey(hKey);
6422             }
6423         }
6424
6425         /* Write CompObj Structure to stream */
6426         hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
6427
6428         WriteClassStm(pStream,&(IStorageCompObj.clsid));
6429
6430         hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
6431         if(IStorageCompObj.dwCLSIDNameLength > 0)
6432         {
6433             hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
6434         }
6435         hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
6436         if(IStorageCompObj.dwOleTypeNameLength > 0)
6437         {
6438             hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
6439         }
6440         hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
6441         if(IStorageCompObj.dwProgIDNameLength > 0)
6442         {
6443             hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
6444         }
6445         hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
6446         IStream_Release(pStream);
6447     }
6448     return hRes;
6449 }
6450
6451
6452 /*************************************************************************
6453  * OLECONVERT_CreateOlePresStream[Internal] 
6454  *
6455  * Creates the "\002OlePres000" Stream with the Metafile data
6456  *
6457  * PARAMS
6458  *     pStorage     [I] The dest IStorage to create \002OLEPres000 stream in.
6459  *     dwExtentX    [I] Width of the Metafile
6460  *     dwExtentY    [I] Height of the Metafile 
6461  *     pData        [I] Metafile data
6462  *     dwDataLength [I] Size of the Metafile data
6463  *
6464  * RETURNS
6465  *     Success:  S_OK
6466  *     Failure:  CONVERT10_E_OLESTREAM_PUT for invalid Put
6467  *
6468  * NOTES
6469  *     This function is used by OleConvertOLESTREAMToIStorage only.
6470  *     
6471  */
6472 void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
6473 {
6474     HRESULT hRes;
6475     IStream *pStream;
6476     WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
6477     BYTE pOlePresStreamHeader [] = 
6478     {
6479         0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 
6480         0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 
6481         0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6482         0x00, 0x00, 0x00, 0x00
6483     };
6484
6485     BYTE pOlePresStreamHeaderEmpty [] = 
6486     {
6487         0x00, 0x00, 0x00, 0x00, 
6488         0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 
6489         0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6490         0x00, 0x00, 0x00, 0x00
6491     };
6492      
6493     /* Create the OlePres000 Stream */
6494     hRes = IStorage_CreateStream(pStorage, wstrStreamName, 
6495         STGM_CREATE | STGM_WRITE  | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6496
6497     if(hRes == S_OK)
6498     {
6499         DWORD nHeaderSize;
6500         OLECONVERT_ISTORAGE_OLEPRES OlePres;
6501
6502         memset(&OlePres, 0, sizeof(OlePres));
6503         /* Do we have any metafile data to save */
6504         if(dwDataLength > 0)
6505         {
6506             memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
6507             nHeaderSize = sizeof(pOlePresStreamHeader);
6508         }
6509         else
6510         {
6511             memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
6512             nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
6513         }
6514         /* Set width and height of the metafile */
6515         OlePres.dwExtentX = dwExtentX;
6516         OlePres.dwExtentY = -dwExtentY;
6517
6518         /* Set Data and Lenght */
6519         if(dwDataLength > sizeof(METAFILEPICT16))
6520         {
6521             OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
6522             OlePres.pData = &(pData[8]);
6523         }
6524         /* Save OlePres000 Data to Stream */
6525         hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
6526         hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
6527         hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
6528         hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
6529         if(OlePres.dwSize > 0)
6530         {
6531             hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
6532         }
6533         IStream_Release(pStream);
6534     }
6535 }
6536
6537 /*************************************************************************
6538  * OLECONVERT_CreateOle10NativeStream [Internal] 
6539  *
6540  * Creates the "\001Ole10Native" Stream (should contain a BMP)
6541  *
6542  * PARAMS
6543  *     pStorage     [I] Dest storage to create the stream in
6544  *     pData        [I] Ole10 Native Data (ex. bmp)
6545  *     dwDataLength [I] Size of the Ole10 Native Data
6546  *
6547  * RETURNS
6548  *     Nothing
6549  *
6550  * NOTES
6551  *     This function is used by OleConvertOLESTREAMToIStorage only.
6552  *
6553  *     Might need to verify the data and return appropriate error message
6554  *     
6555  */
6556 void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
6557 {
6558     HRESULT hRes;
6559     IStream *pStream;
6560     WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
6561     
6562     /* Create the Ole10Native Stream */
6563     hRes = IStorage_CreateStream(pStorage, wstrStreamName, 
6564         STGM_CREATE | STGM_WRITE  | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6565
6566     if(hRes == S_OK)
6567     {
6568         /* Write info to stream */
6569         hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
6570         hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
6571         IStream_Release(pStream);
6572     }
6573
6574 }
6575
6576 /*************************************************************************
6577  * OLECONVERT_GetOLE10ProgID [Internal] 
6578  *
6579  * Finds the ProgID (or OleTypeID) from the IStorage
6580  *
6581  * PARAMS
6582  *     pStorage        [I] The Src IStorage to get the ProgID
6583  *     strProgID       [I] the ProgID string to get
6584  *     dwSize          [I] the size of the string
6585  *
6586  * RETURNS
6587  *     Success:  S_OK
6588  *     Failure:  REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6589  *
6590  * NOTES
6591  *     This function is used by OleConvertIStorageToOLESTREAM only.
6592  *
6593  *     
6594  */
6595 HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
6596 {
6597     HRESULT hRes;
6598     IStream *pStream;
6599     LARGE_INTEGER iSeekPos;
6600     OLECONVERT_ISTORAGE_COMPOBJ CompObj;
6601     WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6602
6603     /* Open the CompObj Stream */
6604     hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,  
6605         STGM_READ  | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6606     if(hRes == S_OK)
6607     {
6608
6609         /*Get the OleType from the CompObj Stream */
6610         iSeekPos.s.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
6611         iSeekPos.s.HighPart = 0;
6612
6613         IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
6614         IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
6615         iSeekPos.s.LowPart = CompObj.dwCLSIDNameLength;
6616         IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
6617         IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
6618         iSeekPos.s.LowPart = CompObj.dwOleTypeNameLength;
6619         IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
6620
6621         IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
6622         if(*dwSize > 0)
6623         {
6624             IStream_Read(pStream, strProgID, *dwSize, NULL);
6625         }
6626         IStream_Release(pStream);
6627     }
6628     else
6629     {
6630         STATSTG stat;
6631         LPOLESTR wstrProgID;
6632
6633         /* Get the OleType from the registry */
6634         REFCLSID clsid = &(stat.clsid);
6635         IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
6636         hRes = ProgIDFromCLSID(clsid, &wstrProgID);
6637         if(hRes == S_OK)
6638         {
6639             *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
6640         }
6641  
6642     }
6643     return hRes;
6644 }
6645
6646 /*************************************************************************
6647  * OLECONVERT_GetOle10PresData [Internal] 
6648  *
6649  * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
6650  *
6651  * PARAMS
6652  *     pStorage     [I] Src IStroage
6653  *     pOleStream   [I] Dest OleStream Mem Struct
6654  *
6655  * RETURNS
6656  *     Nothing
6657  *
6658  * NOTES
6659  *     This function is used by OleConvertIStorageToOLESTREAM only.
6660  *
6661  *     Memory allocated for pData must be freed by the caller
6662  *      
6663  *     
6664  */
6665 void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
6666 {
6667
6668     HRESULT hRes;
6669     IStream *pStream;
6670     WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
6671
6672     /* Initialize Default data for OLESTREAM */
6673     pOleStreamData[0].dwOleID = OLESTREAM_ID;
6674     pOleStreamData[0].dwTypeID = 2;
6675     pOleStreamData[1].dwOleID = OLESTREAM_ID;
6676     pOleStreamData[1].dwTypeID = 0;
6677     pOleStreamData[0].dwMetaFileWidth = 0;
6678     pOleStreamData[0].dwMetaFileHeight = 0;
6679     pOleStreamData[0].pData = NULL;
6680     pOleStreamData[1].pData = NULL;
6681
6682     /* Open Ole10Native Stream */
6683     hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,  
6684         STGM_READ  | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6685     if(hRes == S_OK)
6686     {
6687
6688         /* Read Size and Data */
6689         IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
6690         if(pOleStreamData->dwDataLength > 0)
6691         {
6692             pOleStreamData->pData = (LPSTR) HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
6693             IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
6694         }
6695         IStream_Release(pStream);
6696     }
6697
6698 }
6699
6700
6701 /*************************************************************************
6702  * OLECONVERT_GetOle20PresData[Internal] 
6703  *
6704  * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
6705  *
6706  * PARAMS
6707  *     pStorage         [I] Src IStroage
6708  *     pOleStreamData   [I] Dest OleStream Mem Struct
6709  *
6710  * RETURNS
6711  *     Nothing
6712  *
6713  * NOTES
6714  *     This function is used by OleConvertIStorageToOLESTREAM only.
6715  *     
6716  *     Memory allocated for pData must be freed by the caller
6717  */
6718 void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
6719 {
6720     HRESULT hRes;
6721     IStream *pStream;
6722     OLECONVERT_ISTORAGE_OLEPRES olePress;
6723     WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
6724
6725     /* Initialize Default data for OLESTREAM */
6726     pOleStreamData[0].dwOleID = OLESTREAM_ID;
6727     pOleStreamData[0].dwTypeID = 2;
6728     pOleStreamData[0].dwMetaFileWidth = 0;
6729     pOleStreamData[0].dwMetaFileHeight = 0;
6730     pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
6731     pOleStreamData[1].dwOleID = OLESTREAM_ID;
6732     pOleStreamData[1].dwTypeID = 0;
6733     pOleStreamData[1].dwOleTypeNameLength = 0;
6734     pOleStreamData[1].strOleTypeName[0] = 0;
6735     pOleStreamData[1].dwMetaFileWidth = 0;
6736     pOleStreamData[1].dwMetaFileHeight = 0;
6737     pOleStreamData[1].pData = NULL;
6738     pOleStreamData[1].dwDataLength = 0;
6739
6740
6741     /* Open OlePress000 stream */
6742     hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,  
6743         STGM_READ  | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6744     if(hRes == S_OK)
6745     {
6746         LARGE_INTEGER iSeekPos;
6747         METAFILEPICT16 MetaFilePict;
6748         char strMetafilePictName[] = "METAFILEPICT";
6749
6750         /* Set the TypeID for a Metafile */
6751         pOleStreamData[1].dwTypeID = 5;
6752
6753         /* Set the OleTypeName to Metafile */
6754         pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
6755         strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
6756
6757         iSeekPos.s.HighPart = 0;
6758         iSeekPos.s.LowPart = sizeof(olePress.byUnknown1);
6759
6760         /* Get Presentation Data */
6761         IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
6762         IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
6763         IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
6764         IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
6765
6766         /*Set width and Height */
6767         pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
6768         pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
6769         if(olePress.dwSize > 0)
6770         {
6771             /* Set Length */
6772             pOleStreamData[1].dwDataLength  = olePress.dwSize + sizeof(METAFILEPICT16);
6773
6774             /* Set MetaFilePict struct */
6775             MetaFilePict.mm = 8;
6776             MetaFilePict.xExt = olePress.dwExtentX;
6777             MetaFilePict.yExt = olePress.dwExtentY;
6778             MetaFilePict.hMF = 0;
6779
6780             /* Get Metafile Data */
6781             pOleStreamData[1].pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
6782             memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
6783             IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
6784         }
6785         IStream_Release(pStream);
6786     }
6787 }
6788
6789 /*************************************************************************
6790  * OleConvertOLESTREAMToIStorage [OLE32.87] 
6791  *
6792  * Read info on MSDN
6793  *
6794  * TODO
6795  *      DVTARGETDEVICE paramenter is not handled
6796  *      Still unsure of some mem fields for OLE 10 Stream
6797  *      Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
6798  *      and "\001OLE" streams
6799  *     
6800  */
6801 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
6802     LPOLESTREAM pOleStream, 
6803     LPSTORAGE pstg, 
6804     const DVTARGETDEVICE* ptd)
6805 {
6806     int i;
6807     HRESULT hRes=S_OK;
6808     OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
6809
6810     memset(pOleStreamData, 0, sizeof(pOleStreamData));
6811
6812     if(ptd != NULL)
6813     {
6814         FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
6815     }
6816
6817     if(pstg == NULL || pOleStream == NULL)
6818     {
6819         hRes = E_INVALIDARG;
6820     }
6821
6822     if(hRes == S_OK)
6823     {
6824         /* Load the OLESTREAM to Memory */
6825         hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
6826     }
6827
6828     if(hRes == S_OK)
6829     {
6830         /* Load the OLESTREAM to Memory (part 2)*/
6831         hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
6832     }
6833
6834     if(hRes == S_OK)
6835     {
6836
6837         if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
6838         {
6839             /* Do we have the IStorage Data in the OLESTREAM */
6840             if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
6841             {
6842                 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
6843                 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
6844             }
6845             else
6846             {
6847                 /* It must be an original OLE 1.0 source */
6848                 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
6849             }
6850         }
6851         else
6852         {
6853             /* It must be an original OLE 1.0 source */
6854             OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
6855         }
6856
6857         /* Create CompObj Stream if necessary */
6858         hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
6859         if(hRes == S_OK)
6860         {
6861             /*Create the Ole Stream if necessary */
6862             OLECONVERT_CreateOleStream(pstg);
6863         }
6864     }
6865
6866
6867     /* Free allocated memory */
6868     for(i=0; i < 2; i++)
6869     {
6870         if(pOleStreamData[i].pData != NULL)
6871         {
6872             HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
6873         }
6874         if(pOleStreamData[i].pstrOleObjFileName != NULL)
6875         {
6876                 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
6877                 pOleStreamData[i].pstrOleObjFileName = NULL;
6878         }
6879     }
6880     return hRes;
6881 }
6882
6883 /*************************************************************************
6884  * OleConvertIStorageToOLESTREAM [OLE32.85]
6885  *
6886  * Read info on MSDN
6887  *
6888  * Read info on MSDN
6889  *
6890  * TODO
6891  *      Still unsure of some mem fields for OLE 10 Stream
6892  *      Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
6893  *      and "\001OLE" streams.
6894  *     
6895  */
6896 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
6897     LPSTORAGE pstg, 
6898     LPOLESTREAM pOleStream)
6899 {
6900     int i;
6901     HRESULT hRes = S_OK;
6902     IStream *pStream;
6903     OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
6904     WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
6905
6906
6907     memset(pOleStreamData, 0, sizeof(pOleStreamData));
6908
6909     if(pstg == NULL || pOleStream == NULL)
6910     {
6911         hRes = E_INVALIDARG;
6912     }
6913     if(hRes == S_OK)
6914     {
6915         /* Get the ProgID */
6916         pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
6917         hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
6918     }
6919     if(hRes == S_OK)
6920     {
6921         /*Was it originaly Ole10 */
6922         hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);    
6923         if(hRes == S_OK)
6924         {
6925             IStream_Release(pStream);
6926             /*Get Presentation Data for Ole10Native */
6927             OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
6928         }
6929         else
6930         {
6931             /*Get Presentation Data (OLE20)*/
6932             OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
6933         }
6934
6935         /* Save OLESTREAM */
6936         hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
6937         if(hRes == S_OK)
6938         {
6939             hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
6940         }
6941
6942     }
6943
6944     /* Free allocated memory */
6945     for(i=0; i < 2; i++)
6946     {
6947         if(pOleStreamData[i].pData != NULL)
6948         {
6949             HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
6950         }
6951     }
6952
6953     return hRes;
6954 }