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