LookupIconIdFromDirectoryEx16() fixed: if GetSystemPaletteEntries()
[wine] / ole / stg_stream.c
1 /*
2  * Compound Storage (32 bit version)
3  * Stream implementation
4  *
5  * This file contains the implementation of the stream interface
6  * for streams contained in a compound storage.
7  *
8  * Copyright 1999 Francis Beaudet
9  * Copyright 1999 Thuy Nguyen
10  */
11 #include <assert.h>
12 #include <stdlib.h>
13 #include <stdio.h>
14 #include <string.h>
15
16 #include "winbase.h"
17 #include "winerror.h"
18 #include "debug.h"
19 #include "wine/obj_storage.h"
20
21 #include "storage32.h"
22
23
24 /*
25  * Virtual function table for the StgStreamImpl class.
26  */
27 static ICOM_VTABLE(IStream) StgStreamImpl_Vtbl =
28 {
29     StgStreamImpl_QueryInterface,
30     StgStreamImpl_AddRef,
31     StgStreamImpl_Release,
32     StgStreamImpl_Read,
33     StgStreamImpl_Write,
34     StgStreamImpl_Seek,
35     StgStreamImpl_SetSize,
36     StgStreamImpl_CopyTo,
37     StgStreamImpl_Commit,
38     StgStreamImpl_Revert,
39     StgStreamImpl_LockRegion,
40     StgStreamImpl_UnlockRegion,
41     StgStreamImpl_Stat,
42     StgStreamImpl_Clone
43 };
44
45 /******************************************************************************
46 ** StgStreamImpl implementation
47 */
48
49 /***
50  * This is the constructor for the StgStreamImpl class.
51  *
52  * Params:
53  *    parentStorage - Pointer to the storage that contains the stream to open
54  *    ownerProperty - Index of the property that points to this stream.
55  */
56 StgStreamImpl* StgStreamImpl_Construct(
57                 StorageBaseImpl* parentStorage,
58                 ULONG              ownerProperty)
59 {
60   StgStreamImpl* newStream;
61
62   newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(StgStreamImpl));
63   
64   if (newStream!=0)
65   {
66     /*
67      * Set-up the virtual function table and reference count.
68      */
69     newStream->lpvtbl = &StgStreamImpl_Vtbl;
70     newStream->ref    = 0;
71     
72     /*
73      * We want to nail-down the reference to the storage in case the
74      * stream out-lives the storage in the client application.
75      */
76     newStream->parentStorage = parentStorage;
77     IStorage_AddRef((IStorage*)newStream->parentStorage);
78     
79     newStream->ownerProperty = ownerProperty;
80     
81     /*
82      * Start the stream at the begining.
83      */
84     newStream->currentPosition.HighPart = 0;
85     newStream->currentPosition.LowPart = 0;
86     
87     /*
88      * Initialize the rest of the data.
89      */
90     newStream->streamSize.HighPart = 0;
91     newStream->streamSize.LowPart  = 0;
92     newStream->bigBlockChain       = 0;
93     newStream->smallBlockChain     = 0;
94     
95     /*
96      * Read the size from the property and determine if the blocks forming
97      * this stream are large or small.
98      */
99     StgStreamImpl_OpenBlockChain(newStream);
100   }
101   
102   return newStream;
103 }
104
105 /***
106  * This is the destructor of the StgStreamImpl class.
107  *
108  * This method will clean-up all the resources used-up by the given StgStreamImpl 
109  * class. The pointer passed-in to this function will be freed and will not
110  * be valid anymore.
111  */
112 void StgStreamImpl_Destroy(StgStreamImpl* This)
113 {
114   /*
115    * Release the reference we are holding on the parent storage.
116    */
117   IStorage_Release((IStorage*)This->parentStorage);
118   This->parentStorage = 0;
119
120   /*
121    * Make sure we clean-up the block chain stream objects that we were using.
122    */
123   if (This->bigBlockChain != 0)
124   {
125     BlockChainStream_Destroy(This->bigBlockChain);
126     This->bigBlockChain = 0;
127   }
128
129   if (This->smallBlockChain != 0)
130   {
131     SmallBlockChainStream_Destroy(This->smallBlockChain);
132     This->smallBlockChain = 0;
133   }
134
135   /*
136    * Finally, free the memory used-up by the class.
137    */
138   HeapFree(GetProcessHeap(), 0, This);  
139 }
140
141 /***
142  * This implements the IUnknown method QueryInterface for this
143  * class
144  */
145 HRESULT WINAPI StgStreamImpl_QueryInterface(
146                   IStream*     iface,
147                   REFIID         riid,        /* [in] */          
148                   void**         ppvObject)   /* [iid_is][out] */ 
149 {
150   StgStreamImpl* const This=(StgStreamImpl*)iface;
151
152   /*
153    * Perform a sanity check on the parameters.
154    */
155   if (ppvObject==0)
156     return E_INVALIDARG;
157   
158   /*
159    * Initialize the return parameter.
160    */
161   *ppvObject = 0;
162   
163   /*
164    * Compare the riid with the interface IDs implemented by this object.
165    */
166   if (memcmp(&IID_IUnknown, riid, sizeof(IID_IUnknown)) == 0) 
167   {
168     *ppvObject = (IStream*)This;
169   }
170   else if (memcmp(&IID_IStorage, riid, sizeof(IID_IStream)) == 0) 
171   {
172     *ppvObject = (IStream*)This;
173   }
174   
175   /*
176    * Check that we obtained an interface.
177    */
178   if ((*ppvObject)==0)
179     return E_NOINTERFACE;
180   
181   /*
182    * Query Interface always increases the reference count by one when it is
183    * successful
184    */
185   StgStreamImpl_AddRef(iface);
186   
187   return S_OK;;
188 }
189
190 /***
191  * This implements the IUnknown method AddRef for this
192  * class
193  */
194 ULONG WINAPI StgStreamImpl_AddRef(
195                 IStream* iface)
196 {
197   StgStreamImpl* const This=(StgStreamImpl*)iface;
198
199   This->ref++;
200   
201   return This->ref;
202 }
203
204 /***
205  * This implements the IUnknown method Release for this
206  * class
207  */
208 ULONG WINAPI StgStreamImpl_Release(
209                 IStream* iface)
210 {
211   StgStreamImpl* const This=(StgStreamImpl*)iface;
212
213   ULONG newRef;
214   
215   This->ref--;
216   
217   newRef = This->ref;
218   
219   /*
220    * If the reference count goes down to 0, perform suicide.
221    */
222   if (newRef==0)
223   {
224     StgStreamImpl_Destroy(This);
225   }
226   
227   return newRef;
228 }
229
230 /***
231  * This method will open the block chain pointed by the property
232  * that describes the stream.
233  * If the stream's size is null, no chain is opened.
234  */
235 void StgStreamImpl_OpenBlockChain(
236         StgStreamImpl* This)
237 {
238   StgProperty    curProperty;
239   BOOL         readSucessful;
240
241   /*
242    * Make sure no old object is staying behind.
243    */
244   if (This->smallBlockChain != 0)
245   {
246     SmallBlockChainStream_Destroy(This->smallBlockChain);
247     This->smallBlockChain = 0;
248   }
249
250   if (This->bigBlockChain != 0)
251   {
252     BlockChainStream_Destroy(This->bigBlockChain);
253     This->bigBlockChain = 0;
254   }
255
256   /*
257    * Read the information from the property.
258    */
259   readSucessful = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
260                                              This->ownerProperty,
261                                              &curProperty);
262   
263   if (readSucessful)
264   {
265     This->streamSize = curProperty.size;
266     
267     /*
268      * This code supports only streams that are <32 bits in size.
269      */
270     assert(This->streamSize.HighPart == 0);
271     
272     if(curProperty.startingBlock == BLOCK_END_OF_CHAIN)
273     {
274       assert( (This->streamSize.HighPart == 0) && (This->streamSize.LowPart == 0) );
275     }
276     else
277     {
278       if ( (This->streamSize.HighPart == 0) &&
279            (This->streamSize.LowPart < LIMIT_TO_USE_SMALL_BLOCK) )
280       {
281         This->smallBlockChain = SmallBlockChainStream_Construct(
282                                                                 This->parentStorage->ancestorStorage,   
283                                                                 This->ownerProperty);
284       }
285       else
286       {
287         This->bigBlockChain = BlockChainStream_Construct(
288                                                          This->parentStorage->ancestorStorage,
289                                                          NULL,
290                                                          This->ownerProperty);
291       }
292     }
293   }
294 }
295
296 /***
297  * This method is part of the ISequentialStream interface.
298  *
299  * If reads a block of information from the stream at the current
300  * position. It then moves the current position at the end of the
301  * read block
302  *
303  * See the documentation of ISequentialStream for more info.
304  */
305 HRESULT WINAPI StgStreamImpl_Read( 
306                   IStream*     iface,
307                   void*          pv,        /* [length_is][size_is][out] */
308                   ULONG          cb,        /* [in] */                     
309                   ULONG*         pcbRead)   /* [out] */                    
310 {
311   StgStreamImpl* const This=(StgStreamImpl*)iface;
312
313   ULONG bytesReadBuffer;
314   ULONG bytesToReadFromBuffer;
315   
316   /* 
317    * If the caller is not interested in the nubmer of bytes read,
318    * we use another buffer to avoid "if" statements in the code.
319    */
320   if (pcbRead==0)
321     pcbRead = &bytesReadBuffer;
322   
323   /*
324    * Using the known size of the stream, calculate the number of bytes
325    * to read from the block chain
326    */
327   bytesToReadFromBuffer = MIN( This->streamSize.LowPart - This->currentPosition.LowPart, cb);
328   
329   /*
330    * Depending on the type of chain that was opened when the stream was constructed,
331    * we delegate the work to the method that read the block chains.
332    */
333   if (This->smallBlockChain!=0)
334   {
335     SmallBlockChainStream_ReadAt(This->smallBlockChain,
336                                  This->currentPosition,
337                                  bytesToReadFromBuffer,
338                                  pv,
339                                  pcbRead);
340     
341   }
342   else if (This->bigBlockChain!=0)
343   {
344     BlockChainStream_ReadAt(This->bigBlockChain,
345                             This->currentPosition,
346                             bytesToReadFromBuffer,
347                             pv,
348                             pcbRead);
349   }
350   else
351     assert(FALSE);
352
353   /*
354    * We should always be able to read the proper amount of data from the
355    * chain.
356    */
357   assert(bytesToReadFromBuffer == *pcbRead);
358
359   /*
360    * Advance the pointer for the number of positions read.
361    */
362   This->currentPosition.LowPart += *pcbRead;
363   
364   /*
365    * The function returns S_OK if the buffer was filled completely
366    * it returns S_FALSE if the end of the stream is reached before the
367    * buffer is filled
368    */
369   if(*pcbRead == cb)
370     return S_OK;
371   
372   return S_FALSE;
373 }
374         
375 /***
376  * This method is part of the ISequentialStream interface.
377  *
378  * It writes a block of information to the stream at the current
379  * position. It then moves the current position at the end of the
380  * written block. If the stream is too small to fit the block,
381  * the stream is grown to fit.
382  *
383  * See the documentation of ISequentialStream for more info.
384  */
385 HRESULT WINAPI StgStreamImpl_Write(
386                   IStream*     iface,
387                   const void*    pv,          /* [size_is][in] */ 
388                   ULONG          cb,          /* [in] */          
389                   ULONG*         pcbWritten)  /* [out] */         
390 {
391   StgStreamImpl* const This=(StgStreamImpl*)iface;
392
393   ULARGE_INTEGER newSize;
394   ULONG bytesWritten = 0;
395   
396   /*
397    * If the caller is not interested in the number of bytes written,
398    * we use another buffer to avoid "if" statements in the code.
399    */
400   if (pcbWritten == 0)
401     pcbWritten = &bytesWritten;
402   
403   if (cb == 0)
404   {
405     return S_OK;
406   }
407   else
408   {
409     newSize.HighPart = 0;
410     newSize.LowPart = This->currentPosition.LowPart + cb;
411   }
412   
413   /*
414    * Verify if we need to grow the stream
415    */
416   if (newSize.LowPart > This->streamSize.LowPart)
417   {
418     /* grow stream */
419     StgStreamImpl_SetSize(iface, newSize);
420   }
421   
422   /*
423    * Depending on the type of chain that was opened when the stream was constructed,
424    * we delegate the work to the method that readwrites to the block chains.
425    */
426   if (This->smallBlockChain!=0)
427   {
428     SmallBlockChainStream_WriteAt(This->smallBlockChain,
429                                   This->currentPosition,
430                                   cb,
431                                   pv,
432                                   pcbWritten);
433     
434   }
435   else if (This->bigBlockChain!=0)
436   {
437     BlockChainStream_WriteAt(This->bigBlockChain,
438                              This->currentPosition,
439                              cb,
440                              pv,
441                              pcbWritten);
442   }
443   else
444     assert(FALSE);
445   
446   /*
447    * Advance the position pointer for the number of positions written.
448    */
449   This->currentPosition.LowPart += *pcbWritten;
450   
451   return S_OK;
452 }
453
454 /***
455  * This method is part of the IStream interface.
456  *
457  * It will move the current stream pointer according to the parameters
458  * given.
459  *
460  * See the documentation of IStream for more info.
461  */        
462 HRESULT WINAPI StgStreamImpl_Seek( 
463                   IStream*      iface,
464                   LARGE_INTEGER   dlibMove,         /* [in] */ 
465                   DWORD           dwOrigin,         /* [in] */ 
466                   ULARGE_INTEGER* plibNewPosition) /* [out] */
467 {
468   StgStreamImpl* const This=(StgStreamImpl*)iface;
469
470   ULARGE_INTEGER newPosition;
471
472   /* 
473    * The caller is allowed to pass in NULL as the new position return value.
474    * If it happens, we assign it to a dynamic variable to avoid special cases
475    * in the code below.
476    */
477   if (plibNewPosition == 0)
478   {
479     plibNewPosition = &newPosition;
480   }
481
482   /*
483    * The file pointer is moved depending on the given "function"
484    * parameter.
485    */
486   switch (dwOrigin)
487   {
488     case STREAM_SEEK_SET:
489       plibNewPosition->HighPart = 0;
490       plibNewPosition->LowPart  = 0;
491       break;
492     case STREAM_SEEK_CUR:
493       *plibNewPosition = This->currentPosition;
494       break;
495     case STREAM_SEEK_END:
496       *plibNewPosition = This->streamSize;
497       break;
498     default:
499       return STG_E_INVALIDFUNCTION;
500   }
501
502   /*
503    * We don't support files with offsets of 64 bits.
504    */
505   assert(dlibMove.HighPart == 0);
506
507   /*
508    * Check if we end-up before the beginning of the file. That should trigger an
509    * error.
510    */
511   if ( (dlibMove.LowPart<0) && (plibNewPosition->LowPart < (ULONG)(-dlibMove.LowPart)) )
512   {
513     /*
514      * I don't know what error to send there.
515      */
516     return E_FAIL;
517   }
518
519   /*
520    * Move the actual file pointer
521    * If the file pointer ends-up after the end of the stream, the next Write operation will
522    * make the file larger. This is how it is documented.
523    */
524   plibNewPosition->LowPart += dlibMove.LowPart;
525   This->currentPosition = *plibNewPosition;
526  
527   return S_OK;
528 }
529
530 /***
531  * This method is part of the IStream interface.
532  *
533  * It will change the size of a stream.
534  *
535  * TODO: Switch from small blocks to big blocks and vice versa.
536  *
537  * See the documentation of IStream for more info.
538  */
539 HRESULT WINAPI StgStreamImpl_SetSize( 
540                                      IStream*      iface,
541                                      ULARGE_INTEGER  libNewSize)   /* [in] */ 
542 {
543   StgStreamImpl* const This=(StgStreamImpl*)iface;
544
545   StgProperty    curProperty;
546   BOOL         Success;
547
548   /*
549    * As documented.
550    */
551   if (libNewSize.HighPart != 0)
552     return STG_E_INVALIDFUNCTION;
553   
554   if (This->streamSize.LowPart == libNewSize.LowPart)
555     return S_OK;
556
557   /*
558    * This will happen if we're creating a stream
559    */
560   if ((This->smallBlockChain == 0) && (This->bigBlockChain == 0))
561   {
562     if (libNewSize.LowPart < LIMIT_TO_USE_SMALL_BLOCK)
563     {
564       This->smallBlockChain = SmallBlockChainStream_Construct(
565                                     This->parentStorage->ancestorStorage,
566                                     This->ownerProperty);
567     }
568     else
569     {
570       This->bigBlockChain = BlockChainStream_Construct(
571                                 This->parentStorage->ancestorStorage,
572                                 NULL,
573                                 This->ownerProperty);
574     }
575   }
576
577   /*
578    * Read this stream's property to see if it's small blocks or big blocks
579    */
580   Success = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
581                                        This->ownerProperty,
582                                        &curProperty); 
583   /*
584    * Determine if we have to switch from small to big blocks or vice versa
585    */
586   
587   if (curProperty.size.LowPart < LIMIT_TO_USE_SMALL_BLOCK)
588   {
589     if (libNewSize.LowPart >= LIMIT_TO_USE_SMALL_BLOCK)
590     {
591       /*
592        * Transform the small block chain into a big block chain
593        */
594       This->bigBlockChain = Storage32Impl_SmallBlocksToBigBlocks(
595                                 This->parentStorage->ancestorStorage,
596                                 &This->smallBlockChain);
597     }
598   }
599
600   if (This->smallBlockChain!=0)
601   {
602     Success = SmallBlockChainStream_SetSize(This->smallBlockChain, libNewSize);
603   }
604   else
605   {
606     Success = BlockChainStream_SetSize(This->bigBlockChain, libNewSize);
607   }
608
609   /*
610    * Write to the property the new information about this stream
611    */
612   Success = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
613                                        This->ownerProperty,
614                                        &curProperty);
615
616   curProperty.size.HighPart = libNewSize.HighPart;
617   curProperty.size.LowPart = libNewSize.LowPart;
618   
619   if (Success)
620   {
621     StorageImpl_WriteProperty(This->parentStorage->ancestorStorage,
622                                 This->ownerProperty,
623                                 &curProperty);
624   }
625   
626   This->streamSize = libNewSize;
627   
628   return S_OK;
629 }
630         
631 /***
632  * This method is part of the IStream interface.
633  *
634  * It will copy the 'cb' Bytes to 'pstm' IStream.
635  *
636  * See the documentation of IStream for more info.
637  */
638 HRESULT WINAPI StgStreamImpl_CopyTo( 
639                                     IStream*      iface,
640                                     IStream*      pstm,         /* [unique][in] */ 
641                                     ULARGE_INTEGER  cb,           /* [in] */         
642                                     ULARGE_INTEGER* pcbRead,      /* [out] */        
643                                     ULARGE_INTEGER* pcbWritten)   /* [out] */        
644 {
645   StgStreamImpl* const This=(StgStreamImpl*)iface;
646   HRESULT        hr = S_OK;
647   BYTE           tmpBuffer[128];
648   ULONG          bytesRead, bytesWritten, copySize;
649   ULARGE_INTEGER totalBytesRead;
650   ULARGE_INTEGER totalBytesWritten;
651
652   /*
653    * Sanity check
654    */
655   if ( pstm == 0 )
656     return STG_E_INVALIDPOINTER;
657
658   totalBytesRead.LowPart = totalBytesRead.HighPart = 0;
659   totalBytesWritten.LowPart = totalBytesWritten.HighPart = 0;
660
661   /*
662    * use stack to store data temporarly
663    * there is surely more performant way of doing it, for now this basic
664    * implementation will do the job
665    */
666   while ( cb.LowPart > 0 )
667   {
668     if ( cb.LowPart >= 128 )
669       copySize = 128;
670     else
671       copySize = cb.LowPart;
672     
673     StgStreamImpl_Read(iface, tmpBuffer, 128, &bytesRead);
674
675     totalBytesRead.LowPart += bytesRead;
676     
677     StgStreamImpl_Write(pstm, tmpBuffer, bytesRead, &bytesWritten);
678
679     totalBytesWritten.LowPart += bytesWritten;
680
681     /*
682      * Check that read & write operations were succesfull
683      */
684     if ( (bytesRead != copySize) && (bytesWritten != copySize) )
685     {
686       hr = STG_E_MEDIUMFULL;
687       break;
688     }
689     
690     cb.LowPart = cb.LowPart - copySize;
691   }
692
693   /*
694    * Update number of bytes read and written
695    */
696   if (pcbRead)
697   {
698     pcbRead->LowPart = totalBytesRead.LowPart;
699     pcbRead->HighPart = totalBytesRead.HighPart;
700   }
701
702   if (pcbWritten)
703   {
704     pcbWritten->LowPart = totalBytesWritten.LowPart;
705     pcbWritten->HighPart = totalBytesWritten.HighPart;
706   }
707   return hr;
708 }
709
710 /***
711  * This method is part of the IStream interface.
712  *
713  * For streams contained in structured storages, this method
714  * does nothing. This is what the documentation tells us.
715  *
716  * See the documentation of IStream for more info.
717  */        
718 HRESULT WINAPI StgStreamImpl_Commit( 
719                   IStream*      iface,
720                   DWORD           grfCommitFlags)  /* [in] */ 
721 {
722   return S_OK;
723 }
724
725 /***
726  * This method is part of the IStream interface.
727  *
728  * For streams contained in structured storages, this method
729  * does nothing. This is what the documentation tells us.
730  *
731  * See the documentation of IStream for more info.
732  */        
733 HRESULT WINAPI StgStreamImpl_Revert( 
734                   IStream* iface)
735 {
736   return S_OK;
737 }
738
739 HRESULT WINAPI StgStreamImpl_LockRegion( 
740                                         IStream*     iface,
741                                         ULARGE_INTEGER libOffset,   /* [in] */ 
742                                         ULARGE_INTEGER cb,          /* [in] */ 
743                                         DWORD          dwLockType)  /* [in] */ 
744 {
745   FIXME(ole, "not implemented!\n");
746   return E_NOTIMPL;
747 }
748
749 HRESULT WINAPI StgStreamImpl_UnlockRegion( 
750                                           IStream*     iface,
751                                           ULARGE_INTEGER libOffset,   /* [in] */ 
752                                           ULARGE_INTEGER cb,          /* [in] */ 
753                                           DWORD          dwLockType)  /* [in] */ 
754 {
755   FIXME(ole, "not implemented!\n");
756   return E_NOTIMPL;
757 }
758
759 /***
760  * This method is part of the IStream interface.
761  *
762  * This method returns information about the current
763  * stream.
764  *
765  * See the documentation of IStream for more info.
766  */        
767 HRESULT WINAPI StgStreamImpl_Stat( 
768                   IStream*     iface,
769                   STATSTG*       pstatstg,     /* [out] */
770                   DWORD          grfStatFlag)  /* [in] */ 
771 {
772   StgStreamImpl* const This=(StgStreamImpl*)iface;
773
774   StgProperty    curProperty;
775   BOOL         readSucessful;
776   
777   /*
778    * Read the information from the property.
779    */
780   readSucessful = StorageImpl_ReadProperty(This->parentStorage->ancestorStorage,
781                                              This->ownerProperty,
782                                              &curProperty);
783   
784   if (readSucessful)
785   {
786     StorageUtl_CopyPropertyToSTATSTG(pstatstg, 
787                                      &curProperty, 
788                                      grfStatFlag);
789     
790     return S_OK;
791   }
792   
793   return E_FAIL;
794 }
795         
796 HRESULT WINAPI StgStreamImpl_Clone( 
797                                    IStream*     iface,
798                                    IStream**    ppstm) /* [out] */ 
799 {
800   FIXME(ole, "not implemented!\n");
801   return E_NOTIMPL;
802 }