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