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