Documentation fixes.
[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  *    StgOpenStorageOnILockBytes    [STORAGE.4]
5561  */
5562 HRESULT WINAPI StgOpenStorageOnILockBytes(
5563       ILockBytes *plkbyt,
5564       IStorage *pstgPriority,
5565       DWORD grfMode,
5566       SNB snbExclude,
5567       DWORD reserved,
5568       IStorage **ppstgOpen)
5569 {
5570   StorageImpl* newStorage = 0;
5571   HRESULT        hr = S_OK;
5572
5573   /*
5574    * Perform a sanity check
5575    */
5576   if ((plkbyt == 0) || (ppstgOpen == 0))
5577     return STG_E_INVALIDPOINTER;
5578
5579   /*
5580    * Validate the STGM flags
5581    */
5582   if ( FAILED( validateSTGM(grfMode) ))
5583     return STG_E_INVALIDFLAG;
5584
5585   /*
5586    * Initialize the "out" parameter.
5587    */
5588   *ppstgOpen = 0;
5589
5590   /*
5591    * Allocate and initialize the new IStorage object.
5592    */
5593   newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5594  
5595   if (newStorage == 0)
5596     return STG_E_INSUFFICIENTMEMORY;
5597
5598   hr = StorageImpl_Construct(
5599          newStorage,
5600          0,
5601          plkbyt,
5602          grfMode,
5603          FALSE,
5604          FALSE);
5605
5606   if (FAILED(hr))
5607   {
5608     HeapFree(GetProcessHeap(), 0, newStorage);
5609     return hr;
5610   }
5611
5612   /*
5613    * Get an "out" pointer for the caller.
5614    */
5615   hr = StorageBaseImpl_QueryInterface(
5616          (IStorage*)newStorage,
5617          (REFIID)&IID_IStorage,
5618          (void**)ppstgOpen);
5619
5620   return hr;
5621 }
5622
5623 /******************************************************************************
5624  *              StgSetTimes [ole32.150]
5625  *              StgSetTimes [OLE32.150]
5626  *              StgSetTimes [STORAGE.7]
5627  *
5628  *
5629  */
5630 HRESULT WINAPI StgSetTimes(WCHAR * str, FILETIME * a, FILETIME * b, FILETIME *c )
5631 {
5632  
5633   FIXME("(%p, %p, %p, %p),stub!\n", str, a, b, c);
5634   return FALSE;
5635 }
5636
5637 /******************************************************************************
5638  *              StgIsStorageILockBytes        [OLE32.147]
5639  *              StgIsStorageILockBytes        [STORAGE.6]
5640  *
5641  * Determines if the ILockBytes contains a storage object.
5642  */
5643 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
5644 {
5645   BYTE sig[8];
5646   ULARGE_INTEGER offset;
5647
5648   offset.s.HighPart = 0;
5649   offset.s.LowPart  = 0;
5650
5651   ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
5652
5653   if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
5654     return S_OK;
5655
5656   return S_FALSE;
5657 }
5658
5659 /******************************************************************************
5660  *              WriteClassStg        [OLE32.158]
5661  *
5662  * This method will store the specified CLSID in the specified storage object
5663  */
5664 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
5665 {
5666   HRESULT hRes;
5667
5668   assert(pStg != 0);
5669
5670   hRes = IStorage_SetClass(pStg, rclsid);
5671
5672   return hRes;
5673 }
5674
5675 /***********************************************************************
5676  *    ReadClassStg (OLE32.134)
5677  *
5678  * This method reads the CLSID previously written to a storage object with the WriteClassStg.
5679  */
5680 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
5681
5682     STATSTG pstatstg;
5683     HRESULT hRes;
5684     
5685     TRACE("()\n");
5686
5687     if(pclsid==NULL)
5688         return E_POINTER;
5689    /*
5690     * read a STATSTG structure (contains the clsid) from the storage
5691     */
5692     hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
5693
5694     if(SUCCEEDED(hRes))
5695         *pclsid=pstatstg.clsid;
5696
5697     return hRes;
5698 }
5699
5700 /***********************************************************************
5701  *    OleLoadFromStream (OLE32.113)
5702  *
5703  * This function loads an object from stream
5704  */
5705 HRESULT  WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
5706 {
5707     CLSID       clsid;
5708     HRESULT     res;
5709     LPPERSISTSTREAM     xstm;
5710
5711     TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
5712
5713     res=ReadClassStm(pStm,&clsid);
5714     if (!SUCCEEDED(res))
5715         return res;
5716     res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
5717     if (!SUCCEEDED(res))
5718         return res;
5719     res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
5720     if (!SUCCEEDED(res)) {
5721         IUnknown_Release((IUnknown*)*ppvObj);
5722         return res;
5723     }
5724     res=IPersistStream_Load(xstm,pStm);
5725     IPersistStream_Release(xstm);
5726     /* FIXME: all refcounts ok at this point? I think they should be:
5727      *          pStm    : unchanged
5728      *          ppvObj  : 1
5729      *          xstm    : 0 (released)
5730      */
5731     return res;
5732 }
5733
5734 /***********************************************************************
5735  *    OleSaveToStream (OLE32.125)
5736  *
5737  * This function saves an object with the IPersistStream interface on it
5738  * to the specified stream.
5739  */
5740 HRESULT  WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
5741 {
5742
5743     CLSID clsid;
5744     HRESULT res;
5745     
5746     TRACE("(%p,%p)\n",pPStm,pStm);
5747
5748     res=IPersistStream_GetClassID(pPStm,&clsid);
5749
5750     if (SUCCEEDED(res)){
5751         
5752         res=WriteClassStm(pStm,&clsid);
5753
5754         if (SUCCEEDED(res))
5755
5756             res=IPersistStream_Save(pPStm,pStm,TRUE);
5757     }
5758
5759     TRACE("Finished Save\n");
5760     return res;
5761 }
5762
5763 /****************************************************************************
5764  * This method validate a STGM parameter that can contain the values below
5765  *
5766  * STGM_DIRECT               0x00000000
5767  * STGM_TRANSACTED           0x00010000
5768  * STGM_SIMPLE               0x08000000
5769  * 
5770  * STGM_READ                 0x00000000
5771  * STGM_WRITE                0x00000001
5772  * STGM_READWRITE            0x00000002
5773  * 
5774  * STGM_SHARE_DENY_NONE      0x00000040
5775  * STGM_SHARE_DENY_READ      0x00000030
5776  * STGM_SHARE_DENY_WRITE     0x00000020
5777  * STGM_SHARE_EXCLUSIVE      0x00000010
5778  * 
5779  * STGM_PRIORITY             0x00040000
5780  * STGM_DELETEONRELEASE      0x04000000
5781  *
5782  * STGM_CREATE               0x00001000
5783  * STGM_CONVERT              0x00020000
5784  * STGM_FAILIFTHERE          0x00000000
5785  *
5786  * STGM_NOSCRATCH            0x00100000
5787  * STGM_NOSNAPSHOT           0x00200000
5788  */
5789 static HRESULT validateSTGM(DWORD stgm)
5790 {
5791   BOOL bSTGM_TRANSACTED       = ((stgm & STGM_TRANSACTED) == STGM_TRANSACTED);
5792   BOOL bSTGM_SIMPLE           = ((stgm & STGM_SIMPLE) == STGM_SIMPLE);
5793   BOOL bSTGM_DIRECT           = ! (bSTGM_TRANSACTED || bSTGM_SIMPLE);
5794    
5795   BOOL bSTGM_WRITE            = ((stgm & STGM_WRITE) == STGM_WRITE);
5796   BOOL bSTGM_READWRITE        = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5797   BOOL bSTGM_READ             = ! (bSTGM_WRITE || bSTGM_READWRITE);
5798    
5799   BOOL bSTGM_SHARE_DENY_NONE  =
5800                      ((stgm & STGM_SHARE_DENY_NONE)  == STGM_SHARE_DENY_NONE);
5801
5802   BOOL bSTGM_SHARE_DENY_READ  =
5803                      ((stgm & STGM_SHARE_DENY_READ)  == STGM_SHARE_DENY_READ);
5804
5805   BOOL bSTGM_SHARE_DENY_WRITE =
5806                      ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5807
5808   BOOL bSTGM_SHARE_EXCLUSIVE  =
5809                      ((stgm & STGM_SHARE_EXCLUSIVE)  == STGM_SHARE_EXCLUSIVE);
5810
5811   BOOL bSTGM_CREATE           = ((stgm & STGM_CREATE) == STGM_CREATE);
5812   BOOL bSTGM_CONVERT          = ((stgm & STGM_CONVERT) == STGM_CONVERT);
5813    
5814   BOOL bSTGM_NOSCRATCH        = ((stgm & STGM_NOSCRATCH) == STGM_NOSCRATCH);
5815   BOOL bSTGM_NOSNAPSHOT       = ((stgm & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT);
5816
5817   /* 
5818    * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
5819    */
5820   if ( ! bSTGM_DIRECT )
5821     if( bSTGM_TRANSACTED && bSTGM_SIMPLE )
5822       return E_FAIL;
5823
5824   /* 
5825    * STGM_WRITE |  STGM_READWRITE | STGM_READ
5826    */
5827   if ( ! bSTGM_READ )
5828     if( bSTGM_WRITE && bSTGM_READWRITE )
5829       return E_FAIL;
5830
5831   /*
5832    * STGM_SHARE_DENY_NONE | others 
5833    * (I assume here that DENY_READ implies DENY_WRITE)
5834    */
5835   if ( bSTGM_SHARE_DENY_NONE )
5836     if ( bSTGM_SHARE_DENY_READ ||
5837          bSTGM_SHARE_DENY_WRITE || 
5838          bSTGM_SHARE_EXCLUSIVE) 
5839       return E_FAIL;
5840
5841   /*
5842    * STGM_CREATE | STGM_CONVERT
5843    * if both are false, STGM_FAILIFTHERE is set to TRUE
5844    */
5845   if ( bSTGM_CREATE && bSTGM_CONVERT )
5846     return E_FAIL;
5847
5848   /*
5849    * STGM_NOSCRATCH requires STGM_TRANSACTED
5850    */
5851   if ( bSTGM_NOSCRATCH && ! bSTGM_TRANSACTED )
5852     return E_FAIL;
5853   
5854   /*
5855    * STGM_NOSNAPSHOT requires STGM_TRANSACTED and 
5856    * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
5857    */
5858   if (bSTGM_NOSNAPSHOT)
5859   {
5860     if ( ! ( bSTGM_TRANSACTED && 
5861            !(bSTGM_SHARE_EXCLUSIVE || bSTGM_SHARE_DENY_WRITE)) )
5862     return E_FAIL;
5863   }
5864
5865   return S_OK;
5866 }
5867
5868 /****************************************************************************
5869  *      GetShareModeFromSTGM
5870  *
5871  * This method will return a share mode flag from a STGM value.
5872  * The STGM value is assumed valid. 
5873  */
5874 static DWORD GetShareModeFromSTGM(DWORD stgm)
5875 {
5876   DWORD dwShareMode = 0;
5877   BOOL bSTGM_SHARE_DENY_NONE  =
5878                      ((stgm & STGM_SHARE_DENY_NONE)  == STGM_SHARE_DENY_NONE);
5879
5880   BOOL bSTGM_SHARE_DENY_READ  =
5881                      ((stgm & STGM_SHARE_DENY_READ)  == STGM_SHARE_DENY_READ);
5882
5883   BOOL bSTGM_SHARE_DENY_WRITE =
5884                      ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5885
5886   BOOL bSTGM_SHARE_EXCLUSIVE  =
5887                      ((stgm & STGM_SHARE_EXCLUSIVE)  == STGM_SHARE_EXCLUSIVE);
5888
5889   if ((bSTGM_SHARE_EXCLUSIVE) || (bSTGM_SHARE_DENY_READ))
5890     dwShareMode = 0;
5891
5892   if (bSTGM_SHARE_DENY_NONE)
5893     dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
5894
5895   if (bSTGM_SHARE_DENY_WRITE)
5896     dwShareMode = FILE_SHARE_READ;
5897
5898   return dwShareMode;
5899 }
5900
5901 /****************************************************************************
5902  *      GetAccessModeFromSTGM
5903  *
5904  * This method will return an access mode flag from a STGM value.
5905  * The STGM value is assumed valid.
5906  */
5907 static DWORD GetAccessModeFromSTGM(DWORD stgm)
5908 {
5909   DWORD dwDesiredAccess = GENERIC_READ;
5910   BOOL bSTGM_WRITE     = ((stgm & STGM_WRITE) == STGM_WRITE);
5911   BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5912   BOOL bSTGM_READ      = ! (bSTGM_WRITE || bSTGM_READWRITE);
5913
5914   if (bSTGM_READ)
5915     dwDesiredAccess = GENERIC_READ;
5916
5917   if (bSTGM_WRITE)
5918     dwDesiredAccess |= GENERIC_WRITE;
5919
5920   if (bSTGM_READWRITE)
5921     dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
5922
5923   return dwDesiredAccess;
5924 }
5925
5926 /****************************************************************************
5927  *      GetCreationModeFromSTGM
5928  *
5929  * This method will return a creation mode flag from a STGM value.
5930  * The STGM value is assumed valid.
5931  */
5932 static DWORD GetCreationModeFromSTGM(DWORD stgm)
5933 {
5934   if ( stgm & STGM_CREATE)
5935     return CREATE_ALWAYS;
5936   if (stgm & STGM_CONVERT) {
5937     FIXME("STGM_CONVERT not implemented!\n");
5938     return CREATE_NEW;
5939   }
5940   /* All other cases */
5941   if (stgm & ~ (STGM_CREATE|STGM_CONVERT))
5942         FIXME("unhandled storage mode : 0x%08lx\n",stgm & ~ (STGM_CREATE|STGM_CONVERT));
5943   return CREATE_NEW;
5944 }
5945
5946
5947 /*************************************************************************
5948  * OLECONVERT_LoadOLE10 [Internal] 
5949  *
5950  * Loads the OLE10 STREAM to memory 
5951  *
5952  * PARAMS
5953  *     pOleStream   [I] The OLESTREAM
5954  *     pData        [I] Data Structure for the OLESTREAM Data
5955  *
5956  * RETURNS
5957  *     Success:  S_OK
5958  *     Failure:  CONVERT10_E_OLESTREAM_GET for invalid Get
5959  *               CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
5960  *
5961  * NOTES
5962  *     This function is used by OleConvertOLESTREAMToIStorage only.
5963  *     
5964  *     Memory allocated for pData must be freed by the caller
5965  */
5966 HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
5967 {
5968         DWORD dwSize;
5969         HRESULT hRes = S_OK;
5970         int nTryCnt=0;
5971         int max_try = 6;
5972
5973         pData->pData = NULL;
5974         pData->pstrOleObjFileName = (CHAR *) NULL;
5975
5976         for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
5977         {
5978         /* Get the OleID */
5979         dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
5980         if(dwSize != sizeof(pData->dwOleID))
5981         {
5982                 hRes = CONVERT10_E_OLESTREAM_GET;
5983         }
5984         else if(pData->dwOleID != OLESTREAM_ID)
5985         {
5986                 hRes = CONVERT10_E_OLESTREAM_FMT;
5987         }
5988                 else
5989                 {
5990                         hRes = S_OK;
5991                         break;
5992                 }
5993         }
5994
5995         if(hRes == S_OK)
5996         {
5997                 /* Get the TypeID...more info needed for this field */
5998                 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
5999                 if(dwSize != sizeof(pData->dwTypeID))
6000                 {
6001                         hRes = CONVERT10_E_OLESTREAM_GET;
6002                 }
6003         }
6004         if(hRes == S_OK)
6005         {
6006                 if(pData->dwTypeID != 0)
6007                 {
6008                         /* Get the lenght of the OleTypeName */
6009                         dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6010                         if(dwSize != sizeof(pData->dwOleTypeNameLength))
6011                         {
6012                                 hRes = CONVERT10_E_OLESTREAM_GET;
6013                         }
6014
6015                         if(hRes == S_OK)
6016                         {
6017                                 if(pData->dwOleTypeNameLength > 0)
6018                                 {
6019                                         /* Get the OleTypeName */
6020                                         dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6021                                         if(dwSize != pData->dwOleTypeNameLength)
6022                                         {
6023                                                 hRes = CONVERT10_E_OLESTREAM_GET;
6024                                         }
6025                                 }
6026                         }
6027                         if(bStrem1)
6028                         {
6029                                 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6030                                 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6031                                 {
6032                                         hRes = CONVERT10_E_OLESTREAM_GET;
6033                                 }
6034                         if(hRes == S_OK)
6035                         {
6036                                         if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6037                                                 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6038                                         pData->pstrOleObjFileName = (CHAR *)malloc(pData->dwOleObjFileNameLength);
6039                                         if(pData->pstrOleObjFileName)
6040                                         {
6041                                                 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6042                                                 if(dwSize != pData->dwOleObjFileNameLength)
6043                                                 {
6044                                                         hRes = CONVERT10_E_OLESTREAM_GET;
6045                                                 }
6046                                         }
6047                                         else
6048                                                 hRes = CONVERT10_E_OLESTREAM_GET;
6049                                 }
6050                         }
6051                         else
6052                         {
6053                                 /* Get the Width of the Metafile */
6054                                 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6055                                 if(dwSize != sizeof(pData->dwMetaFileWidth))
6056                                 {
6057                                         hRes = CONVERT10_E_OLESTREAM_GET;
6058                                 }
6059                         if(hRes == S_OK)
6060                         {
6061                                 /* Get the Height of the Metafile */
6062                                 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6063                                 if(dwSize != sizeof(pData->dwMetaFileHeight))
6064                                 {
6065                                         hRes = CONVERT10_E_OLESTREAM_GET;
6066                                 }
6067                         }
6068                         }
6069                         if(hRes == S_OK)
6070                         {
6071                                 /* Get the Lenght of the Data */
6072                                 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6073                                 if(dwSize != sizeof(pData->dwDataLength))
6074                                 {
6075                                         hRes = CONVERT10_E_OLESTREAM_GET;
6076                                 }
6077                         }
6078
6079                         if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6080                         {
6081                                 if(!bStrem1) /* if it is a second OLE stream data */
6082                                 {
6083                                         pData->dwDataLength -= 8;
6084                                         dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6085                                         if(dwSize != sizeof(pData->strUnknown))
6086                                         {
6087                                                 hRes = CONVERT10_E_OLESTREAM_GET;
6088                                         }
6089                                 }
6090                         }
6091                         if(hRes == S_OK)
6092                         {
6093                                 if(pData->dwDataLength > 0)
6094                                 {
6095                                         pData->pData = (BYTE *)HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6096
6097                                         /* Get Data (ex. IStorage, Metafile, or BMP) */
6098                                         if(pData->pData)
6099                                         {
6100                                                 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6101                                                 if(dwSize != pData->dwDataLength)
6102                                                 {
6103                                                         hRes = CONVERT10_E_OLESTREAM_GET;
6104                                                 }
6105                                         }
6106                                         else
6107                                         {
6108                                                 hRes = CONVERT10_E_OLESTREAM_GET;
6109                                         }
6110                                 }
6111                         }
6112                 }
6113         }
6114         return hRes;
6115 }
6116
6117 /*************************************************************************
6118  * OLECONVERT_SaveOLE10 [Internal] 
6119  *
6120  * Saves the OLE10 STREAM From memory 
6121  *
6122  * PARAMS
6123  *     pData        [I] Data Structure for the OLESTREAM Data
6124  *     pOleStream   [I] The OLESTREAM to save
6125  *
6126  * RETURNS
6127  *     Success:  S_OK
6128  *     Failure:  CONVERT10_E_OLESTREAM_PUT for invalid Put
6129  *
6130  * NOTES
6131  *     This function is used by OleConvertIStorageToOLESTREAM only.
6132  *     
6133  */
6134 HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6135 {
6136     DWORD dwSize;
6137     HRESULT hRes = S_OK;
6138
6139
6140    /* Set the OleID */
6141     dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6142     if(dwSize != sizeof(pData->dwOleID))
6143     {
6144         hRes = CONVERT10_E_OLESTREAM_PUT;
6145     }
6146
6147     if(hRes == S_OK)
6148     {
6149         /* Set the TypeID */
6150         dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6151         if(dwSize != sizeof(pData->dwTypeID))
6152         {
6153             hRes = CONVERT10_E_OLESTREAM_PUT;
6154         }
6155     }
6156
6157     if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6158     {
6159         /* Set the Lenght of the OleTypeName */
6160         dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6161         if(dwSize != sizeof(pData->dwOleTypeNameLength))
6162         {
6163             hRes = CONVERT10_E_OLESTREAM_PUT;
6164         }
6165
6166         if(hRes == S_OK)
6167         {
6168             if(pData->dwOleTypeNameLength > 0)
6169             {
6170                 /* Set the OleTypeName */
6171                 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)  pData->strOleTypeName, pData->dwOleTypeNameLength);
6172                 if(dwSize != pData->dwOleTypeNameLength)
6173                 {
6174                     hRes = CONVERT10_E_OLESTREAM_PUT;
6175                 }
6176             }
6177         }
6178
6179         if(hRes == S_OK)
6180         {
6181             /* Set the width of the Metafile */
6182             dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6183             if(dwSize != sizeof(pData->dwMetaFileWidth))
6184             {
6185                 hRes = CONVERT10_E_OLESTREAM_PUT;
6186             }
6187         }
6188
6189         if(hRes == S_OK)
6190         {
6191             /* Set the height of the Metafile */
6192             dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6193             if(dwSize != sizeof(pData->dwMetaFileHeight))
6194             {
6195                 hRes = CONVERT10_E_OLESTREAM_PUT;
6196             }
6197         }
6198
6199         if(hRes == S_OK)
6200         {
6201             /* Set the lenght of the Data */
6202             dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6203             if(dwSize != sizeof(pData->dwDataLength))
6204             {
6205                 hRes = CONVERT10_E_OLESTREAM_PUT;
6206             }
6207         }
6208
6209         if(hRes == S_OK)
6210         {
6211             if(pData->dwDataLength > 0)
6212             {
6213                 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6214                 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)  pData->pData, pData->dwDataLength);
6215                 if(dwSize != pData->dwDataLength)
6216                 {
6217                     hRes = CONVERT10_E_OLESTREAM_PUT;
6218                 }
6219             }
6220         }
6221     }
6222     return hRes;
6223 }
6224
6225 /*************************************************************************
6226  * OLECONVERT_GetOLE20FromOLE10[Internal] 
6227  *
6228  * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6229  * opens it, and copies the content to the dest IStorage for 
6230  * OleConvertOLESTREAMToIStorage
6231  * 
6232  *
6233  * PARAMS
6234  *     pDestStorage  [I] The IStorage to copy the data to
6235  *     pBuffer       [I] Buffer that contains the IStorage from the OLESTREAM
6236  *     nBufferLength [I] The size of the buffer
6237  *
6238  * RETURNS
6239  *     Nothing
6240  *
6241  * NOTES
6242  *     
6243  *     
6244  */
6245 void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6246 {
6247     HRESULT hRes;
6248     HANDLE hFile;
6249     IStorage *pTempStorage;
6250     DWORD dwNumOfBytesWritten;
6251     WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6252     WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6253
6254     /* Create a temp File */
6255     GetTempPathW(MAX_PATH, wstrTempDir);
6256     GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6257     hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6258
6259     if(hFile != INVALID_HANDLE_VALUE)
6260     {
6261         /* Write IStorage Data to File */
6262         WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6263         CloseHandle(hFile);
6264
6265         /* Open and copy temp storage to the Dest Storage */
6266         hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6267         if(hRes == S_OK)
6268         {
6269             hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6270             StorageBaseImpl_Release(pTempStorage);
6271         }
6272         DeleteFileW(wstrTempFile);
6273     }
6274 }
6275
6276
6277 /*************************************************************************
6278  * OLECONVERT_WriteOLE20ToBuffer [Internal] 
6279  *
6280  * Saves the OLE10 STREAM From memory 
6281  *
6282  * PARAMS
6283  *     pStorage  [I] The Src IStorage to copy
6284  *     pData     [I] The Dest Memory to write to.
6285  *
6286  * RETURNS
6287  *     The size in bytes allocated for pData
6288  *
6289  * NOTES
6290  *     Memory allocated for pData must be freed by the caller
6291  *
6292  *     Used by OleConvertIStorageToOLESTREAM only.
6293  *     
6294  */
6295 DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6296 {
6297     HANDLE hFile;
6298     HRESULT hRes;
6299     DWORD nDataLength = 0;
6300     IStorage *pTempStorage;
6301     WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6302     WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6303
6304     *pData = NULL;
6305     
6306     /* Create temp Storage */
6307     GetTempPathW(MAX_PATH, wstrTempDir);
6308     GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6309     hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6310
6311     if(hRes == S_OK)
6312     {
6313         /* Copy Src Storage to the Temp Storage */
6314         StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6315         StorageBaseImpl_Release(pTempStorage);
6316
6317         /* Open Temp Storage as a file and copy to memory */
6318         hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6319         if(hFile != INVALID_HANDLE_VALUE)
6320         {
6321             nDataLength = GetFileSize(hFile, NULL);
6322             *pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,nDataLength);
6323             ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6324             CloseHandle(hFile);
6325         }
6326         DeleteFileW(wstrTempFile);
6327     }
6328     return nDataLength;
6329 }
6330
6331 /*************************************************************************
6332  * OLECONVERT_CreateOleStream [Internal] 
6333  *
6334  * Creates the "\001OLE" stream in the IStorage if neccessary.
6335  *
6336  * PARAMS
6337  *     pStorage     [I] Dest storage to create the stream in
6338  *
6339  * RETURNS
6340  *     Nothing
6341  *
6342  * NOTES
6343  *     This function is used by OleConvertOLESTREAMToIStorage only.
6344  *
6345  *     This stream is still unknown, MS Word seems to have extra data
6346  *     but since the data is stored in the OLESTREAM there should be
6347  *     no need to recreate the stream.  If the stream is manually 
6348  *     deleted it will create it with this default data.
6349  *     
6350  */
6351 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6352 {
6353     HRESULT hRes;
6354     IStream *pStream;
6355     WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6356     BYTE pOleStreamHeader [] = 
6357     {
6358         0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00, 
6359         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 
6360         0x00, 0x00, 0x00, 0x00 
6361     };
6362     
6363     /* Create stream if not present */
6364     hRes = IStorage_CreateStream(pStorage, wstrStreamName, 
6365         STGM_WRITE  | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6366
6367     if(hRes == S_OK)
6368     {
6369         /* Write default Data */
6370         hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6371         IStream_Release(pStream);
6372     }
6373 }
6374
6375
6376 /*************************************************************************
6377  * OLECONVERT_CreateCompObjStream [Internal] 
6378  *
6379  * Creates a "\001CompObj" is the destination IStorage if necessary.
6380  *
6381  * PARAMS
6382  *     pStorage       [I] The dest IStorage to create the CompObj Stream 
6383  *                        if necessary.
6384  *     strOleTypeName [I] The ProgID
6385  *
6386  * RETURNS
6387  *     Success:  S_OK
6388  *     Failure:  REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6389  *
6390  * NOTES
6391  *     This function is used by OleConvertOLESTREAMToIStorage only.
6392  *
6393  *     The stream data is stored in the OLESTREAM and there should be
6394  *     no need to recreate the stream.  If the stream is manually 
6395  *     deleted it will attempt to create it by querying the registry.
6396  *
6397  *     
6398  */
6399 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
6400 {
6401     IStream *pStream;
6402     HRESULT hStorageRes, hRes = S_OK;
6403     OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
6404     WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6405
6406     BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
6407     BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
6408
6409     /* Initialize the CompObj structure */
6410     memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
6411     memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
6412     memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
6413
6414
6415     /*  Create a CompObj stream if it doesn't exist */
6416     hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName, 
6417         STGM_WRITE  | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6418     if(hStorageRes == S_OK)
6419     {
6420         /* copy the OleTypeName to the compobj struct */
6421         IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
6422         strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
6423
6424         /* copy the OleTypeName to the compobj struct */
6425         /* Note: in the test made, these were Identical      */
6426         IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
6427         strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
6428
6429         /* Get the CLSID */
6430         hRes = CLSIDFromProgID16(IStorageCompObj.strProgIDName, &(IStorageCompObj.clsid));
6431
6432         if(hRes == S_OK)
6433         {
6434             HKEY hKey;
6435             LONG hErr;
6436             /* Get the CLSID Default Name from the Registry */
6437             hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey); 
6438             if(hErr == ERROR_SUCCESS)
6439             {
6440                 char strTemp[OLESTREAM_MAX_STR_LEN];
6441                 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
6442                 hErr = RegQueryValueA(hKey, NULL, strTemp, &(IStorageCompObj.dwCLSIDNameLength));
6443                 if(hErr == ERROR_SUCCESS)
6444                 {
6445                     strcpy(IStorageCompObj.strCLSIDName, strTemp);
6446                 }
6447                 RegCloseKey(hKey);
6448             }
6449         }
6450
6451         /* Write CompObj Structure to stream */
6452         hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
6453
6454         WriteClassStm(pStream,&(IStorageCompObj.clsid));
6455
6456         hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
6457         if(IStorageCompObj.dwCLSIDNameLength > 0)
6458         {
6459             hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
6460         }
6461         hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
6462         if(IStorageCompObj.dwOleTypeNameLength > 0)
6463         {
6464             hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
6465         }
6466         hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
6467         if(IStorageCompObj.dwProgIDNameLength > 0)
6468         {
6469             hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
6470         }
6471         hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
6472         IStream_Release(pStream);
6473     }
6474     return hRes;
6475 }
6476
6477
6478 /*************************************************************************
6479  * OLECONVERT_CreateOlePresStream[Internal] 
6480  *
6481  * Creates the "\002OlePres000" Stream with the Metafile data
6482  *
6483  * PARAMS
6484  *     pStorage     [I] The dest IStorage to create \002OLEPres000 stream in.
6485  *     dwExtentX    [I] Width of the Metafile
6486  *     dwExtentY    [I] Height of the Metafile 
6487  *     pData        [I] Metafile data
6488  *     dwDataLength [I] Size of the Metafile data
6489  *
6490  * RETURNS
6491  *     Success:  S_OK
6492  *     Failure:  CONVERT10_E_OLESTREAM_PUT for invalid Put
6493  *
6494  * NOTES
6495  *     This function is used by OleConvertOLESTREAMToIStorage only.
6496  *     
6497  */
6498 void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
6499 {
6500     HRESULT hRes;
6501     IStream *pStream;
6502     WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
6503     BYTE pOlePresStreamHeader [] = 
6504     {
6505         0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00, 
6506         0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 
6507         0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6508         0x00, 0x00, 0x00, 0x00
6509     };
6510
6511     BYTE pOlePresStreamHeaderEmpty [] = 
6512     {
6513         0x00, 0x00, 0x00, 0x00, 
6514         0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00, 
6515         0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6516         0x00, 0x00, 0x00, 0x00
6517     };
6518      
6519     /* Create the OlePres000 Stream */
6520     hRes = IStorage_CreateStream(pStorage, wstrStreamName, 
6521         STGM_CREATE | STGM_WRITE  | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6522
6523     if(hRes == S_OK)
6524     {
6525         DWORD nHeaderSize;
6526         OLECONVERT_ISTORAGE_OLEPRES OlePres;
6527
6528         memset(&OlePres, 0, sizeof(OlePres));
6529         /* Do we have any metafile data to save */
6530         if(dwDataLength > 0)
6531         {
6532             memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
6533             nHeaderSize = sizeof(pOlePresStreamHeader);
6534         }
6535         else
6536         {
6537             memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
6538             nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
6539         }
6540         /* Set width and height of the metafile */
6541         OlePres.dwExtentX = dwExtentX;
6542         OlePres.dwExtentY = -dwExtentY;
6543
6544         /* Set Data and Lenght */
6545         if(dwDataLength > sizeof(METAFILEPICT16))
6546         {
6547             OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
6548             OlePres.pData = &(pData[8]);
6549         }
6550         /* Save OlePres000 Data to Stream */
6551         hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
6552         hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
6553         hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
6554         hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
6555         if(OlePres.dwSize > 0)
6556         {
6557             hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
6558         }
6559         IStream_Release(pStream);
6560     }
6561 }
6562
6563 /*************************************************************************
6564  * OLECONVERT_CreateOle10NativeStream [Internal] 
6565  *
6566  * Creates the "\001Ole10Native" Stream (should contain a BMP)
6567  *
6568  * PARAMS
6569  *     pStorage     [I] Dest storage to create the stream in
6570  *     pData        [I] Ole10 Native Data (ex. bmp)
6571  *     dwDataLength [I] Size of the Ole10 Native Data
6572  *
6573  * RETURNS
6574  *     Nothing
6575  *
6576  * NOTES
6577  *     This function is used by OleConvertOLESTREAMToIStorage only.
6578  *
6579  *     Might need to verify the data and return appropriate error message
6580  *     
6581  */
6582 void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
6583 {
6584     HRESULT hRes;
6585     IStream *pStream;
6586     WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
6587     
6588     /* Create the Ole10Native Stream */
6589     hRes = IStorage_CreateStream(pStorage, wstrStreamName, 
6590         STGM_CREATE | STGM_WRITE  | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6591
6592     if(hRes == S_OK)
6593     {
6594         /* Write info to stream */
6595         hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
6596         hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
6597         IStream_Release(pStream);
6598     }
6599
6600 }
6601
6602 /*************************************************************************
6603  * OLECONVERT_GetOLE10ProgID [Internal] 
6604  *
6605  * Finds the ProgID (or OleTypeID) from the IStorage
6606  *
6607  * PARAMS
6608  *     pStorage        [I] The Src IStorage to get the ProgID
6609  *     strProgID       [I] the ProgID string to get
6610  *     dwSize          [I] the size of the string
6611  *
6612  * RETURNS
6613  *     Success:  S_OK
6614  *     Failure:  REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6615  *
6616  * NOTES
6617  *     This function is used by OleConvertIStorageToOLESTREAM only.
6618  *
6619  *     
6620  */
6621 HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
6622 {
6623     HRESULT hRes;
6624     IStream *pStream;
6625     LARGE_INTEGER iSeekPos;
6626     OLECONVERT_ISTORAGE_COMPOBJ CompObj;
6627     WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6628
6629     /* Open the CompObj Stream */
6630     hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,  
6631         STGM_READ  | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6632     if(hRes == S_OK)
6633     {
6634
6635         /*Get the OleType from the CompObj Stream */
6636         iSeekPos.s.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
6637         iSeekPos.s.HighPart = 0;
6638
6639         IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
6640         IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
6641         iSeekPos.s.LowPart = CompObj.dwCLSIDNameLength;
6642         IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
6643         IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
6644         iSeekPos.s.LowPart = CompObj.dwOleTypeNameLength;
6645         IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
6646
6647         IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
6648         if(*dwSize > 0)
6649         {
6650             IStream_Read(pStream, strProgID, *dwSize, NULL);
6651         }
6652         IStream_Release(pStream);
6653     }
6654     else
6655     {
6656         STATSTG stat;
6657         LPOLESTR wstrProgID;
6658
6659         /* Get the OleType from the registry */
6660         REFCLSID clsid = &(stat.clsid);
6661         IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
6662         hRes = ProgIDFromCLSID(clsid, &wstrProgID);
6663         if(hRes == S_OK)
6664         {
6665             *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
6666         }
6667  
6668     }
6669     return hRes;
6670 }
6671
6672 /*************************************************************************
6673  * OLECONVERT_GetOle10PresData [Internal] 
6674  *
6675  * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
6676  *
6677  * PARAMS
6678  *     pStorage     [I] Src IStroage
6679  *     pOleStream   [I] Dest OleStream Mem Struct
6680  *
6681  * RETURNS
6682  *     Nothing
6683  *
6684  * NOTES
6685  *     This function is used by OleConvertIStorageToOLESTREAM only.
6686  *
6687  *     Memory allocated for pData must be freed by the caller
6688  *      
6689  *     
6690  */
6691 void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
6692 {
6693
6694     HRESULT hRes;
6695     IStream *pStream;
6696     WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
6697
6698     /* Initialize Default data for OLESTREAM */
6699     pOleStreamData[0].dwOleID = OLESTREAM_ID;
6700     pOleStreamData[0].dwTypeID = 2;
6701     pOleStreamData[1].dwOleID = OLESTREAM_ID;
6702     pOleStreamData[1].dwTypeID = 0;
6703     pOleStreamData[0].dwMetaFileWidth = 0;
6704     pOleStreamData[0].dwMetaFileHeight = 0;
6705     pOleStreamData[0].pData = NULL;
6706     pOleStreamData[1].pData = NULL;
6707
6708     /* Open Ole10Native Stream */
6709     hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,  
6710         STGM_READ  | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6711     if(hRes == S_OK)
6712     {
6713
6714         /* Read Size and Data */
6715         IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
6716         if(pOleStreamData->dwDataLength > 0)
6717         {
6718             pOleStreamData->pData = (LPSTR) HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
6719             IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
6720         }
6721         IStream_Release(pStream);
6722     }
6723
6724 }
6725
6726
6727 /*************************************************************************
6728  * OLECONVERT_GetOle20PresData[Internal] 
6729  *
6730  * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
6731  *
6732  * PARAMS
6733  *     pStorage         [I] Src IStroage
6734  *     pOleStreamData   [I] Dest OleStream Mem Struct
6735  *
6736  * RETURNS
6737  *     Nothing
6738  *
6739  * NOTES
6740  *     This function is used by OleConvertIStorageToOLESTREAM only.
6741  *     
6742  *     Memory allocated for pData must be freed by the caller
6743  */
6744 void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
6745 {
6746     HRESULT hRes;
6747     IStream *pStream;
6748     OLECONVERT_ISTORAGE_OLEPRES olePress;
6749     WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
6750
6751     /* Initialize Default data for OLESTREAM */
6752     pOleStreamData[0].dwOleID = OLESTREAM_ID;
6753     pOleStreamData[0].dwTypeID = 2;
6754     pOleStreamData[0].dwMetaFileWidth = 0;
6755     pOleStreamData[0].dwMetaFileHeight = 0;
6756     pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
6757     pOleStreamData[1].dwOleID = OLESTREAM_ID;
6758     pOleStreamData[1].dwTypeID = 0;
6759     pOleStreamData[1].dwOleTypeNameLength = 0;
6760     pOleStreamData[1].strOleTypeName[0] = 0;
6761     pOleStreamData[1].dwMetaFileWidth = 0;
6762     pOleStreamData[1].dwMetaFileHeight = 0;
6763     pOleStreamData[1].pData = NULL;
6764     pOleStreamData[1].dwDataLength = 0;
6765
6766
6767     /* Open OlePress000 stream */
6768     hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,  
6769         STGM_READ  | STGM_SHARE_EXCLUSIVE, 0, &pStream );
6770     if(hRes == S_OK)
6771     {
6772         LARGE_INTEGER iSeekPos;
6773         METAFILEPICT16 MetaFilePict;
6774         char strMetafilePictName[] = "METAFILEPICT";
6775
6776         /* Set the TypeID for a Metafile */
6777         pOleStreamData[1].dwTypeID = 5;
6778
6779         /* Set the OleTypeName to Metafile */
6780         pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
6781         strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
6782
6783         iSeekPos.s.HighPart = 0;
6784         iSeekPos.s.LowPart = sizeof(olePress.byUnknown1);
6785
6786         /* Get Presentation Data */
6787         IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
6788         IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
6789         IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
6790         IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
6791
6792         /*Set width and Height */
6793         pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
6794         pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
6795         if(olePress.dwSize > 0)
6796         {
6797             /* Set Length */
6798             pOleStreamData[1].dwDataLength  = olePress.dwSize + sizeof(METAFILEPICT16);
6799
6800             /* Set MetaFilePict struct */
6801             MetaFilePict.mm = 8;
6802             MetaFilePict.xExt = olePress.dwExtentX;
6803             MetaFilePict.yExt = olePress.dwExtentY;
6804             MetaFilePict.hMF = 0;
6805
6806             /* Get Metafile Data */
6807             pOleStreamData[1].pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
6808             memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
6809             IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
6810         }
6811         IStream_Release(pStream);
6812     }
6813 }
6814
6815 /*************************************************************************
6816  * OleConvertOLESTREAMToIStorage [OLE32.87] 
6817  *
6818  * Read info on MSDN
6819  *
6820  * TODO
6821  *      DVTARGETDEVICE paramenter is not handled
6822  *      Still unsure of some mem fields for OLE 10 Stream
6823  *      Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
6824  *      and "\001OLE" streams
6825  *     
6826  */
6827 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
6828     LPOLESTREAM pOleStream, 
6829     LPSTORAGE pstg, 
6830     const DVTARGETDEVICE* ptd)
6831 {
6832     int i;
6833     HRESULT hRes=S_OK;
6834     OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
6835
6836     memset(pOleStreamData, 0, sizeof(pOleStreamData));
6837
6838     if(ptd != NULL)
6839     {
6840         FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
6841     }
6842
6843     if(pstg == NULL || pOleStream == NULL)
6844     {
6845         hRes = E_INVALIDARG;
6846     }
6847
6848     if(hRes == S_OK)
6849     {
6850         /* Load the OLESTREAM to Memory */
6851         hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
6852     }
6853
6854     if(hRes == S_OK)
6855     {
6856         /* Load the OLESTREAM to Memory (part 2)*/
6857         hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
6858     }
6859
6860     if(hRes == S_OK)
6861     {
6862
6863         if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
6864         {
6865             /* Do we have the IStorage Data in the OLESTREAM */
6866             if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
6867             {
6868                 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
6869                 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
6870             }
6871             else
6872             {
6873                 /* It must be an original OLE 1.0 source */
6874                 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
6875             }
6876         }
6877         else
6878         {
6879             /* It must be an original OLE 1.0 source */
6880             OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
6881         }
6882
6883         /* Create CompObj Stream if necessary */
6884         hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
6885         if(hRes == S_OK)
6886         {
6887             /*Create the Ole Stream if necessary */
6888             OLECONVERT_CreateOleStream(pstg);
6889         }
6890     }
6891
6892
6893     /* Free allocated memory */
6894     for(i=0; i < 2; i++)
6895     {
6896         if(pOleStreamData[i].pData != NULL)
6897         {
6898             HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
6899         }
6900         if(pOleStreamData[i].pstrOleObjFileName != NULL)
6901         {
6902                 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
6903                 pOleStreamData[i].pstrOleObjFileName = NULL;
6904         }
6905     }
6906     return hRes;
6907 }
6908
6909 /*************************************************************************
6910  * OleConvertIStorageToOLESTREAM [OLE32.85]
6911  *
6912  * Read info on MSDN
6913  *
6914  * Read info on MSDN
6915  *
6916  * TODO
6917  *      Still unsure of some mem fields for OLE 10 Stream
6918  *      Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
6919  *      and "\001OLE" streams.
6920  *     
6921  */
6922 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
6923     LPSTORAGE pstg, 
6924     LPOLESTREAM pOleStream)
6925 {
6926     int i;
6927     HRESULT hRes = S_OK;
6928     IStream *pStream;
6929     OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
6930     WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
6931
6932
6933     memset(pOleStreamData, 0, sizeof(pOleStreamData));
6934
6935     if(pstg == NULL || pOleStream == NULL)
6936     {
6937         hRes = E_INVALIDARG;
6938     }
6939     if(hRes == S_OK)
6940     {
6941         /* Get the ProgID */
6942         pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
6943         hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
6944     }
6945     if(hRes == S_OK)
6946     {
6947         /*Was it originaly Ole10 */
6948         hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);    
6949         if(hRes == S_OK)
6950         {
6951             IStream_Release(pStream);
6952             /*Get Presentation Data for Ole10Native */
6953             OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
6954         }
6955         else
6956         {
6957             /*Get Presentation Data (OLE20)*/
6958             OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
6959         }
6960
6961         /* Save OLESTREAM */
6962         hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
6963         if(hRes == S_OK)
6964         {
6965             hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
6966         }
6967
6968     }
6969
6970     /* Free allocated memory */
6971     for(i=0; i < 2; i++)
6972     {
6973         if(pOleStreamData[i].pData != NULL)
6974         {
6975             HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
6976         }
6977     }
6978
6979     return hRes;
6980 }
6981
6982 /***********************************************************************
6983  *              GetConvertStg (OLE32.68)
6984  */
6985 HRESULT WINAPI GetConvertStg(LPGUID guid) {
6986     FIXME("(%s), unimplemented stub!\n",debugstr_guid(guid));
6987     return E_FAIL;
6988 }