dsdmo: Add Flanger effect stub.
[wine.git] / dlls / ole32 / stg_stream.c
blob73fc1cc948041356c62601a1329c07e950655cb3
1 /*
2 * Compound Storage (32 bit version)
3 * Stream implementation
5 * This file contains the implementation of the stream interface
6 * for streams contained in a compound storage.
8 * Copyright 1999 Francis Beaudet
9 * Copyright 1999 Thuy Nguyen
11 * This library is free software; you can redistribute it and/or
12 * modify it under the terms of the GNU Lesser General Public
13 * License as published by the Free Software Foundation; either
14 * version 2.1 of the License, or (at your option) any later version.
16 * This library is distributed in the hope that it will be useful,
17 * but WITHOUT ANY WARRANTY; without even the implied warranty of
18 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
19 * Lesser General Public License for more details.
21 * You should have received a copy of the GNU Lesser General Public
22 * License along with this library; if not, write to the Free Software
23 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
26 #include <stdlib.h>
27 #include <stdarg.h>
28 #include <stdio.h>
29 #include <string.h>
31 #define COBJMACROS
32 #define NONAMELESSUNION
34 #include "windef.h"
35 #include "winbase.h"
36 #include "winerror.h"
37 #include "winternl.h"
38 #include "wine/debug.h"
40 #include "storage32.h"
42 WINE_DEFAULT_DEBUG_CHANNEL(storage);
44 /***
45 * This implements the IUnknown method QueryInterface for this
46 * class
48 static HRESULT WINAPI StgStreamImpl_QueryInterface(
49 IStream* iface,
50 REFIID riid, /* [in] */
51 void** ppvObject) /* [iid_is][out] */
53 StgStreamImpl* This = impl_from_IStream(iface);
55 if (ppvObject==0)
56 return E_INVALIDARG;
58 *ppvObject = 0;
60 if (IsEqualIID(&IID_IUnknown, riid) ||
61 IsEqualIID(&IID_ISequentialStream, riid) ||
62 IsEqualIID(&IID_IStream, riid))
64 *ppvObject = &This->IStream_iface;
66 else
67 return E_NOINTERFACE;
69 IStream_AddRef(iface);
71 return S_OK;
74 /***
75 * This implements the IUnknown method AddRef for this
76 * class
78 static ULONG WINAPI StgStreamImpl_AddRef(
79 IStream* iface)
81 StgStreamImpl* This = impl_from_IStream(iface);
82 return InterlockedIncrement(&This->ref);
85 /***
86 * This implements the IUnknown method Release for this
87 * class
89 static ULONG WINAPI StgStreamImpl_Release(
90 IStream* iface)
92 StgStreamImpl* This = impl_from_IStream(iface);
93 ULONG ref = InterlockedDecrement(&This->ref);
95 if (!ref)
97 TRACE("(%p)\n", This);
100 * Release the reference we are holding on the parent storage.
101 * IStorage_Release(&This->parentStorage->IStorage_iface);
103 * No, don't do this. Some apps call IStorage_Release without
104 * calling IStream_Release first. If we grab a reference the
105 * file is not closed, and the app fails when it tries to
106 * reopen the file (Easy-PC, for example). Just inform the
107 * storage that we have closed the stream
110 if (This->parentStorage)
111 StorageBaseImpl_RemoveStream(This->parentStorage, This);
112 This->parentStorage = 0;
113 HeapFree(GetProcessHeap(), 0, This);
116 return ref;
119 /***
120 * This method is part of the ISequentialStream interface.
122 * It reads a block of information from the stream at the current
123 * position. It then moves the current position at the end of the
124 * read block
126 * See the documentation of ISequentialStream for more info.
128 static HRESULT WINAPI StgStreamImpl_Read(
129 IStream* iface,
130 void* pv, /* [length_is][size_is][out] */
131 ULONG cb, /* [in] */
132 ULONG* pcbRead) /* [out] */
134 StgStreamImpl* This = impl_from_IStream(iface);
136 ULONG bytesReadBuffer;
137 HRESULT res;
139 TRACE("%p, %p, %lu, %p.\n", iface, pv, cb, pcbRead);
141 if (!This->parentStorage)
143 WARN("storage reverted\n");
144 return STG_E_REVERTED;
148 * If the caller is not interested in the number of bytes read,
149 * we use another buffer to avoid "if" statements in the code.
151 if (pcbRead==0)
152 pcbRead = &bytesReadBuffer;
154 res = StorageBaseImpl_StreamReadAt(This->parentStorage,
155 This->dirEntry,
156 This->currentPosition,
159 pcbRead);
161 if (SUCCEEDED(res))
164 * Advance the pointer for the number of positions read.
166 This->currentPosition.QuadPart += *pcbRead;
169 TRACE("<-- %#lx\n", res);
170 return res;
173 /***
174 * This method is part of the ISequentialStream interface.
176 * It writes a block of information to the stream at the current
177 * position. It then moves the current position at the end of the
178 * written block. If the stream is too small to fit the block,
179 * the stream is grown to fit.
181 * See the documentation of ISequentialStream for more info.
183 static HRESULT WINAPI StgStreamImpl_Write(
184 IStream* iface,
185 const void* pv, /* [size_is][in] */
186 ULONG cb, /* [in] */
187 ULONG* pcbWritten) /* [out] */
189 StgStreamImpl* This = impl_from_IStream(iface);
191 ULONG bytesWritten = 0;
192 HRESULT res;
194 TRACE("%p, %p, %lu, %p.\n", iface, pv, cb, pcbWritten);
197 * Do we have permission to write to this stream?
199 switch(STGM_ACCESS_MODE(This->grfMode))
201 case STGM_WRITE:
202 case STGM_READWRITE:
203 break;
204 default:
205 WARN("access denied by flags: %#lx\n", STGM_ACCESS_MODE(This->grfMode));
206 return STG_E_ACCESSDENIED;
209 if (!pv)
210 return STG_E_INVALIDPOINTER;
212 if (!This->parentStorage)
214 WARN("storage reverted\n");
215 return STG_E_REVERTED;
219 * If the caller is not interested in the number of bytes written,
220 * we use another buffer to avoid "if" statements in the code.
222 if (pcbWritten == 0)
223 pcbWritten = &bytesWritten;
226 * Initialize the out parameter
228 *pcbWritten = 0;
230 if (cb == 0)
232 TRACE("<-- S_OK, written 0\n");
233 return S_OK;
236 res = StorageBaseImpl_StreamWriteAt(This->parentStorage,
237 This->dirEntry,
238 This->currentPosition,
241 pcbWritten);
244 * Advance the position pointer for the number of positions written.
246 This->currentPosition.QuadPart += *pcbWritten;
248 if (SUCCEEDED(res))
249 res = StorageBaseImpl_Flush(This->parentStorage);
251 TRACE("<-- %#lx, written %lu\n", res, *pcbWritten);
252 return res;
255 /***
256 * This method is part of the IStream interface.
258 * It will move the current stream pointer according to the parameters
259 * given.
261 * See the documentation of IStream for more info.
263 static HRESULT WINAPI StgStreamImpl_Seek(
264 IStream* iface,
265 LARGE_INTEGER dlibMove, /* [in] */
266 DWORD dwOrigin, /* [in] */
267 ULARGE_INTEGER* plibNewPosition) /* [out] */
269 StgStreamImpl* This = impl_from_IStream(iface);
271 ULARGE_INTEGER newPosition;
272 DirEntry currentEntry;
273 HRESULT hr;
275 TRACE("%p, %ld, %ld, %p.\n", iface, dlibMove.u.LowPart, dwOrigin, plibNewPosition);
278 * fail if the stream has no parent (as does windows)
281 if (!This->parentStorage)
283 WARN("storage reverted\n");
284 return STG_E_REVERTED;
288 * The caller is allowed to pass in NULL as the new position return value.
289 * If it happens, we assign it to a dynamic variable to avoid special cases
290 * in the code below.
292 if (plibNewPosition == 0)
294 plibNewPosition = &newPosition;
298 * The file pointer is moved depending on the given "function"
299 * parameter.
301 switch (dwOrigin)
303 case STREAM_SEEK_SET:
304 plibNewPosition->u.HighPart = 0;
305 plibNewPosition->u.LowPart = 0;
306 break;
307 case STREAM_SEEK_CUR:
308 *plibNewPosition = This->currentPosition;
309 break;
310 case STREAM_SEEK_END:
311 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage, This->dirEntry, &currentEntry);
312 if (FAILED(hr)) return hr;
313 *plibNewPosition = currentEntry.size;
314 break;
315 default:
316 WARN("invalid dwOrigin %ld\n", dwOrigin);
317 return STG_E_INVALIDFUNCTION;
320 plibNewPosition->QuadPart += dlibMove.QuadPart;
323 * tell the caller what we calculated
325 This->currentPosition = *plibNewPosition;
327 return S_OK;
330 /***
331 * This method is part of the IStream interface.
333 * It will change the size of a stream.
335 * See the documentation of IStream for more info.
337 static HRESULT WINAPI StgStreamImpl_SetSize(
338 IStream* iface,
339 ULARGE_INTEGER libNewSize) /* [in] */
341 StgStreamImpl* This = impl_from_IStream(iface);
343 HRESULT hr;
345 TRACE("%p, %ld.\n", iface, libNewSize.u.LowPart);
347 if(!This->parentStorage)
349 WARN("storage reverted\n");
350 return STG_E_REVERTED;
354 * As documented.
356 if (libNewSize.u.HighPart != 0)
358 WARN("invalid value for libNewSize.u.HighPart %ld\n", libNewSize.u.HighPart);
359 return STG_E_INVALIDFUNCTION;
363 * Do we have permission?
365 if (!(This->grfMode & (STGM_WRITE | STGM_READWRITE)))
367 WARN("access denied\n");
368 return STG_E_ACCESSDENIED;
371 hr = StorageBaseImpl_StreamSetSize(This->parentStorage, This->dirEntry, libNewSize);
373 if (SUCCEEDED(hr))
374 hr = StorageBaseImpl_Flush(This->parentStorage);
376 return hr;
379 /***
380 * This method is part of the IStream interface.
382 * It will copy the 'cb' Bytes to 'pstm' IStream.
384 * See the documentation of IStream for more info.
386 static HRESULT WINAPI StgStreamImpl_CopyTo(
387 IStream* iface,
388 IStream* pstm, /* [unique][in] */
389 ULARGE_INTEGER cb, /* [in] */
390 ULARGE_INTEGER* pcbRead, /* [out] */
391 ULARGE_INTEGER* pcbWritten) /* [out] */
393 StgStreamImpl* This = impl_from_IStream(iface);
394 HRESULT hr = S_OK;
395 BYTE tmpBuffer[128];
396 ULONG bytesRead, bytesWritten, copySize;
397 ULARGE_INTEGER totalBytesRead;
398 ULARGE_INTEGER totalBytesWritten;
400 TRACE("%p, %p, %ld, %p, %p.\n", iface, pstm, cb.u.LowPart, pcbRead, pcbWritten);
403 * Sanity check
406 if (!This->parentStorage)
408 WARN("storage reverted\n");
409 return STG_E_REVERTED;
412 if ( pstm == 0 )
413 return STG_E_INVALIDPOINTER;
415 totalBytesRead.QuadPart = 0;
416 totalBytesWritten.QuadPart = 0;
418 while ( cb.QuadPart > 0 )
420 if ( cb.QuadPart >= sizeof(tmpBuffer) )
421 copySize = sizeof(tmpBuffer);
422 else
423 copySize = cb.u.LowPart;
425 IStream_Read(iface, tmpBuffer, copySize, &bytesRead);
427 totalBytesRead.QuadPart += bytesRead;
429 IStream_Write(pstm, tmpBuffer, bytesRead, &bytesWritten);
431 totalBytesWritten.QuadPart += bytesWritten;
434 * Check that read & write operations were successful
436 if (bytesRead != bytesWritten)
438 hr = STG_E_MEDIUMFULL;
439 WARN("medium full\n");
440 break;
443 if (bytesRead!=copySize)
444 cb.QuadPart = 0;
445 else
446 cb.QuadPart -= bytesRead;
449 if (pcbRead) pcbRead->QuadPart = totalBytesRead.QuadPart;
450 if (pcbWritten) pcbWritten->QuadPart = totalBytesWritten.QuadPart;
452 return hr;
455 /***
456 * This method is part of the IStream interface.
458 * For streams contained in structured storages, this method
459 * does nothing. This is what the documentation tells us.
461 * See the documentation of IStream for more info.
463 static HRESULT WINAPI StgStreamImpl_Commit(
464 IStream* iface,
465 DWORD grfCommitFlags) /* [in] */
467 StgStreamImpl* This = impl_from_IStream(iface);
469 if (!This->parentStorage)
471 WARN("storage reverted\n");
472 return STG_E_REVERTED;
475 return StorageBaseImpl_Flush(This->parentStorage);
478 /***
479 * This method is part of the IStream interface.
481 * For streams contained in structured storages, this method
482 * does nothing. This is what the documentation tells us.
484 * See the documentation of IStream for more info.
486 static HRESULT WINAPI StgStreamImpl_Revert(
487 IStream* iface)
489 return S_OK;
492 static HRESULT WINAPI StgStreamImpl_LockRegion(
493 IStream* iface,
494 ULARGE_INTEGER libOffset, /* [in] */
495 ULARGE_INTEGER cb, /* [in] */
496 DWORD dwLockType) /* [in] */
498 StgStreamImpl* This = impl_from_IStream(iface);
500 if (!This->parentStorage)
502 WARN("storage reverted\n");
503 return STG_E_REVERTED;
506 FIXME("not implemented!\n");
507 return E_NOTIMPL;
510 static HRESULT WINAPI StgStreamImpl_UnlockRegion(
511 IStream* iface,
512 ULARGE_INTEGER libOffset, /* [in] */
513 ULARGE_INTEGER cb, /* [in] */
514 DWORD dwLockType) /* [in] */
516 StgStreamImpl* This = impl_from_IStream(iface);
518 if (!This->parentStorage)
520 WARN("storage reverted\n");
521 return STG_E_REVERTED;
524 FIXME("not implemented!\n");
525 return E_NOTIMPL;
528 /***
529 * This method is part of the IStream interface.
531 * This method returns information about the current
532 * stream.
534 * See the documentation of IStream for more info.
536 static HRESULT WINAPI StgStreamImpl_Stat(
537 IStream* iface,
538 STATSTG* pstatstg, /* [out] */
539 DWORD grfStatFlag) /* [in] */
541 StgStreamImpl* This = impl_from_IStream(iface);
543 DirEntry currentEntry;
544 HRESULT hr;
546 TRACE("%p, %p, %#lx.\n", This, pstatstg, grfStatFlag);
549 * if stream has no parent, return STG_E_REVERTED
552 if (!This->parentStorage)
554 WARN("storage reverted\n");
555 return STG_E_REVERTED;
559 * Read the information from the directory entry.
561 hr = StorageBaseImpl_ReadDirEntry(This->parentStorage,
562 This->dirEntry,
563 &currentEntry);
565 if (SUCCEEDED(hr))
567 StorageUtl_CopyDirEntryToSTATSTG(This->parentStorage,
568 pstatstg,
569 &currentEntry,
570 grfStatFlag);
572 pstatstg->grfMode = This->grfMode;
574 /* In simple create mode cbSize is the current pos */
575 if((This->parentStorage->openFlags & STGM_SIMPLE) && This->parentStorage->create)
576 pstatstg->cbSize = This->currentPosition;
578 return S_OK;
581 WARN("failed to read entry\n");
582 return hr;
585 /***
586 * This method is part of the IStream interface.
588 * This method returns a clone of the interface that allows for
589 * another seek pointer
591 * See the documentation of IStream for more info.
593 * I am not totally sure what I am doing here but I presume that this
594 * should be basically as simple as creating a new stream with the same
595 * parent etc and positioning its seek cursor.
597 static HRESULT WINAPI StgStreamImpl_Clone(
598 IStream* iface,
599 IStream** ppstm) /* [out] */
601 StgStreamImpl* This = impl_from_IStream(iface);
602 StgStreamImpl* new_stream;
603 LARGE_INTEGER seek_pos;
605 TRACE("%p %p\n", This, ppstm);
608 * Sanity check
611 if (!This->parentStorage)
612 return STG_E_REVERTED;
614 if ( ppstm == 0 )
615 return STG_E_INVALIDPOINTER;
617 new_stream = StgStreamImpl_Construct (This->parentStorage, This->grfMode, This->dirEntry);
619 if (!new_stream)
620 return STG_E_INSUFFICIENTMEMORY; /* Currently the only reason for new_stream=0 */
622 *ppstm = &new_stream->IStream_iface;
623 IStream_AddRef(*ppstm);
625 seek_pos.QuadPart = This->currentPosition.QuadPart;
627 return IStream_Seek(*ppstm, seek_pos, STREAM_SEEK_SET, NULL);
631 * Virtual function table for the StgStreamImpl class.
633 static const IStreamVtbl StgStreamVtbl =
635 StgStreamImpl_QueryInterface,
636 StgStreamImpl_AddRef,
637 StgStreamImpl_Release,
638 StgStreamImpl_Read,
639 StgStreamImpl_Write,
640 StgStreamImpl_Seek,
641 StgStreamImpl_SetSize,
642 StgStreamImpl_CopyTo,
643 StgStreamImpl_Commit,
644 StgStreamImpl_Revert,
645 StgStreamImpl_LockRegion,
646 StgStreamImpl_UnlockRegion,
647 StgStreamImpl_Stat,
648 StgStreamImpl_Clone
651 /******************************************************************************
652 ** StgStreamImpl implementation
655 /***
656 * This is the constructor for the StgStreamImpl class.
658 * Params:
659 * parentStorage - Pointer to the storage that contains the stream to open
660 * dirEntry - Index of the directory entry that points to this stream.
662 StgStreamImpl* StgStreamImpl_Construct(
663 StorageBaseImpl* parentStorage,
664 DWORD grfMode,
665 DirRef dirEntry)
667 StgStreamImpl* newStream;
669 newStream = HeapAlloc(GetProcessHeap(), 0, sizeof(StgStreamImpl));
671 if (newStream)
674 * Set-up the virtual function table and reference count.
676 newStream->IStream_iface.lpVtbl = &StgStreamVtbl;
677 newStream->ref = 0;
679 newStream->parentStorage = parentStorage;
682 * We want to nail-down the reference to the storage in case the
683 * stream out-lives the storage in the client application.
685 * -- IStorage_AddRef(&newStream->parentStorage->IStorage_iface);
687 * No, don't do this. Some apps call IStorage_Release without
688 * calling IStream_Release first. If we grab a reference the
689 * file is not closed, and the app fails when it tries to
690 * reopen the file (Easy-PC, for example)
693 newStream->grfMode = grfMode;
694 newStream->dirEntry = dirEntry;
697 * Start the stream at the beginning.
699 newStream->currentPosition.u.HighPart = 0;
700 newStream->currentPosition.u.LowPart = 0;
702 /* add us to the storage's list of active streams */
703 StorageBaseImpl_AddStream(parentStorage, newStream);
706 return newStream;