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