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