Include stdio.h for MinGW.
[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 <stdarg.h>
29 #include <stdio.h>
30 #include <stdlib.h>
31 #include <string.h>
32
33 #define COBJMACROS
34 #define NONAMELESSUNION
35 #define NONAMELESSSTRUCT
36
37 #include "windef.h"
38 #include "winbase.h"
39 #include "winnls.h"
40 #include "winuser.h"
41 #include "wine/unicode.h"
42 #include "wine/debug.h"
43
44 #include "storage32.h"
45 #include "ole2.h"      /* For Write/ReadClassStm */
46
47 #include "winreg.h"
48 #include "wine/wingdi16.h"
49
50 WINE_DEFAULT_DEBUG_CHANNEL(storage);
51
52 #define FILE_BEGIN 0
53
54
55 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
56 #define OLESTREAM_ID 0x501
57 #define OLESTREAM_MAX_STR_LEN 255
58
59 static const char rootPropertyName[] = "Root Entry";
60
61
62 /* OLESTREAM memory structure to use for Get and Put Routines */
63 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
64 typedef struct
65 {
66     DWORD dwOleID;
67     DWORD dwTypeID;
68     DWORD dwOleTypeNameLength;
69     CHAR  strOleTypeName[OLESTREAM_MAX_STR_LEN];
70     CHAR  *pstrOleObjFileName;
71     DWORD dwOleObjFileNameLength;
72     DWORD dwMetaFileWidth;
73     DWORD dwMetaFileHeight;
74     CHAR  strUnknown[8]; /* don't know what is this 8 byts information in OLE stream. */
75     DWORD dwDataLength;
76     BYTE *pData;
77 }OLECONVERT_OLESTREAM_DATA;
78
79 /* CompObj Stream structure */
80 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
81 typedef struct
82 {
83     BYTE byUnknown1[12];
84     CLSID clsid;
85     DWORD dwCLSIDNameLength;
86     CHAR strCLSIDName[OLESTREAM_MAX_STR_LEN];
87     DWORD dwOleTypeNameLength;
88     CHAR strOleTypeName[OLESTREAM_MAX_STR_LEN];
89     DWORD dwProgIDNameLength;
90     CHAR strProgIDName[OLESTREAM_MAX_STR_LEN];
91     BYTE byUnknown2[16];
92 }OLECONVERT_ISTORAGE_COMPOBJ;
93
94
95 /* Ole Presention Stream structure */
96 /* Used for OleConvertIStorageToOLESTREAM and OleConvertOLESTREAMToIStorage */
97 typedef struct
98 {
99     BYTE byUnknown1[28];
100     DWORD dwExtentX;
101     DWORD dwExtentY;
102     DWORD dwSize;
103     BYTE *pData;
104 }OLECONVERT_ISTORAGE_OLEPRES;
105
106
107
108 /***********************************************************************
109  * Forward declaration of internal functions used by the method DestroyElement
110  */
111 static HRESULT deleteStorageProperty(
112   StorageImpl *parentStorage,
113   ULONG        foundPropertyIndexToDelete,
114   StgProperty  propertyToDelete);
115
116 static HRESULT deleteStreamProperty(
117   StorageImpl *parentStorage,
118   ULONG         foundPropertyIndexToDelete,
119   StgProperty   propertyToDelete);
120
121 static HRESULT findPlaceholder(
122   StorageImpl *storage,
123   ULONG         propertyIndexToStore,
124   ULONG         storagePropertyIndex,
125   INT         typeOfRelation);
126
127 static HRESULT adjustPropertyChain(
128   StorageImpl *This,
129   StgProperty   propertyToDelete,
130   StgProperty   parentProperty,
131   ULONG         parentPropertyId,
132   INT         typeOfRelation);
133
134 /***********************************************************************
135  * Declaration of the functions used to manipulate StgProperty
136  */
137
138 static ULONG getFreeProperty(
139   StorageImpl *storage);
140
141 static void updatePropertyChain(
142   StorageImpl *storage,
143   ULONG       newPropertyIndex,
144   StgProperty newProperty);
145
146 static LONG propertyNameCmp(
147   OLECHAR *newProperty,
148   OLECHAR *currentProperty);
149
150
151 /***********************************************************************
152  * Declaration of miscellaneous functions...
153  */
154 static HRESULT validateSTGM(DWORD stgmValue);
155
156 static DWORD GetShareModeFromSTGM(DWORD stgm);
157 static DWORD GetAccessModeFromSTGM(DWORD stgm);
158 static DWORD GetCreationModeFromSTGM(DWORD stgm);
159
160 /*
161  * Virtual function table for the IStorage32Impl class.
162  */
163 static IStorageVtbl Storage32Impl_Vtbl =
164 {
165     StorageBaseImpl_QueryInterface,
166     StorageBaseImpl_AddRef,
167     StorageBaseImpl_Release,
168     StorageBaseImpl_CreateStream,
169     StorageBaseImpl_OpenStream,
170     StorageImpl_CreateStorage,
171     StorageBaseImpl_OpenStorage,
172     StorageImpl_CopyTo,
173     StorageImpl_MoveElementTo,
174     StorageImpl_Commit,
175     StorageImpl_Revert,
176     StorageBaseImpl_EnumElements,
177     StorageImpl_DestroyElement,
178     StorageBaseImpl_RenameElement,
179     StorageImpl_SetElementTimes,
180     StorageBaseImpl_SetClass,
181     StorageImpl_SetStateBits,
182     StorageImpl_Stat
183 };
184
185 /*
186  * Virtual function table for the Storage32InternalImpl class.
187  */
188 static IStorageVtbl Storage32InternalImpl_Vtbl =
189   {
190     StorageBaseImpl_QueryInterface,
191     StorageBaseImpl_AddRef,
192     StorageBaseImpl_Release,
193     StorageBaseImpl_CreateStream,
194     StorageBaseImpl_OpenStream,
195     StorageImpl_CreateStorage,
196     StorageBaseImpl_OpenStorage,
197     StorageImpl_CopyTo,
198     StorageImpl_MoveElementTo,
199     StorageInternalImpl_Commit,
200     StorageInternalImpl_Revert,
201     StorageBaseImpl_EnumElements,
202     StorageImpl_DestroyElement,
203     StorageBaseImpl_RenameElement,
204     StorageImpl_SetElementTimes,
205     StorageBaseImpl_SetClass,
206     StorageImpl_SetStateBits,
207     StorageBaseImpl_Stat
208 };
209
210 /*
211  * Virtual function table for the IEnumSTATSTGImpl class.
212  */
213 static IEnumSTATSTGVtbl IEnumSTATSTGImpl_Vtbl =
214 {
215     IEnumSTATSTGImpl_QueryInterface,
216     IEnumSTATSTGImpl_AddRef,
217     IEnumSTATSTGImpl_Release,
218     IEnumSTATSTGImpl_Next,
219     IEnumSTATSTGImpl_Skip,
220     IEnumSTATSTGImpl_Reset,
221     IEnumSTATSTGImpl_Clone
222 };
223
224
225
226
227
228 /************************************************************************
229 ** Storage32BaseImpl implementatiion
230 */
231
232 /************************************************************************
233  * Storage32BaseImpl_QueryInterface (IUnknown)
234  *
235  * This method implements the common QueryInterface for all IStorage32
236  * implementations contained in this file.
237  *
238  * See Windows documentation for more details on IUnknown methods.
239  */
240 HRESULT WINAPI StorageBaseImpl_QueryInterface(
241   IStorage*        iface,
242   REFIID             riid,
243   void**             ppvObject)
244 {
245   StorageBaseImpl *This = (StorageBaseImpl *)iface;
246   /*
247    * Perform a sanity check on the parameters.
248    */
249   if ( (This==0) || (ppvObject==0) )
250     return E_INVALIDARG;
251
252   /*
253    * Initialize the return parameter.
254    */
255   *ppvObject = 0;
256
257   /*
258    * Compare the riid with the interface IDs implemented by this object.
259    */
260   if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
261   {
262     *ppvObject = (IStorage*)This;
263   }
264   else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStorage)) == 0)
265   {
266     *ppvObject = (IStorage*)This;
267   }
268
269   /*
270    * Check that we obtained an interface.
271    */
272   if ((*ppvObject)==0)
273     return E_NOINTERFACE;
274
275   /*
276    * Query Interface always increases the reference count by one when it is
277    * successful
278    */
279   StorageBaseImpl_AddRef(iface);
280
281   return S_OK;
282 }
283
284 /************************************************************************
285  * Storage32BaseImpl_AddRef (IUnknown)
286  *
287  * This method implements the common AddRef for all IStorage32
288  * implementations contained in this file.
289  *
290  * See Windows documentation for more details on IUnknown methods.
291  */
292 ULONG WINAPI StorageBaseImpl_AddRef(
293             IStorage* iface)
294 {
295   StorageBaseImpl *This = (StorageBaseImpl *)iface;
296   return InterlockedIncrement(&This->ref);
297 }
298
299 /************************************************************************
300  * Storage32BaseImpl_Release (IUnknown)
301  *
302  * This method implements the common Release for all IStorage32
303  * implementations contained in this file.
304  *
305  * See Windows documentation for more details on IUnknown methods.
306  */
307 ULONG WINAPI StorageBaseImpl_Release(
308       IStorage* iface)
309 {
310   StorageBaseImpl *This = (StorageBaseImpl *)iface;
311   /*
312    * Decrease the reference count on this object.
313    */
314   ULONG ref = InterlockedDecrement(&This->ref);
315
316   /*
317    * If the reference count goes down to 0, perform suicide.
318    */
319   if (ref == 0)
320   {
321     /*
322      * Since we are using a system of base-classes, we want to call the
323      * destructor of the appropriate derived class. To do this, we are
324      * using virtual functions to implement the destructor.
325      */
326     This->v_destructor(This);
327   }
328
329   return 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   StorageBaseImpl *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   StorageBaseImpl *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   StorageBaseImpl *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   StorageBaseImpl *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.u.LowPart, pstatstg->cbSize.u.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   StorageBaseImpl *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.u.LowPart  = currentProperty.size.u.LowPart;
761     renamedProperty.size.u.HighPart = currentProperty.size.u.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   StorageBaseImpl *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.u.LowPart  = 0;
935   newStreamProperty.size.u.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   StorageBaseImpl *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.u.LowPart  = 0;
1120   newProperty.size.u.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.u.HighPart = 0;
1242     newSize.u.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 length
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.u.HighPart = 0;
1871   size.u.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   This->lpVtbl = &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.u.HighPart = 0;
2279     size.u.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   if(!(This->rootBlockChain =
2320        BlockChainStream_Construct(This, &This->rootStartBlock, PROPERTY_NULL)))
2321     return STG_E_READFAULT;
2322
2323   if(!(This->smallBlockDepotChain =
2324        BlockChainStream_Construct(This, &This->smallBlockDepotStart,
2325                                   PROPERTY_NULL)))
2326     return STG_E_READFAULT;
2327
2328   /*
2329    * Write the root property
2330    */
2331   if (fileCreate)
2332   {
2333     StgProperty rootProp;
2334     /*
2335      * Initialize the property chain
2336      */
2337     memset(&rootProp, 0, sizeof(rootProp));
2338     MultiByteToWideChar( CP_ACP, 0, rootPropertyName, -1, rootProp.name,
2339                          sizeof(rootProp.name)/sizeof(WCHAR) );
2340     rootProp.sizeOfNameString = (strlenW(rootProp.name)+1) * sizeof(WCHAR);
2341     rootProp.propertyType     = PROPTYPE_ROOT;
2342     rootProp.previousProperty = PROPERTY_NULL;
2343     rootProp.nextProperty     = PROPERTY_NULL;
2344     rootProp.dirProperty      = PROPERTY_NULL;
2345     rootProp.startingBlock    = BLOCK_END_OF_CHAIN;
2346     rootProp.size.u.HighPart    = 0;
2347     rootProp.size.u.LowPart     = 0;
2348
2349     StorageImpl_WriteProperty(This, 0, &rootProp);
2350   }
2351
2352   /*
2353    * Find the ID of the root in the property sets.
2354    */
2355   currentPropertyIndex = 0;
2356
2357   do
2358   {
2359     readSuccessful = StorageImpl_ReadProperty(
2360                       This,
2361                       currentPropertyIndex,
2362                       &currentProperty);
2363
2364     if (readSuccessful)
2365     {
2366       if ( (currentProperty.sizeOfNameString != 0 ) &&
2367            (currentProperty.propertyType     == PROPTYPE_ROOT) )
2368       {
2369         This->rootPropertySetIndex = currentPropertyIndex;
2370       }
2371     }
2372
2373     currentPropertyIndex++;
2374
2375   } while (readSuccessful && (This->rootPropertySetIndex == PROPERTY_NULL) );
2376
2377   if (!readSuccessful)
2378   {
2379     /* TODO CLEANUP */
2380     return STG_E_READFAULT;
2381   }
2382
2383   /*
2384    * Create the block chain abstraction for the small block root chain.
2385    */
2386   if(!(This->smallBlockRootChain =
2387        BlockChainStream_Construct(This, NULL, This->rootPropertySetIndex)))
2388     return STG_E_READFAULT;
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     unsigned 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  *          nextBlockIndex - receives the return value.
2730  *
2731  * Returns: This method returns the index of the next block in the chain.
2732  *          It will return the constants:
2733  *              BLOCK_SPECIAL - If the block given was not part of a
2734  *                              chain.
2735  *              BLOCK_END_OF_CHAIN - If the block given was the last in
2736  *                                   a chain.
2737  *              BLOCK_UNUSED - If the block given was not past of a chain
2738  *                             and is available.
2739  *              BLOCK_EXTBBDEPOT - This block is part of the extended
2740  *                                 big block depot.
2741  *
2742  * See Windows documentation for more details on IStorage methods.
2743  */
2744 HRESULT StorageImpl_GetNextBlockInChain(
2745   StorageImpl* This,
2746   ULONG        blockIndex,
2747   ULONG*       nextBlockIndex)
2748 {
2749   ULONG offsetInDepot    = blockIndex * sizeof (ULONG);
2750   ULONG depotBlockCount  = offsetInDepot / This->bigBlockSize;
2751   ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2752   void* depotBuffer;
2753   ULONG depotBlockIndexPos;
2754   int index;
2755
2756   *nextBlockIndex   = BLOCK_SPECIAL;
2757
2758   if(depotBlockCount >= This->bigBlockDepotCount)
2759   {
2760     WARN("depotBlockCount %ld, bigBlockDepotCount %ld\n", depotBlockCount,
2761          This->bigBlockDepotCount);
2762     return STG_E_READFAULT;
2763   }
2764
2765   /*
2766    * Cache the currently accessed depot block.
2767    */
2768   if (depotBlockCount != This->indexBlockDepotCached)
2769   {
2770     This->indexBlockDepotCached = depotBlockCount;
2771
2772     if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2773     {
2774       depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2775     }
2776     else
2777     {
2778       /*
2779        * We have to look in the extended depot.
2780        */
2781       depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2782     }
2783
2784     depotBuffer = StorageImpl_GetROBigBlock(This, depotBlockIndexPos);
2785
2786     if (!depotBuffer)
2787       return STG_E_READFAULT;
2788
2789     for (index = 0; index < NUM_BLOCKS_PER_DEPOT_BLOCK; index++)
2790     {
2791       StorageUtl_ReadDWord(depotBuffer, index*sizeof(ULONG), nextBlockIndex);
2792       This->blockDepotCached[index] = *nextBlockIndex;
2793     }
2794     StorageImpl_ReleaseBigBlock(This, depotBuffer);
2795   }
2796
2797   *nextBlockIndex = This->blockDepotCached[depotBlockOffset/sizeof(ULONG)];
2798
2799   return S_OK;
2800 }
2801
2802 /******************************************************************************
2803  *      Storage32Impl_GetNextExtendedBlock
2804  *
2805  * Given an extended block this method will return the next extended block.
2806  *
2807  * NOTES:
2808  * The last ULONG of an extended block is the block index of the next
2809  * extended block. Extended blocks are marked as BLOCK_EXTBBDEPOT in the
2810  * depot.
2811  *
2812  * Return values:
2813  *    - The index of the next extended block
2814  *    - BLOCK_UNUSED: there is no next extended block.
2815  *    - Any other return values denotes failure.
2816  */
2817 ULONG Storage32Impl_GetNextExtendedBlock(StorageImpl* This, ULONG blockIndex)
2818 {
2819   ULONG nextBlockIndex   = BLOCK_SPECIAL;
2820   ULONG depotBlockOffset = This->bigBlockSize - sizeof(ULONG);
2821   void* depotBuffer;
2822
2823   depotBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
2824
2825   if (depotBuffer!=0)
2826   {
2827     StorageUtl_ReadDWord(depotBuffer, depotBlockOffset, &nextBlockIndex);
2828
2829     StorageImpl_ReleaseBigBlock(This, depotBuffer);
2830   }
2831
2832   return nextBlockIndex;
2833 }
2834
2835 /******************************************************************************
2836  *      Storage32Impl_SetNextBlockInChain
2837  *
2838  * This method will write the index of the specified block's next block
2839  * in the big block depot.
2840  *
2841  * For example: to create the chain 3 -> 1 -> 7 -> End of Chain
2842  *              do the following
2843  *
2844  * Storage32Impl_SetNextBlockInChain(This, 3, 1);
2845  * Storage32Impl_SetNextBlockInChain(This, 1, 7);
2846  * Storage32Impl_SetNextBlockInChain(This, 7, BLOCK_END_OF_CHAIN);
2847  *
2848  */
2849 void  StorageImpl_SetNextBlockInChain(
2850           StorageImpl* This,
2851           ULONG          blockIndex,
2852           ULONG          nextBlock)
2853 {
2854   ULONG offsetInDepot    = blockIndex * sizeof (ULONG);
2855   ULONG depotBlockCount  = offsetInDepot / This->bigBlockSize;
2856   ULONG depotBlockOffset = offsetInDepot % This->bigBlockSize;
2857   ULONG depotBlockIndexPos;
2858   void* depotBuffer;
2859
2860   assert(depotBlockCount < This->bigBlockDepotCount);
2861   assert(blockIndex != nextBlock);
2862
2863   if (depotBlockCount < COUNT_BBDEPOTINHEADER)
2864   {
2865     depotBlockIndexPos = This->bigBlockDepotStart[depotBlockCount];
2866   }
2867   else
2868   {
2869     /*
2870      * We have to look in the extended depot.
2871      */
2872     depotBlockIndexPos = Storage32Impl_GetExtDepotBlock(This, depotBlockCount);
2873   }
2874
2875   depotBuffer = StorageImpl_GetBigBlock(This, depotBlockIndexPos);
2876
2877   if (depotBuffer!=0)
2878   {
2879     StorageUtl_WriteDWord(depotBuffer, depotBlockOffset, nextBlock);
2880     StorageImpl_ReleaseBigBlock(This, depotBuffer);
2881   }
2882
2883   /*
2884    * Update the cached block depot, if necessary.
2885    */
2886   if (depotBlockCount == This->indexBlockDepotCached)
2887   {
2888     This->blockDepotCached[depotBlockOffset/sizeof(ULONG)] = nextBlock;
2889   }
2890 }
2891
2892 /******************************************************************************
2893  *      Storage32Impl_LoadFileHeader
2894  *
2895  * This method will read in the file header, i.e. big block index -1.
2896  */
2897 HRESULT StorageImpl_LoadFileHeader(
2898           StorageImpl* This)
2899 {
2900   HRESULT hr = STG_E_FILENOTFOUND;
2901   void*   headerBigBlock = NULL;
2902   int     index;
2903
2904   /*
2905    * Get a pointer to the big block of data containing the header.
2906    */
2907   headerBigBlock = StorageImpl_GetROBigBlock(This, -1);
2908
2909   /*
2910    * Extract the information from the header.
2911    */
2912   if (headerBigBlock!=0)
2913   {
2914     /*
2915      * Check for the "magic number" signature and return an error if it is not
2916      * found.
2917      */
2918     if (memcmp(headerBigBlock, STORAGE_oldmagic, sizeof(STORAGE_oldmagic))==0)
2919     {
2920       StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2921       return STG_E_OLDFORMAT;
2922     }
2923
2924     if (memcmp(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic))!=0)
2925     {
2926       StorageImpl_ReleaseBigBlock(This, headerBigBlock);
2927       return STG_E_INVALIDHEADER;
2928     }
2929
2930     StorageUtl_ReadWord(
2931       headerBigBlock,
2932       OFFSET_BIGBLOCKSIZEBITS,
2933       &This->bigBlockSizeBits);
2934
2935     StorageUtl_ReadWord(
2936       headerBigBlock,
2937       OFFSET_SMALLBLOCKSIZEBITS,
2938       &This->smallBlockSizeBits);
2939
2940     StorageUtl_ReadDWord(
2941       headerBigBlock,
2942       OFFSET_BBDEPOTCOUNT,
2943       &This->bigBlockDepotCount);
2944
2945     StorageUtl_ReadDWord(
2946       headerBigBlock,
2947       OFFSET_ROOTSTARTBLOCK,
2948       &This->rootStartBlock);
2949
2950     StorageUtl_ReadDWord(
2951       headerBigBlock,
2952       OFFSET_SBDEPOTSTART,
2953       &This->smallBlockDepotStart);
2954
2955     StorageUtl_ReadDWord(
2956       headerBigBlock,
2957       OFFSET_EXTBBDEPOTSTART,
2958       &This->extBigBlockDepotStart);
2959
2960     StorageUtl_ReadDWord(
2961       headerBigBlock,
2962       OFFSET_EXTBBDEPOTCOUNT,
2963       &This->extBigBlockDepotCount);
2964
2965     for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
2966     {
2967       StorageUtl_ReadDWord(
2968         headerBigBlock,
2969         OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
2970         &(This->bigBlockDepotStart[index]));
2971     }
2972
2973     /*
2974      * Make the bitwise arithmetic to get the size of the blocks in bytes.
2975      */
2976     if ((1 << 2) == 4)
2977     {
2978       This->bigBlockSize   = 0x000000001 << (DWORD)This->bigBlockSizeBits;
2979       This->smallBlockSize = 0x000000001 << (DWORD)This->smallBlockSizeBits;
2980     }
2981     else
2982     {
2983       This->bigBlockSize   = 0x000000001 >> (DWORD)This->bigBlockSizeBits;
2984       This->smallBlockSize = 0x000000001 >> (DWORD)This->smallBlockSizeBits;
2985     }
2986
2987     /*
2988      * Right now, the code is making some assumptions about the size of the
2989      * blocks, just make sure they are what we're expecting.
2990      */
2991     if (This->bigBlockSize != DEF_BIG_BLOCK_SIZE ||
2992         This->smallBlockSize != DEF_SMALL_BLOCK_SIZE)
2993     {
2994         WARN("Broken OLE storage file\n");
2995         hr = STG_E_INVALIDHEADER;
2996     }
2997     else
2998         hr = S_OK;
2999
3000     /*
3001      * Release the block.
3002      */
3003     StorageImpl_ReleaseBigBlock(This, headerBigBlock);
3004   }
3005
3006   return hr;
3007 }
3008
3009 /******************************************************************************
3010  *      Storage32Impl_SaveFileHeader
3011  *
3012  * This method will save to the file the header, i.e. big block -1.
3013  */
3014 void StorageImpl_SaveFileHeader(
3015           StorageImpl* This)
3016 {
3017   BYTE   headerBigBlock[BIG_BLOCK_SIZE];
3018   int    index;
3019   BOOL success;
3020
3021   /*
3022    * Get a pointer to the big block of data containing the header.
3023    */
3024   success = StorageImpl_ReadBigBlock(This, -1, headerBigBlock);
3025
3026   /*
3027    * If the block read failed, the file is probably new.
3028    */
3029   if (!success)
3030   {
3031     /*
3032      * Initialize for all unknown fields.
3033      */
3034     memset(headerBigBlock, 0, BIG_BLOCK_SIZE);
3035
3036     /*
3037      * Initialize the magic number.
3038      */
3039     memcpy(headerBigBlock, STORAGE_magic, sizeof(STORAGE_magic));
3040
3041     /*
3042      * And a bunch of things we don't know what they mean
3043      */
3044     StorageUtl_WriteWord(headerBigBlock,  0x18, 0x3b);
3045     StorageUtl_WriteWord(headerBigBlock,  0x1a, 0x3);
3046     StorageUtl_WriteWord(headerBigBlock,  0x1c, (WORD)-2);
3047     StorageUtl_WriteDWord(headerBigBlock, 0x38, (DWORD)0x1000);
3048   }
3049
3050   /*
3051    * Write the information to the header.
3052    */
3053   StorageUtl_WriteWord(
3054     headerBigBlock,
3055     OFFSET_BIGBLOCKSIZEBITS,
3056     This->bigBlockSizeBits);
3057
3058   StorageUtl_WriteWord(
3059     headerBigBlock,
3060     OFFSET_SMALLBLOCKSIZEBITS,
3061     This->smallBlockSizeBits);
3062
3063   StorageUtl_WriteDWord(
3064     headerBigBlock,
3065     OFFSET_BBDEPOTCOUNT,
3066     This->bigBlockDepotCount);
3067
3068   StorageUtl_WriteDWord(
3069     headerBigBlock,
3070     OFFSET_ROOTSTARTBLOCK,
3071     This->rootStartBlock);
3072
3073   StorageUtl_WriteDWord(
3074     headerBigBlock,
3075     OFFSET_SBDEPOTSTART,
3076     This->smallBlockDepotStart);
3077
3078   StorageUtl_WriteDWord(
3079     headerBigBlock,
3080     OFFSET_SBDEPOTCOUNT,
3081     This->smallBlockDepotChain ?
3082      BlockChainStream_GetCount(This->smallBlockDepotChain) : 0);
3083
3084   StorageUtl_WriteDWord(
3085     headerBigBlock,
3086     OFFSET_EXTBBDEPOTSTART,
3087     This->extBigBlockDepotStart);
3088
3089   StorageUtl_WriteDWord(
3090     headerBigBlock,
3091     OFFSET_EXTBBDEPOTCOUNT,
3092     This->extBigBlockDepotCount);
3093
3094   for (index = 0; index < COUNT_BBDEPOTINHEADER; index ++)
3095   {
3096     StorageUtl_WriteDWord(
3097       headerBigBlock,
3098       OFFSET_BBDEPOTSTART + (sizeof(ULONG)*index),
3099       (This->bigBlockDepotStart[index]));
3100   }
3101
3102   /*
3103    * Write the big block back to the file.
3104    */
3105   StorageImpl_WriteBigBlock(This, -1, headerBigBlock);
3106 }
3107
3108 /******************************************************************************
3109  *      Storage32Impl_ReadProperty
3110  *
3111  * This method will read the specified property from the property chain.
3112  */
3113 BOOL StorageImpl_ReadProperty(
3114   StorageImpl* This,
3115   ULONG          index,
3116   StgProperty*   buffer)
3117 {
3118   BYTE           currentProperty[PROPSET_BLOCK_SIZE];
3119   ULARGE_INTEGER offsetInPropSet;
3120   BOOL         readSuccessful;
3121   ULONG          bytesRead;
3122
3123   offsetInPropSet.u.HighPart = 0;
3124   offsetInPropSet.u.LowPart  = index * PROPSET_BLOCK_SIZE;
3125
3126   readSuccessful = BlockChainStream_ReadAt(
3127                     This->rootBlockChain,
3128                     offsetInPropSet,
3129                     PROPSET_BLOCK_SIZE,
3130                     currentProperty,
3131                     &bytesRead);
3132
3133   if (readSuccessful)
3134   {
3135     /* replace the name of root entry (often "Root Entry") by the file name */
3136     WCHAR *propName = (index == This->rootPropertySetIndex) ?
3137                         This->filename : (WCHAR *)currentProperty+OFFSET_PS_NAME;
3138
3139     memset(buffer->name, 0, sizeof(buffer->name));
3140     memcpy(
3141       buffer->name,
3142       propName,
3143       PROPERTY_NAME_BUFFER_LEN );
3144     TRACE("storage name: %s\n", debugstr_w(buffer->name));
3145
3146     memcpy(&buffer->propertyType, currentProperty + OFFSET_PS_PROPERTYTYPE, 1);
3147
3148     StorageUtl_ReadWord(
3149       currentProperty,
3150       OFFSET_PS_NAMELENGTH,
3151       &buffer->sizeOfNameString);
3152
3153     StorageUtl_ReadDWord(
3154       currentProperty,
3155       OFFSET_PS_PREVIOUSPROP,
3156       &buffer->previousProperty);
3157
3158     StorageUtl_ReadDWord(
3159       currentProperty,
3160       OFFSET_PS_NEXTPROP,
3161       &buffer->nextProperty);
3162
3163     StorageUtl_ReadDWord(
3164       currentProperty,
3165       OFFSET_PS_DIRPROP,
3166       &buffer->dirProperty);
3167
3168     StorageUtl_ReadGUID(
3169       currentProperty,
3170       OFFSET_PS_GUID,
3171       &buffer->propertyUniqueID);
3172
3173     StorageUtl_ReadDWord(
3174       currentProperty,
3175       OFFSET_PS_TSS1,
3176       &buffer->timeStampS1);
3177
3178     StorageUtl_ReadDWord(
3179       currentProperty,
3180       OFFSET_PS_TSD1,
3181       &buffer->timeStampD1);
3182
3183     StorageUtl_ReadDWord(
3184       currentProperty,
3185       OFFSET_PS_TSS2,
3186       &buffer->timeStampS2);
3187
3188     StorageUtl_ReadDWord(
3189       currentProperty,
3190       OFFSET_PS_TSD2,
3191       &buffer->timeStampD2);
3192
3193     StorageUtl_ReadDWord(
3194       currentProperty,
3195       OFFSET_PS_STARTBLOCK,
3196       &buffer->startingBlock);
3197
3198     StorageUtl_ReadDWord(
3199       currentProperty,
3200       OFFSET_PS_SIZE,
3201       &buffer->size.u.LowPart);
3202
3203     buffer->size.u.HighPart = 0;
3204   }
3205
3206   return readSuccessful;
3207 }
3208
3209 /*********************************************************************
3210  * Write the specified property into the property chain
3211  */
3212 BOOL StorageImpl_WriteProperty(
3213   StorageImpl* This,
3214   ULONG          index,
3215   StgProperty*   buffer)
3216 {
3217   BYTE           currentProperty[PROPSET_BLOCK_SIZE];
3218   ULARGE_INTEGER offsetInPropSet;
3219   BOOL         writeSuccessful;
3220   ULONG          bytesWritten;
3221
3222   offsetInPropSet.u.HighPart = 0;
3223   offsetInPropSet.u.LowPart  = index * PROPSET_BLOCK_SIZE;
3224
3225   memset(currentProperty, 0, PROPSET_BLOCK_SIZE);
3226
3227   memcpy(
3228     currentProperty + OFFSET_PS_NAME,
3229     buffer->name,
3230     PROPERTY_NAME_BUFFER_LEN );
3231
3232   memcpy(currentProperty + OFFSET_PS_PROPERTYTYPE, &buffer->propertyType, 1);
3233
3234   StorageUtl_WriteWord(
3235     currentProperty,
3236       OFFSET_PS_NAMELENGTH,
3237       buffer->sizeOfNameString);
3238
3239   StorageUtl_WriteDWord(
3240     currentProperty,
3241       OFFSET_PS_PREVIOUSPROP,
3242       buffer->previousProperty);
3243
3244   StorageUtl_WriteDWord(
3245     currentProperty,
3246       OFFSET_PS_NEXTPROP,
3247       buffer->nextProperty);
3248
3249   StorageUtl_WriteDWord(
3250     currentProperty,
3251       OFFSET_PS_DIRPROP,
3252       buffer->dirProperty);
3253
3254   StorageUtl_WriteGUID(
3255     currentProperty,
3256       OFFSET_PS_GUID,
3257       &buffer->propertyUniqueID);
3258
3259   StorageUtl_WriteDWord(
3260     currentProperty,
3261       OFFSET_PS_TSS1,
3262       buffer->timeStampS1);
3263
3264   StorageUtl_WriteDWord(
3265     currentProperty,
3266       OFFSET_PS_TSD1,
3267       buffer->timeStampD1);
3268
3269   StorageUtl_WriteDWord(
3270     currentProperty,
3271       OFFSET_PS_TSS2,
3272       buffer->timeStampS2);
3273
3274   StorageUtl_WriteDWord(
3275     currentProperty,
3276       OFFSET_PS_TSD2,
3277       buffer->timeStampD2);
3278
3279   StorageUtl_WriteDWord(
3280     currentProperty,
3281       OFFSET_PS_STARTBLOCK,
3282       buffer->startingBlock);
3283
3284   StorageUtl_WriteDWord(
3285     currentProperty,
3286       OFFSET_PS_SIZE,
3287       buffer->size.u.LowPart);
3288
3289   writeSuccessful = BlockChainStream_WriteAt(This->rootBlockChain,
3290                                             offsetInPropSet,
3291                                             PROPSET_BLOCK_SIZE,
3292                                             currentProperty,
3293                                             &bytesWritten);
3294   return writeSuccessful;
3295 }
3296
3297 BOOL StorageImpl_ReadBigBlock(
3298   StorageImpl* This,
3299   ULONG          blockIndex,
3300   void*          buffer)
3301 {
3302   void* bigBlockBuffer;
3303
3304   bigBlockBuffer = StorageImpl_GetROBigBlock(This, blockIndex);
3305
3306   if (bigBlockBuffer!=0)
3307   {
3308     memcpy(buffer, bigBlockBuffer, This->bigBlockSize);
3309
3310     StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3311
3312     return TRUE;
3313   }
3314
3315   return FALSE;
3316 }
3317
3318 BOOL StorageImpl_WriteBigBlock(
3319   StorageImpl* This,
3320   ULONG          blockIndex,
3321   void*          buffer)
3322 {
3323   void* bigBlockBuffer;
3324
3325   bigBlockBuffer = StorageImpl_GetBigBlock(This, blockIndex);
3326
3327   if (bigBlockBuffer!=0)
3328   {
3329     memcpy(bigBlockBuffer, buffer, This->bigBlockSize);
3330
3331     StorageImpl_ReleaseBigBlock(This, bigBlockBuffer);
3332
3333     return TRUE;
3334   }
3335
3336   return FALSE;
3337 }
3338
3339 void* StorageImpl_GetROBigBlock(
3340   StorageImpl* This,
3341   ULONG          blockIndex)
3342 {
3343   return BIGBLOCKFILE_GetROBigBlock(This->bigBlockFile, blockIndex);
3344 }
3345
3346 void* StorageImpl_GetBigBlock(
3347   StorageImpl* This,
3348   ULONG          blockIndex)
3349 {
3350   return BIGBLOCKFILE_GetBigBlock(This->bigBlockFile, blockIndex);
3351 }
3352
3353 void StorageImpl_ReleaseBigBlock(
3354   StorageImpl* This,
3355   void*          pBigBlock)
3356 {
3357   BIGBLOCKFILE_ReleaseBigBlock(This->bigBlockFile, pBigBlock);
3358 }
3359
3360 /******************************************************************************
3361  *              Storage32Impl_SmallBlocksToBigBlocks
3362  *
3363  * This method will convert a small block chain to a big block chain.
3364  * The small block chain will be destroyed.
3365  */
3366 BlockChainStream* Storage32Impl_SmallBlocksToBigBlocks(
3367                       StorageImpl* This,
3368                       SmallBlockChainStream** ppsbChain)
3369 {
3370   ULONG bbHeadOfChain = BLOCK_END_OF_CHAIN;
3371   ULARGE_INTEGER size, offset;
3372   ULONG cbRead, cbWritten, cbTotalRead, cbTotalWritten;
3373   ULONG propertyIndex;
3374   BOOL successRead, successWrite;
3375   StgProperty chainProperty;
3376   BYTE *buffer;
3377   BlockChainStream *bbTempChain = NULL;
3378   BlockChainStream *bigBlockChain = NULL;
3379
3380   /*
3381    * Create a temporary big block chain that doesn't have
3382    * an associated property. This temporary chain will be
3383    * used to copy data from small blocks to big blocks.
3384    */
3385   bbTempChain = BlockChainStream_Construct(This,
3386                                            &bbHeadOfChain,
3387                                            PROPERTY_NULL);
3388   if(!bbTempChain) return NULL;
3389   /*
3390    * Grow the big block chain.
3391    */
3392   size = SmallBlockChainStream_GetSize(*ppsbChain);
3393   BlockChainStream_SetSize(bbTempChain, size);
3394
3395   /*
3396    * Copy the contents of the small block chain to the big block chain
3397    * by small block size increments.
3398    */
3399   offset.u.LowPart = 0;
3400   offset.u.HighPart = 0;
3401   cbTotalRead = 0;
3402   cbTotalWritten = 0;
3403
3404   buffer = (BYTE *) HeapAlloc(GetProcessHeap(),0,DEF_SMALL_BLOCK_SIZE);
3405   do
3406   {
3407     successRead = SmallBlockChainStream_ReadAt(*ppsbChain,
3408                                                offset,
3409                                                DEF_SMALL_BLOCK_SIZE,
3410                                                buffer,
3411                                                &cbRead);
3412     cbTotalRead += cbRead;
3413
3414     successWrite = BlockChainStream_WriteAt(bbTempChain,
3415                                             offset,
3416                                             cbRead,
3417                                             buffer,
3418                                             &cbWritten);
3419     cbTotalWritten += cbWritten;
3420
3421     offset.u.LowPart += This->smallBlockSize;
3422
3423   } while (successRead && successWrite);
3424   HeapFree(GetProcessHeap(),0,buffer);
3425
3426   assert(cbTotalRead == cbTotalWritten);
3427
3428   /*
3429    * Destroy the small block chain.
3430    */
3431   propertyIndex = (*ppsbChain)->ownerPropertyIndex;
3432   size.u.HighPart = 0;
3433   size.u.LowPart  = 0;
3434   SmallBlockChainStream_SetSize(*ppsbChain, size);
3435   SmallBlockChainStream_Destroy(*ppsbChain);
3436   *ppsbChain = 0;
3437
3438   /*
3439    * Change the property information. This chain is now a big block chain
3440    * and it doesn't reside in the small blocks chain anymore.
3441    */
3442   StorageImpl_ReadProperty(This, propertyIndex, &chainProperty);
3443
3444   chainProperty.startingBlock = bbHeadOfChain;
3445
3446   StorageImpl_WriteProperty(This, propertyIndex, &chainProperty);
3447
3448   /*
3449    * Destroy the temporary propertyless big block chain.
3450    * Create a new big block chain associated with this property.
3451    */
3452   BlockChainStream_Destroy(bbTempChain);
3453   bigBlockChain = BlockChainStream_Construct(This,
3454                                              NULL,
3455                                              propertyIndex);
3456
3457   return bigBlockChain;
3458 }
3459
3460 /******************************************************************************
3461 ** Storage32InternalImpl implementation
3462 */
3463
3464 StorageInternalImpl* StorageInternalImpl_Construct(
3465   StorageImpl* ancestorStorage,
3466   ULONG          rootPropertyIndex)
3467 {
3468   StorageInternalImpl* newStorage;
3469
3470   /*
3471    * Allocate space for the new storage object
3472    */
3473   newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageInternalImpl));
3474
3475   if (newStorage!=0)
3476   {
3477     memset(newStorage, 0, sizeof(StorageInternalImpl));
3478
3479     /*
3480      * Initialize the virtual function table.
3481      */
3482     newStorage->lpVtbl = &Storage32InternalImpl_Vtbl;
3483     newStorage->v_destructor = &StorageInternalImpl_Destroy;
3484
3485     /*
3486      * Keep the ancestor storage pointer and nail a reference to it.
3487      */
3488     newStorage->ancestorStorage = ancestorStorage;
3489     StorageBaseImpl_AddRef((IStorage*)(newStorage->ancestorStorage));
3490
3491     /*
3492      * Keep the index of the root property set for this storage,
3493      */
3494     newStorage->rootPropertySetIndex = rootPropertyIndex;
3495
3496     return newStorage;
3497   }
3498
3499   return 0;
3500 }
3501
3502 void StorageInternalImpl_Destroy(
3503   StorageInternalImpl* This)
3504 {
3505   StorageBaseImpl_Release((IStorage*)This->ancestorStorage);
3506   HeapFree(GetProcessHeap(), 0, This);
3507 }
3508
3509 /******************************************************************************
3510 **
3511 ** Storage32InternalImpl_Commit
3512 **
3513 ** The non-root storages cannot be opened in transacted mode thus this function
3514 ** does nothing.
3515 */
3516 HRESULT WINAPI StorageInternalImpl_Commit(
3517   IStorage*            iface,
3518   DWORD                  grfCommitFlags)  /* [in] */
3519 {
3520   return S_OK;
3521 }
3522
3523 /******************************************************************************
3524 **
3525 ** Storage32InternalImpl_Revert
3526 **
3527 ** The non-root storages cannot be opened in transacted mode thus this function
3528 ** does nothing.
3529 */
3530 HRESULT WINAPI StorageInternalImpl_Revert(
3531   IStorage*            iface)
3532 {
3533   return S_OK;
3534 }
3535
3536 /******************************************************************************
3537 ** IEnumSTATSTGImpl implementation
3538 */
3539
3540 IEnumSTATSTGImpl* IEnumSTATSTGImpl_Construct(
3541   StorageImpl* parentStorage,
3542   ULONG          firstPropertyNode)
3543 {
3544   IEnumSTATSTGImpl* newEnumeration;
3545
3546   newEnumeration = HeapAlloc(GetProcessHeap(), 0, sizeof(IEnumSTATSTGImpl));
3547
3548   if (newEnumeration!=0)
3549   {
3550     /*
3551      * Set-up the virtual function table and reference count.
3552      */
3553     newEnumeration->lpVtbl    = &IEnumSTATSTGImpl_Vtbl;
3554     newEnumeration->ref       = 0;
3555
3556     /*
3557      * We want to nail-down the reference to the storage in case the
3558      * enumeration out-lives the storage in the client application.
3559      */
3560     newEnumeration->parentStorage = parentStorage;
3561     IStorage_AddRef((IStorage*)newEnumeration->parentStorage);
3562
3563     newEnumeration->firstPropertyNode   = firstPropertyNode;
3564
3565     /*
3566      * Initialize the search stack
3567      */
3568     newEnumeration->stackSize    = 0;
3569     newEnumeration->stackMaxSize = ENUMSTATSGT_SIZE_INCREMENT;
3570     newEnumeration->stackToVisit =
3571       HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG)*ENUMSTATSGT_SIZE_INCREMENT);
3572
3573     /*
3574      * Make sure the current node of the iterator is the first one.
3575      */
3576     IEnumSTATSTGImpl_Reset((IEnumSTATSTG*)newEnumeration);
3577   }
3578
3579   return newEnumeration;
3580 }
3581
3582 void IEnumSTATSTGImpl_Destroy(IEnumSTATSTGImpl* This)
3583 {
3584   IStorage_Release((IStorage*)This->parentStorage);
3585   HeapFree(GetProcessHeap(), 0, This->stackToVisit);
3586   HeapFree(GetProcessHeap(), 0, This);
3587 }
3588
3589 HRESULT WINAPI IEnumSTATSTGImpl_QueryInterface(
3590   IEnumSTATSTG*     iface,
3591   REFIID            riid,
3592   void**            ppvObject)
3593 {
3594   IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3595
3596   /*
3597    * Perform a sanity check on the parameters.
3598    */
3599   if (ppvObject==0)
3600     return E_INVALIDARG;
3601
3602   /*
3603    * Initialize the return parameter.
3604    */
3605   *ppvObject = 0;
3606
3607   /*
3608    * Compare the riid with the interface IDs implemented by this object.
3609    */
3610   if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0)
3611   {
3612     *ppvObject = (IEnumSTATSTG*)This;
3613   }
3614   else if (memcmp(&IID_IStorage, riid, sizeof(IID_IEnumSTATSTG)) == 0)
3615   {
3616     *ppvObject = (IEnumSTATSTG*)This;
3617   }
3618
3619   /*
3620    * Check that we obtained an interface.
3621    */
3622   if ((*ppvObject)==0)
3623     return E_NOINTERFACE;
3624
3625   /*
3626    * Query Interface always increases the reference count by one when it is
3627    * successful
3628    */
3629   IEnumSTATSTGImpl_AddRef((IEnumSTATSTG*)This);
3630
3631   return S_OK;
3632 }
3633
3634 ULONG   WINAPI IEnumSTATSTGImpl_AddRef(
3635   IEnumSTATSTG* iface)
3636 {
3637   IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3638   return InterlockedIncrement(&This->ref);
3639 }
3640
3641 ULONG   WINAPI IEnumSTATSTGImpl_Release(
3642   IEnumSTATSTG* iface)
3643 {
3644   IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3645
3646   ULONG newRef;
3647
3648   newRef = InterlockedDecrement(&This->ref);
3649
3650   /*
3651    * If the reference count goes down to 0, perform suicide.
3652    */
3653   if (newRef==0)
3654   {
3655     IEnumSTATSTGImpl_Destroy(This);
3656   }
3657
3658   return newRef;
3659 }
3660
3661 HRESULT WINAPI IEnumSTATSTGImpl_Next(
3662   IEnumSTATSTG* iface,
3663   ULONG             celt,
3664   STATSTG*          rgelt,
3665   ULONG*            pceltFetched)
3666 {
3667   IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3668
3669   StgProperty currentProperty;
3670   STATSTG*    currentReturnStruct = rgelt;
3671   ULONG       objectFetched       = 0;
3672   ULONG      currentSearchNode;
3673
3674   /*
3675    * Perform a sanity check on the parameters.
3676    */
3677   if ( (rgelt==0) || ( (celt!=1) && (pceltFetched==0) ) )
3678     return E_INVALIDARG;
3679
3680   /*
3681    * To avoid the special case, get another pointer to a ULONG value if
3682    * the caller didn't supply one.
3683    */
3684   if (pceltFetched==0)
3685     pceltFetched = &objectFetched;
3686
3687   /*
3688    * Start the iteration, we will iterate until we hit the end of the
3689    * linked list or until we hit the number of items to iterate through
3690    */
3691   *pceltFetched = 0;
3692
3693   /*
3694    * Start with the node at the top of the stack.
3695    */
3696   currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3697
3698   while ( ( *pceltFetched < celt) &&
3699           ( currentSearchNode!=PROPERTY_NULL) )
3700   {
3701     /*
3702      * Remove the top node from the stack
3703      */
3704     IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3705
3706     /*
3707      * Read the property from the storage.
3708      */
3709     StorageImpl_ReadProperty(This->parentStorage,
3710       currentSearchNode,
3711       &currentProperty);
3712
3713     /*
3714      * Copy the information to the return buffer.
3715      */
3716     StorageUtl_CopyPropertyToSTATSTG(currentReturnStruct,
3717       &currentProperty,
3718       STATFLAG_DEFAULT);
3719
3720     /*
3721      * Step to the next item in the iteration
3722      */
3723     (*pceltFetched)++;
3724     currentReturnStruct++;
3725
3726     /*
3727      * Push the next search node in the search stack.
3728      */
3729     IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3730
3731     /*
3732      * continue the iteration.
3733      */
3734     currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3735   }
3736
3737   if (*pceltFetched == celt)
3738     return S_OK;
3739
3740   return S_FALSE;
3741 }
3742
3743
3744 HRESULT WINAPI IEnumSTATSTGImpl_Skip(
3745   IEnumSTATSTG* iface,
3746   ULONG             celt)
3747 {
3748   IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3749
3750   StgProperty currentProperty;
3751   ULONG       objectFetched       = 0;
3752   ULONG       currentSearchNode;
3753
3754   /*
3755    * Start with the node at the top of the stack.
3756    */
3757   currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3758
3759   while ( (objectFetched < celt) &&
3760           (currentSearchNode!=PROPERTY_NULL) )
3761   {
3762     /*
3763      * Remove the top node from the stack
3764      */
3765     IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3766
3767     /*
3768      * Read the property from the storage.
3769      */
3770     StorageImpl_ReadProperty(This->parentStorage,
3771       currentSearchNode,
3772       &currentProperty);
3773
3774     /*
3775      * Step to the next item in the iteration
3776      */
3777     objectFetched++;
3778
3779     /*
3780      * Push the next search node in the search stack.
3781      */
3782     IEnumSTATSTGImpl_PushSearchNode(This, currentProperty.nextProperty);
3783
3784     /*
3785      * continue the iteration.
3786      */
3787     currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3788   }
3789
3790   if (objectFetched == celt)
3791     return S_OK;
3792
3793   return S_FALSE;
3794 }
3795
3796 HRESULT WINAPI IEnumSTATSTGImpl_Reset(
3797   IEnumSTATSTG* iface)
3798 {
3799   IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3800
3801   StgProperty rootProperty;
3802   BOOL      readSuccessful;
3803
3804   /*
3805    * Re-initialize the search stack to an empty stack
3806    */
3807   This->stackSize = 0;
3808
3809   /*
3810    * Read the root property from the storage.
3811    */
3812   readSuccessful = StorageImpl_ReadProperty(
3813                     This->parentStorage,
3814                     This->firstPropertyNode,
3815                     &rootProperty);
3816
3817   if (readSuccessful)
3818   {
3819     assert(rootProperty.sizeOfNameString!=0);
3820
3821     /*
3822      * Push the search node in the search stack.
3823      */
3824     IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.dirProperty);
3825   }
3826
3827   return S_OK;
3828 }
3829
3830 HRESULT WINAPI IEnumSTATSTGImpl_Clone(
3831   IEnumSTATSTG* iface,
3832   IEnumSTATSTG**    ppenum)
3833 {
3834   IEnumSTATSTGImpl* const This=(IEnumSTATSTGImpl*)iface;
3835
3836   IEnumSTATSTGImpl* newClone;
3837
3838   /*
3839    * Perform a sanity check on the parameters.
3840    */
3841   if (ppenum==0)
3842     return E_INVALIDARG;
3843
3844   newClone = IEnumSTATSTGImpl_Construct(This->parentStorage,
3845                This->firstPropertyNode);
3846
3847
3848   /*
3849    * The new clone enumeration must point to the same current node as
3850    * the ole one.
3851    */
3852   newClone->stackSize    = This->stackSize    ;
3853   newClone->stackMaxSize = This->stackMaxSize ;
3854   newClone->stackToVisit =
3855     HeapAlloc(GetProcessHeap(), 0, sizeof(ULONG) * newClone->stackMaxSize);
3856
3857   memcpy(
3858     newClone->stackToVisit,
3859     This->stackToVisit,
3860     sizeof(ULONG) * newClone->stackSize);
3861
3862   *ppenum = (IEnumSTATSTG*)newClone;
3863
3864   /*
3865    * Don't forget to nail down a reference to the clone before
3866    * returning it.
3867    */
3868   IEnumSTATSTGImpl_AddRef(*ppenum);
3869
3870   return S_OK;
3871 }
3872
3873 INT IEnumSTATSTGImpl_FindParentProperty(
3874   IEnumSTATSTGImpl *This,
3875   ULONG             childProperty,
3876   StgProperty      *currentProperty,
3877   ULONG            *thisNodeId)
3878 {
3879   ULONG currentSearchNode;
3880   ULONG foundNode;
3881
3882   /*
3883    * To avoid the special case, get another pointer to a ULONG value if
3884    * the caller didn't supply one.
3885    */
3886   if (thisNodeId==0)
3887     thisNodeId = &foundNode;
3888
3889   /*
3890    * Start with the node at the top of the stack.
3891    */
3892   currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3893
3894
3895   while (currentSearchNode!=PROPERTY_NULL)
3896   {
3897     /*
3898      * Store the current node in the returned parameters
3899      */
3900     *thisNodeId = currentSearchNode;
3901
3902     /*
3903      * Remove the top node from the stack
3904      */
3905     IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3906
3907     /*
3908      * Read the property from the storage.
3909      */
3910     StorageImpl_ReadProperty(
3911       This->parentStorage,
3912       currentSearchNode,
3913       currentProperty);
3914
3915     if (currentProperty->previousProperty == childProperty)
3916       return PROPERTY_RELATION_PREVIOUS;
3917
3918     else if (currentProperty->nextProperty == childProperty)
3919       return PROPERTY_RELATION_NEXT;
3920
3921     else if (currentProperty->dirProperty == childProperty)
3922       return PROPERTY_RELATION_DIR;
3923
3924     /*
3925      * Push the next search node in the search stack.
3926      */
3927     IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3928
3929     /*
3930      * continue the iteration.
3931      */
3932     currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3933   }
3934
3935   return PROPERTY_NULL;
3936 }
3937
3938 ULONG IEnumSTATSTGImpl_FindProperty(
3939   IEnumSTATSTGImpl* This,
3940   const OLECHAR*  lpszPropName,
3941   StgProperty*      currentProperty)
3942 {
3943   ULONG currentSearchNode;
3944
3945   /*
3946    * Start with the node at the top of the stack.
3947    */
3948   currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3949
3950   while (currentSearchNode!=PROPERTY_NULL)
3951   {
3952     /*
3953      * Remove the top node from the stack
3954      */
3955     IEnumSTATSTGImpl_PopSearchNode(This, TRUE);
3956
3957     /*
3958      * Read the property from the storage.
3959      */
3960     StorageImpl_ReadProperty(This->parentStorage,
3961       currentSearchNode,
3962       currentProperty);
3963
3964     if ( propertyNameCmp(
3965           (OLECHAR*)currentProperty->name,
3966           (OLECHAR*)lpszPropName) == 0)
3967       return currentSearchNode;
3968
3969     /*
3970      * Push the next search node in the search stack.
3971      */
3972     IEnumSTATSTGImpl_PushSearchNode(This, currentProperty->nextProperty);
3973
3974     /*
3975      * continue the iteration.
3976      */
3977     currentSearchNode = IEnumSTATSTGImpl_PopSearchNode(This, FALSE);
3978   }
3979
3980   return PROPERTY_NULL;
3981 }
3982
3983 void IEnumSTATSTGImpl_PushSearchNode(
3984   IEnumSTATSTGImpl* This,
3985   ULONG             nodeToPush)
3986 {
3987   StgProperty rootProperty;
3988   BOOL      readSuccessful;
3989
3990   /*
3991    * First, make sure we're not trying to push an unexisting node.
3992    */
3993   if (nodeToPush==PROPERTY_NULL)
3994     return;
3995
3996   /*
3997    * First push the node to the stack
3998    */
3999   if (This->stackSize == This->stackMaxSize)
4000   {
4001     This->stackMaxSize += ENUMSTATSGT_SIZE_INCREMENT;
4002
4003     This->stackToVisit = HeapReAlloc(
4004                            GetProcessHeap(),
4005                            0,
4006                            This->stackToVisit,
4007                            sizeof(ULONG) * This->stackMaxSize);
4008   }
4009
4010   This->stackToVisit[This->stackSize] = nodeToPush;
4011   This->stackSize++;
4012
4013   /*
4014    * Read the root property from the storage.
4015    */
4016   readSuccessful = StorageImpl_ReadProperty(
4017                     This->parentStorage,
4018                     nodeToPush,
4019                     &rootProperty);
4020
4021   if (readSuccessful)
4022   {
4023     assert(rootProperty.sizeOfNameString!=0);
4024
4025     /*
4026      * Push the previous search node in the search stack.
4027      */
4028     IEnumSTATSTGImpl_PushSearchNode(This, rootProperty.previousProperty);
4029   }
4030 }
4031
4032 ULONG IEnumSTATSTGImpl_PopSearchNode(
4033   IEnumSTATSTGImpl* This,
4034   BOOL            remove)
4035 {
4036   ULONG topNode;
4037
4038   if (This->stackSize == 0)
4039     return PROPERTY_NULL;
4040
4041   topNode = This->stackToVisit[This->stackSize-1];
4042
4043   if (remove)
4044     This->stackSize--;
4045
4046   return topNode;
4047 }
4048
4049 /******************************************************************************
4050 ** StorageUtl implementation
4051 */
4052
4053 void StorageUtl_ReadWord(void* buffer, ULONG offset, WORD* value)
4054 {
4055   memcpy(value, (BYTE*)buffer+offset, sizeof(WORD));
4056 }
4057
4058 void StorageUtl_WriteWord(void* buffer, ULONG offset, WORD value)
4059 {
4060   memcpy((BYTE*)buffer+offset, &value, sizeof(WORD));
4061 }
4062
4063 void StorageUtl_ReadDWord(void* buffer, ULONG offset, DWORD* value)
4064 {
4065   memcpy(value, (BYTE*)buffer+offset, sizeof(DWORD));
4066 }
4067
4068 void StorageUtl_WriteDWord(void* buffer, ULONG offset, DWORD value)
4069 {
4070   memcpy((BYTE*)buffer+offset, &value, sizeof(DWORD));
4071 }
4072
4073 void StorageUtl_ReadGUID(void* buffer, ULONG offset, GUID* value)
4074 {
4075   StorageUtl_ReadDWord(buffer, offset,   &(value->Data1));
4076   StorageUtl_ReadWord(buffer,  offset+4, &(value->Data2));
4077   StorageUtl_ReadWord(buffer,  offset+6, &(value->Data3));
4078
4079   memcpy(value->Data4, (BYTE*)buffer+offset+8, sizeof(value->Data4));
4080 }
4081
4082 void StorageUtl_WriteGUID(void* buffer, ULONG offset, GUID* value)
4083 {
4084   StorageUtl_WriteDWord(buffer, offset,   value->Data1);
4085   StorageUtl_WriteWord(buffer,  offset+4, value->Data2);
4086   StorageUtl_WriteWord(buffer,  offset+6, value->Data3);
4087
4088   memcpy((BYTE*)buffer+offset+8, value->Data4, sizeof(value->Data4));
4089 }
4090
4091 void StorageUtl_CopyPropertyToSTATSTG(
4092   STATSTG*     destination,
4093   StgProperty* source,
4094   int          statFlags)
4095 {
4096   /*
4097    * The copy of the string occurs only when the flag is not set
4098    */
4099   if( ((statFlags & STATFLAG_NONAME) != 0) || 
4100        (source->name == NULL) || 
4101        (source->name[0] == 0) )
4102   {
4103     destination->pwcsName = 0;
4104   }
4105   else
4106   {
4107     destination->pwcsName =
4108       CoTaskMemAlloc((lstrlenW(source->name)+1)*sizeof(WCHAR));
4109
4110     strcpyW((LPWSTR)destination->pwcsName, source->name);
4111   }
4112
4113   switch (source->propertyType)
4114   {
4115     case PROPTYPE_STORAGE:
4116     case PROPTYPE_ROOT:
4117       destination->type = STGTY_STORAGE;
4118       break;
4119     case PROPTYPE_STREAM:
4120       destination->type = STGTY_STREAM;
4121       break;
4122     default:
4123       destination->type = STGTY_STREAM;
4124       break;
4125   }
4126
4127   destination->cbSize            = source->size;
4128 /*
4129   currentReturnStruct->mtime     = {0}; TODO
4130   currentReturnStruct->ctime     = {0};
4131   currentReturnStruct->atime     = {0};
4132 */
4133   destination->grfMode           = 0;
4134   destination->grfLocksSupported = 0;
4135   destination->clsid             = source->propertyUniqueID;
4136   destination->grfStateBits      = 0;
4137   destination->reserved          = 0;
4138 }
4139
4140 /******************************************************************************
4141 ** BlockChainStream implementation
4142 */
4143
4144 BlockChainStream* BlockChainStream_Construct(
4145   StorageImpl* parentStorage,
4146   ULONG*         headOfStreamPlaceHolder,
4147   ULONG          propertyIndex)
4148 {
4149   BlockChainStream* newStream;
4150   ULONG blockIndex;
4151
4152   newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(BlockChainStream));
4153
4154   newStream->parentStorage           = parentStorage;
4155   newStream->headOfStreamPlaceHolder = headOfStreamPlaceHolder;
4156   newStream->ownerPropertyIndex      = propertyIndex;
4157   newStream->lastBlockNoInSequence   = 0xFFFFFFFF;
4158   newStream->tailIndex               = BLOCK_END_OF_CHAIN;
4159   newStream->numBlocks               = 0;
4160
4161   blockIndex = BlockChainStream_GetHeadOfChain(newStream);
4162
4163   while (blockIndex != BLOCK_END_OF_CHAIN)
4164   {
4165     newStream->numBlocks++;
4166     newStream->tailIndex = blockIndex;
4167
4168     if(FAILED(StorageImpl_GetNextBlockInChain(
4169               parentStorage,
4170               blockIndex,
4171               &blockIndex)))
4172     {
4173       HeapFree(GetProcessHeap(), 0, newStream);
4174       return NULL;
4175     }
4176   }
4177
4178   return newStream;
4179 }
4180
4181 void BlockChainStream_Destroy(BlockChainStream* This)
4182 {
4183   HeapFree(GetProcessHeap(), 0, This);
4184 }
4185
4186 /******************************************************************************
4187  *      BlockChainStream_GetHeadOfChain
4188  *
4189  * Returns the head of this stream chain.
4190  * Some special chains don't have properties, their heads are kept in
4191  * This->headOfStreamPlaceHolder.
4192  *
4193  */
4194 ULONG BlockChainStream_GetHeadOfChain(BlockChainStream* This)
4195 {
4196   StgProperty chainProperty;
4197   BOOL      readSuccessful;
4198
4199   if (This->headOfStreamPlaceHolder != 0)
4200     return *(This->headOfStreamPlaceHolder);
4201
4202   if (This->ownerPropertyIndex != PROPERTY_NULL)
4203   {
4204     readSuccessful = StorageImpl_ReadProperty(
4205                       This->parentStorage,
4206                       This->ownerPropertyIndex,
4207                       &chainProperty);
4208
4209     if (readSuccessful)
4210     {
4211       return chainProperty.startingBlock;
4212     }
4213   }
4214
4215   return BLOCK_END_OF_CHAIN;
4216 }
4217
4218 /******************************************************************************
4219  *       BlockChainStream_GetCount
4220  *
4221  * Returns the number of blocks that comprises this chain.
4222  * This is not the size of the stream as the last block may not be full!
4223  *
4224  */
4225 ULONG BlockChainStream_GetCount(BlockChainStream* This)
4226 {
4227   ULONG blockIndex;
4228   ULONG count = 0;
4229
4230   blockIndex = BlockChainStream_GetHeadOfChain(This);
4231
4232   while (blockIndex != BLOCK_END_OF_CHAIN)
4233   {
4234     count++;
4235
4236     if(FAILED(StorageImpl_GetNextBlockInChain(
4237                    This->parentStorage,
4238                    blockIndex,
4239                    &blockIndex)))
4240       return 0;
4241   }
4242
4243   return count;
4244 }
4245
4246 /******************************************************************************
4247  *      BlockChainStream_ReadAt
4248  *
4249  * Reads a specified number of bytes from this chain at the specified offset.
4250  * bytesRead may be NULL.
4251  * Failure will be returned if the specified number of bytes has not been read.
4252  */
4253 BOOL BlockChainStream_ReadAt(BlockChainStream* This,
4254   ULARGE_INTEGER offset,
4255   ULONG          size,
4256   void*          buffer,
4257   ULONG*         bytesRead)
4258 {
4259   ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4260   ULONG offsetInBlock     = offset.u.LowPart % This->parentStorage->bigBlockSize;
4261   ULONG bytesToReadInBuffer;
4262   ULONG blockIndex;
4263   BYTE* bufferWalker;
4264   BYTE* bigBlockBuffer;
4265
4266   /*
4267    * Find the first block in the stream that contains part of the buffer.
4268    */
4269   if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4270        (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4271        (blockNoInSequence < This->lastBlockNoInSequence) )
4272   {
4273     blockIndex = BlockChainStream_GetHeadOfChain(This);
4274     This->lastBlockNoInSequence = blockNoInSequence;
4275   }
4276   else
4277   {
4278     ULONG temp = blockNoInSequence;
4279
4280     blockIndex = This->lastBlockNoInSequenceIndex;
4281     blockNoInSequence -= This->lastBlockNoInSequence;
4282     This->lastBlockNoInSequence = temp;
4283   }
4284
4285   while ( (blockNoInSequence > 0) &&  (blockIndex != BLOCK_END_OF_CHAIN))
4286   {
4287     if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4288       return FALSE;
4289     blockNoInSequence--;
4290   }
4291
4292   This->lastBlockNoInSequenceIndex = blockIndex;
4293
4294   /*
4295    * Start reading the buffer.
4296    */
4297   *bytesRead   = 0;
4298   bufferWalker = buffer;
4299
4300   while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4301   {
4302     /*
4303      * Calculate how many bytes we can copy from this big block.
4304      */
4305     bytesToReadInBuffer =
4306       min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4307
4308     /*
4309      * Copy those bytes to the buffer
4310      */
4311     bigBlockBuffer =
4312       StorageImpl_GetROBigBlock(This->parentStorage, blockIndex);
4313
4314     memcpy(bufferWalker, bigBlockBuffer + offsetInBlock, bytesToReadInBuffer);
4315
4316     StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4317
4318     /*
4319      * Step to the next big block.
4320      */
4321     if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex, &blockIndex)))
4322       return FALSE;
4323
4324     bufferWalker += bytesToReadInBuffer;
4325     size         -= bytesToReadInBuffer;
4326     *bytesRead   += bytesToReadInBuffer;
4327     offsetInBlock = 0;  /* There is no offset on the next block */
4328
4329   }
4330
4331   return (size == 0);
4332 }
4333
4334 /******************************************************************************
4335  *      BlockChainStream_WriteAt
4336  *
4337  * Writes the specified number of bytes to this chain at the specified offset.
4338  * bytesWritten may be NULL.
4339  * Will fail if not all specified number of bytes have been written.
4340  */
4341 BOOL BlockChainStream_WriteAt(BlockChainStream* This,
4342   ULARGE_INTEGER    offset,
4343   ULONG             size,
4344   const void*       buffer,
4345   ULONG*            bytesWritten)
4346 {
4347   ULONG blockNoInSequence = offset.u.LowPart / This->parentStorage->bigBlockSize;
4348   ULONG offsetInBlock     = offset.u.LowPart % This->parentStorage->bigBlockSize;
4349   ULONG bytesToWrite;
4350   ULONG blockIndex;
4351   BYTE* bufferWalker;
4352   BYTE* bigBlockBuffer;
4353
4354   /*
4355    * Find the first block in the stream that contains part of the buffer.
4356    */
4357   if ( (This->lastBlockNoInSequence == 0xFFFFFFFF) ||
4358        (This->lastBlockNoInSequenceIndex == BLOCK_END_OF_CHAIN) ||
4359        (blockNoInSequence < This->lastBlockNoInSequence) )
4360   {
4361     blockIndex = BlockChainStream_GetHeadOfChain(This);
4362     This->lastBlockNoInSequence = blockNoInSequence;
4363   }
4364   else
4365   {
4366     ULONG temp = blockNoInSequence;
4367
4368     blockIndex = This->lastBlockNoInSequenceIndex;
4369     blockNoInSequence -= This->lastBlockNoInSequence;
4370     This->lastBlockNoInSequence = temp;
4371   }
4372
4373   while ( (blockNoInSequence > 0) &&  (blockIndex != BLOCK_END_OF_CHAIN))
4374   {
4375     if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4376                                               &blockIndex)))
4377       return FALSE;
4378     blockNoInSequence--;
4379   }
4380
4381   This->lastBlockNoInSequenceIndex = blockIndex;
4382
4383   /*
4384    * Here, I'm casting away the constness on the buffer variable
4385    * This is OK since we don't intend to modify that buffer.
4386    */
4387   *bytesWritten   = 0;
4388   bufferWalker = (BYTE*)buffer;
4389
4390   while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
4391   {
4392     /*
4393      * Calculate how many bytes we can copy from this big block.
4394      */
4395     bytesToWrite =
4396       min(This->parentStorage->bigBlockSize - offsetInBlock, size);
4397
4398     /*
4399      * Copy those bytes to the buffer
4400      */
4401     bigBlockBuffer = StorageImpl_GetBigBlock(This->parentStorage, blockIndex);
4402
4403     memcpy(bigBlockBuffer + offsetInBlock, bufferWalker, bytesToWrite);
4404
4405     StorageImpl_ReleaseBigBlock(This->parentStorage, bigBlockBuffer);
4406
4407     /*
4408      * Step to the next big block.
4409      */
4410     if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4411                                               &blockIndex)))
4412       return FALSE;
4413     bufferWalker  += bytesToWrite;
4414     size          -= bytesToWrite;
4415     *bytesWritten += bytesToWrite;
4416     offsetInBlock  = 0;      /* There is no offset on the next block */
4417   }
4418
4419   return (size == 0);
4420 }
4421
4422 /******************************************************************************
4423  *      BlockChainStream_Shrink
4424  *
4425  * Shrinks this chain in the big block depot.
4426  */
4427 BOOL BlockChainStream_Shrink(BlockChainStream* This,
4428                                ULARGE_INTEGER    newSize)
4429 {
4430   ULONG blockIndex, extraBlock;
4431   ULONG numBlocks;
4432   ULONG count = 1;
4433
4434   /*
4435    * Reset the last accessed block cache.
4436    */
4437   This->lastBlockNoInSequence = 0xFFFFFFFF;
4438   This->lastBlockNoInSequenceIndex = BLOCK_END_OF_CHAIN;
4439
4440   /*
4441    * Figure out how many blocks are needed to contain the new size
4442    */
4443   numBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4444
4445   if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4446     numBlocks++;
4447
4448   blockIndex = BlockChainStream_GetHeadOfChain(This);
4449
4450   /*
4451    * Go to the new end of chain
4452    */
4453   while (count < numBlocks)
4454   {
4455     if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4456                                               &blockIndex)))
4457       return FALSE;
4458     count++;
4459   }
4460
4461   /* Get the next block before marking the new end */
4462   if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, blockIndex,
4463                                             &extraBlock)))
4464     return FALSE;
4465
4466   /* Mark the new end of chain */
4467   StorageImpl_SetNextBlockInChain(
4468     This->parentStorage,
4469     blockIndex,
4470     BLOCK_END_OF_CHAIN);
4471
4472   This->tailIndex = blockIndex;
4473   This->numBlocks = numBlocks;
4474
4475   /*
4476    * Mark the extra blocks as free
4477    */
4478   while (extraBlock != BLOCK_END_OF_CHAIN)
4479   {
4480     if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, extraBlock,
4481                                               &blockIndex)))
4482       return FALSE;
4483     StorageImpl_FreeBigBlock(This->parentStorage, extraBlock);
4484     extraBlock = blockIndex;
4485   }
4486
4487   return TRUE;
4488 }
4489
4490 /******************************************************************************
4491  *      BlockChainStream_Enlarge
4492  *
4493  * Grows this chain in the big block depot.
4494  */
4495 BOOL BlockChainStream_Enlarge(BlockChainStream* This,
4496                                 ULARGE_INTEGER    newSize)
4497 {
4498   ULONG blockIndex, currentBlock;
4499   ULONG newNumBlocks;
4500   ULONG oldNumBlocks = 0;
4501
4502   blockIndex = BlockChainStream_GetHeadOfChain(This);
4503
4504   /*
4505    * Empty chain. Create the head.
4506    */
4507   if (blockIndex == BLOCK_END_OF_CHAIN)
4508   {
4509     blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4510     StorageImpl_SetNextBlockInChain(This->parentStorage,
4511                                       blockIndex,
4512                                       BLOCK_END_OF_CHAIN);
4513
4514     if (This->headOfStreamPlaceHolder != 0)
4515     {
4516       *(This->headOfStreamPlaceHolder) = blockIndex;
4517     }
4518     else
4519     {
4520       StgProperty chainProp;
4521       assert(This->ownerPropertyIndex != PROPERTY_NULL);
4522
4523       StorageImpl_ReadProperty(
4524         This->parentStorage,
4525         This->ownerPropertyIndex,
4526         &chainProp);
4527
4528       chainProp.startingBlock = blockIndex;
4529
4530       StorageImpl_WriteProperty(
4531         This->parentStorage,
4532         This->ownerPropertyIndex,
4533         &chainProp);
4534     }
4535
4536     This->tailIndex = blockIndex;
4537     This->numBlocks = 1;
4538   }
4539
4540   /*
4541    * Figure out how many blocks are needed to contain this stream
4542    */
4543   newNumBlocks = newSize.u.LowPart / This->parentStorage->bigBlockSize;
4544
4545   if ((newSize.u.LowPart % This->parentStorage->bigBlockSize) != 0)
4546     newNumBlocks++;
4547
4548   /*
4549    * Go to the current end of chain
4550    */
4551   if (This->tailIndex == BLOCK_END_OF_CHAIN)
4552   {
4553     currentBlock = blockIndex;
4554
4555     while (blockIndex != BLOCK_END_OF_CHAIN)
4556     {
4557       This->numBlocks++;
4558       currentBlock = blockIndex;
4559
4560       if(FAILED(StorageImpl_GetNextBlockInChain(This->parentStorage, currentBlock,
4561                                                 &blockIndex)))
4562         return FALSE;
4563     }
4564
4565     This->tailIndex = currentBlock;
4566   }
4567
4568   currentBlock = This->tailIndex;
4569   oldNumBlocks = This->numBlocks;
4570
4571   /*
4572    * Add new blocks to the chain
4573    */
4574   if (oldNumBlocks < newNumBlocks)
4575   {
4576     while (oldNumBlocks < newNumBlocks)
4577     {
4578       blockIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4579
4580       StorageImpl_SetNextBlockInChain(
4581         This->parentStorage,
4582         currentBlock,
4583         blockIndex);
4584
4585       StorageImpl_SetNextBlockInChain(
4586         This->parentStorage,
4587         blockIndex,
4588         BLOCK_END_OF_CHAIN);
4589
4590       currentBlock = blockIndex;
4591       oldNumBlocks++;
4592     }
4593
4594     This->tailIndex = blockIndex;
4595     This->numBlocks = newNumBlocks;
4596   }
4597
4598   return TRUE;
4599 }
4600
4601 /******************************************************************************
4602  *      BlockChainStream_SetSize
4603  *
4604  * Sets the size of this stream. The big block depot will be updated.
4605  * The file will grow if we grow the chain.
4606  *
4607  * TODO: Free the actual blocks in the file when we shrink the chain.
4608  *       Currently, the blocks are still in the file. So the file size
4609  *       doesn't shrink even if we shrink streams.
4610  */
4611 BOOL BlockChainStream_SetSize(
4612   BlockChainStream* This,
4613   ULARGE_INTEGER    newSize)
4614 {
4615   ULARGE_INTEGER size = BlockChainStream_GetSize(This);
4616
4617   if (newSize.u.LowPart == size.u.LowPart)
4618     return TRUE;
4619
4620   if (newSize.u.LowPart < size.u.LowPart)
4621   {
4622     BlockChainStream_Shrink(This, newSize);
4623   }
4624   else
4625   {
4626     ULARGE_INTEGER fileSize =
4627       BIGBLOCKFILE_GetSize(This->parentStorage->bigBlockFile);
4628
4629     ULONG diff = newSize.u.LowPart - size.u.LowPart;
4630
4631     /*
4632      * Make sure the file stays a multiple of blocksize
4633      */
4634     if ((diff % This->parentStorage->bigBlockSize) != 0)
4635       diff += (This->parentStorage->bigBlockSize -
4636                 (diff % This->parentStorage->bigBlockSize) );
4637
4638     fileSize.u.LowPart += diff;
4639     BIGBLOCKFILE_SetSize(This->parentStorage->bigBlockFile, fileSize);
4640
4641     BlockChainStream_Enlarge(This, newSize);
4642   }
4643
4644   return TRUE;
4645 }
4646
4647 /******************************************************************************
4648  *      BlockChainStream_GetSize
4649  *
4650  * Returns the size of this chain.
4651  * Will return the block count if this chain doesn't have a property.
4652  */
4653 ULARGE_INTEGER BlockChainStream_GetSize(BlockChainStream* This)
4654 {
4655   StgProperty chainProperty;
4656
4657   if(This->headOfStreamPlaceHolder == NULL)
4658   {
4659     /*
4660      * This chain is a data stream read the property and return
4661      * the appropriate size
4662      */
4663     StorageImpl_ReadProperty(
4664       This->parentStorage,
4665       This->ownerPropertyIndex,
4666       &chainProperty);
4667
4668     return chainProperty.size;
4669   }
4670   else
4671   {
4672     /*
4673      * this chain is a chain that does not have a property, figure out the
4674      * size by making the product number of used blocks times the
4675      * size of them
4676      */
4677     ULARGE_INTEGER result;
4678     result.u.HighPart = 0;
4679
4680     result.u.LowPart  =
4681       BlockChainStream_GetCount(This) *
4682       This->parentStorage->bigBlockSize;
4683
4684     return result;
4685   }
4686 }
4687
4688 /******************************************************************************
4689 ** SmallBlockChainStream implementation
4690 */
4691
4692 SmallBlockChainStream* SmallBlockChainStream_Construct(
4693   StorageImpl* parentStorage,
4694   ULONG          propertyIndex)
4695 {
4696   SmallBlockChainStream* newStream;
4697
4698   newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(SmallBlockChainStream));
4699
4700   newStream->parentStorage      = parentStorage;
4701   newStream->ownerPropertyIndex = propertyIndex;
4702
4703   return newStream;
4704 }
4705
4706 void SmallBlockChainStream_Destroy(
4707   SmallBlockChainStream* This)
4708 {
4709   HeapFree(GetProcessHeap(), 0, This);
4710 }
4711
4712 /******************************************************************************
4713  *      SmallBlockChainStream_GetHeadOfChain
4714  *
4715  * Returns the head of this chain of small blocks.
4716  */
4717 ULONG SmallBlockChainStream_GetHeadOfChain(
4718   SmallBlockChainStream* This)
4719 {
4720   StgProperty chainProperty;
4721   BOOL      readSuccessful;
4722
4723   if (This->ownerPropertyIndex)
4724   {
4725     readSuccessful = StorageImpl_ReadProperty(
4726                       This->parentStorage,
4727                       This->ownerPropertyIndex,
4728                       &chainProperty);
4729
4730     if (readSuccessful)
4731     {
4732       return chainProperty.startingBlock;
4733     }
4734
4735   }
4736
4737   return BLOCK_END_OF_CHAIN;
4738 }
4739
4740 /******************************************************************************
4741  *      SmallBlockChainStream_GetNextBlockInChain
4742  *
4743  * Returns the index of the next small block in this chain.
4744  *
4745  * Return Values:
4746  *    - BLOCK_END_OF_CHAIN: end of this chain
4747  *    - BLOCK_UNUSED: small block 'blockIndex' is free
4748  */
4749 HRESULT SmallBlockChainStream_GetNextBlockInChain(
4750   SmallBlockChainStream* This,
4751   ULONG                  blockIndex,
4752   ULONG*                 nextBlockInChain)
4753 {
4754   ULARGE_INTEGER offsetOfBlockInDepot;
4755   DWORD  buffer;
4756   ULONG  bytesRead;
4757   BOOL success;
4758
4759   *nextBlockInChain = BLOCK_END_OF_CHAIN;
4760
4761   offsetOfBlockInDepot.u.HighPart = 0;
4762   offsetOfBlockInDepot.u.LowPart  = blockIndex * sizeof(ULONG);
4763
4764   /*
4765    * Read those bytes in the buffer from the small block file.
4766    */
4767   success = BlockChainStream_ReadAt(
4768               This->parentStorage->smallBlockDepotChain,
4769               offsetOfBlockInDepot,
4770               sizeof(DWORD),
4771               &buffer,
4772               &bytesRead);
4773
4774   if (success)
4775   {
4776     StorageUtl_ReadDWord(&buffer, 0, nextBlockInChain);
4777     return S_OK;
4778   }
4779
4780   return STG_E_READFAULT;
4781 }
4782
4783 /******************************************************************************
4784  *       SmallBlockChainStream_SetNextBlockInChain
4785  *
4786  * Writes the index of the next block of the specified block in the small
4787  * block depot.
4788  * To set the end of chain use BLOCK_END_OF_CHAIN as nextBlock.
4789  * To flag a block as free use BLOCK_UNUSED as nextBlock.
4790  */
4791 void SmallBlockChainStream_SetNextBlockInChain(
4792   SmallBlockChainStream* This,
4793   ULONG                  blockIndex,
4794   ULONG                  nextBlock)
4795 {
4796   ULARGE_INTEGER offsetOfBlockInDepot;
4797   DWORD  buffer;
4798   ULONG  bytesWritten;
4799
4800   offsetOfBlockInDepot.u.HighPart = 0;
4801   offsetOfBlockInDepot.u.LowPart  = blockIndex * sizeof(ULONG);
4802
4803   StorageUtl_WriteDWord(&buffer, 0, nextBlock);
4804
4805   /*
4806    * Read those bytes in the buffer from the small block file.
4807    */
4808   BlockChainStream_WriteAt(
4809     This->parentStorage->smallBlockDepotChain,
4810     offsetOfBlockInDepot,
4811     sizeof(DWORD),
4812     &buffer,
4813     &bytesWritten);
4814 }
4815
4816 /******************************************************************************
4817  *      SmallBlockChainStream_FreeBlock
4818  *
4819  * Flag small block 'blockIndex' as free in the small block depot.
4820  */
4821 void SmallBlockChainStream_FreeBlock(
4822   SmallBlockChainStream* This,
4823   ULONG                  blockIndex)
4824 {
4825   SmallBlockChainStream_SetNextBlockInChain(This, blockIndex, BLOCK_UNUSED);
4826 }
4827
4828 /******************************************************************************
4829  *      SmallBlockChainStream_GetNextFreeBlock
4830  *
4831  * Returns the index of a free small block. The small block depot will be
4832  * enlarged if necessary. The small block chain will also be enlarged if
4833  * necessary.
4834  */
4835 ULONG SmallBlockChainStream_GetNextFreeBlock(
4836   SmallBlockChainStream* This)
4837 {
4838   ULARGE_INTEGER offsetOfBlockInDepot;
4839   DWORD buffer;
4840   ULONG bytesRead;
4841   ULONG blockIndex = 0;
4842   ULONG nextBlockIndex = BLOCK_END_OF_CHAIN;
4843   BOOL success = TRUE;
4844   ULONG smallBlocksPerBigBlock;
4845
4846   offsetOfBlockInDepot.u.HighPart = 0;
4847
4848   /*
4849    * Scan the small block depot for a free block
4850    */
4851   while (nextBlockIndex != BLOCK_UNUSED)
4852   {
4853     offsetOfBlockInDepot.u.LowPart = blockIndex * sizeof(ULONG);
4854
4855     success = BlockChainStream_ReadAt(
4856                 This->parentStorage->smallBlockDepotChain,
4857                 offsetOfBlockInDepot,
4858                 sizeof(DWORD),
4859                 &buffer,
4860                 &bytesRead);
4861
4862     /*
4863      * If we run out of space for the small block depot, enlarge it
4864      */
4865     if (success)
4866     {
4867       StorageUtl_ReadDWord(&buffer, 0, &nextBlockIndex);
4868
4869       if (nextBlockIndex != BLOCK_UNUSED)
4870         blockIndex++;
4871     }
4872     else
4873     {
4874       ULONG count =
4875         BlockChainStream_GetCount(This->parentStorage->smallBlockDepotChain);
4876
4877       ULONG sbdIndex = This->parentStorage->smallBlockDepotStart;
4878       ULONG nextBlock, newsbdIndex;
4879       BYTE* smallBlockDepot;
4880
4881       nextBlock = sbdIndex;
4882       while (nextBlock != BLOCK_END_OF_CHAIN)
4883       {
4884         sbdIndex = nextBlock;
4885         StorageImpl_GetNextBlockInChain(This->parentStorage, sbdIndex, &nextBlock);
4886       }
4887
4888       newsbdIndex = StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4889       if (sbdIndex != BLOCK_END_OF_CHAIN)
4890         StorageImpl_SetNextBlockInChain(
4891           This->parentStorage,
4892           sbdIndex,
4893           newsbdIndex);
4894
4895       StorageImpl_SetNextBlockInChain(
4896         This->parentStorage,
4897         newsbdIndex,
4898         BLOCK_END_OF_CHAIN);
4899
4900       /*
4901        * Initialize all the small blocks to free
4902        */
4903       smallBlockDepot =
4904         StorageImpl_GetBigBlock(This->parentStorage, newsbdIndex);
4905
4906       memset(smallBlockDepot, BLOCK_UNUSED, This->parentStorage->bigBlockSize);
4907       StorageImpl_ReleaseBigBlock(This->parentStorage, smallBlockDepot);
4908
4909       if (count == 0)
4910       {
4911         /*
4912          * We have just created the small block depot.
4913          */
4914         StgProperty rootProp;
4915         ULONG sbStartIndex;
4916
4917         /*
4918          * Save it in the header
4919          */
4920         This->parentStorage->smallBlockDepotStart = newsbdIndex;
4921         StorageImpl_SaveFileHeader(This->parentStorage);
4922
4923         /*
4924          * And allocate the first big block that will contain small blocks
4925          */
4926         sbStartIndex =
4927           StorageImpl_GetNextFreeBigBlock(This->parentStorage);
4928
4929         StorageImpl_SetNextBlockInChain(
4930           This->parentStorage,
4931           sbStartIndex,
4932           BLOCK_END_OF_CHAIN);
4933
4934         StorageImpl_ReadProperty(
4935           This->parentStorage,
4936           This->parentStorage->rootPropertySetIndex,
4937           &rootProp);
4938
4939         rootProp.startingBlock = sbStartIndex;
4940         rootProp.size.u.HighPart = 0;
4941         rootProp.size.u.LowPart  = This->parentStorage->bigBlockSize;
4942
4943         StorageImpl_WriteProperty(
4944           This->parentStorage,
4945           This->parentStorage->rootPropertySetIndex,
4946           &rootProp);
4947       }
4948     }
4949   }
4950
4951   smallBlocksPerBigBlock =
4952     This->parentStorage->bigBlockSize / This->parentStorage->smallBlockSize;
4953
4954   /*
4955    * Verify if we have to allocate big blocks to contain small blocks
4956    */
4957   if (blockIndex % smallBlocksPerBigBlock == 0)
4958   {
4959     StgProperty rootProp;
4960     ULONG blocksRequired = (blockIndex / smallBlocksPerBigBlock) + 1;
4961
4962     StorageImpl_ReadProperty(
4963       This->parentStorage,
4964       This->parentStorage->rootPropertySetIndex,
4965       &rootProp);
4966
4967     if (rootProp.size.u.LowPart <
4968        (blocksRequired * This->parentStorage->bigBlockSize))
4969     {
4970       rootProp.size.u.LowPart += This->parentStorage->bigBlockSize;
4971
4972       BlockChainStream_SetSize(
4973         This->parentStorage->smallBlockRootChain,
4974         rootProp.size);
4975
4976       StorageImpl_WriteProperty(
4977         This->parentStorage,
4978         This->parentStorage->rootPropertySetIndex,
4979         &rootProp);
4980     }
4981   }
4982
4983   return blockIndex;
4984 }
4985
4986 /******************************************************************************
4987  *      SmallBlockChainStream_ReadAt
4988  *
4989  * Reads a specified number of bytes from this chain at the specified offset.
4990  * bytesRead may be NULL.
4991  * Failure will be returned if the specified number of bytes has not been read.
4992  */
4993 BOOL SmallBlockChainStream_ReadAt(
4994   SmallBlockChainStream* This,
4995   ULARGE_INTEGER         offset,
4996   ULONG                  size,
4997   void*                  buffer,
4998   ULONG*                 bytesRead)
4999 {
5000   ULARGE_INTEGER offsetInBigBlockFile;
5001   ULONG blockNoInSequence =
5002     offset.u.LowPart / This->parentStorage->smallBlockSize;
5003
5004   ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5005   ULONG bytesToReadInBuffer;
5006   ULONG blockIndex;
5007   ULONG bytesReadFromBigBlockFile;
5008   BYTE* bufferWalker;
5009
5010   /*
5011    * This should never happen on a small block file.
5012    */
5013   assert(offset.u.HighPart==0);
5014
5015   /*
5016    * Find the first block in the stream that contains part of the buffer.
5017    */
5018   blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5019
5020   while ( (blockNoInSequence > 0) &&  (blockIndex != BLOCK_END_OF_CHAIN))
5021   {
5022     if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5023                                                         &blockIndex)))
5024       return FALSE;
5025     blockNoInSequence--;
5026   }
5027
5028   /*
5029    * Start reading the buffer.
5030    */
5031   *bytesRead   = 0;
5032   bufferWalker = buffer;
5033
5034   while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5035   {
5036     /*
5037      * Calculate how many bytes we can copy from this small block.
5038      */
5039     bytesToReadInBuffer =
5040       min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5041
5042     /*
5043      * Calculate the offset of the small block in the small block file.
5044      */
5045     offsetInBigBlockFile.u.HighPart  = 0;
5046     offsetInBigBlockFile.u.LowPart   =
5047       blockIndex * This->parentStorage->smallBlockSize;
5048
5049     offsetInBigBlockFile.u.LowPart  += offsetInBlock;
5050
5051     /*
5052      * Read those bytes in the buffer from the small block file.
5053      */
5054     BlockChainStream_ReadAt(This->parentStorage->smallBlockRootChain,
5055       offsetInBigBlockFile,
5056       bytesToReadInBuffer,
5057       bufferWalker,
5058       &bytesReadFromBigBlockFile);
5059
5060     assert(bytesReadFromBigBlockFile == bytesToReadInBuffer);
5061
5062     /*
5063      * Step to the next big block.
5064      */
5065     if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5066       return FALSE;
5067     bufferWalker += bytesToReadInBuffer;
5068     size         -= bytesToReadInBuffer;
5069     *bytesRead   += bytesToReadInBuffer;
5070     offsetInBlock = 0;  /* There is no offset on the next block */
5071   }
5072
5073   return (size == 0);
5074 }
5075
5076 /******************************************************************************
5077  *       SmallBlockChainStream_WriteAt
5078  *
5079  * Writes the specified number of bytes to this chain at the specified offset.
5080  * bytesWritten may be NULL.
5081  * Will fail if not all specified number of bytes have been written.
5082  */
5083 BOOL SmallBlockChainStream_WriteAt(
5084   SmallBlockChainStream* This,
5085   ULARGE_INTEGER offset,
5086   ULONG          size,
5087   const void*    buffer,
5088   ULONG*         bytesWritten)
5089 {
5090   ULARGE_INTEGER offsetInBigBlockFile;
5091   ULONG blockNoInSequence =
5092     offset.u.LowPart / This->parentStorage->smallBlockSize;
5093
5094   ULONG offsetInBlock = offset.u.LowPart % This->parentStorage->smallBlockSize;
5095   ULONG bytesToWriteInBuffer;
5096   ULONG blockIndex;
5097   ULONG bytesWrittenFromBigBlockFile;
5098   BYTE* bufferWalker;
5099
5100   /*
5101    * This should never happen on a small block file.
5102    */
5103   assert(offset.u.HighPart==0);
5104
5105   /*
5106    * Find the first block in the stream that contains part of the buffer.
5107    */
5108   blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5109
5110   while ( (blockNoInSequence > 0) &&  (blockIndex != BLOCK_END_OF_CHAIN))
5111   {
5112     if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5113       return FALSE;
5114     blockNoInSequence--;
5115   }
5116
5117   /*
5118    * Start writing the buffer.
5119    *
5120    * Here, I'm casting away the constness on the buffer variable
5121    * This is OK since we don't intend to modify that buffer.
5122    */
5123   *bytesWritten   = 0;
5124   bufferWalker = (BYTE*)buffer;
5125   while ( (size > 0) && (blockIndex != BLOCK_END_OF_CHAIN) )
5126   {
5127     /*
5128      * Calculate how many bytes we can copy to this small block.
5129      */
5130     bytesToWriteInBuffer =
5131       min(This->parentStorage->smallBlockSize - offsetInBlock, size);
5132
5133     /*
5134      * Calculate the offset of the small block in the small block file.
5135      */
5136     offsetInBigBlockFile.u.HighPart  = 0;
5137     offsetInBigBlockFile.u.LowPart   =
5138       blockIndex * This->parentStorage->smallBlockSize;
5139
5140     offsetInBigBlockFile.u.LowPart  += offsetInBlock;
5141
5142     /*
5143      * Write those bytes in the buffer to the small block file.
5144      */
5145     BlockChainStream_WriteAt(This->parentStorage->smallBlockRootChain,
5146       offsetInBigBlockFile,
5147       bytesToWriteInBuffer,
5148       bufferWalker,
5149       &bytesWrittenFromBigBlockFile);
5150
5151     assert(bytesWrittenFromBigBlockFile == bytesToWriteInBuffer);
5152
5153     /*
5154      * Step to the next big block.
5155      */
5156     if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5157                                                         &blockIndex)))
5158       return FALSE;
5159     bufferWalker  += bytesToWriteInBuffer;
5160     size          -= bytesToWriteInBuffer;
5161     *bytesWritten += bytesToWriteInBuffer;
5162     offsetInBlock  = 0;     /* There is no offset on the next block */
5163   }
5164
5165   return (size == 0);
5166 }
5167
5168 /******************************************************************************
5169  *       SmallBlockChainStream_Shrink
5170  *
5171  * Shrinks this chain in the small block depot.
5172  */
5173 BOOL SmallBlockChainStream_Shrink(
5174   SmallBlockChainStream* This,
5175   ULARGE_INTEGER newSize)
5176 {
5177   ULONG blockIndex, extraBlock;
5178   ULONG numBlocks;
5179   ULONG count = 0;
5180
5181   numBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5182
5183   if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5184     numBlocks++;
5185
5186   blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5187
5188   /*
5189    * Go to the new end of chain
5190    */
5191   while (count < numBlocks)
5192   {
5193     if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5194                                                         &blockIndex)))
5195       return FALSE;
5196     count++;
5197   }
5198
5199   /*
5200    * If the count is 0, we have a special case, the head of the chain was
5201    * just freed.
5202    */
5203   if (count == 0)
5204   {
5205     StgProperty chainProp;
5206
5207     StorageImpl_ReadProperty(This->parentStorage,
5208                              This->ownerPropertyIndex,
5209                              &chainProp);
5210
5211     chainProp.startingBlock = BLOCK_END_OF_CHAIN;
5212
5213     StorageImpl_WriteProperty(This->parentStorage,
5214                               This->ownerPropertyIndex,
5215                               &chainProp);
5216
5217     /*
5218      * We start freeing the chain at the head block.
5219      */
5220     extraBlock = blockIndex;
5221   }
5222   else
5223   {
5224     /* Get the next block before marking the new end */
5225     if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex,
5226                                                         &extraBlock)))
5227       return FALSE;
5228
5229     /* Mark the new end of chain */
5230     SmallBlockChainStream_SetNextBlockInChain(
5231       This,
5232       blockIndex,
5233       BLOCK_END_OF_CHAIN);
5234   }
5235
5236   /*
5237    * Mark the extra blocks as free
5238    */
5239   while (extraBlock != BLOCK_END_OF_CHAIN)
5240   {
5241     if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, extraBlock,
5242                                                         &blockIndex)))
5243       return FALSE;
5244     SmallBlockChainStream_FreeBlock(This, extraBlock);
5245     extraBlock = blockIndex;
5246   }
5247
5248   return TRUE;
5249 }
5250
5251 /******************************************************************************
5252  *      SmallBlockChainStream_Enlarge
5253  *
5254  * Grows this chain in the small block depot.
5255  */
5256 BOOL SmallBlockChainStream_Enlarge(
5257   SmallBlockChainStream* This,
5258   ULARGE_INTEGER newSize)
5259 {
5260   ULONG blockIndex, currentBlock;
5261   ULONG newNumBlocks;
5262   ULONG oldNumBlocks = 0;
5263
5264   blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5265
5266   /*
5267    * Empty chain
5268    */
5269   if (blockIndex == BLOCK_END_OF_CHAIN)
5270   {
5271
5272     StgProperty chainProp;
5273
5274     StorageImpl_ReadProperty(This->parentStorage, This->ownerPropertyIndex,
5275                                &chainProp);
5276
5277     chainProp.startingBlock = SmallBlockChainStream_GetNextFreeBlock(This);
5278
5279     StorageImpl_WriteProperty(This->parentStorage, This->ownerPropertyIndex,
5280                                 &chainProp);
5281
5282     blockIndex = chainProp.startingBlock;
5283     SmallBlockChainStream_SetNextBlockInChain(
5284       This,
5285       blockIndex,
5286       BLOCK_END_OF_CHAIN);
5287   }
5288
5289   currentBlock = blockIndex;
5290
5291   /*
5292    * Figure out how many blocks are needed to contain this stream
5293    */
5294   newNumBlocks = newSize.u.LowPart / This->parentStorage->smallBlockSize;
5295
5296   if ((newSize.u.LowPart % This->parentStorage->smallBlockSize) != 0)
5297     newNumBlocks++;
5298
5299   /*
5300    * Go to the current end of chain
5301    */
5302   while (blockIndex != BLOCK_END_OF_CHAIN)
5303   {
5304     oldNumBlocks++;
5305     currentBlock = blockIndex;
5306     if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, currentBlock, &blockIndex)))
5307       return FALSE;
5308   }
5309
5310   /*
5311    * Add new blocks to the chain
5312    */
5313   while (oldNumBlocks < newNumBlocks)
5314   {
5315     blockIndex = SmallBlockChainStream_GetNextFreeBlock(This);
5316     SmallBlockChainStream_SetNextBlockInChain(This, currentBlock, blockIndex);
5317
5318     SmallBlockChainStream_SetNextBlockInChain(
5319       This,
5320       blockIndex,
5321       BLOCK_END_OF_CHAIN);
5322
5323     currentBlock = blockIndex;
5324     oldNumBlocks++;
5325   }
5326
5327   return TRUE;
5328 }
5329
5330 /******************************************************************************
5331  *      SmallBlockChainStream_GetCount
5332  *
5333  * Returns the number of blocks that comprises this chain.
5334  * This is not the size of this chain as the last block may not be full!
5335  */
5336 ULONG SmallBlockChainStream_GetCount(SmallBlockChainStream* This)
5337 {
5338   ULONG blockIndex;
5339   ULONG count = 0;
5340
5341   blockIndex = SmallBlockChainStream_GetHeadOfChain(This);
5342
5343   while (blockIndex != BLOCK_END_OF_CHAIN)
5344   {
5345     count++;
5346
5347     if(FAILED(SmallBlockChainStream_GetNextBlockInChain(This, blockIndex, &blockIndex)))
5348       return 0;
5349   }
5350
5351   return count;
5352 }
5353
5354 /******************************************************************************
5355  *      SmallBlockChainStream_SetSize
5356  *
5357  * Sets the size of this stream.
5358  * The file will grow if we grow the chain.
5359  *
5360  * TODO: Free the actual blocks in the file when we shrink the chain.
5361  *       Currently, the blocks are still in the file. So the file size
5362  *       doesn't shrink even if we shrink streams.
5363  */
5364 BOOL SmallBlockChainStream_SetSize(
5365                 SmallBlockChainStream* This,
5366                 ULARGE_INTEGER    newSize)
5367 {
5368   ULARGE_INTEGER size = SmallBlockChainStream_GetSize(This);
5369
5370   if (newSize.u.LowPart == size.u.LowPart)
5371     return TRUE;
5372
5373   if (newSize.u.LowPart < size.u.LowPart)
5374   {
5375     SmallBlockChainStream_Shrink(This, newSize);
5376   }
5377   else
5378   {
5379     SmallBlockChainStream_Enlarge(This, newSize);
5380   }
5381
5382   return TRUE;
5383 }
5384
5385 /******************************************************************************
5386  *      SmallBlockChainStream_GetSize
5387  *
5388  * Returns the size of this chain.
5389  */
5390 ULARGE_INTEGER SmallBlockChainStream_GetSize(SmallBlockChainStream* This)
5391 {
5392   StgProperty chainProperty;
5393
5394   StorageImpl_ReadProperty(
5395     This->parentStorage,
5396     This->ownerPropertyIndex,
5397     &chainProperty);
5398
5399   return chainProperty.size;
5400 }
5401
5402 /******************************************************************************
5403  *    StgCreateDocfile  [OLE32.@]
5404  */
5405 HRESULT WINAPI StgCreateDocfile(
5406   LPCOLESTR pwcsName,
5407   DWORD       grfMode,
5408   DWORD       reserved,
5409   IStorage  **ppstgOpen)
5410 {
5411   StorageImpl* newStorage = 0;
5412   HANDLE       hFile      = INVALID_HANDLE_VALUE;
5413   HRESULT        hr         = S_OK;
5414   DWORD          shareMode;
5415   DWORD          accessMode;
5416   DWORD          creationMode;
5417   DWORD          fileAttributes;
5418   WCHAR          tempFileName[MAX_PATH];
5419
5420   TRACE("(%s, %lx, %ld, %p)\n",
5421         debugstr_w(pwcsName), grfMode,
5422         reserved, ppstgOpen);
5423
5424   /*
5425    * Validate the parameters
5426    */
5427   if (ppstgOpen == 0)
5428     return STG_E_INVALIDPOINTER;
5429
5430   /*
5431    * Validate the STGM flags
5432    */
5433   if ( FAILED( validateSTGM(grfMode) ))
5434     return STG_E_INVALIDFLAG;
5435
5436   /*
5437    * Generate a unique name.
5438    */
5439   if (pwcsName == 0)
5440   {
5441     WCHAR tempPath[MAX_PATH];
5442     static const WCHAR prefix[] = { 'S', 'T', 'O', 0 };
5443
5444     if (!(grfMode & STGM_SHARE_EXCLUSIVE))
5445       return STG_E_INVALIDFLAG;
5446     if (!(grfMode & (STGM_WRITE|STGM_READWRITE)))
5447       return STG_E_INVALIDFLAG;
5448
5449     memset(tempPath, 0, sizeof(tempPath));
5450     memset(tempFileName, 0, sizeof(tempFileName));
5451
5452     if ((GetTempPathW(MAX_PATH, tempPath)) == 0 )
5453       tempPath[0] = '.';
5454
5455     if (GetTempFileNameW(tempPath, prefix, 0, tempFileName) != 0)
5456       pwcsName = tempFileName;
5457     else
5458       return STG_E_INSUFFICIENTMEMORY;
5459
5460     creationMode = TRUNCATE_EXISTING;
5461   }
5462   else
5463   {
5464     creationMode = GetCreationModeFromSTGM(grfMode);
5465   }
5466
5467   /*
5468    * Interpret the STGM value grfMode
5469    */
5470   shareMode    = GetShareModeFromSTGM(grfMode);
5471   accessMode   = GetAccessModeFromSTGM(grfMode);
5472
5473   if (grfMode & STGM_DELETEONRELEASE)
5474     fileAttributes = FILE_FLAG_RANDOM_ACCESS | FILE_FLAG_DELETE_ON_CLOSE;
5475   else
5476     fileAttributes = FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS;
5477
5478   if (grfMode & STGM_TRANSACTED)
5479     FIXME("Transacted mode not implemented.\n");
5480
5481   /*
5482    * Initialize the "out" parameter.
5483    */
5484   *ppstgOpen = 0;
5485
5486   hFile = CreateFileW(pwcsName,
5487                         accessMode,
5488                         shareMode,
5489             NULL,
5490                         creationMode,
5491                         fileAttributes,
5492             0);
5493
5494   if (hFile == INVALID_HANDLE_VALUE)
5495   {
5496     return E_FAIL;
5497   }
5498
5499   /*
5500    * Allocate and initialize the new IStorage32object.
5501    */
5502   newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5503
5504   if (newStorage == 0)
5505     return STG_E_INSUFFICIENTMEMORY;
5506
5507   hr = StorageImpl_Construct(
5508          newStorage,
5509          hFile,
5510         pwcsName,
5511          NULL,
5512          grfMode,
5513          TRUE,
5514          TRUE);
5515
5516   if (FAILED(hr))
5517   {
5518     HeapFree(GetProcessHeap(), 0, newStorage);
5519     return hr;
5520   }
5521
5522   /*
5523    * Get an "out" pointer for the caller.
5524    */
5525   hr = StorageBaseImpl_QueryInterface(
5526          (IStorage*)newStorage,
5527          (REFIID)&IID_IStorage,
5528          (void**)ppstgOpen);
5529
5530   return hr;
5531 }
5532
5533 /******************************************************************************
5534  *              StgCreateStorageEx        [OLE32.@]
5535  */
5536 HRESULT WINAPI StgCreateStorageEx(const WCHAR* pwcsName, DWORD grfMode, DWORD stgfmt, DWORD grfAttrs, STGOPTIONS* pStgOptions, void* reserved, REFIID riid, void** ppObjectOpen)
5537 {
5538     TRACE("(%s, %lx, %lx, %lx, %p, %p, %p, %p)\n", debugstr_w(pwcsName),
5539           grfMode, stgfmt, grfAttrs, pStgOptions, reserved, riid, ppObjectOpen);
5540     return STG_E_UNIMPLEMENTEDFUNCTION;
5541 }
5542
5543 /******************************************************************************
5544  *              StgOpenStorage        [OLE32.@]
5545  */
5546 HRESULT WINAPI StgOpenStorage(
5547   const OLECHAR *pwcsName,
5548   IStorage      *pstgPriority,
5549   DWORD           grfMode,
5550   SNB           snbExclude,
5551   DWORD           reserved,
5552   IStorage      **ppstgOpen)
5553 {
5554   StorageImpl* newStorage = 0;
5555   HRESULT        hr = S_OK;
5556   HANDLE       hFile = 0;
5557   DWORD          shareMode;
5558   DWORD          accessMode;
5559   WCHAR          fullname[MAX_PATH];
5560   DWORD          length;
5561
5562   TRACE("(%s, %p, %lx, %p, %ld, %p)\n",
5563         debugstr_w(pwcsName), pstgPriority, grfMode,
5564         snbExclude, reserved, ppstgOpen);
5565
5566   /*
5567    * Perform a sanity check
5568    */
5569   if (( pwcsName == 0) || (ppstgOpen == 0) )
5570   {
5571     hr = STG_E_INVALIDPOINTER;
5572     goto end;
5573   }
5574
5575   /*
5576    * Validate the STGM flags
5577    */
5578   if ( FAILED( validateSTGM(grfMode) ))
5579   {
5580     hr = STG_E_INVALIDFLAG;
5581     goto end;
5582   }
5583
5584   /*
5585    * Interpret the STGM value grfMode
5586    */
5587   shareMode    = GetShareModeFromSTGM(grfMode);
5588   accessMode   = GetAccessModeFromSTGM(grfMode);
5589
5590   /*
5591    * Initialize the "out" parameter.
5592    */
5593   *ppstgOpen = 0;
5594
5595   hFile = CreateFileW( pwcsName,
5596                        accessMode,
5597                        shareMode,
5598                        NULL,
5599                        OPEN_EXISTING,
5600                        FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS,
5601                        0);
5602
5603   length = GetFileSize(hFile, NULL);
5604
5605   if (hFile==INVALID_HANDLE_VALUE)
5606   {
5607     DWORD last_error = GetLastError();
5608
5609     hr = E_FAIL;
5610
5611     switch (last_error)
5612     {
5613       case ERROR_FILE_NOT_FOUND:
5614         hr = STG_E_FILENOTFOUND;
5615         break;
5616
5617       case ERROR_PATH_NOT_FOUND:
5618         hr = STG_E_PATHNOTFOUND;
5619         break;
5620
5621       case ERROR_ACCESS_DENIED:
5622       case ERROR_WRITE_PROTECT:
5623         hr = STG_E_ACCESSDENIED;
5624         break;
5625
5626       case ERROR_SHARING_VIOLATION:
5627         hr = STG_E_SHAREVIOLATION;
5628         break;
5629
5630       default:
5631         hr = E_FAIL;
5632     }
5633
5634     goto end;
5635   }
5636
5637   /*
5638    * Allocate and initialize the new IStorage32object.
5639    */
5640   newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5641
5642   if (newStorage == 0)
5643   {
5644     hr = STG_E_INSUFFICIENTMEMORY;
5645     goto end;
5646   }
5647
5648   /* if the file's length was zero, initialize the storage */
5649   hr = StorageImpl_Construct(
5650          newStorage,
5651          hFile,
5652         pwcsName,
5653          NULL,
5654          grfMode,
5655          TRUE,
5656          !length );
5657
5658   if (FAILED(hr))
5659   {
5660     HeapFree(GetProcessHeap(), 0, newStorage);
5661     /*
5662      * According to the docs if the file is not a storage, return STG_E_FILEALREADYEXISTS
5663      */
5664     if(hr == STG_E_INVALIDHEADER)
5665         hr = STG_E_FILEALREADYEXISTS;
5666     goto end;
5667   }
5668
5669   /* prepare the file name string given in lieu of the root property name */
5670   GetFullPathNameW(pwcsName, MAX_PATH, fullname, NULL);
5671   memcpy(newStorage->filename, fullname, PROPERTY_NAME_BUFFER_LEN);
5672   newStorage->filename[PROPERTY_NAME_BUFFER_LEN-1] = '\0';
5673
5674   /*
5675    * Get an "out" pointer for the caller.
5676    */
5677   hr = StorageBaseImpl_QueryInterface(
5678          (IStorage*)newStorage,
5679          (REFIID)&IID_IStorage,
5680          (void**)ppstgOpen);
5681
5682 end:
5683   TRACE("<-- %08lx, IStorage %p\n", hr, ppstgOpen ? *ppstgOpen : NULL);
5684   return hr;
5685 }
5686
5687 /******************************************************************************
5688  *    StgCreateDocfileOnILockBytes    [OLE32.@]
5689  */
5690 HRESULT WINAPI StgCreateDocfileOnILockBytes(
5691       ILockBytes *plkbyt,
5692       DWORD grfMode,
5693       DWORD reserved,
5694       IStorage** ppstgOpen)
5695 {
5696   StorageImpl*   newStorage = 0;
5697   HRESULT        hr         = S_OK;
5698
5699   /*
5700    * Validate the parameters
5701    */
5702   if ((ppstgOpen == 0) || (plkbyt == 0))
5703     return STG_E_INVALIDPOINTER;
5704
5705   /*
5706    * Allocate and initialize the new IStorage object.
5707    */
5708   newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5709
5710   if (newStorage == 0)
5711     return STG_E_INSUFFICIENTMEMORY;
5712
5713   hr = StorageImpl_Construct(
5714          newStorage,
5715          0,
5716         0,
5717          plkbyt,
5718          grfMode,
5719          FALSE,
5720          TRUE);
5721
5722   if (FAILED(hr))
5723   {
5724     HeapFree(GetProcessHeap(), 0, newStorage);
5725     return hr;
5726   }
5727
5728   /*
5729    * Get an "out" pointer for the caller.
5730    */
5731   hr = StorageBaseImpl_QueryInterface(
5732          (IStorage*)newStorage,
5733          (REFIID)&IID_IStorage,
5734          (void**)ppstgOpen);
5735
5736   return hr;
5737 }
5738
5739 /******************************************************************************
5740  *    StgOpenStorageOnILockBytes    [OLE32.@]
5741  */
5742 HRESULT WINAPI StgOpenStorageOnILockBytes(
5743       ILockBytes *plkbyt,
5744       IStorage *pstgPriority,
5745       DWORD grfMode,
5746       SNB snbExclude,
5747       DWORD reserved,
5748       IStorage **ppstgOpen)
5749 {
5750   StorageImpl* newStorage = 0;
5751   HRESULT        hr = S_OK;
5752
5753   /*
5754    * Perform a sanity check
5755    */
5756   if ((plkbyt == 0) || (ppstgOpen == 0))
5757     return STG_E_INVALIDPOINTER;
5758
5759   /*
5760    * Validate the STGM flags
5761    */
5762   if ( FAILED( validateSTGM(grfMode) ))
5763     return STG_E_INVALIDFLAG;
5764
5765   /*
5766    * Initialize the "out" parameter.
5767    */
5768   *ppstgOpen = 0;
5769
5770   /*
5771    * Allocate and initialize the new IStorage object.
5772    */
5773   newStorage = HeapAlloc(GetProcessHeap(), 0, sizeof(StorageImpl));
5774
5775   if (newStorage == 0)
5776     return STG_E_INSUFFICIENTMEMORY;
5777
5778   hr = StorageImpl_Construct(
5779          newStorage,
5780          0,
5781          0,
5782          plkbyt,
5783          grfMode,
5784          FALSE,
5785          FALSE);
5786
5787   if (FAILED(hr))
5788   {
5789     HeapFree(GetProcessHeap(), 0, newStorage);
5790     return hr;
5791   }
5792
5793   /*
5794    * Get an "out" pointer for the caller.
5795    */
5796   hr = StorageBaseImpl_QueryInterface(
5797          (IStorage*)newStorage,
5798          (REFIID)&IID_IStorage,
5799          (void**)ppstgOpen);
5800
5801   return hr;
5802 }
5803
5804 /******************************************************************************
5805  *              StgSetTimes [ole32.@]
5806  *              StgSetTimes [OLE32.@]
5807  *
5808  *
5809  */
5810 HRESULT WINAPI StgSetTimes(OLECHAR const *str, FILETIME const *a,
5811                            FILETIME const *b, FILETIME const *c )
5812 {
5813   FIXME("(%s, %p, %p, %p),stub!\n", debugstr_w(str), a, b, c);
5814   return S_OK;
5815 }
5816
5817 /******************************************************************************
5818  *              StgIsStorageILockBytes        [OLE32.@]
5819  *
5820  * Determines if the ILockBytes contains a storage object.
5821  */
5822 HRESULT WINAPI StgIsStorageILockBytes(ILockBytes *plkbyt)
5823 {
5824   BYTE sig[8];
5825   ULARGE_INTEGER offset;
5826
5827   offset.u.HighPart = 0;
5828   offset.u.LowPart  = 0;
5829
5830   ILockBytes_ReadAt(plkbyt, offset, sig, sizeof(sig), NULL);
5831
5832   if (memcmp(sig, STORAGE_magic, sizeof(STORAGE_magic)) == 0)
5833     return S_OK;
5834
5835   return S_FALSE;
5836 }
5837
5838 /******************************************************************************
5839  *              WriteClassStg        [OLE32.@]
5840  *
5841  * This method will store the specified CLSID in the specified storage object
5842  */
5843 HRESULT WINAPI WriteClassStg(IStorage* pStg, REFCLSID rclsid)
5844 {
5845   HRESULT hRes;
5846
5847   assert(pStg != 0);
5848
5849   hRes = IStorage_SetClass(pStg, rclsid);
5850
5851   return hRes;
5852 }
5853
5854 /***********************************************************************
5855  *    ReadClassStg (OLE32.@)
5856  *
5857  * This method reads the CLSID previously written to a storage object with the WriteClassStg.
5858  */
5859 HRESULT WINAPI ReadClassStg(IStorage *pstg,CLSID *pclsid){
5860
5861     STATSTG pstatstg;
5862     HRESULT hRes;
5863
5864     TRACE("()\n");
5865
5866     if(pclsid==NULL)
5867         return E_POINTER;
5868    /*
5869     * read a STATSTG structure (contains the clsid) from the storage
5870     */
5871     hRes=IStorage_Stat(pstg,&pstatstg,STATFLAG_DEFAULT);
5872
5873     if(SUCCEEDED(hRes))
5874         *pclsid=pstatstg.clsid;
5875
5876     return hRes;
5877 }
5878
5879 /***********************************************************************
5880  *    OleLoadFromStream (OLE32.@)
5881  *
5882  * This function loads an object from stream
5883  */
5884 HRESULT  WINAPI OleLoadFromStream(IStream *pStm,REFIID iidInterface,void** ppvObj)
5885 {
5886     CLSID       clsid;
5887     HRESULT     res;
5888     LPPERSISTSTREAM     xstm;
5889
5890     TRACE("(%p,%s,%p)\n",pStm,debugstr_guid(iidInterface),ppvObj);
5891
5892     res=ReadClassStm(pStm,&clsid);
5893     if (!SUCCEEDED(res))
5894         return res;
5895     res=CoCreateInstance(&clsid,NULL,CLSCTX_INPROC_SERVER,iidInterface,ppvObj);
5896     if (!SUCCEEDED(res))
5897         return res;
5898     res=IUnknown_QueryInterface((IUnknown*)*ppvObj,&IID_IPersistStream,(LPVOID*)&xstm);
5899     if (!SUCCEEDED(res)) {
5900         IUnknown_Release((IUnknown*)*ppvObj);
5901         return res;
5902     }
5903     res=IPersistStream_Load(xstm,pStm);
5904     IPersistStream_Release(xstm);
5905     /* FIXME: all refcounts ok at this point? I think they should be:
5906      *          pStm    : unchanged
5907      *          ppvObj  : 1
5908      *          xstm    : 0 (released)
5909      */
5910     return res;
5911 }
5912
5913 /***********************************************************************
5914  *    OleSaveToStream (OLE32.@)
5915  *
5916  * This function saves an object with the IPersistStream interface on it
5917  * to the specified stream.
5918  */
5919 HRESULT  WINAPI OleSaveToStream(IPersistStream *pPStm,IStream *pStm)
5920 {
5921
5922     CLSID clsid;
5923     HRESULT res;
5924
5925     TRACE("(%p,%p)\n",pPStm,pStm);
5926
5927     res=IPersistStream_GetClassID(pPStm,&clsid);
5928
5929     if (SUCCEEDED(res)){
5930
5931         res=WriteClassStm(pStm,&clsid);
5932
5933         if (SUCCEEDED(res))
5934
5935             res=IPersistStream_Save(pPStm,pStm,TRUE);
5936     }
5937
5938     TRACE("Finished Save\n");
5939     return res;
5940 }
5941
5942 /****************************************************************************
5943  * This method validate a STGM parameter that can contain the values below
5944  *
5945  * STGM_DIRECT               0x00000000
5946  * STGM_TRANSACTED           0x00010000
5947  * STGM_SIMPLE               0x08000000
5948  *
5949  * STGM_READ                 0x00000000
5950  * STGM_WRITE                0x00000001
5951  * STGM_READWRITE            0x00000002
5952  *
5953  * STGM_SHARE_DENY_NONE      0x00000040
5954  * STGM_SHARE_DENY_READ      0x00000030
5955  * STGM_SHARE_DENY_WRITE     0x00000020
5956  * STGM_SHARE_EXCLUSIVE      0x00000010
5957  *
5958  * STGM_PRIORITY             0x00040000
5959  * STGM_DELETEONRELEASE      0x04000000
5960  *
5961  * STGM_CREATE               0x00001000
5962  * STGM_CONVERT              0x00020000
5963  * STGM_FAILIFTHERE          0x00000000
5964  *
5965  * STGM_NOSCRATCH            0x00100000
5966  * STGM_NOSNAPSHOT           0x00200000
5967  */
5968 static HRESULT validateSTGM(DWORD stgm)
5969 {
5970   BOOL bSTGM_TRANSACTED       = ((stgm & STGM_TRANSACTED) == STGM_TRANSACTED);
5971   BOOL bSTGM_SIMPLE           = ((stgm & STGM_SIMPLE) == STGM_SIMPLE);
5972   BOOL bSTGM_DIRECT           = ! (bSTGM_TRANSACTED || bSTGM_SIMPLE);
5973
5974   BOOL bSTGM_WRITE            = ((stgm & STGM_WRITE) == STGM_WRITE);
5975   BOOL bSTGM_READWRITE        = ((stgm & STGM_READWRITE) == STGM_READWRITE);
5976   BOOL bSTGM_READ             = ! (bSTGM_WRITE || bSTGM_READWRITE);
5977
5978   BOOL bSTGM_SHARE_DENY_NONE  =
5979                      ((stgm & STGM_SHARE_DENY_NONE)  == STGM_SHARE_DENY_NONE);
5980
5981   BOOL bSTGM_SHARE_DENY_READ  =
5982                      ((stgm & STGM_SHARE_DENY_READ)  == STGM_SHARE_DENY_READ);
5983
5984   BOOL bSTGM_SHARE_DENY_WRITE =
5985                      ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
5986
5987   BOOL bSTGM_SHARE_EXCLUSIVE  =
5988                      ((stgm & STGM_SHARE_EXCLUSIVE)  == STGM_SHARE_EXCLUSIVE);
5989
5990   BOOL bSTGM_CREATE           = ((stgm & STGM_CREATE) == STGM_CREATE);
5991   BOOL bSTGM_CONVERT          = ((stgm & STGM_CONVERT) == STGM_CONVERT);
5992
5993   BOOL bSTGM_NOSCRATCH        = ((stgm & STGM_NOSCRATCH) == STGM_NOSCRATCH);
5994   BOOL bSTGM_NOSNAPSHOT       = ((stgm & STGM_NOSNAPSHOT) == STGM_NOSNAPSHOT);
5995
5996   /*
5997    * STGM_DIRECT | STGM_TRANSACTED | STGM_SIMPLE
5998    */
5999   if ( ! bSTGM_DIRECT )
6000     if( bSTGM_TRANSACTED && bSTGM_SIMPLE )
6001       return E_FAIL;
6002
6003   /*
6004    * STGM_WRITE |  STGM_READWRITE | STGM_READ
6005    */
6006   if ( ! bSTGM_READ )
6007     if( bSTGM_WRITE && bSTGM_READWRITE )
6008       return E_FAIL;
6009
6010   /*
6011    * STGM_SHARE_DENY_NONE | others
6012    * (I assume here that DENY_READ implies DENY_WRITE)
6013    */
6014   if ( bSTGM_SHARE_DENY_NONE )
6015     if ( bSTGM_SHARE_DENY_READ ||
6016          bSTGM_SHARE_DENY_WRITE ||
6017          bSTGM_SHARE_EXCLUSIVE)
6018       return E_FAIL;
6019
6020   /*
6021    * STGM_CREATE | STGM_CONVERT
6022    * if both are false, STGM_FAILIFTHERE is set to TRUE
6023    */
6024   if ( bSTGM_CREATE && bSTGM_CONVERT )
6025     return E_FAIL;
6026
6027   /*
6028    * STGM_NOSCRATCH requires STGM_TRANSACTED
6029    */
6030   if ( bSTGM_NOSCRATCH && ! bSTGM_TRANSACTED )
6031     return E_FAIL;
6032
6033   /*
6034    * STGM_NOSNAPSHOT requires STGM_TRANSACTED and
6035    * not STGM_SHARE_EXCLUSIVE or STGM_SHARE_DENY_WRITE`
6036    */
6037   if (bSTGM_NOSNAPSHOT)
6038   {
6039     if ( ! ( bSTGM_TRANSACTED &&
6040            !(bSTGM_SHARE_EXCLUSIVE || bSTGM_SHARE_DENY_WRITE)) )
6041     return E_FAIL;
6042   }
6043
6044   return S_OK;
6045 }
6046
6047 /****************************************************************************
6048  *      GetShareModeFromSTGM
6049  *
6050  * This method will return a share mode flag from a STGM value.
6051  * The STGM value is assumed valid.
6052  */
6053 static DWORD GetShareModeFromSTGM(DWORD stgm)
6054 {
6055   DWORD dwShareMode = 0;
6056   BOOL bSTGM_SHARE_DENY_NONE  =
6057                      ((stgm & STGM_SHARE_DENY_NONE)  == STGM_SHARE_DENY_NONE);
6058
6059   BOOL bSTGM_SHARE_DENY_READ  =
6060                      ((stgm & STGM_SHARE_DENY_READ)  == STGM_SHARE_DENY_READ);
6061
6062   BOOL bSTGM_SHARE_DENY_WRITE =
6063                      ((stgm & STGM_SHARE_DENY_WRITE) == STGM_SHARE_DENY_WRITE);
6064
6065   BOOL bSTGM_SHARE_EXCLUSIVE  =
6066                      ((stgm & STGM_SHARE_EXCLUSIVE)  == STGM_SHARE_EXCLUSIVE);
6067
6068   if ((bSTGM_SHARE_EXCLUSIVE) || (bSTGM_SHARE_DENY_READ))
6069     dwShareMode = 0;
6070
6071   if (bSTGM_SHARE_DENY_NONE)
6072     dwShareMode = FILE_SHARE_READ | FILE_SHARE_WRITE;
6073
6074   if (bSTGM_SHARE_DENY_WRITE)
6075     dwShareMode = FILE_SHARE_READ;
6076
6077   return dwShareMode;
6078 }
6079
6080 /****************************************************************************
6081  *      GetAccessModeFromSTGM
6082  *
6083  * This method will return an access mode flag from a STGM value.
6084  * The STGM value is assumed valid.
6085  */
6086 static DWORD GetAccessModeFromSTGM(DWORD stgm)
6087 {
6088   DWORD dwDesiredAccess = GENERIC_READ;
6089   BOOL bSTGM_WRITE     = ((stgm & STGM_WRITE) == STGM_WRITE);
6090   BOOL bSTGM_READWRITE = ((stgm & STGM_READWRITE) == STGM_READWRITE);
6091   BOOL bSTGM_READ      = ! (bSTGM_WRITE || bSTGM_READWRITE);
6092
6093   if (bSTGM_READ)
6094     dwDesiredAccess = GENERIC_READ;
6095
6096   if (bSTGM_WRITE)
6097     dwDesiredAccess |= GENERIC_WRITE;
6098
6099   if (bSTGM_READWRITE)
6100     dwDesiredAccess = GENERIC_READ | GENERIC_WRITE;
6101
6102   return dwDesiredAccess;
6103 }
6104
6105 /****************************************************************************
6106  *      GetCreationModeFromSTGM
6107  *
6108  * This method will return a creation mode flag from a STGM value.
6109  * The STGM value is assumed valid.
6110  */
6111 static DWORD GetCreationModeFromSTGM(DWORD stgm)
6112 {
6113   if ( stgm & STGM_CREATE)
6114     return CREATE_ALWAYS;
6115   if (stgm & STGM_CONVERT) {
6116     FIXME("STGM_CONVERT not implemented!\n");
6117     return CREATE_NEW;
6118   }
6119   /* All other cases */
6120   if (stgm & ~ (STGM_CREATE|STGM_CONVERT))
6121         FIXME("unhandled storage mode : 0x%08lx\n",stgm & ~ (STGM_CREATE|STGM_CONVERT));
6122   return CREATE_NEW;
6123 }
6124
6125
6126 /*************************************************************************
6127  * OLECONVERT_LoadOLE10 [Internal]
6128  *
6129  * Loads the OLE10 STREAM to memory
6130  *
6131  * PARAMS
6132  *     pOleStream   [I] The OLESTREAM
6133  *     pData        [I] Data Structure for the OLESTREAM Data
6134  *
6135  * RETURNS
6136  *     Success:  S_OK
6137  *     Failure:  CONVERT10_E_OLESTREAM_GET for invalid Get
6138  *               CONVERT10_E_OLESTREAM_FMT if the OLEID is invalide
6139  *
6140  * NOTES
6141  *     This function is used by OleConvertOLESTREAMToIStorage only.
6142  *
6143  *     Memory allocated for pData must be freed by the caller
6144  */
6145 HRESULT OLECONVERT_LoadOLE10(LPOLESTREAM pOleStream, OLECONVERT_OLESTREAM_DATA *pData, BOOL bStrem1)
6146 {
6147         DWORD dwSize;
6148         HRESULT hRes = S_OK;
6149         int nTryCnt=0;
6150         int max_try = 6;
6151
6152         pData->pData = NULL;
6153         pData->pstrOleObjFileName = (CHAR *) NULL;
6154
6155         for( nTryCnt=0;nTryCnt < max_try; nTryCnt++)
6156         {
6157         /* Get the OleID */
6158         dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6159         if(dwSize != sizeof(pData->dwOleID))
6160         {
6161                 hRes = CONVERT10_E_OLESTREAM_GET;
6162         }
6163         else if(pData->dwOleID != OLESTREAM_ID)
6164         {
6165                 hRes = CONVERT10_E_OLESTREAM_FMT;
6166         }
6167                 else
6168                 {
6169                         hRes = S_OK;
6170                         break;
6171                 }
6172         }
6173
6174         if(hRes == S_OK)
6175         {
6176                 /* Get the TypeID...more info needed for this field */
6177                 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6178                 if(dwSize != sizeof(pData->dwTypeID))
6179                 {
6180                         hRes = CONVERT10_E_OLESTREAM_GET;
6181                 }
6182         }
6183         if(hRes == S_OK)
6184         {
6185                 if(pData->dwTypeID != 0)
6186                 {
6187                         /* Get the length of the OleTypeName */
6188                         dwSize = pOleStream->lpstbl->Get(pOleStream, (void *) &(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6189                         if(dwSize != sizeof(pData->dwOleTypeNameLength))
6190                         {
6191                                 hRes = CONVERT10_E_OLESTREAM_GET;
6192                         }
6193
6194                         if(hRes == S_OK)
6195                         {
6196                                 if(pData->dwOleTypeNameLength > 0)
6197                                 {
6198                                         /* Get the OleTypeName */
6199                                         dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->strOleTypeName, pData->dwOleTypeNameLength);
6200                                         if(dwSize != pData->dwOleTypeNameLength)
6201                                         {
6202                                                 hRes = CONVERT10_E_OLESTREAM_GET;
6203                                         }
6204                                 }
6205                         }
6206                         if(bStrem1)
6207                         {
6208                                 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwOleObjFileNameLength), sizeof(pData->dwOleObjFileNameLength));
6209                                 if(dwSize != sizeof(pData->dwOleObjFileNameLength))
6210                                 {
6211                                         hRes = CONVERT10_E_OLESTREAM_GET;
6212                                 }
6213                         if(hRes == S_OK)
6214                         {
6215                                         if(pData->dwOleObjFileNameLength < 1) /* there is no file name exist */
6216                                                 pData->dwOleObjFileNameLength = sizeof(pData->dwOleObjFileNameLength);
6217                                         pData->pstrOleObjFileName = (CHAR *)HeapAlloc(GetProcessHeap(), 0, pData->dwOleObjFileNameLength);
6218                                         if(pData->pstrOleObjFileName)
6219                                         {
6220                                                 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->pstrOleObjFileName),pData->dwOleObjFileNameLength);
6221                                                 if(dwSize != pData->dwOleObjFileNameLength)
6222                                                 {
6223                                                         hRes = CONVERT10_E_OLESTREAM_GET;
6224                                                 }
6225                                         }
6226                                         else
6227                                                 hRes = CONVERT10_E_OLESTREAM_GET;
6228                                 }
6229                         }
6230                         else
6231                         {
6232                                 /* Get the Width of the Metafile */
6233                                 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6234                                 if(dwSize != sizeof(pData->dwMetaFileWidth))
6235                                 {
6236                                         hRes = CONVERT10_E_OLESTREAM_GET;
6237                                 }
6238                         if(hRes == S_OK)
6239                         {
6240                                 /* Get the Height of the Metafile */
6241                                 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6242                                 if(dwSize != sizeof(pData->dwMetaFileHeight))
6243                                 {
6244                                         hRes = CONVERT10_E_OLESTREAM_GET;
6245                                 }
6246                         }
6247                         }
6248                         if(hRes == S_OK)
6249                         {
6250                                 /* Get the Length of the Data */
6251                                 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6252                                 if(dwSize != sizeof(pData->dwDataLength))
6253                                 {
6254                                         hRes = CONVERT10_E_OLESTREAM_GET;
6255                                 }
6256                         }
6257
6258                         if(hRes == S_OK) /* I don't know what is this 8 byts information is we have to figure out */
6259                         {
6260                                 if(!bStrem1) /* if it is a second OLE stream data */
6261                                 {
6262                                         pData->dwDataLength -= 8;
6263                                         dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)(pData->strUnknown), sizeof(pData->strUnknown));
6264                                         if(dwSize != sizeof(pData->strUnknown))
6265                                         {
6266                                                 hRes = CONVERT10_E_OLESTREAM_GET;
6267                                         }
6268                                 }
6269                         }
6270                         if(hRes == S_OK)
6271                         {
6272                                 if(pData->dwDataLength > 0)
6273                                 {
6274                                         pData->pData = (BYTE *)HeapAlloc(GetProcessHeap(),0,pData->dwDataLength);
6275
6276                                         /* Get Data (ex. IStorage, Metafile, or BMP) */
6277                                         if(pData->pData)
6278                                         {
6279                                                 dwSize = pOleStream->lpstbl->Get(pOleStream, (void *)pData->pData, pData->dwDataLength);
6280                                                 if(dwSize != pData->dwDataLength)
6281                                                 {
6282                                                         hRes = CONVERT10_E_OLESTREAM_GET;
6283                                                 }
6284                                         }
6285                                         else
6286                                         {
6287                                                 hRes = CONVERT10_E_OLESTREAM_GET;
6288                                         }
6289                                 }
6290                         }
6291                 }
6292         }
6293         return hRes;
6294 }
6295
6296 /*************************************************************************
6297  * OLECONVERT_SaveOLE10 [Internal]
6298  *
6299  * Saves the OLE10 STREAM From memory
6300  *
6301  * PARAMS
6302  *     pData        [I] Data Structure for the OLESTREAM Data
6303  *     pOleStream   [I] The OLESTREAM to save
6304  *
6305  * RETURNS
6306  *     Success:  S_OK
6307  *     Failure:  CONVERT10_E_OLESTREAM_PUT for invalid Put
6308  *
6309  * NOTES
6310  *     This function is used by OleConvertIStorageToOLESTREAM only.
6311  *
6312  */
6313 HRESULT OLECONVERT_SaveOLE10(OLECONVERT_OLESTREAM_DATA *pData, LPOLESTREAM pOleStream)
6314 {
6315     DWORD dwSize;
6316     HRESULT hRes = S_OK;
6317
6318
6319    /* Set the OleID */
6320     dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleID), sizeof(pData->dwOleID));
6321     if(dwSize != sizeof(pData->dwOleID))
6322     {
6323         hRes = CONVERT10_E_OLESTREAM_PUT;
6324     }
6325
6326     if(hRes == S_OK)
6327     {
6328         /* Set the TypeID */
6329         dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwTypeID), sizeof(pData->dwTypeID));
6330         if(dwSize != sizeof(pData->dwTypeID))
6331         {
6332             hRes = CONVERT10_E_OLESTREAM_PUT;
6333         }
6334     }
6335
6336     if(pData->dwOleID == OLESTREAM_ID && pData->dwTypeID != 0 && hRes == S_OK)
6337     {
6338         /* Set the Length of the OleTypeName */
6339         dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwOleTypeNameLength), sizeof(pData->dwOleTypeNameLength));
6340         if(dwSize != sizeof(pData->dwOleTypeNameLength))
6341         {
6342             hRes = CONVERT10_E_OLESTREAM_PUT;
6343         }
6344
6345         if(hRes == S_OK)
6346         {
6347             if(pData->dwOleTypeNameLength > 0)
6348             {
6349                 /* Set the OleTypeName */
6350                 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)  pData->strOleTypeName, pData->dwOleTypeNameLength);
6351                 if(dwSize != pData->dwOleTypeNameLength)
6352                 {
6353                     hRes = CONVERT10_E_OLESTREAM_PUT;
6354                 }
6355             }
6356         }
6357
6358         if(hRes == S_OK)
6359         {
6360             /* Set the width of the Metafile */
6361             dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileWidth), sizeof(pData->dwMetaFileWidth));
6362             if(dwSize != sizeof(pData->dwMetaFileWidth))
6363             {
6364                 hRes = CONVERT10_E_OLESTREAM_PUT;
6365             }
6366         }
6367
6368         if(hRes == S_OK)
6369         {
6370             /* Set the height of the Metafile */
6371             dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwMetaFileHeight), sizeof(pData->dwMetaFileHeight));
6372             if(dwSize != sizeof(pData->dwMetaFileHeight))
6373             {
6374                 hRes = CONVERT10_E_OLESTREAM_PUT;
6375             }
6376         }
6377
6378         if(hRes == S_OK)
6379         {
6380             /* Set the length of the Data */
6381             dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)&(pData->dwDataLength), sizeof(pData->dwDataLength));
6382             if(dwSize != sizeof(pData->dwDataLength))
6383             {
6384                 hRes = CONVERT10_E_OLESTREAM_PUT;
6385             }
6386         }
6387
6388         if(hRes == S_OK)
6389         {
6390             if(pData->dwDataLength > 0)
6391             {
6392                 /* Set the Data (eg. IStorage, Metafile, Bitmap) */
6393                 dwSize = pOleStream->lpstbl->Put(pOleStream, (void *)  pData->pData, pData->dwDataLength);
6394                 if(dwSize != pData->dwDataLength)
6395                 {
6396                     hRes = CONVERT10_E_OLESTREAM_PUT;
6397                 }
6398             }
6399         }
6400     }
6401     return hRes;
6402 }
6403
6404 /*************************************************************************
6405  * OLECONVERT_GetOLE20FromOLE10[Internal]
6406  *
6407  * This function copies OLE10 Data (the IStorage in the OLESTREAM) to disk,
6408  * opens it, and copies the content to the dest IStorage for
6409  * OleConvertOLESTREAMToIStorage
6410  *
6411  *
6412  * PARAMS
6413  *     pDestStorage  [I] The IStorage to copy the data to
6414  *     pBuffer       [I] Buffer that contains the IStorage from the OLESTREAM
6415  *     nBufferLength [I] The size of the buffer
6416  *
6417  * RETURNS
6418  *     Nothing
6419  *
6420  * NOTES
6421  *
6422  *
6423  */
6424 void OLECONVERT_GetOLE20FromOLE10(LPSTORAGE pDestStorage, BYTE *pBuffer, DWORD nBufferLength)
6425 {
6426     HRESULT hRes;
6427     HANDLE hFile;
6428     IStorage *pTempStorage;
6429     DWORD dwNumOfBytesWritten;
6430     WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6431     static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6432
6433     /* Create a temp File */
6434     GetTempPathW(MAX_PATH, wstrTempDir);
6435     GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6436     hFile = CreateFileW(wstrTempFile, GENERIC_WRITE, 0, NULL, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, 0);
6437
6438     if(hFile != INVALID_HANDLE_VALUE)
6439     {
6440         /* Write IStorage Data to File */
6441         WriteFile(hFile, pBuffer, nBufferLength, &dwNumOfBytesWritten, NULL);
6442         CloseHandle(hFile);
6443
6444         /* Open and copy temp storage to the Dest Storage */
6445         hRes = StgOpenStorage(wstrTempFile, NULL, STGM_READ, NULL, 0, &pTempStorage);
6446         if(hRes == S_OK)
6447         {
6448             hRes = StorageImpl_CopyTo(pTempStorage, 0, NULL, NULL, pDestStorage);
6449             StorageBaseImpl_Release(pTempStorage);
6450         }
6451         DeleteFileW(wstrTempFile);
6452     }
6453 }
6454
6455
6456 /*************************************************************************
6457  * OLECONVERT_WriteOLE20ToBuffer [Internal]
6458  *
6459  * Saves the OLE10 STREAM From memory
6460  *
6461  * PARAMS
6462  *     pStorage  [I] The Src IStorage to copy
6463  *     pData     [I] The Dest Memory to write to.
6464  *
6465  * RETURNS
6466  *     The size in bytes allocated for pData
6467  *
6468  * NOTES
6469  *     Memory allocated for pData must be freed by the caller
6470  *
6471  *     Used by OleConvertIStorageToOLESTREAM only.
6472  *
6473  */
6474 DWORD OLECONVERT_WriteOLE20ToBuffer(LPSTORAGE pStorage, BYTE **pData)
6475 {
6476     HANDLE hFile;
6477     HRESULT hRes;
6478     DWORD nDataLength = 0;
6479     IStorage *pTempStorage;
6480     WCHAR wstrTempDir[MAX_PATH], wstrTempFile[MAX_PATH];
6481     static const WCHAR wstrPrefix[] = {'s', 'i', 's', 0};
6482
6483     *pData = NULL;
6484
6485     /* Create temp Storage */
6486     GetTempPathW(MAX_PATH, wstrTempDir);
6487     GetTempFileNameW(wstrTempDir, wstrPrefix, 0, wstrTempFile);
6488     hRes = StgCreateDocfile(wstrTempFile, STGM_CREATE | STGM_READWRITE | STGM_SHARE_EXCLUSIVE, 0, &pTempStorage);
6489
6490     if(hRes == S_OK)
6491     {
6492         /* Copy Src Storage to the Temp Storage */
6493         StorageImpl_CopyTo(pStorage, 0, NULL, NULL, pTempStorage);
6494         StorageBaseImpl_Release(pTempStorage);
6495
6496         /* Open Temp Storage as a file and copy to memory */
6497         hFile = CreateFileW(wstrTempFile, GENERIC_READ, 0, NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
6498         if(hFile != INVALID_HANDLE_VALUE)
6499         {
6500             nDataLength = GetFileSize(hFile, NULL);
6501             *pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,nDataLength);
6502             ReadFile(hFile, *pData, nDataLength, &nDataLength, 0);
6503             CloseHandle(hFile);
6504         }
6505         DeleteFileW(wstrTempFile);
6506     }
6507     return nDataLength;
6508 }
6509
6510 /*************************************************************************
6511  * OLECONVERT_CreateOleStream [Internal]
6512  *
6513  * Creates the "\001OLE" stream in the IStorage if necessary.
6514  *
6515  * PARAMS
6516  *     pStorage     [I] Dest storage to create the stream in
6517  *
6518  * RETURNS
6519  *     Nothing
6520  *
6521  * NOTES
6522  *     This function is used by OleConvertOLESTREAMToIStorage only.
6523  *
6524  *     This stream is still unknown, MS Word seems to have extra data
6525  *     but since the data is stored in the OLESTREAM there should be
6526  *     no need to recreate the stream.  If the stream is manually
6527  *     deleted it will create it with this default data.
6528  *
6529  */
6530 void OLECONVERT_CreateOleStream(LPSTORAGE pStorage)
6531 {
6532     HRESULT hRes;
6533     IStream *pStream;
6534     static const WCHAR wstrStreamName[] = {1,'O', 'l', 'e', 0};
6535     BYTE pOleStreamHeader [] =
6536     {
6537         0x01, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00, 0x00,
6538         0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
6539         0x00, 0x00, 0x00, 0x00
6540     };
6541
6542     /* Create stream if not present */
6543     hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6544         STGM_WRITE  | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6545
6546     if(hRes == S_OK)
6547     {
6548         /* Write default Data */
6549         hRes = IStream_Write(pStream, pOleStreamHeader, sizeof(pOleStreamHeader), NULL);
6550         IStream_Release(pStream);
6551     }
6552 }
6553
6554 /* write a string to a stream, preceded by its length */
6555 static HRESULT STREAM_WriteString( IStream *stm, LPCWSTR string )
6556 {
6557     HRESULT r;
6558     LPSTR str;
6559     DWORD len = 0;
6560
6561     if( string )
6562         len = WideCharToMultiByte( CP_ACP, 0, string, -1, NULL, 0, NULL, NULL);
6563     r = IStream_Write( stm, &len, sizeof(len), NULL);
6564     if( FAILED( r ) )
6565         return r;
6566     if(len == 0)
6567         return r;
6568     str = CoTaskMemAlloc( len );
6569     WideCharToMultiByte( CP_ACP, 0, string, -1, str, len, NULL, NULL);
6570     r = IStream_Write( stm, str, len, NULL);
6571     CoTaskMemFree( str );
6572     return r;
6573 }
6574
6575 /* read a string preceded by its length from a stream */
6576 static HRESULT STREAM_ReadString( IStream *stm, LPWSTR *string )
6577 {
6578     HRESULT r;
6579     DWORD len, count = 0;
6580     LPSTR str;
6581     LPWSTR wstr;
6582
6583     r = IStream_Read( stm, &len, sizeof(len), &count );
6584     if( FAILED( r ) )
6585         return r;
6586     if( count != sizeof(len) )
6587         return E_OUTOFMEMORY;
6588
6589     TRACE("%ld bytes\n",len);
6590     
6591     str = CoTaskMemAlloc( len );
6592     if( !str )
6593         return E_OUTOFMEMORY;
6594     count = 0;
6595     r = IStream_Read( stm, str, len, &count );
6596     if( FAILED( r ) )
6597         return r;
6598     if( count != len )
6599     {
6600         CoTaskMemFree( str );
6601         return E_OUTOFMEMORY;
6602     }
6603
6604     TRACE("Read string %s\n",debugstr_an(str,len));
6605
6606     len = MultiByteToWideChar( CP_ACP, 0, str, count, NULL, 0 );
6607     wstr = CoTaskMemAlloc( (len + 1)*sizeof (WCHAR) );
6608     if( wstr )
6609          MultiByteToWideChar( CP_ACP, 0, str, count, wstr, len );
6610     CoTaskMemFree( str );
6611
6612     *string = wstr;
6613
6614     return r;
6615 }
6616
6617
6618 static HRESULT STORAGE_WriteCompObj( LPSTORAGE pstg, CLSID *clsid,
6619     LPCWSTR lpszUserType, LPCWSTR szClipName, LPCWSTR szProgIDName )
6620 {
6621     IStream *pstm;
6622     HRESULT r = S_OK;
6623     static const WCHAR szwStreamName[] = {1, 'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6624
6625     static const BYTE unknown1[12] =
6626        { 0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00,
6627          0xFF, 0xFF, 0xFF, 0xFF};
6628     static const BYTE unknown2[16] =
6629        { 0xF4, 0x39, 0xB2, 0x71, 0x00, 0x00, 0x00, 0x00,
6630          0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 };
6631
6632     TRACE("%p %s %s %s %s\n", pstg, debugstr_guid(clsid),
6633            debugstr_w(lpszUserType), debugstr_w(szClipName),
6634            debugstr_w(szProgIDName));
6635
6636     /*  Create a CompObj stream if it doesn't exist */
6637     r = IStorage_CreateStream(pstg, szwStreamName,
6638         STGM_WRITE  | STGM_SHARE_EXCLUSIVE, 0, 0, &pstm );
6639     if( FAILED (r) )
6640         return r;
6641
6642     /* Write CompObj Structure to stream */
6643     r = IStream_Write(pstm, unknown1, sizeof(unknown1), NULL);
6644
6645     if( SUCCEEDED( r ) )
6646         r = WriteClassStm( pstm, clsid );
6647
6648     if( SUCCEEDED( r ) )
6649         r = STREAM_WriteString( pstm, lpszUserType );
6650     if( SUCCEEDED( r ) )
6651         r = STREAM_WriteString( pstm, szClipName );
6652     if( SUCCEEDED( r ) )
6653         r = STREAM_WriteString( pstm, szProgIDName );
6654     if( SUCCEEDED( r ) )
6655         r = IStream_Write(pstm, unknown2, sizeof(unknown2), NULL);
6656
6657     IStream_Release( pstm );
6658
6659     return r;
6660 }
6661
6662 /* enumerate HKEY_CLASSES_ROOT\\CLSID looking for a CLSID whose name matches */
6663 static HRESULT CLSIDFromUserType(LPCWSTR lpszUserType, CLSID *clsid)
6664 {
6665     LONG r, i, len;
6666     ULONG count;
6667     WCHAR szKey[0x40];
6668     HKEY hkey, hkeyclsid;
6669     LPWSTR buffer = NULL;
6670     BOOL found = FALSE;
6671     static const WCHAR szclsid[] = { 'C','L','S','I','D',0 };
6672
6673     TRACE("Finding CLSID for %s\n", debugstr_w(lpszUserType));
6674
6675     r = RegOpenKeyW( HKEY_CLASSES_ROOT, szclsid, &hkeyclsid );
6676     if( r )
6677         return E_INVALIDARG;
6678
6679     len = lstrlenW( lpszUserType ) + 1;
6680     buffer = CoTaskMemAlloc( len * sizeof (WCHAR) );
6681     if( !buffer )
6682         goto end;
6683
6684     for(i=0; !found; i++ )
6685     {
6686         r = RegEnumKeyW( hkeyclsid, i, szKey, sizeof(szKey)/sizeof(WCHAR));
6687         if( r != ERROR_SUCCESS )
6688             break;
6689         hkey = 0;
6690         r = RegOpenKeyW( hkeyclsid, szKey, &hkey );
6691         if( r != ERROR_SUCCESS )
6692             break;
6693         count = len * sizeof (WCHAR);
6694         r = RegQueryValueW( hkey, NULL, buffer, &count );
6695         found = ( r == ERROR_SUCCESS ) &&
6696                 ( count == len*sizeof(WCHAR) ) && 
6697                 !lstrcmpW( buffer, lpszUserType ) ;
6698         RegCloseKey( hkey );
6699     }
6700
6701 end:
6702     if( buffer )
6703         CoTaskMemFree( buffer );
6704     RegCloseKey( hkeyclsid );
6705
6706     if ( !found )
6707         return E_INVALIDARG;
6708
6709     TRACE("clsid is %s\n", debugstr_w( szKey ) );
6710
6711     r = CLSIDFromString( szKey, clsid );
6712
6713     return r;
6714 }
6715
6716
6717 /***********************************************************************
6718  *               WriteFmtUserTypeStg (OLE32.@)
6719  */
6720 HRESULT WINAPI WriteFmtUserTypeStg(
6721           LPSTORAGE pstg, CLIPFORMAT cf, LPOLESTR lpszUserType)
6722 {
6723     HRESULT r;
6724     WCHAR szwClipName[0x40];
6725     WCHAR szCLSIDName[OLESTREAM_MAX_STR_LEN];
6726     CLSID clsid;
6727     LPWSTR wstrProgID;
6728     DWORD n;
6729     LPMALLOC allocator = NULL;
6730
6731     TRACE("(%p,%x,%s)\n",pstg,cf,debugstr_w(lpszUserType));
6732
6733     r = CoGetMalloc(0, &allocator);
6734     if( FAILED( r) )
6735         return E_OUTOFMEMORY;
6736
6737     /* get the clipboard format name */
6738     n = GetClipboardFormatNameW( cf, szwClipName, sizeof(szwClipName) );
6739     szwClipName[n]=0;
6740
6741     TRACE("Clipboard name is %s\n", debugstr_w(szwClipName));
6742
6743     /* Get the CLSID */
6744     szCLSIDName[0]=0;
6745     r = CLSIDFromUserType(lpszUserType, &clsid);
6746     if( FAILED( r ) )
6747         return r;
6748
6749     TRACE("CLSID is %s\n",debugstr_guid(&clsid));
6750
6751     /* get the real program ID */
6752     r = ProgIDFromCLSID( &clsid, &wstrProgID);
6753     if( FAILED( r ) )
6754         return r;
6755
6756     TRACE("progid is %s\n",debugstr_w(wstrProgID));
6757
6758     /* if we have a good string, write the stream */
6759     if( wstrProgID )
6760         r = STORAGE_WriteCompObj( pstg, &clsid, 
6761                 lpszUserType, szwClipName, wstrProgID );
6762     else
6763         r = E_OUTOFMEMORY;
6764
6765     IMalloc_Free( allocator, wstrProgID);
6766
6767     return r;
6768 }
6769
6770
6771 /******************************************************************************
6772  *              ReadFmtUserTypeStg        [OLE32.@]
6773  */
6774 HRESULT WINAPI ReadFmtUserTypeStg (LPSTORAGE pstg, CLIPFORMAT* pcf, LPOLESTR* lplpszUserType)
6775 {
6776     HRESULT r;
6777     IStream *stm = 0;
6778     static const WCHAR szCompObj[] = { 1, 'C','o','m','p','O','b','j', 0 };
6779     unsigned char unknown1[12];
6780     unsigned char unknown2[16];
6781     DWORD count;
6782     LPWSTR szProgIDName = NULL, szCLSIDName = NULL, szOleTypeName = NULL;
6783     CLSID clsid;
6784
6785     TRACE("(%p,%p,%p)\n", pstg, pcf, lplpszUserType);
6786
6787     r = IStorage_OpenStream( pstg, szCompObj, NULL, 
6788                     STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &stm );
6789     if( FAILED ( r ) )
6790     {
6791         ERR("Failed to open stream\n");
6792         return r;
6793     }
6794
6795     /* read the various parts of the structure */
6796     r = IStream_Read( stm, unknown1, sizeof(unknown1), &count );
6797     if( FAILED( r ) || ( count != sizeof(unknown1) ) )
6798         goto end;
6799     r = ReadClassStm( stm, &clsid );
6800     if( FAILED( r ) )
6801         goto end;
6802
6803     r = STREAM_ReadString( stm, &szCLSIDName );
6804     if( FAILED( r ) )
6805         goto end;
6806
6807     r = STREAM_ReadString( stm, &szOleTypeName );
6808     if( FAILED( r ) )
6809         goto end;
6810
6811     r = STREAM_ReadString( stm, &szProgIDName );
6812     if( FAILED( r ) )
6813         goto end;
6814
6815     r = IStream_Read( stm, unknown2, sizeof(unknown2), &count );
6816     if( FAILED( r ) || ( count != sizeof(unknown2) ) )
6817         goto end;
6818
6819     /* ok, success... now we just need to store what we found */
6820     if( pcf )
6821         *pcf = RegisterClipboardFormatW( szOleTypeName );
6822     CoTaskMemFree( szOleTypeName );
6823
6824     if( lplpszUserType )
6825         *lplpszUserType = szCLSIDName;
6826     CoTaskMemFree( szProgIDName );
6827
6828 end:
6829     IStream_Release( stm );
6830
6831     return r;
6832 }
6833
6834
6835 /*************************************************************************
6836  * OLECONVERT_CreateCompObjStream [Internal]
6837  *
6838  * Creates a "\001CompObj" is the destination IStorage if necessary.
6839  *
6840  * PARAMS
6841  *     pStorage       [I] The dest IStorage to create the CompObj Stream
6842  *                        if necessary.
6843  *     strOleTypeName [I] The ProgID
6844  *
6845  * RETURNS
6846  *     Success:  S_OK
6847  *     Failure:  REGDB_E_CLASSNOTREG if cannot reconstruct the stream
6848  *
6849  * NOTES
6850  *     This function is used by OleConvertOLESTREAMToIStorage only.
6851  *
6852  *     The stream data is stored in the OLESTREAM and there should be
6853  *     no need to recreate the stream.  If the stream is manually
6854  *     deleted it will attempt to create it by querying the registry.
6855  *
6856  *
6857  */
6858 HRESULT OLECONVERT_CreateCompObjStream(LPSTORAGE pStorage, LPCSTR strOleTypeName)
6859 {
6860     IStream *pStream;
6861     HRESULT hStorageRes, hRes = S_OK;
6862     OLECONVERT_ISTORAGE_COMPOBJ IStorageCompObj;
6863     static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
6864     WCHAR bufferW[OLESTREAM_MAX_STR_LEN];
6865
6866     BYTE pCompObjUnknown1[] = {0x01, 0x00, 0xFE, 0xFF, 0x03, 0x0A, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0xFF};
6867     BYTE pCompObjUnknown2[] = {0xF4, 0x39, 0xB2, 0x71};
6868
6869     /* Initialize the CompObj structure */
6870     memset(&IStorageCompObj, 0, sizeof(IStorageCompObj));
6871     memcpy(&(IStorageCompObj.byUnknown1), pCompObjUnknown1, sizeof(pCompObjUnknown1));
6872     memcpy(&(IStorageCompObj.byUnknown2), pCompObjUnknown2, sizeof(pCompObjUnknown2));
6873
6874
6875     /*  Create a CompObj stream if it doesn't exist */
6876     hStorageRes = IStorage_CreateStream(pStorage, wstrStreamName,
6877         STGM_WRITE  | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6878     if(hStorageRes == S_OK)
6879     {
6880         /* copy the OleTypeName to the compobj struct */
6881         IStorageCompObj.dwOleTypeNameLength = strlen(strOleTypeName)+1;
6882         strcpy(IStorageCompObj.strOleTypeName, strOleTypeName);
6883
6884         /* copy the OleTypeName to the compobj struct */
6885         /* Note: in the test made, these were Identical      */
6886         IStorageCompObj.dwProgIDNameLength = strlen(strOleTypeName)+1;
6887         strcpy(IStorageCompObj.strProgIDName, strOleTypeName);
6888
6889         /* Get the CLSID */
6890         MultiByteToWideChar( CP_ACP, 0, IStorageCompObj.strProgIDName, -1,
6891                              bufferW, OLESTREAM_MAX_STR_LEN );
6892         hRes = CLSIDFromProgID(bufferW, &(IStorageCompObj.clsid));
6893
6894         if(hRes == S_OK)
6895         {
6896             HKEY hKey;
6897             LONG hErr;
6898             /* Get the CLSID Default Name from the Registry */
6899             hErr = RegOpenKeyA(HKEY_CLASSES_ROOT, IStorageCompObj.strProgIDName, &hKey);
6900             if(hErr == ERROR_SUCCESS)
6901             {
6902                 char strTemp[OLESTREAM_MAX_STR_LEN];
6903                 IStorageCompObj.dwCLSIDNameLength = OLESTREAM_MAX_STR_LEN;
6904                 hErr = RegQueryValueA(hKey, NULL, strTemp, &(IStorageCompObj.dwCLSIDNameLength));
6905                 if(hErr == ERROR_SUCCESS)
6906                 {
6907                     strcpy(IStorageCompObj.strCLSIDName, strTemp);
6908                 }
6909                 RegCloseKey(hKey);
6910             }
6911         }
6912
6913         /* Write CompObj Structure to stream */
6914         hRes = IStream_Write(pStream, IStorageCompObj.byUnknown1, sizeof(IStorageCompObj.byUnknown1), NULL);
6915
6916         WriteClassStm(pStream,&(IStorageCompObj.clsid));
6917
6918         hRes = IStream_Write(pStream, &(IStorageCompObj.dwCLSIDNameLength), sizeof(IStorageCompObj.dwCLSIDNameLength), NULL);
6919         if(IStorageCompObj.dwCLSIDNameLength > 0)
6920         {
6921             hRes = IStream_Write(pStream, IStorageCompObj.strCLSIDName, IStorageCompObj.dwCLSIDNameLength, NULL);
6922         }
6923         hRes = IStream_Write(pStream, &(IStorageCompObj.dwOleTypeNameLength) , sizeof(IStorageCompObj.dwOleTypeNameLength), NULL);
6924         if(IStorageCompObj.dwOleTypeNameLength > 0)
6925         {
6926             hRes = IStream_Write(pStream, IStorageCompObj.strOleTypeName , IStorageCompObj.dwOleTypeNameLength, NULL);
6927         }
6928         hRes = IStream_Write(pStream, &(IStorageCompObj.dwProgIDNameLength) , sizeof(IStorageCompObj.dwProgIDNameLength), NULL);
6929         if(IStorageCompObj.dwProgIDNameLength > 0)
6930         {
6931             hRes = IStream_Write(pStream, IStorageCompObj.strProgIDName , IStorageCompObj.dwProgIDNameLength, NULL);
6932         }
6933         hRes = IStream_Write(pStream, IStorageCompObj.byUnknown2 , sizeof(IStorageCompObj.byUnknown2), NULL);
6934         IStream_Release(pStream);
6935     }
6936     return hRes;
6937 }
6938
6939
6940 /*************************************************************************
6941  * OLECONVERT_CreateOlePresStream[Internal]
6942  *
6943  * Creates the "\002OlePres000" Stream with the Metafile data
6944  *
6945  * PARAMS
6946  *     pStorage     [I] The dest IStorage to create \002OLEPres000 stream in.
6947  *     dwExtentX    [I] Width of the Metafile
6948  *     dwExtentY    [I] Height of the Metafile
6949  *     pData        [I] Metafile data
6950  *     dwDataLength [I] Size of the Metafile data
6951  *
6952  * RETURNS
6953  *     Success:  S_OK
6954  *     Failure:  CONVERT10_E_OLESTREAM_PUT for invalid Put
6955  *
6956  * NOTES
6957  *     This function is used by OleConvertOLESTREAMToIStorage only.
6958  *
6959  */
6960 void OLECONVERT_CreateOlePresStream(LPSTORAGE pStorage, DWORD dwExtentX, DWORD dwExtentY , BYTE *pData, DWORD dwDataLength)
6961 {
6962     HRESULT hRes;
6963     IStream *pStream;
6964     static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
6965     BYTE pOlePresStreamHeader [] =
6966     {
6967         0xFF, 0xFF, 0xFF, 0xFF, 0x03, 0x00, 0x00, 0x00,
6968         0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6969         0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6970         0x00, 0x00, 0x00, 0x00
6971     };
6972
6973     BYTE pOlePresStreamHeaderEmpty [] =
6974     {
6975         0x00, 0x00, 0x00, 0x00,
6976         0x04, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00, 0x00,
6977         0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0x00, 0x00,
6978         0x00, 0x00, 0x00, 0x00
6979     };
6980
6981     /* Create the OlePres000 Stream */
6982     hRes = IStorage_CreateStream(pStorage, wstrStreamName,
6983         STGM_CREATE | STGM_WRITE  | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
6984
6985     if(hRes == S_OK)
6986     {
6987         DWORD nHeaderSize;
6988         OLECONVERT_ISTORAGE_OLEPRES OlePres;
6989
6990         memset(&OlePres, 0, sizeof(OlePres));
6991         /* Do we have any metafile data to save */
6992         if(dwDataLength > 0)
6993         {
6994             memcpy(OlePres.byUnknown1, pOlePresStreamHeader, sizeof(pOlePresStreamHeader));
6995             nHeaderSize = sizeof(pOlePresStreamHeader);
6996         }
6997         else
6998         {
6999             memcpy(OlePres.byUnknown1, pOlePresStreamHeaderEmpty, sizeof(pOlePresStreamHeaderEmpty));
7000             nHeaderSize = sizeof(pOlePresStreamHeaderEmpty);
7001         }
7002         /* Set width and height of the metafile */
7003         OlePres.dwExtentX = dwExtentX;
7004         OlePres.dwExtentY = -dwExtentY;
7005
7006         /* Set Data and Length */
7007         if(dwDataLength > sizeof(METAFILEPICT16))
7008         {
7009             OlePres.dwSize = dwDataLength - sizeof(METAFILEPICT16);
7010             OlePres.pData = &(pData[8]);
7011         }
7012         /* Save OlePres000 Data to Stream */
7013         hRes = IStream_Write(pStream, OlePres.byUnknown1, nHeaderSize, NULL);
7014         hRes = IStream_Write(pStream, &(OlePres.dwExtentX), sizeof(OlePres.dwExtentX), NULL);
7015         hRes = IStream_Write(pStream, &(OlePres.dwExtentY), sizeof(OlePres.dwExtentY), NULL);
7016         hRes = IStream_Write(pStream, &(OlePres.dwSize), sizeof(OlePres.dwSize), NULL);
7017         if(OlePres.dwSize > 0)
7018         {
7019             hRes = IStream_Write(pStream, OlePres.pData, OlePres.dwSize, NULL);
7020         }
7021         IStream_Release(pStream);
7022     }
7023 }
7024
7025 /*************************************************************************
7026  * OLECONVERT_CreateOle10NativeStream [Internal]
7027  *
7028  * Creates the "\001Ole10Native" Stream (should contain a BMP)
7029  *
7030  * PARAMS
7031  *     pStorage     [I] Dest storage to create the stream in
7032  *     pData        [I] Ole10 Native Data (ex. bmp)
7033  *     dwDataLength [I] Size of the Ole10 Native Data
7034  *
7035  * RETURNS
7036  *     Nothing
7037  *
7038  * NOTES
7039  *     This function is used by OleConvertOLESTREAMToIStorage only.
7040  *
7041  *     Might need to verify the data and return appropriate error message
7042  *
7043  */
7044 void OLECONVERT_CreateOle10NativeStream(LPSTORAGE pStorage, BYTE *pData, DWORD dwDataLength)
7045 {
7046     HRESULT hRes;
7047     IStream *pStream;
7048     static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7049
7050     /* Create the Ole10Native Stream */
7051     hRes = IStorage_CreateStream(pStorage, wstrStreamName,
7052         STGM_CREATE | STGM_WRITE  | STGM_SHARE_EXCLUSIVE, 0, 0, &pStream );
7053
7054     if(hRes == S_OK)
7055     {
7056         /* Write info to stream */
7057         hRes = IStream_Write(pStream, &dwDataLength, sizeof(dwDataLength), NULL);
7058         hRes = IStream_Write(pStream, pData, dwDataLength, NULL);
7059         IStream_Release(pStream);
7060     }
7061
7062 }
7063
7064 /*************************************************************************
7065  * OLECONVERT_GetOLE10ProgID [Internal]
7066  *
7067  * Finds the ProgID (or OleTypeID) from the IStorage
7068  *
7069  * PARAMS
7070  *     pStorage        [I] The Src IStorage to get the ProgID
7071  *     strProgID       [I] the ProgID string to get
7072  *     dwSize          [I] the size of the string
7073  *
7074  * RETURNS
7075  *     Success:  S_OK
7076  *     Failure:  REGDB_E_CLASSNOTREG if cannot reconstruct the stream
7077  *
7078  * NOTES
7079  *     This function is used by OleConvertIStorageToOLESTREAM only.
7080  *
7081  *
7082  */
7083 HRESULT OLECONVERT_GetOLE10ProgID(LPSTORAGE pStorage, char *strProgID, DWORD *dwSize)
7084 {
7085     HRESULT hRes;
7086     IStream *pStream;
7087     LARGE_INTEGER iSeekPos;
7088     OLECONVERT_ISTORAGE_COMPOBJ CompObj;
7089     static const WCHAR wstrStreamName[] = {1,'C', 'o', 'm', 'p', 'O', 'b', 'j', 0};
7090
7091     /* Open the CompObj Stream */
7092     hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7093         STGM_READ  | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7094     if(hRes == S_OK)
7095     {
7096
7097         /*Get the OleType from the CompObj Stream */
7098         iSeekPos.u.LowPart = sizeof(CompObj.byUnknown1) + sizeof(CompObj.clsid);
7099         iSeekPos.u.HighPart = 0;
7100
7101         IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7102         IStream_Read(pStream, &CompObj.dwCLSIDNameLength, sizeof(CompObj.dwCLSIDNameLength), NULL);
7103         iSeekPos.u.LowPart = CompObj.dwCLSIDNameLength;
7104         IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7105         IStream_Read(pStream, &CompObj.dwOleTypeNameLength, sizeof(CompObj.dwOleTypeNameLength), NULL);
7106         iSeekPos.u.LowPart = CompObj.dwOleTypeNameLength;
7107         IStream_Seek(pStream, iSeekPos, STREAM_SEEK_CUR , NULL);
7108
7109         IStream_Read(pStream, dwSize, sizeof(*dwSize), NULL);
7110         if(*dwSize > 0)
7111         {
7112             IStream_Read(pStream, strProgID, *dwSize, NULL);
7113         }
7114         IStream_Release(pStream);
7115     }
7116     else
7117     {
7118         STATSTG stat;
7119         LPOLESTR wstrProgID;
7120
7121         /* Get the OleType from the registry */
7122         REFCLSID clsid = &(stat.clsid);
7123         IStorage_Stat(pStorage, &stat, STATFLAG_NONAME);
7124         hRes = ProgIDFromCLSID(clsid, &wstrProgID);
7125         if(hRes == S_OK)
7126         {
7127             *dwSize = WideCharToMultiByte(CP_ACP, 0, wstrProgID, -1, strProgID, *dwSize, NULL, FALSE);
7128         }
7129
7130     }
7131     return hRes;
7132 }
7133
7134 /*************************************************************************
7135  * OLECONVERT_GetOle10PresData [Internal]
7136  *
7137  * Converts IStorage "/001Ole10Native" stream to a OLE10 Stream
7138  *
7139  * PARAMS
7140  *     pStorage     [I] Src IStroage
7141  *     pOleStream   [I] Dest OleStream Mem Struct
7142  *
7143  * RETURNS
7144  *     Nothing
7145  *
7146  * NOTES
7147  *     This function is used by OleConvertIStorageToOLESTREAM only.
7148  *
7149  *     Memory allocated for pData must be freed by the caller
7150  *
7151  *
7152  */
7153 void OLECONVERT_GetOle10PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7154 {
7155
7156     HRESULT hRes;
7157     IStream *pStream;
7158     static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7159
7160     /* Initialize Default data for OLESTREAM */
7161     pOleStreamData[0].dwOleID = OLESTREAM_ID;
7162     pOleStreamData[0].dwTypeID = 2;
7163     pOleStreamData[1].dwOleID = OLESTREAM_ID;
7164     pOleStreamData[1].dwTypeID = 0;
7165     pOleStreamData[0].dwMetaFileWidth = 0;
7166     pOleStreamData[0].dwMetaFileHeight = 0;
7167     pOleStreamData[0].pData = NULL;
7168     pOleStreamData[1].pData = NULL;
7169
7170     /* Open Ole10Native Stream */
7171     hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7172         STGM_READ  | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7173     if(hRes == S_OK)
7174     {
7175
7176         /* Read Size and Data */
7177         IStream_Read(pStream, &(pOleStreamData->dwDataLength), sizeof(pOleStreamData->dwDataLength), NULL);
7178         if(pOleStreamData->dwDataLength > 0)
7179         {
7180             pOleStreamData->pData = (LPSTR) HeapAlloc(GetProcessHeap(),0,pOleStreamData->dwDataLength);
7181             IStream_Read(pStream, pOleStreamData->pData, pOleStreamData->dwDataLength, NULL);
7182         }
7183         IStream_Release(pStream);
7184     }
7185
7186 }
7187
7188
7189 /*************************************************************************
7190  * OLECONVERT_GetOle20PresData[Internal]
7191  *
7192  * Converts IStorage "/002OlePres000" stream to a OLE10 Stream
7193  *
7194  * PARAMS
7195  *     pStorage         [I] Src IStroage
7196  *     pOleStreamData   [I] Dest OleStream Mem Struct
7197  *
7198  * RETURNS
7199  *     Nothing
7200  *
7201  * NOTES
7202  *     This function is used by OleConvertIStorageToOLESTREAM only.
7203  *
7204  *     Memory allocated for pData must be freed by the caller
7205  */
7206 void OLECONVERT_GetOle20PresData(LPSTORAGE pStorage, OLECONVERT_OLESTREAM_DATA *pOleStreamData)
7207 {
7208     HRESULT hRes;
7209     IStream *pStream;
7210     OLECONVERT_ISTORAGE_OLEPRES olePress;
7211     static const WCHAR wstrStreamName[] = {2, 'O', 'l', 'e', 'P', 'r', 'e', 's', '0', '0', '0', 0};
7212
7213     /* Initialize Default data for OLESTREAM */
7214     pOleStreamData[0].dwOleID = OLESTREAM_ID;
7215     pOleStreamData[0].dwTypeID = 2;
7216     pOleStreamData[0].dwMetaFileWidth = 0;
7217     pOleStreamData[0].dwMetaFileHeight = 0;
7218     pOleStreamData[0].dwDataLength = OLECONVERT_WriteOLE20ToBuffer(pStorage, &(pOleStreamData[0].pData));
7219     pOleStreamData[1].dwOleID = OLESTREAM_ID;
7220     pOleStreamData[1].dwTypeID = 0;
7221     pOleStreamData[1].dwOleTypeNameLength = 0;
7222     pOleStreamData[1].strOleTypeName[0] = 0;
7223     pOleStreamData[1].dwMetaFileWidth = 0;
7224     pOleStreamData[1].dwMetaFileHeight = 0;
7225     pOleStreamData[1].pData = NULL;
7226     pOleStreamData[1].dwDataLength = 0;
7227
7228
7229     /* Open OlePress000 stream */
7230     hRes = IStorage_OpenStream(pStorage, wstrStreamName, NULL,
7231         STGM_READ  | STGM_SHARE_EXCLUSIVE, 0, &pStream );
7232     if(hRes == S_OK)
7233     {
7234         LARGE_INTEGER iSeekPos;
7235         METAFILEPICT16 MetaFilePict;
7236         static const char strMetafilePictName[] = "METAFILEPICT";
7237
7238         /* Set the TypeID for a Metafile */
7239         pOleStreamData[1].dwTypeID = 5;
7240
7241         /* Set the OleTypeName to Metafile */
7242         pOleStreamData[1].dwOleTypeNameLength = strlen(strMetafilePictName) +1;
7243         strcpy(pOleStreamData[1].strOleTypeName, strMetafilePictName);
7244
7245         iSeekPos.u.HighPart = 0;
7246         iSeekPos.u.LowPart = sizeof(olePress.byUnknown1);
7247
7248         /* Get Presentation Data */
7249         IStream_Seek(pStream, iSeekPos, STREAM_SEEK_SET, NULL);
7250         IStream_Read(pStream, &(olePress.dwExtentX), sizeof(olePress.dwExtentX), NULL);
7251         IStream_Read(pStream, &(olePress.dwExtentY), sizeof(olePress.dwExtentY), NULL);
7252         IStream_Read(pStream, &(olePress.dwSize), sizeof(olePress.dwSize), NULL);
7253
7254         /*Set width and Height */
7255         pOleStreamData[1].dwMetaFileWidth = olePress.dwExtentX;
7256         pOleStreamData[1].dwMetaFileHeight = -olePress.dwExtentY;
7257         if(olePress.dwSize > 0)
7258         {
7259             /* Set Length */
7260             pOleStreamData[1].dwDataLength  = olePress.dwSize + sizeof(METAFILEPICT16);
7261
7262             /* Set MetaFilePict struct */
7263             MetaFilePict.mm = 8;
7264             MetaFilePict.xExt = olePress.dwExtentX;
7265             MetaFilePict.yExt = olePress.dwExtentY;
7266             MetaFilePict.hMF = 0;
7267
7268             /* Get Metafile Data */
7269             pOleStreamData[1].pData = (BYTE *) HeapAlloc(GetProcessHeap(),0,pOleStreamData[1].dwDataLength);
7270             memcpy(pOleStreamData[1].pData, &MetaFilePict, sizeof(MetaFilePict));
7271             IStream_Read(pStream, &(pOleStreamData[1].pData[sizeof(MetaFilePict)]), pOleStreamData[1].dwDataLength-sizeof(METAFILEPICT16), NULL);
7272         }
7273         IStream_Release(pStream);
7274     }
7275 }
7276
7277 /*************************************************************************
7278  * OleConvertOLESTREAMToIStorage [OLE32.@]
7279  *
7280  * Read info on MSDN
7281  *
7282  * TODO
7283  *      DVTARGETDEVICE paramenter is not handled
7284  *      Still unsure of some mem fields for OLE 10 Stream
7285  *      Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7286  *      and "\001OLE" streams
7287  *
7288  */
7289 HRESULT WINAPI OleConvertOLESTREAMToIStorage (
7290     LPOLESTREAM pOleStream,
7291     LPSTORAGE pstg,
7292     const DVTARGETDEVICE* ptd)
7293 {
7294     int i;
7295     HRESULT hRes=S_OK;
7296     OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7297
7298     memset(pOleStreamData, 0, sizeof(pOleStreamData));
7299
7300     if(ptd != NULL)
7301     {
7302         FIXME("DVTARGETDEVICE is not NULL, unhandled parameter\n");
7303     }
7304
7305     if(pstg == NULL || pOleStream == NULL)
7306     {
7307         hRes = E_INVALIDARG;
7308     }
7309
7310     if(hRes == S_OK)
7311     {
7312         /* Load the OLESTREAM to Memory */
7313         hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[0], TRUE);
7314     }
7315
7316     if(hRes == S_OK)
7317     {
7318         /* Load the OLESTREAM to Memory (part 2)*/
7319         hRes = OLECONVERT_LoadOLE10(pOleStream, &pOleStreamData[1], FALSE);
7320     }
7321
7322     if(hRes == S_OK)
7323     {
7324
7325         if(pOleStreamData[0].dwDataLength > sizeof(STORAGE_magic))
7326         {
7327             /* Do we have the IStorage Data in the OLESTREAM */
7328             if(memcmp(pOleStreamData[0].pData, STORAGE_magic, sizeof(STORAGE_magic)) ==0)
7329             {
7330                 OLECONVERT_GetOLE20FromOLE10(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7331                 OLECONVERT_CreateOlePresStream(pstg, pOleStreamData[1].dwMetaFileWidth, pOleStreamData[1].dwMetaFileHeight, pOleStreamData[1].pData, pOleStreamData[1].dwDataLength);
7332             }
7333             else
7334             {
7335                 /* It must be an original OLE 1.0 source */
7336                 OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7337             }
7338         }
7339         else
7340         {
7341             /* It must be an original OLE 1.0 source */
7342             OLECONVERT_CreateOle10NativeStream(pstg, pOleStreamData[0].pData, pOleStreamData[0].dwDataLength);
7343         }
7344
7345         /* Create CompObj Stream if necessary */
7346         hRes = OLECONVERT_CreateCompObjStream(pstg, pOleStreamData[0].strOleTypeName);
7347         if(hRes == S_OK)
7348         {
7349             /*Create the Ole Stream if necessary */
7350             OLECONVERT_CreateOleStream(pstg);
7351         }
7352     }
7353
7354
7355     /* Free allocated memory */
7356     for(i=0; i < 2; i++)
7357     {
7358         if(pOleStreamData[i].pData != NULL)
7359         {
7360             HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7361         }
7362         if(pOleStreamData[i].pstrOleObjFileName != NULL)
7363         {
7364                 HeapFree(GetProcessHeap(),0,pOleStreamData[i].pstrOleObjFileName);
7365                 pOleStreamData[i].pstrOleObjFileName = NULL;
7366         }
7367     }
7368     return hRes;
7369 }
7370
7371 /*************************************************************************
7372  * OleConvertIStorageToOLESTREAM [OLE32.@]
7373  *
7374  * Read info on MSDN
7375  *
7376  * Read info on MSDN
7377  *
7378  * TODO
7379  *      Still unsure of some mem fields for OLE 10 Stream
7380  *      Still some unknowns for the IStorage: "\002OlePres000", "\001CompObj",
7381  *      and "\001OLE" streams.
7382  *
7383  */
7384 HRESULT WINAPI OleConvertIStorageToOLESTREAM (
7385     LPSTORAGE pstg,
7386     LPOLESTREAM pOleStream)
7387 {
7388     int i;
7389     HRESULT hRes = S_OK;
7390     IStream *pStream;
7391     OLECONVERT_OLESTREAM_DATA pOleStreamData[2];
7392     static const WCHAR wstrStreamName[] = {1, 'O', 'l', 'e', '1', '0', 'N', 'a', 't', 'i', 'v', 'e', 0};
7393
7394
7395     memset(pOleStreamData, 0, sizeof(pOleStreamData));
7396
7397     if(pstg == NULL || pOleStream == NULL)
7398     {
7399         hRes = E_INVALIDARG;
7400     }
7401     if(hRes == S_OK)
7402     {
7403         /* Get the ProgID */
7404         pOleStreamData[0].dwOleTypeNameLength = OLESTREAM_MAX_STR_LEN;
7405         hRes = OLECONVERT_GetOLE10ProgID(pstg, pOleStreamData[0].strOleTypeName, &(pOleStreamData[0].dwOleTypeNameLength));
7406     }
7407     if(hRes == S_OK)
7408     {
7409         /* Was it originally Ole10 */
7410         hRes = IStorage_OpenStream(pstg, wstrStreamName, 0, STGM_READ | STGM_SHARE_EXCLUSIVE, 0, &pStream);
7411         if(hRes == S_OK)
7412         {
7413             IStream_Release(pStream);
7414             /* Get Presentation Data for Ole10Native */
7415             OLECONVERT_GetOle10PresData(pstg, pOleStreamData);
7416         }
7417         else
7418         {
7419             /* Get Presentation Data (OLE20) */
7420             OLECONVERT_GetOle20PresData(pstg, pOleStreamData);
7421         }
7422
7423         /* Save OLESTREAM */
7424         hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[0]), pOleStream);
7425         if(hRes == S_OK)
7426         {
7427             hRes = OLECONVERT_SaveOLE10(&(pOleStreamData[1]), pOleStream);
7428         }
7429
7430     }
7431
7432     /* Free allocated memory */
7433     for(i=0; i < 2; i++)
7434     {
7435         if(pOleStreamData[i].pData != NULL)
7436         {
7437             HeapFree(GetProcessHeap(),0,pOleStreamData[i].pData);
7438         }
7439     }
7440
7441     return hRes;
7442 }
7443
7444 /***********************************************************************
7445  *              GetConvertStg (OLE32.@)
7446  */
7447 HRESULT WINAPI GetConvertStg(IStorage *stg) {
7448     FIXME("unimplemented stub!\n");
7449     return E_FAIL;
7450 }
7451
7452 /******************************************************************************
7453  * StgIsStorageFile [OLE32.@]
7454  */
7455 HRESULT WINAPI
7456 StgIsStorageFile(LPCOLESTR fn)
7457 {
7458         HANDLE          hf;
7459         BYTE            magic[8];
7460         DWORD           bytes_read;
7461
7462         TRACE("(\'%s\')\n", debugstr_w(fn));
7463         hf = CreateFileW(fn, GENERIC_READ,
7464                          FILE_SHARE_DELETE | FILE_SHARE_READ | FILE_SHARE_WRITE,
7465                          NULL, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0);
7466
7467         if (hf == INVALID_HANDLE_VALUE)
7468                 return STG_E_FILENOTFOUND;
7469
7470         if (!ReadFile(hf, magic, 8, &bytes_read, NULL))
7471         {
7472                 WARN(" unable to read file\n");
7473                 CloseHandle(hf);
7474                 return S_FALSE;
7475         }
7476
7477         CloseHandle(hf);
7478
7479         if (bytes_read != 8) {
7480                 WARN(" too short\n");
7481                 return S_FALSE;
7482         }
7483
7484         if (!memcmp(magic,STORAGE_magic,8)) {
7485                 WARN(" -> YES\n");
7486                 return S_OK;
7487         }
7488
7489         WARN(" -> Invalid header.\n");
7490         return S_FALSE;
7491 }