Fix the ITS moniker implementation.
[wine] / dlls / itss / storage.c
1 /*
2  *    ITSS Storage implementation
3  *
4  * Copyright 2004 Mike McCormack
5  *
6  *  see http://bonedaddy.net/pabs3/hhm/#chmspec
7  *
8  * This library is free software; you can redistribute it and/or
9  * modify it under the terms of the GNU Lesser General Public
10  * License as published by the Free Software Foundation; either
11  * version 2.1 of the License, or (at your option) any later version.
12  *
13  * This library is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16  * Lesser General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public
19  * License along with this library; if not, write to the Free Software
20  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21  */
22
23 #include "config.h"
24
25 #include <stdarg.h>
26 #include <stdio.h>
27
28 #include "windef.h"
29 #include "winbase.h"
30 #include "winuser.h"
31 #include "winnls.h"
32 #include "winreg.h"
33 #include "ole2.h"
34
35 #include "uuids.h"
36
37 #include "itss.h"
38 #include "chm_lib.h"
39 #include "itsstor.h"
40
41 #include "wine/unicode.h"
42 #include "wine/debug.h"
43
44 WINE_DEFAULT_DEBUG_CHANNEL(itss);
45
46 /************************************************************************/
47
48 typedef struct _ITSS_IStorageImpl
49 {
50     IStorageVtbl *vtbl_IStorage;
51     DWORD ref;
52     struct chmFile *chmfile;
53     WCHAR dir[1];
54 } ITSS_IStorageImpl;
55
56 struct enum_info
57 {
58     struct enum_info *next, *prev;
59     struct chmUnitInfo ui;
60 };
61
62 typedef struct _IEnumSTATSTG_Impl
63 {
64     IEnumSTATSTGVtbl *vtbl_IEnumSTATSTG;
65     DWORD ref;
66     struct enum_info *first, *last, *current;
67 } IEnumSTATSTG_Impl;
68
69 typedef struct _IStream_Impl
70 {
71     IStreamVtbl *vtbl_IStream;
72     DWORD ref;
73     ITSS_IStorageImpl *stg;
74     ULONGLONG addr;
75     struct chmUnitInfo ui;
76 } IStream_Impl;
77
78 static HRESULT ITSS_create_chm_storage(
79            struct chmFile *chmfile, const WCHAR *dir, IStorage** ppstgOpen );
80 static IStream_Impl* ITSS_create_stream( 
81            ITSS_IStorageImpl *stg, struct chmUnitInfo *ui );
82
83 /************************************************************************/
84
85 static HRESULT WINAPI ITSS_IEnumSTATSTG_QueryInterface(
86     IEnumSTATSTG* iface,
87     REFIID riid,
88     void** ppvObject)
89 {
90     ICOM_THIS(IEnumSTATSTG_Impl,iface);
91
92     if (IsEqualGUID(riid, &IID_IUnknown)
93         || IsEqualGUID(riid, &IID_IEnumSTATSTG))
94     {
95         IEnumSTATSTG_AddRef(iface);
96         *ppvObject = This;
97         return S_OK;
98     }
99
100     WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppvObject);
101     return E_NOINTERFACE;
102 }
103
104 static ULONG WINAPI ITSS_IEnumSTATSTG_AddRef(
105     IEnumSTATSTG* iface)
106 {
107     ICOM_THIS(IEnumSTATSTG_Impl,iface);
108     return ++(This->ref);
109 }
110
111 static ULONG WINAPI ITSS_IEnumSTATSTG_Release(
112     IEnumSTATSTG* iface)
113 {
114     ICOM_THIS(IEnumSTATSTG_Impl,iface);
115
116     ULONG ref = --This->ref;
117
118     if (ref == 0)
119     {
120         while( This->first )
121         {
122             struct enum_info *t = This->first->next;
123             HeapFree( GetProcessHeap(), 0, This->first );
124             This->first = t;
125         }
126         HeapFree(GetProcessHeap(), 0, This);
127     }
128
129     return ref;
130 }
131
132 static HRESULT WINAPI ITSS_IEnumSTATSTG_Next(
133         IEnumSTATSTG* iface,
134         ULONG celt,
135         STATSTG* rgelt,
136         ULONG* pceltFetched)
137 {
138     ICOM_THIS(IEnumSTATSTG_Impl,iface);
139     DWORD len, n;
140     struct enum_info *cur;
141
142     TRACE("%p %lu %p %p\n", This, celt, rgelt, pceltFetched );
143
144     cur = This->current;
145     n = 0;
146     while( (n<celt) && cur) 
147     {
148         WCHAR *str;
149
150         memset( rgelt, 0, sizeof *rgelt );
151
152         /* copy the name */
153         str = cur->ui.path;
154         if( *str == '/' )
155             str++;
156         len = strlenW( str ) + 1;
157         rgelt->pwcsName = CoTaskMemAlloc( len*sizeof(WCHAR) );
158         strcpyW( rgelt->pwcsName, str );
159
160         /* determine the type */
161         if( rgelt->pwcsName[len-2] == '/' )
162         {
163             rgelt->pwcsName[len-2] = 0;
164             rgelt->type = STGTY_STORAGE;
165         }
166         else
167             rgelt->type = STGTY_STREAM;
168
169         /* copy the size */
170         rgelt->cbSize.QuadPart = cur->ui.length;
171
172         /* advance to the next item if it exists */
173         n++;
174         cur = cur->next;
175     }
176
177     This->current = cur;
178     *pceltFetched = n;
179
180     if( n < celt )
181         return S_FALSE;
182
183     return S_OK;
184 }
185
186 static HRESULT WINAPI ITSS_IEnumSTATSTG_Skip(
187         IEnumSTATSTG* iface,
188         ULONG celt)
189 {
190     ICOM_THIS(IEnumSTATSTG_Impl,iface);
191     DWORD n;
192     struct enum_info *cur;
193
194     TRACE("%p %lu\n", This, celt );
195
196     cur = This->current;
197     n = 0;
198     while( (n<celt) && cur) 
199     {
200         n++;
201         cur = cur->next;
202     }
203     This->current = cur;
204
205     if( n < celt )
206         return S_FALSE;
207
208     return S_OK;
209 }
210
211 static HRESULT WINAPI ITSS_IEnumSTATSTG_Reset(
212         IEnumSTATSTG* iface)
213 {
214     ICOM_THIS(IEnumSTATSTG_Impl,iface);
215
216     TRACE("%p\n", This );
217
218     This->current = This->first;
219
220     return S_OK;
221 }
222
223 static HRESULT WINAPI ITSS_IEnumSTATSTG_Clone(
224         IEnumSTATSTG* iface,
225         IEnumSTATSTG** ppenum)
226 {
227     FIXME("\n");
228     return E_NOTIMPL;
229 }
230
231 struct IEnumSTATSTGVtbl IEnumSTATSTG_vtbl =
232 {
233     ITSS_IEnumSTATSTG_QueryInterface,
234     ITSS_IEnumSTATSTG_AddRef,
235     ITSS_IEnumSTATSTG_Release,
236     ITSS_IEnumSTATSTG_Next,
237     ITSS_IEnumSTATSTG_Skip,
238     ITSS_IEnumSTATSTG_Reset,
239     ITSS_IEnumSTATSTG_Clone
240 };
241
242 static IEnumSTATSTG_Impl *ITSS_create_enum( void )
243 {
244     IEnumSTATSTG_Impl *stgenum;
245
246     stgenum = HeapAlloc( GetProcessHeap(), 0, sizeof (IEnumSTATSTG_Impl) );
247     stgenum->vtbl_IEnumSTATSTG = &IEnumSTATSTG_vtbl;
248     stgenum->ref = 1;
249     stgenum->first = NULL;
250     stgenum->last = NULL;
251     stgenum->current = NULL;
252
253     TRACE(" -> %p\n", stgenum );
254
255     return stgenum;
256 }
257
258 /************************************************************************/
259
260 HRESULT WINAPI ITSS_IStorageImpl_QueryInterface(
261     IStorage* iface,
262     REFIID riid,
263     void** ppvObject)
264 {
265     ICOM_THIS(ITSS_IStorageImpl,iface);
266
267     if (IsEqualGUID(riid, &IID_IUnknown)
268         || IsEqualGUID(riid, &IID_IStorage))
269     {
270         IStorage_AddRef(iface);
271         *ppvObject = This;
272         return S_OK;
273     }
274
275     WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppvObject);
276     return E_NOINTERFACE;
277 }
278
279 ULONG WINAPI ITSS_IStorageImpl_AddRef(
280     IStorage* iface)
281 {
282     ICOM_THIS(ITSS_IStorageImpl,iface);
283     return ++(This->ref);
284 }
285
286 ULONG WINAPI ITSS_IStorageImpl_Release(
287     IStorage* iface)
288 {
289     ICOM_THIS(ITSS_IStorageImpl,iface);
290
291     ULONG ref = --This->ref;
292
293     if (ref == 0)
294     {
295         HeapFree(GetProcessHeap(), 0, This);
296     }
297
298     return ref;
299 }
300
301 HRESULT WINAPI ITSS_IStorageImpl_CreateStream(
302     IStorage* iface,
303     LPCOLESTR pwcsName,
304     DWORD grfMode,
305     DWORD reserved1,
306     DWORD reserved2,
307     IStream** ppstm)
308 {
309     FIXME("\n");
310     return E_NOTIMPL;
311 }
312
313 HRESULT WINAPI ITSS_IStorageImpl_OpenStream(
314     IStorage* iface,
315     LPCOLESTR pwcsName,
316     void* reserved1,
317     DWORD grfMode,
318     DWORD reserved2,
319     IStream** ppstm)
320 {
321     ICOM_THIS(ITSS_IStorageImpl,iface);
322     IStream_Impl *stm;
323     DWORD len;
324     struct chmUnitInfo ui;
325     int r;
326     WCHAR *path;
327
328     TRACE("%p %s %p %lu %lu %p\n", This, debugstr_w(pwcsName),
329           reserved1, grfMode, reserved2, ppstm );
330
331     len = strlenW( This->dir ) + strlenW( pwcsName ) + 1;
332     path = HeapAlloc( GetProcessHeap(), 0, len*sizeof(WCHAR) );
333     strcpyW( path, This->dir );
334     if( pwcsName[0] == '/' )
335     {
336         WCHAR *p = &path[strlenW( path ) - 1];
337         while( ( path <= p ) && ( *p = '/' ) )
338             *p-- = 0;
339     }
340     strcatW( path, pwcsName );
341
342     TRACE("Resolving %s\n", debugstr_w(path));
343
344     r = chm_resolve_object(This->chmfile, path, &ui);
345     HeapFree( GetProcessHeap(), 0, path );
346
347     if( r != CHM_RESOLVE_SUCCESS )
348         return STG_E_FILENOTFOUND;
349
350     stm = ITSS_create_stream( This, &ui );
351     if( !stm )
352         return E_FAIL;
353
354     *ppstm = (IStream*) stm;
355
356     return S_OK;
357 }
358
359 HRESULT WINAPI ITSS_IStorageImpl_CreateStorage(
360     IStorage* iface,
361     LPCOLESTR pwcsName,
362     DWORD grfMode,
363     DWORD dwStgFmt,
364     DWORD reserved2,
365     IStorage** ppstg)
366 {
367     FIXME("\n");
368     return E_NOTIMPL;
369 }
370
371 HRESULT WINAPI ITSS_IStorageImpl_OpenStorage(
372     IStorage* iface,
373     LPCOLESTR pwcsName,
374     IStorage* pstgPriority,
375     DWORD grfMode,
376     SNB snbExclude,
377     DWORD reserved,
378     IStorage** ppstg)
379 {
380     ICOM_THIS(ITSS_IStorageImpl,iface);
381
382     FIXME("%p %s %p %lu %p %lu %p\n", This, debugstr_w(pwcsName),
383           pstgPriority, grfMode, snbExclude, reserved, ppstg);
384     return E_NOTIMPL;
385 }
386
387 HRESULT WINAPI ITSS_IStorageImpl_CopyTo(
388     IStorage* iface,
389     DWORD ciidExclude,
390     const IID* rgiidExclude,
391     SNB snbExclude,
392     IStorage* pstgDest)
393 {
394     FIXME("\n");
395     return E_NOTIMPL;
396 }
397
398 HRESULT WINAPI ITSS_IStorageImpl_MoveElementTo(
399     IStorage* iface,
400     LPCOLESTR pwcsName,
401     IStorage* pstgDest,
402     LPCOLESTR pwcsNewName,
403     DWORD grfFlags)
404 {
405     FIXME("\n");
406     return E_NOTIMPL;
407 }
408
409 HRESULT WINAPI ITSS_IStorageImpl_Commit(
410     IStorage* iface,
411     DWORD grfCommitFlags)
412 {
413     FIXME("\n");
414     return E_NOTIMPL;
415 }
416
417 HRESULT WINAPI ITSS_IStorageImpl_Revert(
418     IStorage* iface)
419 {
420     FIXME("\n");
421     return E_NOTIMPL;
422 }
423
424 static int ITSS_chm_enumerator(
425     struct chmFile *h,
426     struct chmUnitInfo *ui,
427     void *context)
428 {
429     struct enum_info *info;
430     IEnumSTATSTG_Impl* stgenum = context;
431
432     TRACE("adding %s to enumeration\n", debugstr_w(ui->path) );
433
434     info = HeapAlloc( GetProcessHeap(), 0, sizeof (struct enum_info) );
435     memcpy( &info->ui, ui, sizeof info->ui );
436
437     info->next = NULL;
438     info->prev = stgenum->last;
439     if( stgenum->last )
440         stgenum->last->next = info;
441     else
442         stgenum->first = info;
443     stgenum->last = info;
444     
445     return CHM_ENUMERATOR_CONTINUE;
446 }
447
448 HRESULT WINAPI ITSS_IStorageImpl_EnumElements(
449     IStorage* iface,
450     DWORD reserved1,
451     void* reserved2,
452     DWORD reserved3,
453     IEnumSTATSTG** ppenum)
454 {
455     ICOM_THIS(ITSS_IStorageImpl,iface);
456     IEnumSTATSTG_Impl* stgenum;
457
458     TRACE("%p %ld %p %ld %p\n", This, reserved1, reserved2, reserved3, ppenum );
459
460     stgenum = ITSS_create_enum();
461     if( !stgenum )
462         return E_FAIL;
463
464     chm_enumerate_dir(This->chmfile,
465                   This->dir,
466                   CHM_ENUMERATE_ALL,
467                   ITSS_chm_enumerator,
468                   stgenum );
469
470     stgenum->current = stgenum->first;
471
472     *ppenum = (IEnumSTATSTG*) stgenum;
473
474     return S_OK;
475 }
476
477 HRESULT WINAPI ITSS_IStorageImpl_DestroyElement(
478     IStorage* iface,
479     LPCOLESTR pwcsName)
480 {
481     FIXME("\n");
482     return E_NOTIMPL;
483 }
484
485 HRESULT WINAPI ITSS_IStorageImpl_RenameElement(
486     IStorage* iface,
487     LPCOLESTR pwcsOldName,
488     LPCOLESTR pwcsNewName)
489 {
490     FIXME("\n");
491     return E_NOTIMPL;
492 }
493
494 HRESULT WINAPI ITSS_IStorageImpl_SetElementTimes(
495     IStorage* iface,
496     LPCOLESTR pwcsName,
497     const FILETIME* pctime,
498     const FILETIME* patime,
499     const FILETIME* pmtime)
500 {
501     FIXME("\n");
502     return E_NOTIMPL;
503 }
504
505 HRESULT WINAPI ITSS_IStorageImpl_SetClass(
506     IStorage* iface,
507     REFCLSID clsid)
508 {
509     FIXME("\n");
510     return E_NOTIMPL;
511 }
512
513 HRESULT WINAPI ITSS_IStorageImpl_SetStateBits(
514     IStorage* iface,
515     DWORD grfStateBits,
516     DWORD grfMask)
517 {
518     FIXME("\n");
519     return E_NOTIMPL;
520 }
521
522 HRESULT WINAPI ITSS_IStorageImpl_Stat(
523     IStorage* iface,
524     STATSTG* pstatstg,
525     DWORD grfStatFlag)
526 {
527     FIXME("\n");
528     return E_NOTIMPL;
529 }
530
531 static IStorageVtbl ITSS_IStorageImpl_Vtbl =
532 {
533     ITSS_IStorageImpl_QueryInterface,
534     ITSS_IStorageImpl_AddRef,
535     ITSS_IStorageImpl_Release,
536     ITSS_IStorageImpl_CreateStream,
537     ITSS_IStorageImpl_OpenStream,
538     ITSS_IStorageImpl_CreateStorage,
539     ITSS_IStorageImpl_OpenStorage,
540     ITSS_IStorageImpl_CopyTo,
541     ITSS_IStorageImpl_MoveElementTo,
542     ITSS_IStorageImpl_Commit,
543     ITSS_IStorageImpl_Revert,
544     ITSS_IStorageImpl_EnumElements,
545     ITSS_IStorageImpl_DestroyElement,
546     ITSS_IStorageImpl_RenameElement,
547     ITSS_IStorageImpl_SetElementTimes,
548     ITSS_IStorageImpl_SetClass,
549     ITSS_IStorageImpl_SetStateBits,
550     ITSS_IStorageImpl_Stat,
551 };
552
553 static HRESULT ITSS_create_chm_storage(
554       struct chmFile *chmfile, const WCHAR *dir, IStorage** ppstgOpen )
555 {
556     ITSS_IStorageImpl *stg;
557     DWORD len;
558
559     TRACE("%p %s\n", chmfile, debugstr_w( dir ) );
560
561     len = strlenW( dir ) + 1;
562     stg = HeapAlloc( GetProcessHeap(), 0, 
563                      sizeof (ITSS_IStorageImpl) + len*sizeof(WCHAR) );
564     stg->vtbl_IStorage = &ITSS_IStorageImpl_Vtbl;
565     stg->ref = 1;
566     stg->chmfile = chmfile;
567     strcpyW( stg->dir, dir );
568
569     *ppstgOpen = (IStorage*) stg;
570
571     return S_OK;
572 }
573
574 HRESULT ITSS_StgOpenStorage( 
575     const WCHAR* pwcsName,
576     IStorage* pstgPriority,
577     DWORD grfMode,
578     SNB snbExclude,
579     DWORD reserved,
580     IStorage** ppstgOpen)
581 {
582     struct chmFile *chmfile;
583     static const WCHAR szRoot[] = { '/', 0 };
584
585     TRACE("%s\n", debugstr_w(pwcsName) );
586
587     chmfile = chm_openW( pwcsName );
588     if( !chmfile )
589         return E_FAIL;
590
591     return ITSS_create_chm_storage( chmfile, szRoot, ppstgOpen );
592 }
593
594 /************************************************************************/
595
596 static HRESULT WINAPI ITSS_IStream_QueryInterface(
597     IStream* iface,
598     REFIID riid,
599     void** ppvObject)
600 {
601     ICOM_THIS(IStream_Impl,iface);
602
603     if (IsEqualGUID(riid, &IID_IUnknown)
604         || IsEqualGUID(riid, &IID_ISequentialStream)
605         || IsEqualGUID(riid, &IID_IStream))
606     {
607         IStream_AddRef(iface);
608         *ppvObject = This;
609         return S_OK;
610     }
611
612     WARN("(%p)->(%s,%p),not found\n",This,debugstr_guid(riid),ppvObject);
613     return E_NOINTERFACE;
614 }
615
616 static ULONG WINAPI ITSS_IStream_AddRef(
617     IStream* iface)
618 {
619     ICOM_THIS(IStream_Impl,iface);
620     return ++(This->ref);
621 }
622
623 static ULONG WINAPI ITSS_IStream_Release(
624     IStream* iface)
625 {
626     ICOM_THIS(IStream_Impl,iface);
627
628     ULONG ref = --This->ref;
629
630     if (ref == 0)
631     {
632         IStorage_Release( (IStorage*) This->stg );
633         HeapFree(GetProcessHeap(), 0, This);
634     }
635
636     return ref;
637 }
638
639 static HRESULT WINAPI ITSS_IStream_Read(
640         IStream* iface,
641         void* pv,
642         ULONG cb,
643         ULONG* pcbRead)
644 {
645     ICOM_THIS(IStream_Impl,iface);
646     ULONG count;
647
648     TRACE("%p %p %lu %p\n", This, pv, cb, pcbRead);
649
650     count = chm_retrieve_object(This->stg->chmfile, 
651                           &This->ui, pv, This->addr, cb);
652     This->addr += count;
653     if( pcbRead )
654         *pcbRead = count;
655     
656     return S_OK;
657 }
658
659 static HRESULT WINAPI ITSS_IStream_Write(
660         IStream* iface,
661         const void* pv,
662         ULONG cb,
663         ULONG* pcbWritten)
664 {
665     FIXME("\n");
666     return E_NOTIMPL;
667 }
668
669 static HRESULT WINAPI ITSS_IStream_Seek(
670         IStream* iface,
671         LARGE_INTEGER dlibMove,
672         DWORD dwOrigin,
673         ULARGE_INTEGER* plibNewPosition)
674 {
675     ICOM_THIS(IStream_Impl,iface);
676     LONGLONG newpos;
677
678     TRACE("%p %s %lu %p\n", This,
679           wine_dbgstr_longlong( dlibMove.QuadPart ), dwOrigin, plibNewPosition );
680
681     newpos = This->addr;
682     switch( dwOrigin )
683     {
684     case STREAM_SEEK_CUR:
685         newpos = This->addr + dlibMove.QuadPart;
686         break;
687     case STREAM_SEEK_SET:
688         newpos = dlibMove.QuadPart;
689         break;
690     case STREAM_SEEK_END:
691         newpos = This->ui.length + dlibMove.QuadPart;
692         break;
693     }
694
695     if( ( newpos < 0 ) || ( newpos > This->ui.length ) )
696         return STG_E_INVALIDPOINTER;
697
698     This->addr = newpos;
699     if( plibNewPosition )
700         plibNewPosition->QuadPart = This->addr;
701
702     return S_OK;
703 }
704
705 static HRESULT WINAPI ITSS_IStream_SetSize(
706         IStream* iface,
707         ULARGE_INTEGER libNewSize)
708 {
709     FIXME("\n");
710     return E_NOTIMPL;
711 }
712
713 static HRESULT WINAPI ITSS_IStream_CopyTo(
714         IStream* iface,
715         IStream* pstm,
716         ULARGE_INTEGER cb,
717         ULARGE_INTEGER* pcbRead,
718         ULARGE_INTEGER* pcbWritten)
719 {
720     FIXME("\n");
721     return E_NOTIMPL;
722 }
723
724 static HRESULT WINAPI ITSS_IStream_Commit(
725         IStream* iface,
726         DWORD grfCommitFlags)
727 {
728     FIXME("\n");
729     return E_NOTIMPL;
730 }
731
732 static HRESULT WINAPI ITSS_IStream_Revert(
733         IStream* iface)
734 {
735     FIXME("\n");
736     return E_NOTIMPL;
737 }
738
739 static HRESULT WINAPI ITSS_IStream_LockRegion(
740         IStream* iface,
741         ULARGE_INTEGER libOffset,
742         ULARGE_INTEGER cb,
743         DWORD dwLockType)
744 {
745     FIXME("\n");
746     return E_NOTIMPL;
747 }
748
749 static HRESULT WINAPI ITSS_IStream_UnlockRegion(
750         IStream* iface,
751         ULARGE_INTEGER libOffset,
752         ULARGE_INTEGER cb,
753         DWORD dwLockType)
754 {
755     FIXME("\n");
756     return E_NOTIMPL;
757 }
758
759 static HRESULT WINAPI ITSS_IStream_Stat(
760         IStream* iface,
761         STATSTG* pstatstg,
762         DWORD grfStatFlag)
763 {
764     ICOM_THIS(IStream_Impl,iface);
765
766     TRACE("%p %p %ld\n", This, pstatstg, grfStatFlag);
767
768     memset( pstatstg, 0, sizeof *pstatstg );
769     if( !( grfStatFlag & STATFLAG_NONAME ) )
770     {
771         FIXME("copy the name\n");
772     }
773     pstatstg->type = STGTY_STREAM;
774     pstatstg->cbSize.QuadPart = This->ui.length;
775     pstatstg->grfMode = STGM_READ;
776     memcpy( &pstatstg->clsid, &CLSID_ITStorage, sizeof (CLSID) );
777
778     return S_OK;
779 }
780
781 static HRESULT WINAPI ITSS_IStream_Clone(
782         IStream* iface,
783         IStream** ppstm)
784 {
785     FIXME("\n");
786     return E_NOTIMPL;
787 }
788
789 struct IStreamVtbl ITSS_IStream_vtbl =
790 {
791     ITSS_IStream_QueryInterface,
792     ITSS_IStream_AddRef,
793     ITSS_IStream_Release,
794     ITSS_IStream_Read,
795     ITSS_IStream_Write,
796     ITSS_IStream_Seek,
797     ITSS_IStream_SetSize,
798     ITSS_IStream_CopyTo,
799     ITSS_IStream_Commit,
800     ITSS_IStream_Revert,
801     ITSS_IStream_LockRegion,
802     ITSS_IStream_UnlockRegion,
803     ITSS_IStream_Stat,
804     ITSS_IStream_Clone,
805 };
806
807 static IStream_Impl *ITSS_create_stream(
808            ITSS_IStorageImpl *stg, struct chmUnitInfo *ui )
809 {
810     IStream_Impl *stm;
811
812     stm = HeapAlloc( GetProcessHeap(), 0, sizeof (IStream_Impl) );
813     stm->vtbl_IStream = &ITSS_IStream_vtbl;
814     stm->ref = 1;
815     stm->addr = 0;
816     memcpy( &stm->ui, ui, sizeof stm->ui );
817     stm->stg = stg;
818     IStorage_AddRef( (IStorage*) stg );
819
820     TRACE(" -> %p\n", stm );
821
822     return stm;
823 }