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