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