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