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