fdopen: don't rewind the file after creating the FILE* handle. Added
[wine] / dlls / ole32 / storage.c
1 /* Compound Storage
2  *
3  * Implemented using the documentation of the LAOLA project at
4  * <URL:http://wwwwbs.cs.tu-berlin.de/~schwartz/pmh/index.html>
5  * (Thanks to Martin Schwartz <schwartz@cs.tu-berlin.de>)
6  *
7  * Copyright 1998 Marcus Meissner
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
22  */
23
24 #include "config.h"
25
26 #include <assert.h>
27 #include <time.h>
28 #include <string.h>
29 #include <sys/types.h>
30 #ifdef HAVE_UNISTD_H
31 # include <unistd.h>
32 #endif
33 #include "windef.h"
34 #include "winternl.h"
35 #include "winerror.h"
36 #include "wine/winbase16.h"
37 #include "wine/unicode.h"
38 #include "objbase.h"
39 #include "wine/debug.h"
40
41 WINE_DEFAULT_DEBUG_CHANNEL(ole);
42 WINE_DECLARE_DEBUG_CHANNEL(relay);
43
44 struct storage_header {
45         BYTE    magic[8];       /* 00: magic */
46         BYTE    unknown1[36];   /* 08: unknown */
47         DWORD   num_of_bbd_blocks;/* 2C: length of big datablocks */
48         DWORD   root_startblock;/* 30: root storage first big block */
49         DWORD   unknown2[2];    /* 34: unknown */
50         DWORD   sbd_startblock; /* 3C: small block depot first big block */
51         DWORD   unknown3[3];    /* 40: unknown */
52         DWORD   bbd_list[109];  /* 4C: big data block list (up to end of sector)*/
53 };
54 struct storage_pps_entry {
55         WCHAR   pps_rawname[32];/* 00: \0 terminated widechar name */
56         WORD    pps_sizeofname; /* 40: namelength in bytes */
57         BYTE    pps_type;       /* 42: flags, 1 storage/dir, 2 stream, 5 root */
58         BYTE    pps_unknown0;   /* 43: unknown */
59         DWORD   pps_prev;       /* 44: previous pps */
60         DWORD   pps_next;       /* 48: next pps */
61         DWORD   pps_dir;        /* 4C: directory pps */
62         GUID    pps_guid;       /* 50: class ID */
63         DWORD   pps_unknown1;   /* 60: unknown */
64         FILETIME pps_ft1;       /* 64: filetime1 */
65         FILETIME pps_ft2;       /* 70: filetime2 */
66         DWORD   pps_sb;         /* 74: data startblock */
67         DWORD   pps_size;       /* 78: datalength. (<0x1000)?small:big blocks*/
68         DWORD   pps_unknown2;   /* 7C: unknown */
69 };
70
71 #define STORAGE_CHAINENTRY_FAT          0xfffffffd
72 #define STORAGE_CHAINENTRY_ENDOFCHAIN   0xfffffffe
73 #define STORAGE_CHAINENTRY_FREE         0xffffffff
74
75
76 static const BYTE STORAGE_magic[8]   ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
77
78 #define BIGSIZE         512
79 #define SMALLSIZE               64
80
81 #define SMALLBLOCKS_PER_BIGBLOCK        (BIGSIZE/SMALLSIZE)
82
83 #define READ_HEADER     assert(STORAGE_get_big_block(hf,-1,(LPBYTE)&sth));assert(!memcmp(STORAGE_magic,sth.magic,sizeof(STORAGE_magic)));
84 static ICOM_VTABLE(IStorage16) stvt16;
85 static ICOM_VTABLE(IStorage16) *segstvt16 = NULL;
86 static ICOM_VTABLE(IStream16) strvt16;
87 static ICOM_VTABLE(IStream16) *segstrvt16 = NULL;
88
89 /*ULONG WINAPI IStorage16_AddRef(LPSTORAGE16 this);*/
90 static void _create_istorage16(LPSTORAGE16 *stg);
91 static void _create_istream16(LPSTREAM16 *str);
92
93 #define IMPLEMENTED 1
94
95
96 /******************************************************************************
97  *              STORAGE_get_big_block   [Internal]
98  *
99  * Reading OLE compound storage
100  */
101 static BOOL
102 STORAGE_get_big_block(HANDLE hf,int n,BYTE *block)
103 {
104     DWORD result;
105
106     assert(n>=-1);
107     if (!SetFilePointer( hf, (n+1)*BIGSIZE, NULL, SEEK_SET ))
108     {
109         WARN(" seek failed (%ld)\n",GetLastError());
110         return FALSE;
111     }
112     if (!ReadFile( hf, block, BIGSIZE, &result, NULL ) || result != BIGSIZE)
113     {
114         WARN("(block size %d): read didn't read (%ld)\n",n,GetLastError());
115         return FALSE;
116     }
117     return TRUE;
118 }
119
120 /******************************************************************************
121  * STORAGE_put_big_block [INTERNAL]
122  */
123 static BOOL
124 STORAGE_put_big_block(HANDLE hf,int n,BYTE *block)
125 {
126     DWORD result;
127
128     assert(n>=-1);
129     if (!SetFilePointer( hf, (n+1)*BIGSIZE, NULL, SEEK_SET ))
130     {
131         WARN(" seek failed (%ld)\n",GetLastError());
132         return FALSE;
133     }
134     if (!WriteFile( hf, block, BIGSIZE, &result, NULL ) || result != BIGSIZE)
135     {
136         WARN(" write failed (%ld)\n",GetLastError());
137         return FALSE;
138     }
139     return TRUE;
140 }
141
142 /******************************************************************************
143  * STORAGE_get_next_big_blocknr [INTERNAL]
144  */
145 static int
146 STORAGE_get_next_big_blocknr(HANDLE hf,int blocknr) {
147         INT     bbs[BIGSIZE/sizeof(INT)];
148         struct  storage_header  sth;
149
150         READ_HEADER;
151
152         assert(blocknr>>7<sth.num_of_bbd_blocks);
153         if (sth.bbd_list[blocknr>>7]==0xffffffff)
154                 return -5;
155         if (!STORAGE_get_big_block(hf,sth.bbd_list[blocknr>>7],(LPBYTE)bbs))
156                 return -5;
157         assert(bbs[blocknr&0x7f]!=STORAGE_CHAINENTRY_FREE);
158         return bbs[blocknr&0x7f];
159 }
160
161 /******************************************************************************
162  * STORAGE_get_nth_next_big_blocknr [INTERNAL]
163  */
164 static int
165 STORAGE_get_nth_next_big_blocknr(HANDLE hf,int blocknr,int nr) {
166         INT     bbs[BIGSIZE/sizeof(INT)];
167         int     lastblock = -1;
168         struct storage_header sth;
169
170         READ_HEADER;
171
172         assert(blocknr>=0);
173         while (nr--) {
174                 assert((blocknr>>7)<sth.num_of_bbd_blocks);
175                 assert(sth.bbd_list[blocknr>>7]!=0xffffffff);
176
177                 /* simple caching... */
178                 if (lastblock!=sth.bbd_list[blocknr>>7]) {
179                         assert(STORAGE_get_big_block(hf,sth.bbd_list[blocknr>>7],(LPBYTE)bbs));
180                         lastblock = sth.bbd_list[blocknr>>7];
181                 }
182                 blocknr = bbs[blocknr&0x7f];
183         }
184         return blocknr;
185 }
186
187 /******************************************************************************
188  *              STORAGE_get_root_pps_entry      [Internal]
189  */
190 static BOOL
191 STORAGE_get_root_pps_entry(HANDLE hf,struct storage_pps_entry *pstde) {
192         int     blocknr,i;
193         BYTE    block[BIGSIZE];
194         struct storage_pps_entry        *stde=(struct storage_pps_entry*)block;
195         struct storage_header sth;
196
197         READ_HEADER;
198         blocknr = sth.root_startblock;
199         while (blocknr>=0) {
200                 assert(STORAGE_get_big_block(hf,blocknr,block));
201                 for (i=0;i<4;i++) {
202                         if (!stde[i].pps_sizeofname)
203                                 continue;
204                         if (stde[i].pps_type==5) {
205                                 *pstde=stde[i];
206                                 return TRUE;
207                         }
208                 }
209                 blocknr=STORAGE_get_next_big_blocknr(hf,blocknr);
210         }
211         return FALSE;
212 }
213
214 /******************************************************************************
215  * STORAGE_get_small_block [INTERNAL]
216  */
217 static BOOL
218 STORAGE_get_small_block(HANDLE hf,int blocknr,BYTE *sblock) {
219         BYTE                            block[BIGSIZE];
220         int                             bigblocknr;
221         struct storage_pps_entry        root;
222
223         assert(blocknr>=0);
224         assert(STORAGE_get_root_pps_entry(hf,&root));
225         bigblocknr = STORAGE_get_nth_next_big_blocknr(hf,root.pps_sb,blocknr/SMALLBLOCKS_PER_BIGBLOCK);
226         assert(bigblocknr>=0);
227         assert(STORAGE_get_big_block(hf,bigblocknr,block));
228
229         memcpy(sblock,((LPBYTE)block)+SMALLSIZE*(blocknr&(SMALLBLOCKS_PER_BIGBLOCK-1)),SMALLSIZE);
230         return TRUE;
231 }
232
233 /******************************************************************************
234  * STORAGE_put_small_block [INTERNAL]
235  */
236 static BOOL
237 STORAGE_put_small_block(HANDLE hf,int blocknr,BYTE *sblock) {
238         BYTE                            block[BIGSIZE];
239         int                             bigblocknr;
240         struct storage_pps_entry        root;
241
242         assert(blocknr>=0);
243
244         assert(STORAGE_get_root_pps_entry(hf,&root));
245         bigblocknr = STORAGE_get_nth_next_big_blocknr(hf,root.pps_sb,blocknr/SMALLBLOCKS_PER_BIGBLOCK);
246         assert(bigblocknr>=0);
247         assert(STORAGE_get_big_block(hf,bigblocknr,block));
248
249         memcpy(((LPBYTE)block)+SMALLSIZE*(blocknr&(SMALLBLOCKS_PER_BIGBLOCK-1)),sblock,SMALLSIZE);
250         assert(STORAGE_put_big_block(hf,bigblocknr,block));
251         return TRUE;
252 }
253
254 /******************************************************************************
255  * STORAGE_get_next_small_blocknr [INTERNAL]
256  */
257 static int
258 STORAGE_get_next_small_blocknr(HANDLE hf,int blocknr) {
259         BYTE                            block[BIGSIZE];
260         LPINT                           sbd = (LPINT)block;
261         int                             bigblocknr;
262         struct storage_header           sth;
263
264         READ_HEADER;
265         assert(blocknr>=0);
266         bigblocknr = STORAGE_get_nth_next_big_blocknr(hf,sth.sbd_startblock,blocknr/128);
267         assert(bigblocknr>=0);
268         assert(STORAGE_get_big_block(hf,bigblocknr,block));
269         assert(sbd[blocknr & 127]!=STORAGE_CHAINENTRY_FREE);
270         return sbd[blocknr & (128-1)];
271 }
272
273 /******************************************************************************
274  * STORAGE_get_nth_next_small_blocknr [INTERNAL]
275  */
276 static int
277 STORAGE_get_nth_next_small_blocknr(HANDLE hf,int blocknr,int nr) {
278         int     lastblocknr;
279         BYTE    block[BIGSIZE];
280         LPINT   sbd = (LPINT)block;
281         struct storage_header sth;
282
283         READ_HEADER;
284         lastblocknr=-1;
285         assert(blocknr>=0);
286         while ((nr--) && (blocknr>=0)) {
287                 if (lastblocknr/128!=blocknr/128) {
288                         int     bigblocknr;
289                         bigblocknr = STORAGE_get_nth_next_big_blocknr(hf,sth.sbd_startblock,blocknr/128);
290                         assert(bigblocknr>=0);
291                         assert(STORAGE_get_big_block(hf,bigblocknr,block));
292                         lastblocknr = blocknr;
293                 }
294                 assert(lastblocknr>=0);
295                 lastblocknr=blocknr;
296                 blocknr=sbd[blocknr & (128-1)];
297                 assert(blocknr!=STORAGE_CHAINENTRY_FREE);
298         }
299         return blocknr;
300 }
301
302 /******************************************************************************
303  * STORAGE_get_pps_entry [INTERNAL]
304  */
305 static int
306 STORAGE_get_pps_entry(HANDLE hf,int n,struct storage_pps_entry *pstde) {
307         int     blocknr;
308         BYTE    block[BIGSIZE];
309         struct storage_pps_entry *stde = (struct storage_pps_entry*)(((LPBYTE)block)+128*(n&3));
310         struct storage_header sth;
311
312         READ_HEADER;
313         /* we have 4 pps entries per big block */
314         blocknr = STORAGE_get_nth_next_big_blocknr(hf,sth.root_startblock,n/4);
315         assert(blocknr>=0);
316         assert(STORAGE_get_big_block(hf,blocknr,block));
317
318         *pstde=*stde;
319         return 1;
320 }
321
322 /******************************************************************************
323  *              STORAGE_put_pps_entry   [Internal]
324  */
325 static int
326 STORAGE_put_pps_entry(HANDLE hf,int n,struct storage_pps_entry *pstde) {
327         int     blocknr;
328         BYTE    block[BIGSIZE];
329         struct storage_pps_entry *stde = (struct storage_pps_entry*)(((LPBYTE)block)+128*(n&3));
330         struct storage_header sth;
331
332         READ_HEADER;
333
334         /* we have 4 pps entries per big block */
335         blocknr = STORAGE_get_nth_next_big_blocknr(hf,sth.root_startblock,n/4);
336         assert(blocknr>=0);
337         assert(STORAGE_get_big_block(hf,blocknr,block));
338         *stde=*pstde;
339         assert(STORAGE_put_big_block(hf,blocknr,block));
340         return 1;
341 }
342
343 /******************************************************************************
344  *              STORAGE_look_for_named_pps      [Internal]
345  */
346 static int
347 STORAGE_look_for_named_pps(HANDLE hf,int n,LPOLESTR name) {
348         struct storage_pps_entry        stde;
349         int                             ret;
350
351         if (n==-1)
352                 return -1;
353         if (1!=STORAGE_get_pps_entry(hf,n,&stde))
354                 return -1;
355
356         if (!lstrcmpW(name,stde.pps_rawname))
357                 return n;
358         if (stde.pps_prev != -1) {
359                 ret=STORAGE_look_for_named_pps(hf,stde.pps_prev,name);
360                 if (ret!=-1)
361                         return ret;
362         }
363         if (stde.pps_next != -1) {
364                 ret=STORAGE_look_for_named_pps(hf,stde.pps_next,name);
365                 if (ret!=-1)
366                         return ret;
367         }
368         return -1;
369 }
370
371 /******************************************************************************
372  *              STORAGE_dump_pps_entry  [Internal]
373  *
374  * FIXME
375  *    Function is unused
376  */
377 void
378 STORAGE_dump_pps_entry(struct storage_pps_entry *stde) {
379     char        name[33];
380
381     WideCharToMultiByte( CP_ACP, 0, stde->pps_rawname, -1, name, sizeof(name), NULL, NULL);
382         if (!stde->pps_sizeofname)
383                 return;
384         DPRINTF("name: %s\n",name);
385         DPRINTF("type: %d\n",stde->pps_type);
386         DPRINTF("prev pps: %ld\n",stde->pps_prev);
387         DPRINTF("next pps: %ld\n",stde->pps_next);
388         DPRINTF("dir pps: %ld\n",stde->pps_dir);
389         DPRINTF("guid: %s\n",debugstr_guid(&(stde->pps_guid)));
390         if (stde->pps_type !=2) {
391                 time_t  t;
392                 DWORD dw;
393                 RtlTimeToSecondsSince1970((LARGE_INTEGER *)&(stde->pps_ft1),&dw);
394                 t = dw;
395                 DPRINTF("ts1: %s\n",ctime(&t));
396                 RtlTimeToSecondsSince1970((LARGE_INTEGER *)&(stde->pps_ft2),&dw);
397                 t = dw;
398                 DPRINTF("ts2: %s\n",ctime(&t));
399         }
400         DPRINTF("startblock: %ld\n",stde->pps_sb);
401         DPRINTF("size: %ld\n",stde->pps_size);
402 }
403
404 /******************************************************************************
405  * STORAGE_init_storage [INTERNAL]
406  */
407 static BOOL
408 STORAGE_init_storage(HANDLE hf) {
409         BYTE    block[BIGSIZE];
410         LPDWORD bbs;
411         struct storage_header *sth;
412         struct storage_pps_entry *stde;
413         DWORD result;
414
415         SetFilePointer( hf, 0, NULL, SEEK_SET );
416         /* block -1 is the storage header */
417         sth = (struct storage_header*)block;
418         memcpy(sth->magic,STORAGE_magic,8);
419         memset(sth->unknown1,0,sizeof(sth->unknown1));
420         memset(sth->unknown2,0,sizeof(sth->unknown2));
421         memset(sth->unknown3,0,sizeof(sth->unknown3));
422         sth->num_of_bbd_blocks  = 1;
423         sth->root_startblock    = 1;
424         sth->sbd_startblock     = 0xffffffff;
425         memset(sth->bbd_list,0xff,sizeof(sth->bbd_list));
426         sth->bbd_list[0]        = 0;
427         if (!WriteFile( hf, block, BIGSIZE, &result, NULL ) || result != BIGSIZE) return FALSE;
428         /* block 0 is the big block directory */
429         bbs=(LPDWORD)block;
430         memset(block,0xff,sizeof(block)); /* mark all blocks as free */
431         bbs[0]=STORAGE_CHAINENTRY_ENDOFCHAIN; /* for this block */
432         bbs[1]=STORAGE_CHAINENTRY_ENDOFCHAIN; /* for directory entry */
433         if (!WriteFile( hf, block, BIGSIZE, &result, NULL ) || result != BIGSIZE) return FALSE;
434         /* block 1 is the root directory entry */
435         memset(block,0x00,sizeof(block));
436         stde = (struct storage_pps_entry*)block;
437         MultiByteToWideChar( CP_ACP, 0, "RootEntry", -1, stde->pps_rawname,
438                              sizeof(stde->pps_rawname)/sizeof(WCHAR));
439         stde->pps_sizeofname    = (strlenW(stde->pps_rawname)+1) * sizeof(WCHAR);
440         stde->pps_type          = 5;
441         stde->pps_dir           = -1;
442         stde->pps_next          = -1;
443         stde->pps_prev          = -1;
444         stde->pps_sb            = 0xffffffff;
445         stde->pps_size          = 0;
446         return (WriteFile( hf, block, BIGSIZE, &result, NULL ) && result == BIGSIZE);
447 }
448
449 /******************************************************************************
450  *              STORAGE_set_big_chain   [Internal]
451  */
452 static BOOL
453 STORAGE_set_big_chain(HANDLE hf,int blocknr,INT type) {
454         BYTE    block[BIGSIZE];
455         LPINT   bbd = (LPINT)block;
456         int     nextblocknr,bigblocknr;
457         struct storage_header sth;
458
459         READ_HEADER;
460         assert(blocknr!=type);
461         while (blocknr>=0) {
462                 bigblocknr = sth.bbd_list[blocknr/128];
463                 assert(bigblocknr>=0);
464                 assert(STORAGE_get_big_block(hf,bigblocknr,block));
465
466                 nextblocknr = bbd[blocknr&(128-1)];
467                 bbd[blocknr&(128-1)] = type;
468                 if (type>=0)
469                         return TRUE;
470                 assert(STORAGE_put_big_block(hf,bigblocknr,block));
471                 type = STORAGE_CHAINENTRY_FREE;
472                 blocknr = nextblocknr;
473         }
474         return TRUE;
475 }
476
477 /******************************************************************************
478  * STORAGE_set_small_chain [Internal]
479  */
480 static BOOL
481 STORAGE_set_small_chain(HANDLE hf,int blocknr,INT type) {
482         BYTE    block[BIGSIZE];
483         LPINT   sbd = (LPINT)block;
484         int     lastblocknr,nextsmallblocknr,bigblocknr;
485         struct storage_header sth;
486
487         READ_HEADER;
488
489         assert(blocknr!=type);
490         lastblocknr=-129;bigblocknr=-2;
491         while (blocknr>=0) {
492                 /* cache block ... */
493                 if (lastblocknr/128!=blocknr/128) {
494                         bigblocknr = STORAGE_get_nth_next_big_blocknr(hf,sth.sbd_startblock,blocknr/128);
495                         assert(bigblocknr>=0);
496                         assert(STORAGE_get_big_block(hf,bigblocknr,block));
497                 }
498                 lastblocknr = blocknr;
499                 nextsmallblocknr = sbd[blocknr&(128-1)];
500                 sbd[blocknr&(128-1)] = type;
501                 assert(STORAGE_put_big_block(hf,bigblocknr,block));
502                 if (type>=0)
503                         return TRUE;
504                 type = STORAGE_CHAINENTRY_FREE;
505                 blocknr = nextsmallblocknr;
506         }
507         return TRUE;
508 }
509
510 /******************************************************************************
511  *              STORAGE_get_free_big_blocknr    [Internal]
512  */
513 static int
514 STORAGE_get_free_big_blocknr(HANDLE hf) {
515         BYTE    block[BIGSIZE];
516         LPINT   sbd = (LPINT)block;
517         int     lastbigblocknr,i,curblock,bigblocknr;
518         struct storage_header sth;
519
520         READ_HEADER;
521         curblock        = 0;
522         lastbigblocknr  = -1;
523         bigblocknr      = sth.bbd_list[curblock];
524         while (curblock<sth.num_of_bbd_blocks) {
525                 assert(bigblocknr>=0);
526                 assert(STORAGE_get_big_block(hf,bigblocknr,block));
527                 for (i=0;i<128;i++)
528                         if (sbd[i]==STORAGE_CHAINENTRY_FREE) {
529                                 sbd[i] = STORAGE_CHAINENTRY_ENDOFCHAIN;
530                                 assert(STORAGE_put_big_block(hf,bigblocknr,block));
531                                 memset(block,0x42,sizeof(block));
532                                 assert(STORAGE_put_big_block(hf,i+curblock*128,block));
533                                 return i+curblock*128;
534                         }
535                 lastbigblocknr = bigblocknr;
536                 bigblocknr = sth.bbd_list[++curblock];
537         }
538         bigblocknr = curblock*128;
539         /* since we have marked all blocks from 0 up to curblock*128-1
540          * the next free one is curblock*128, where we happily put our
541          * next large block depot.
542          */
543         memset(block,0xff,sizeof(block));
544         /* mark the block allocated and returned by this function */
545         sbd[1] = STORAGE_CHAINENTRY_ENDOFCHAIN;
546         assert(STORAGE_put_big_block(hf,bigblocknr,block));
547
548         /* if we had a bbd block already (mostlikely) we need
549          * to link the new one into the chain
550          */
551         if (lastbigblocknr!=-1)
552                 assert(STORAGE_set_big_chain(hf,lastbigblocknr,bigblocknr));
553         sth.bbd_list[curblock]=bigblocknr;
554         sth.num_of_bbd_blocks++;
555         assert(sth.num_of_bbd_blocks==curblock+1);
556         assert(STORAGE_put_big_block(hf,-1,(LPBYTE)&sth));
557
558         /* Set the end of the chain for the bigblockdepots */
559         assert(STORAGE_set_big_chain(hf,bigblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN));
560         /* add 1, for the first entry is used for the additional big block
561          * depot. (means we already used bigblocknr) */
562         memset(block,0x42,sizeof(block));
563         /* allocate this block (filled with 0x42) */
564         assert(STORAGE_put_big_block(hf,bigblocknr+1,block));
565         return bigblocknr+1;
566 }
567
568
569 /******************************************************************************
570  *              STORAGE_get_free_small_blocknr  [Internal]
571  */
572 static int
573 STORAGE_get_free_small_blocknr(HANDLE hf) {
574         BYTE    block[BIGSIZE];
575         LPINT   sbd = (LPINT)block;
576         int     lastbigblocknr,newblocknr,i,curblock,bigblocknr;
577         struct storage_pps_entry        root;
578         struct storage_header sth;
579
580         READ_HEADER;
581         bigblocknr      = sth.sbd_startblock;
582         curblock        = 0;
583         lastbigblocknr  = -1;
584         newblocknr      = -1;
585         while (bigblocknr>=0) {
586                 if (!STORAGE_get_big_block(hf,bigblocknr,block))
587                         return -1;
588                 for (i=0;i<128;i++)
589                         if (sbd[i]==STORAGE_CHAINENTRY_FREE) {
590                                 sbd[i]=STORAGE_CHAINENTRY_ENDOFCHAIN;
591                                 newblocknr = i+curblock*128;
592                                 break;
593                         }
594                 if (i!=128)
595                         break;
596                 lastbigblocknr = bigblocknr;
597                 bigblocknr = STORAGE_get_next_big_blocknr(hf,bigblocknr);
598                 curblock++;
599         }
600         if (newblocknr==-1) {
601                 bigblocknr = STORAGE_get_free_big_blocknr(hf);
602                 if (bigblocknr<0)
603                         return -1;
604                 READ_HEADER;
605                 memset(block,0xff,sizeof(block));
606                 sbd[0]=STORAGE_CHAINENTRY_ENDOFCHAIN;
607                 if (!STORAGE_put_big_block(hf,bigblocknr,block))
608                         return -1;
609                 if (lastbigblocknr==-1) {
610                         sth.sbd_startblock = bigblocknr;
611                         if (!STORAGE_put_big_block(hf,-1,(LPBYTE)&sth)) /* need to write it */
612                                 return -1;
613                 } else {
614                         if (!STORAGE_set_big_chain(hf,lastbigblocknr,bigblocknr))
615                                 return -1;
616                 }
617                 if (!STORAGE_set_big_chain(hf,bigblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
618                         return -1;
619                 newblocknr = curblock*128;
620         }
621         /* allocate enough big blocks for storing the allocated small block */
622         if (!STORAGE_get_root_pps_entry(hf,&root))
623                 return -1;
624         if (root.pps_sb==-1)
625                 lastbigblocknr  = -1;
626         else
627                 lastbigblocknr  = STORAGE_get_nth_next_big_blocknr(hf,root.pps_sb,(root.pps_size-1)/BIGSIZE);
628         while (root.pps_size < (newblocknr*SMALLSIZE+SMALLSIZE-1)) {
629                 /* we need to allocate more stuff */
630                 bigblocknr = STORAGE_get_free_big_blocknr(hf);
631                 if (bigblocknr<0)
632                         return -1;
633                 READ_HEADER;
634                 if (root.pps_sb==-1) {
635                         root.pps_sb      = bigblocknr;
636                         root.pps_size   += BIGSIZE;
637                 } else {
638                         if (!STORAGE_set_big_chain(hf,lastbigblocknr,bigblocknr))
639                                 return -1;
640                         root.pps_size   += BIGSIZE;
641                 }
642                 lastbigblocknr = bigblocknr;
643         }
644         if (!STORAGE_set_big_chain(hf,lastbigblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
645                 return -1;
646         if (!STORAGE_put_pps_entry(hf,0,&root))
647                 return -1;
648         return newblocknr;
649 }
650
651 /******************************************************************************
652  *              STORAGE_get_free_pps_entry      [Internal]
653  */
654 static int
655 STORAGE_get_free_pps_entry(HANDLE hf) {
656         int     blocknr,i,curblock,lastblocknr;
657         BYTE    block[BIGSIZE];
658         struct storage_pps_entry *stde = (struct storage_pps_entry*)block;
659         struct storage_header sth;
660
661         READ_HEADER;
662         blocknr = sth.root_startblock;
663         assert(blocknr>=0);
664         curblock=0;
665         while (blocknr>=0) {
666                 if (!STORAGE_get_big_block(hf,blocknr,block))
667                         return -1;
668                 for (i=0;i<4;i++)
669                         if (stde[i].pps_sizeofname==0) /* free */
670                                 return curblock*4+i;
671                 lastblocknr = blocknr;
672                 blocknr = STORAGE_get_next_big_blocknr(hf,blocknr);
673                 curblock++;
674         }
675         assert(blocknr==STORAGE_CHAINENTRY_ENDOFCHAIN);
676         blocknr = STORAGE_get_free_big_blocknr(hf);
677         /* sth invalidated */
678         if (blocknr<0)
679                 return -1;
680
681         if (!STORAGE_set_big_chain(hf,lastblocknr,blocknr))
682                 return -1;
683         if (!STORAGE_set_big_chain(hf,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
684                 return -1;
685         memset(block,0,sizeof(block));
686         STORAGE_put_big_block(hf,blocknr,block);
687         return curblock*4;
688 }
689
690 /* --- IStream16 implementation */
691
692 typedef struct
693 {
694         /* IUnknown fields */
695         ICOM_VFIELD(IStream16);
696         DWORD                           ref;
697         /* IStream16 fields */
698         SEGPTR                          thisptr; /* pointer to this struct as segmented */
699         struct storage_pps_entry        stde;
700         int                             ppsent;
701         HANDLE                         hf;
702         ULARGE_INTEGER                  offset;
703 } IStream16Impl;
704
705 /******************************************************************************
706  *              IStream16_QueryInterface        [STORAGE.518]
707  */
708 HRESULT WINAPI IStream16_fnQueryInterface(
709         IStream16* iface,REFIID refiid,LPVOID *obj
710 ) {
711         ICOM_THIS(IStream16Impl,iface);
712         TRACE_(relay)("(%p)->(%s,%p)\n",This,debugstr_guid(refiid),obj);
713         if (!memcmp(&IID_IUnknown,refiid,sizeof(IID_IUnknown))) {
714                 *obj = This;
715                 return 0;
716         }
717         return OLE_E_ENUM_NOMORE;
718
719 }
720
721 /******************************************************************************
722  * IStream16_AddRef [STORAGE.519]
723  */
724 ULONG WINAPI IStream16_fnAddRef(IStream16* iface) {
725         ICOM_THIS(IStream16Impl,iface);
726         return ++(This->ref);
727 }
728
729 /******************************************************************************
730  * IStream16_Release [STORAGE.520]
731  */
732 ULONG WINAPI IStream16_fnRelease(IStream16* iface) {
733         ICOM_THIS(IStream16Impl,iface);
734         FlushFileBuffers(This->hf);
735         This->ref--;
736         if (!This->ref) {
737                 CloseHandle(This->hf);
738                 UnMapLS( This->thisptr );
739                 HeapFree( GetProcessHeap(), 0, This );
740                 return 0;
741         }
742         return This->ref;
743 }
744
745 /******************************************************************************
746  *              IStream16_Seek  [STORAGE.523]
747  *
748  * FIXME
749  *    Does not handle 64 bits
750  */
751 HRESULT WINAPI IStream16_fnSeek(
752         IStream16* iface,LARGE_INTEGER offset,DWORD whence,ULARGE_INTEGER *newpos
753 ) {
754         ICOM_THIS(IStream16Impl,iface);
755         TRACE_(relay)("(%p)->([%ld.%ld],%ld,%p)\n",This,offset.s.HighPart,offset.s.LowPart,whence,newpos);
756
757         switch (whence) {
758         /* unix SEEK_xx should be the same as win95 ones */
759         case SEEK_SET:
760                 /* offset must be ==0 (<0 is invalid, and >0 cannot be handled
761                  * right now.
762                  */
763                 assert(offset.s.HighPart==0);
764                 This->offset.s.HighPart = offset.s.HighPart;
765                 This->offset.s.LowPart = offset.s.LowPart;
766                 break;
767         case SEEK_CUR:
768                 if (offset.s.HighPart < 0) {
769                         /* FIXME: is this negation correct ? */
770                         offset.s.HighPart = -offset.s.HighPart;
771                         offset.s.LowPart = (0xffffffff ^ offset.s.LowPart)+1;
772
773                         assert(offset.s.HighPart==0);
774                         assert(This->offset.s.LowPart >= offset.s.LowPart);
775                         This->offset.s.LowPart -= offset.s.LowPart;
776                 } else {
777                         assert(offset.s.HighPart==0);
778                         This->offset.s.LowPart+= offset.s.LowPart;
779                 }
780                 break;
781         case SEEK_END:
782                 assert(offset.s.HighPart==0);
783                 This->offset.s.LowPart = This->stde.pps_size-offset.s.LowPart;
784                 break;
785         }
786         if (This->offset.s.LowPart>This->stde.pps_size)
787                 This->offset.s.LowPart=This->stde.pps_size;
788         if (newpos) *newpos = This->offset;
789         return S_OK;
790 }
791
792 /******************************************************************************
793  *              IStream16_Read  [STORAGE.521]
794  */
795 HRESULT WINAPI IStream16_fnRead(
796         IStream16* iface,void  *pv,ULONG cb,ULONG  *pcbRead
797 ) {
798         ICOM_THIS(IStream16Impl,iface);
799         BYTE    block[BIGSIZE];
800         ULONG   *bytesread=pcbRead,xxread;
801         int     blocknr;
802
803         TRACE_(relay)("(%p)->(%p,%ld,%p)\n",This,pv,cb,pcbRead);
804         if (!pcbRead) bytesread=&xxread;
805         *bytesread = 0;
806
807         if (cb>This->stde.pps_size-This->offset.s.LowPart)
808                 cb=This->stde.pps_size-This->offset.s.LowPart;
809         if (This->stde.pps_size < 0x1000) {
810                 /* use small block reader */
811                 blocknr = STORAGE_get_nth_next_small_blocknr(This->hf,This->stde.pps_sb,This->offset.s.LowPart/SMALLSIZE);
812                 while (cb) {
813                         int     cc;
814
815                         if (!STORAGE_get_small_block(This->hf,blocknr,block)) {
816                            WARN("small block read failed!!!\n");
817                                 return E_FAIL;
818                         }
819                         cc = cb;
820                         if (cc>SMALLSIZE-(This->offset.s.LowPart&(SMALLSIZE-1)))
821                                 cc=SMALLSIZE-(This->offset.s.LowPart&(SMALLSIZE-1));
822                         memcpy((LPBYTE)pv,block+(This->offset.s.LowPart&(SMALLSIZE-1)),cc);
823                         This->offset.s.LowPart+=cc;
824                         (LPBYTE)pv+=cc;
825                         *bytesread+=cc;
826                         cb-=cc;
827                         blocknr = STORAGE_get_next_small_blocknr(This->hf,blocknr);
828                 }
829         } else {
830                 /* use big block reader */
831                 blocknr = STORAGE_get_nth_next_big_blocknr(This->hf,This->stde.pps_sb,This->offset.s.LowPart/BIGSIZE);
832                 while (cb) {
833                         int     cc;
834
835                         if (!STORAGE_get_big_block(This->hf,blocknr,block)) {
836                                 WARN("big block read failed!!!\n");
837                                 return E_FAIL;
838                         }
839                         cc = cb;
840                         if (cc>BIGSIZE-(This->offset.s.LowPart&(BIGSIZE-1)))
841                                 cc=BIGSIZE-(This->offset.s.LowPart&(BIGSIZE-1));
842                         memcpy((LPBYTE)pv,block+(This->offset.s.LowPart&(BIGSIZE-1)),cc);
843                         This->offset.s.LowPart+=cc;
844                         (LPBYTE)pv+=cc;
845                         *bytesread+=cc;
846                         cb-=cc;
847                         blocknr=STORAGE_get_next_big_blocknr(This->hf,blocknr);
848                 }
849         }
850         return S_OK;
851 }
852
853 /******************************************************************************
854  *              IStream16_Write [STORAGE.522]
855  */
856 HRESULT WINAPI IStream16_fnWrite(
857         IStream16* iface,const void *pv,ULONG cb,ULONG *pcbWrite
858 ) {
859         ICOM_THIS(IStream16Impl,iface);
860         BYTE    block[BIGSIZE];
861         ULONG   *byteswritten=pcbWrite,xxwritten;
862         int     oldsize,newsize,i,curoffset=0,lastblocknr,blocknr,cc;
863         HANDLE  hf = This->hf;
864
865         if (!pcbWrite) byteswritten=&xxwritten;
866         *byteswritten = 0;
867
868         TRACE_(relay)("(%p)->(%p,%ld,%p)\n",This,pv,cb,pcbWrite);
869         /* do we need to junk some blocks? */
870         newsize = This->offset.s.LowPart+cb;
871         oldsize = This->stde.pps_size;
872         if (newsize < oldsize) {
873                 if (oldsize < 0x1000) {
874                         /* only small blocks */
875                         blocknr=STORAGE_get_nth_next_small_blocknr(hf,This->stde.pps_sb,newsize/SMALLSIZE);
876
877                         assert(blocknr>=0);
878
879                         /* will set the rest of the chain to 'free' */
880                         if (!STORAGE_set_small_chain(hf,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
881                                 return E_FAIL;
882                 } else {
883                         if (newsize >= 0x1000) {
884                                 blocknr=STORAGE_get_nth_next_big_blocknr(hf,This->stde.pps_sb,newsize/BIGSIZE);
885                                 assert(blocknr>=0);
886
887                                 /* will set the rest of the chain to 'free' */
888                                 if (!STORAGE_set_big_chain(hf,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
889                                         return E_FAIL;
890                         } else {
891                                 /* Migrate large blocks to small blocks
892                                  * (we just migrate newsize bytes)
893                                  */
894                                 LPBYTE  curdata,data = HeapAlloc(GetProcessHeap(),0,newsize+BIGSIZE);
895                                 HRESULT r = E_FAIL;
896
897                                 cc      = newsize;
898                                 blocknr = This->stde.pps_sb;
899                                 curdata = data;
900                                 while (cc>0) {
901                                         if (!STORAGE_get_big_block(hf,blocknr,curdata)) {
902                                                 HeapFree(GetProcessHeap(),0,data);
903                                                 return E_FAIL;
904                                         }
905                                         curdata += BIGSIZE;
906                                         cc      -= BIGSIZE;
907                                         blocknr  = STORAGE_get_next_big_blocknr(hf,blocknr);
908                                 }
909                                 /* frees complete chain for this stream */
910                                 if (!STORAGE_set_big_chain(hf,This->stde.pps_sb,STORAGE_CHAINENTRY_FREE))
911                                         goto err;
912                                 curdata = data;
913                                 blocknr = This->stde.pps_sb = STORAGE_get_free_small_blocknr(hf);
914                                 if (blocknr<0)
915                                         goto err;
916                                 cc      = newsize;
917                                 while (cc>0) {
918                                         if (!STORAGE_put_small_block(hf,blocknr,curdata))
919                                                 goto err;
920                                         cc      -= SMALLSIZE;
921                                         if (cc<=0) {
922                                                 if (!STORAGE_set_small_chain(hf,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
923                                                         goto err;
924                                                 break;
925                                         } else {
926                                                 int newblocknr = STORAGE_get_free_small_blocknr(hf);
927                                                 if (newblocknr<0)
928                                                         goto err;
929                                                 if (!STORAGE_set_small_chain(hf,blocknr,newblocknr))
930                                                         goto err;
931                                                 blocknr = newblocknr;
932                                         }
933                                         curdata += SMALLSIZE;
934                                 }
935                                 r = S_OK;
936                         err:
937                                 HeapFree(GetProcessHeap(),0,data);
938                                 if(r != S_OK)
939                                         return r;
940                         }
941                 }
942                 This->stde.pps_size = newsize;
943         }
944
945         if (newsize > oldsize) {
946                 if (oldsize >= 0x1000) {
947                         /* should return the block right before the 'endofchain' */
948                         blocknr = STORAGE_get_nth_next_big_blocknr(hf,This->stde.pps_sb,This->stde.pps_size/BIGSIZE);
949                         assert(blocknr>=0);
950                         lastblocknr     = blocknr;
951                         for (i=oldsize/BIGSIZE;i<newsize/BIGSIZE;i++) {
952                                 blocknr = STORAGE_get_free_big_blocknr(hf);
953                                 if (blocknr<0)
954                                         return E_FAIL;
955                                 if (!STORAGE_set_big_chain(hf,lastblocknr,blocknr))
956                                         return E_FAIL;
957                                 lastblocknr = blocknr;
958                         }
959                         if (!STORAGE_set_big_chain(hf,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
960                                 return E_FAIL;
961                 } else {
962                         if (newsize < 0x1000) {
963                                 /* find startblock */
964                                 if (!oldsize)
965                                         This->stde.pps_sb = blocknr = STORAGE_get_free_small_blocknr(hf);
966                                 else
967                                         blocknr = STORAGE_get_nth_next_small_blocknr(hf,This->stde.pps_sb,This->stde.pps_size/SMALLSIZE);
968                                 if (blocknr<0)
969                                         return E_FAIL;
970
971                                 /* allocate required new small blocks */
972                                 lastblocknr = blocknr;
973                                 for (i=oldsize/SMALLSIZE;i<newsize/SMALLSIZE;i++) {
974                                         blocknr = STORAGE_get_free_small_blocknr(hf);
975                                         if (blocknr<0)
976                                                 return E_FAIL;
977                                         if (!STORAGE_set_small_chain(hf,lastblocknr,blocknr))
978                                                 return E_FAIL;
979                                         lastblocknr = blocknr;
980                                 }
981                                 /* and terminate the chain */
982                                 if (!STORAGE_set_small_chain(hf,lastblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
983                                         return E_FAIL;
984                         } else {
985                                 if (!oldsize) {
986                                         /* no single block allocated yet */
987                                         blocknr=STORAGE_get_free_big_blocknr(hf);
988                                         if (blocknr<0)
989                                                 return E_FAIL;
990                                         This->stde.pps_sb = blocknr;
991                                 } else {
992                                         /* Migrate small blocks to big blocks */
993                                         LPBYTE  curdata,data = HeapAlloc(GetProcessHeap(),0,oldsize+BIGSIZE);
994                                         HRESULT r = E_FAIL;
995
996                                         cc      = oldsize;
997                                         blocknr = This->stde.pps_sb;
998                                         curdata = data;
999                                         /* slurp in */
1000                                         while (cc>0) {
1001                                                 if (!STORAGE_get_small_block(hf,blocknr,curdata))
1002                                                         goto err2;
1003                                                 curdata += SMALLSIZE;
1004                                                 cc      -= SMALLSIZE;
1005                                                 blocknr  = STORAGE_get_next_small_blocknr(hf,blocknr);
1006                                         }
1007                                         /* free small block chain */
1008                                         if (!STORAGE_set_small_chain(hf,This->stde.pps_sb,STORAGE_CHAINENTRY_FREE))
1009                                                 goto err2;
1010                                         curdata = data;
1011                                         blocknr = This->stde.pps_sb = STORAGE_get_free_big_blocknr(hf);
1012                                         if (blocknr<0)
1013                                                 goto err2;
1014                                         /* put the data into the big blocks */
1015                                         cc      = This->stde.pps_size;
1016                                         while (cc>0) {
1017                                                 if (!STORAGE_put_big_block(hf,blocknr,curdata))
1018                                                         goto err2;
1019                                                 cc      -= BIGSIZE;
1020                                                 if (cc<=0) {
1021                                                         if (!STORAGE_set_big_chain(hf,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1022                                                                 goto err2;
1023                                                         break;
1024                                                 } else {
1025                                                         int newblocknr = STORAGE_get_free_big_blocknr(hf);
1026                                                         if (newblocknr<0)
1027                                                                 goto err2;
1028                                                         if (!STORAGE_set_big_chain(hf,blocknr,newblocknr))
1029                                                                 goto err2;
1030                                                         blocknr = newblocknr;
1031                                                 }
1032                                                 curdata += BIGSIZE;
1033                                         }
1034                                         r = S_OK;
1035                                 err2:
1036                                         HeapFree(GetProcessHeap(),0,data);
1037                                         if(r != S_OK)
1038                                                 return r;
1039                                 }
1040                                 /* generate big blocks to fit the new data */
1041                                 lastblocknr     = blocknr;
1042                                 for (i=oldsize/BIGSIZE;i<newsize/BIGSIZE;i++) {
1043                                         blocknr = STORAGE_get_free_big_blocknr(hf);
1044                                         if (blocknr<0)
1045                                                 return E_FAIL;
1046                                         if (!STORAGE_set_big_chain(hf,lastblocknr,blocknr))
1047                                                 return E_FAIL;
1048                                         lastblocknr = blocknr;
1049                                 }
1050                                 /* terminate chain */
1051                                 if (!STORAGE_set_big_chain(hf,lastblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1052                                         return E_FAIL;
1053                         }
1054                 }
1055                 This->stde.pps_size = newsize;
1056         }
1057
1058         /* There are just some cases where we didn't modify it, we write it out
1059          * everytime
1060          */
1061         if (!STORAGE_put_pps_entry(hf,This->ppsent,&(This->stde)))
1062                 return E_FAIL;
1063
1064         /* finally the write pass */
1065         if (This->stde.pps_size < 0x1000) {
1066                 blocknr = STORAGE_get_nth_next_small_blocknr(hf,This->stde.pps_sb,This->offset.s.LowPart/SMALLSIZE);
1067                 assert(blocknr>=0);
1068                 while (cb>0) {
1069                         /* we ensured that it is allocated above */
1070                         assert(blocknr>=0);
1071                         /* Read old block everytime, since we can have
1072                          * overlapping data at START and END of the write
1073                          */
1074                         if (!STORAGE_get_small_block(hf,blocknr,block))
1075                                 return E_FAIL;
1076
1077                         cc = SMALLSIZE-(This->offset.s.LowPart&(SMALLSIZE-1));
1078                         if (cc>cb)
1079                                 cc=cb;
1080                         memcpy( ((LPBYTE)block)+(This->offset.s.LowPart&(SMALLSIZE-1)),
1081                                 (LPBYTE)((char *) pv+curoffset),
1082                                 cc
1083                         );
1084                         if (!STORAGE_put_small_block(hf,blocknr,block))
1085                                 return E_FAIL;
1086                         cb                      -= cc;
1087                         curoffset               += cc;
1088                         (LPBYTE)pv              += cc;
1089                         This->offset.s.LowPart  += cc;
1090                         *byteswritten           += cc;
1091                         blocknr = STORAGE_get_next_small_blocknr(hf,blocknr);
1092                 }
1093         } else {
1094                 blocknr = STORAGE_get_nth_next_big_blocknr(hf,This->stde.pps_sb,This->offset.s.LowPart/BIGSIZE);
1095                 assert(blocknr>=0);
1096                 while (cb>0) {
1097                         /* we ensured that it is allocated above, so it better is */
1098                         assert(blocknr>=0);
1099                         /* read old block everytime, since we can have
1100                          * overlapping data at START and END of the write
1101                          */
1102                         if (!STORAGE_get_big_block(hf,blocknr,block))
1103                                 return E_FAIL;
1104
1105                         cc = BIGSIZE-(This->offset.s.LowPart&(BIGSIZE-1));
1106                         if (cc>cb)
1107                                 cc=cb;
1108                         memcpy( ((LPBYTE)block)+(This->offset.s.LowPart&(BIGSIZE-1)),
1109                                 (LPBYTE)((char *) pv+curoffset),
1110                                 cc
1111                         );
1112                         if (!STORAGE_put_big_block(hf,blocknr,block))
1113                                 return E_FAIL;
1114                         cb                      -= cc;
1115                         curoffset               += cc;
1116                         (LPBYTE)pv              += cc;
1117                         This->offset.s.LowPart  += cc;
1118                         *byteswritten           += cc;
1119                         blocknr = STORAGE_get_next_big_blocknr(hf,blocknr);
1120                 }
1121         }
1122         return S_OK;
1123 }
1124
1125 /******************************************************************************
1126  *              _create_istream16       [Internal]
1127  */
1128 static void _create_istream16(LPSTREAM16 *str) {
1129         IStream16Impl*  lpst;
1130
1131         if (!strvt16.QueryInterface) {
1132                 HMODULE16       wp = GetModuleHandle16("STORAGE");
1133                 if (wp>=32) {
1134                   /* FIXME: what is This GetProcAddress16. Should the name be IStream16_QueryInterface of IStream16_fnQueryInterface */
1135 #define VTENT(xfn)  strvt16.xfn = (void*)GetProcAddress16(wp,"IStream16_"#xfn);assert(strvt16.xfn)
1136                         VTENT(QueryInterface);
1137                         VTENT(AddRef);
1138                         VTENT(Release);
1139                         VTENT(Read);
1140                         VTENT(Write);
1141                         VTENT(Seek);
1142                         VTENT(SetSize);
1143                         VTENT(CopyTo);
1144                         VTENT(Commit);
1145                         VTENT(Revert);
1146                         VTENT(LockRegion);
1147                         VTENT(UnlockRegion);
1148                         VTENT(Stat);
1149                         VTENT(Clone);
1150 #undef VTENT
1151                         segstrvt16 = (ICOM_VTABLE(IStream16)*)MapLS( &strvt16 );
1152                 } else {
1153 #define VTENT(xfn) strvt16.xfn = IStream16_fn##xfn;
1154                         VTENT(QueryInterface);
1155                         VTENT(AddRef);
1156                         VTENT(Release);
1157                         VTENT(Read);
1158                         VTENT(Write);
1159                         VTENT(Seek);
1160         /*
1161                         VTENT(CopyTo);
1162                         VTENT(Commit);
1163                         VTENT(SetSize);
1164                         VTENT(Revert);
1165                         VTENT(LockRegion);
1166                         VTENT(UnlockRegion);
1167                         VTENT(Stat);
1168                         VTENT(Clone);
1169         */
1170 #undef VTENT
1171                         segstrvt16 = &strvt16;
1172                 }
1173         }
1174         lpst = HeapAlloc( GetProcessHeap(), 0, sizeof(*lpst) );
1175         ICOM_VTBL(lpst) = segstrvt16;
1176         lpst->ref       = 1;
1177         lpst->thisptr   = MapLS( lpst );
1178         *str = (void*)lpst->thisptr;
1179 }
1180
1181
1182 /* --- IStream32 implementation */
1183
1184 typedef struct
1185 {
1186         /* IUnknown fields */
1187         ICOM_VFIELD(IStream);
1188         DWORD                           ref;
1189         /* IStream32 fields */
1190         struct storage_pps_entry        stde;
1191         int                             ppsent;
1192         HANDLE                         hf;
1193         ULARGE_INTEGER                  offset;
1194 } IStream32Impl;
1195
1196 /*****************************************************************************
1197  *              IStream32_QueryInterface        [VTABLE]
1198  */
1199 HRESULT WINAPI IStream_fnQueryInterface(
1200         IStream* iface,REFIID refiid,LPVOID *obj
1201 ) {
1202         ICOM_THIS(IStream32Impl,iface);
1203
1204         TRACE_(relay)("(%p)->(%s,%p)\n",This,debugstr_guid(refiid),obj);
1205         if (!memcmp(&IID_IUnknown,refiid,sizeof(IID_IUnknown))) {
1206                 *obj = This;
1207                 return 0;
1208         }
1209         return OLE_E_ENUM_NOMORE;
1210
1211 }
1212
1213 /******************************************************************************
1214  * IStream32_AddRef [VTABLE]
1215  */
1216 ULONG WINAPI IStream_fnAddRef(IStream* iface) {
1217         ICOM_THIS(IStream32Impl,iface);
1218         return ++(This->ref);
1219 }
1220
1221 /******************************************************************************
1222  * IStream32_Release [VTABLE]
1223  */
1224 ULONG WINAPI IStream_fnRelease(IStream* iface) {
1225         ICOM_THIS(IStream32Impl,iface);
1226         FlushFileBuffers(This->hf);
1227         This->ref--;
1228         if (!This->ref) {
1229                 CloseHandle(This->hf);
1230                 HeapFree( GetProcessHeap(), 0, This );
1231                 return 0;
1232         }
1233         return This->ref;
1234 }
1235
1236 /* --- IStorage16 implementation */
1237
1238 typedef struct
1239 {
1240         /* IUnknown fields */
1241         ICOM_VFIELD(IStorage16);
1242         DWORD                           ref;
1243         /* IStorage16 fields */
1244         SEGPTR                          thisptr; /* pointer to this struct as segmented */
1245         struct storage_pps_entry        stde;
1246         int                             ppsent;
1247         HANDLE                         hf;
1248 } IStorage16Impl;
1249
1250 /******************************************************************************
1251  *              IStorage16_QueryInterface       [STORAGE.500]
1252  */
1253 HRESULT WINAPI IStorage16_fnQueryInterface(
1254         IStorage16* iface,REFIID refiid,LPVOID *obj
1255 ) {
1256         ICOM_THIS(IStorage16Impl,iface);
1257
1258         TRACE_(relay)("(%p)->(%s,%p)\n",This,debugstr_guid(refiid),obj);
1259
1260         if (!memcmp(&IID_IUnknown,refiid,sizeof(IID_IUnknown))) {
1261                 *obj = This;
1262                 return 0;
1263         }
1264         return OLE_E_ENUM_NOMORE;
1265 }
1266
1267 /******************************************************************************
1268  * IStorage16_AddRef [STORAGE.501]
1269  */
1270 ULONG WINAPI IStorage16_fnAddRef(IStorage16* iface) {
1271         ICOM_THIS(IStorage16Impl,iface);
1272         return ++(This->ref);
1273 }
1274
1275 /******************************************************************************
1276  * IStorage16_Release [STORAGE.502]
1277  */
1278 ULONG WINAPI IStorage16_fnRelease(IStorage16* iface) {
1279         ICOM_THIS(IStorage16Impl,iface);
1280         This->ref--;
1281         if (This->ref)
1282                 return This->ref;
1283         UnMapLS( This->thisptr );
1284         HeapFree( GetProcessHeap(), 0, This );
1285         return 0;
1286 }
1287
1288 /******************************************************************************
1289  * IStorage16_Stat [STORAGE.517]
1290  */
1291 HRESULT WINAPI IStorage16_fnStat(
1292         LPSTORAGE16 iface,STATSTG16 *pstatstg, DWORD grfStatFlag
1293 ) {
1294         ICOM_THIS(IStorage16Impl,iface);
1295         DWORD len = WideCharToMultiByte( CP_ACP, 0, This->stde.pps_rawname, -1, NULL, 0, NULL, NULL );
1296         LPSTR nameA = HeapAlloc( GetProcessHeap(), 0, len );
1297
1298         TRACE("(%p)->(%p,0x%08lx)\n",
1299                 This,pstatstg,grfStatFlag
1300         );
1301         WideCharToMultiByte( CP_ACP, 0, This->stde.pps_rawname, -1, nameA, len, NULL, NULL );
1302         pstatstg->pwcsName=(LPOLESTR16)MapLS( nameA );
1303         pstatstg->type = This->stde.pps_type;
1304         pstatstg->cbSize.s.LowPart = This->stde.pps_size;
1305         pstatstg->mtime = This->stde.pps_ft1; /* FIXME */ /* why? */
1306         pstatstg->atime = This->stde.pps_ft2; /* FIXME */
1307         pstatstg->ctime = This->stde.pps_ft2; /* FIXME */
1308         pstatstg->grfMode       = 0; /* FIXME */
1309         pstatstg->grfLocksSupported = 0; /* FIXME */
1310         pstatstg->clsid         = This->stde.pps_guid;
1311         pstatstg->grfStateBits  = 0; /* FIXME */
1312         pstatstg->reserved      = 0;
1313         return S_OK;
1314 }
1315
1316 /******************************************************************************
1317  *              IStorage16_Commit       [STORAGE.509]
1318  */
1319 HRESULT WINAPI IStorage16_fnCommit(
1320         LPSTORAGE16 iface,DWORD commitflags
1321 ) {
1322         ICOM_THIS(IStorage16Impl,iface);
1323         FIXME("(%p)->(0x%08lx),STUB!\n",
1324                 This,commitflags
1325         );
1326         return S_OK;
1327 }
1328
1329 /******************************************************************************
1330  * IStorage16_CopyTo [STORAGE.507]
1331  */
1332 HRESULT WINAPI IStorage16_fnCopyTo(LPSTORAGE16 iface,DWORD ciidExclude,const IID *rgiidExclude,SNB16 SNB16Exclude,IStorage16 *pstgDest) {
1333         ICOM_THIS(IStorage16Impl,iface);
1334         FIXME("IStorage16(%p)->(0x%08lx,%s,%p,%p),stub!\n",
1335                 This,ciidExclude,debugstr_guid(rgiidExclude),SNB16Exclude,pstgDest
1336         );
1337         return S_OK;
1338 }
1339
1340
1341 /******************************************************************************
1342  * IStorage16_CreateStorage [STORAGE.505]
1343  */
1344 HRESULT WINAPI IStorage16_fnCreateStorage(
1345         LPSTORAGE16 iface,LPCOLESTR16 pwcsName,DWORD grfMode,DWORD dwStgFormat,DWORD reserved2, IStorage16 **ppstg
1346 ) {
1347         ICOM_THIS(IStorage16Impl,iface);
1348         IStorage16Impl* lpstg;
1349         int             ppsent,x;
1350         struct storage_pps_entry        stde;
1351         struct storage_header sth;
1352         HANDLE          hf=This->hf;
1353
1354         READ_HEADER;
1355
1356         TRACE("(%p)->(%s,0x%08lx,0x%08lx,0x%08lx,%p)\n",
1357                 This,pwcsName,grfMode,dwStgFormat,reserved2,ppstg
1358         );
1359         if (grfMode & STGM_TRANSACTED)
1360                 FIXME("We do not support transacted Compound Storage. Using direct mode.\n");
1361         _create_istorage16(ppstg);
1362         lpstg = MapSL((SEGPTR)*ppstg);
1363         lpstg->hf               = This->hf;
1364
1365         ppsent=STORAGE_get_free_pps_entry(lpstg->hf);
1366         if (ppsent<0)
1367                 return E_FAIL;
1368         stde=This->stde;
1369         if (stde.pps_dir==-1) {
1370                 stde.pps_dir = ppsent;
1371                 x = This->ppsent;
1372         } else {
1373                 FIXME(" use prev chain too ?\n");
1374                 x=stde.pps_dir;
1375                 if (1!=STORAGE_get_pps_entry(lpstg->hf,x,&stde))
1376                         return E_FAIL;
1377                 while (stde.pps_next!=-1) {
1378                         x=stde.pps_next;
1379                         if (1!=STORAGE_get_pps_entry(lpstg->hf,x,&stde))
1380                                 return E_FAIL;
1381                 }
1382                 stde.pps_next = ppsent;
1383         }
1384         assert(STORAGE_put_pps_entry(lpstg->hf,x,&stde));
1385         assert(1==STORAGE_get_pps_entry(lpstg->hf,ppsent,&(lpstg->stde)));
1386         MultiByteToWideChar( CP_ACP, 0, pwcsName, -1, lpstg->stde.pps_rawname,
1387                              sizeof(lpstg->stde.pps_rawname)/sizeof(WCHAR));
1388         lpstg->stde.pps_sizeofname = (strlenW(lpstg->stde.pps_rawname)+1)*sizeof(WCHAR);
1389         lpstg->stde.pps_next    = -1;
1390         lpstg->stde.pps_prev    = -1;
1391         lpstg->stde.pps_dir     = -1;
1392         lpstg->stde.pps_sb      = -1;
1393         lpstg->stde.pps_size    =  0;
1394         lpstg->stde.pps_type    =  1;
1395         lpstg->ppsent           = ppsent;
1396         /* FIXME: timestamps? */
1397         if (!STORAGE_put_pps_entry(lpstg->hf,ppsent,&(lpstg->stde)))
1398                 return E_FAIL;
1399         return S_OK;
1400 }
1401
1402 /******************************************************************************
1403  *              IStorage16_CreateStream [STORAGE.503]
1404  */
1405 HRESULT WINAPI IStorage16_fnCreateStream(
1406         LPSTORAGE16 iface,LPCOLESTR16 pwcsName,DWORD grfMode,DWORD reserved1,DWORD reserved2, IStream16 **ppstm
1407 ) {
1408         ICOM_THIS(IStorage16Impl,iface);
1409         IStream16Impl*  lpstr;
1410         int             ppsent,x;
1411         struct storage_pps_entry        stde;
1412
1413         TRACE("(%p)->(%s,0x%08lx,0x%08lx,0x%08lx,%p)\n",
1414                 This,pwcsName,grfMode,reserved1,reserved2,ppstm
1415         );
1416         if (grfMode & STGM_TRANSACTED)
1417                 FIXME("We do not support transacted Compound Storage. Using direct mode.\n");
1418         _create_istream16(ppstm);
1419         lpstr = MapSL((SEGPTR)*ppstm);
1420         DuplicateHandle( GetCurrentProcess(), This->hf, GetCurrentProcess(),
1421                          &lpstr->hf, 0, TRUE, DUPLICATE_SAME_ACCESS );
1422         lpstr->offset.s.LowPart = 0;
1423         lpstr->offset.s.HighPart        = 0;
1424
1425         ppsent=STORAGE_get_free_pps_entry(lpstr->hf);
1426         if (ppsent<0)
1427                 return E_FAIL;
1428         stde=This->stde;
1429         if (stde.pps_next==-1)
1430                 x=This->ppsent;
1431         else
1432                 while (stde.pps_next!=-1) {
1433                         x=stde.pps_next;
1434                         if (1!=STORAGE_get_pps_entry(lpstr->hf,x,&stde))
1435                                 return E_FAIL;
1436                 }
1437         stde.pps_next = ppsent;
1438         assert(STORAGE_put_pps_entry(lpstr->hf,x,&stde));
1439         assert(1==STORAGE_get_pps_entry(lpstr->hf,ppsent,&(lpstr->stde)));
1440         MultiByteToWideChar( CP_ACP, 0, pwcsName, -1, lpstr->stde.pps_rawname,
1441                              sizeof(lpstr->stde.pps_rawname)/sizeof(WCHAR));
1442         lpstr->stde.pps_sizeofname = (strlenW(lpstr->stde.pps_rawname)+1) * sizeof(WCHAR);
1443         lpstr->stde.pps_next    = -1;
1444         lpstr->stde.pps_prev    = -1;
1445         lpstr->stde.pps_dir     = -1;
1446         lpstr->stde.pps_sb      = -1;
1447         lpstr->stde.pps_size    =  0;
1448         lpstr->stde.pps_type    =  2;
1449         lpstr->ppsent           = ppsent;
1450         /* FIXME: timestamps? */
1451         if (!STORAGE_put_pps_entry(lpstr->hf,ppsent,&(lpstr->stde)))
1452                 return E_FAIL;
1453         return S_OK;
1454 }
1455
1456 /******************************************************************************
1457  *              IStorage16_OpenStorage  [STORAGE.506]
1458  */
1459 HRESULT WINAPI IStorage16_fnOpenStorage(
1460         LPSTORAGE16 iface,LPCOLESTR16 pwcsName, IStorage16 *pstgPrio, DWORD grfMode, SNB16 snbExclude, DWORD reserved, IStorage16 **ppstg
1461 ) {
1462         ICOM_THIS(IStorage16Impl,iface);
1463         IStream16Impl*  lpstg;
1464         WCHAR           name[33];
1465         int             newpps;
1466
1467         TRACE_(relay)("(%p)->(%s,%p,0x%08lx,%p,0x%08lx,%p)\n",
1468                 This,pwcsName,pstgPrio,grfMode,snbExclude,reserved,ppstg
1469         );
1470         if (grfMode & STGM_TRANSACTED)
1471                 FIXME("We do not support transacted Compound Storage. Using direct mode.\n");
1472         _create_istorage16(ppstg);
1473         lpstg = MapSL((SEGPTR)*ppstg);
1474         DuplicateHandle( GetCurrentProcess(), This->hf, GetCurrentProcess(),
1475                          &lpstg->hf, 0, TRUE, DUPLICATE_SAME_ACCESS );
1476         MultiByteToWideChar( CP_ACP, 0, pwcsName, -1, name, sizeof(name)/sizeof(WCHAR));
1477         newpps = STORAGE_look_for_named_pps(lpstg->hf,This->stde.pps_dir,name);
1478         if (newpps==-1) {
1479                 IStream16_fnRelease((IStream16*)lpstg);
1480                 return E_FAIL;
1481         }
1482
1483         if (1!=STORAGE_get_pps_entry(lpstg->hf,newpps,&(lpstg->stde))) {
1484                 IStream16_fnRelease((IStream16*)lpstg);
1485                 return E_FAIL;
1486         }
1487         lpstg->ppsent           = newpps;
1488         return S_OK;
1489 }
1490
1491 /******************************************************************************
1492  * IStorage16_OpenStream [STORAGE.504]
1493  */
1494 HRESULT WINAPI IStorage16_fnOpenStream(
1495         LPSTORAGE16 iface,LPCOLESTR16 pwcsName, void *reserved1, DWORD grfMode, DWORD reserved2, IStream16 **ppstm
1496 ) {
1497         ICOM_THIS(IStorage16Impl,iface);
1498         IStream16Impl*  lpstr;
1499         WCHAR           name[33];
1500         int             newpps;
1501
1502         TRACE_(relay)("(%p)->(%s,%p,0x%08lx,0x%08lx,%p)\n",
1503                 This,pwcsName,reserved1,grfMode,reserved2,ppstm
1504         );
1505         if (grfMode & STGM_TRANSACTED)
1506                 FIXME("We do not support transacted Compound Storage. Using direct mode.\n");
1507         _create_istream16(ppstm);
1508         lpstr = MapSL((SEGPTR)*ppstm);
1509         DuplicateHandle( GetCurrentProcess(), This->hf, GetCurrentProcess(),
1510                          &lpstr->hf, 0, TRUE, DUPLICATE_SAME_ACCESS );
1511         MultiByteToWideChar( CP_ACP, 0, pwcsName, -1, name, sizeof(name)/sizeof(WCHAR));
1512         newpps = STORAGE_look_for_named_pps(lpstr->hf,This->stde.pps_dir,name);
1513         if (newpps==-1) {
1514                 IStream16_fnRelease((IStream16*)lpstr);
1515                 return E_FAIL;
1516         }
1517
1518         if (1!=STORAGE_get_pps_entry(lpstr->hf,newpps,&(lpstr->stde))) {
1519                 IStream16_fnRelease((IStream16*)lpstr);
1520                 return E_FAIL;
1521         }
1522         lpstr->offset.s.LowPart = 0;
1523         lpstr->offset.s.HighPart        = 0;
1524         lpstr->ppsent           = newpps;
1525         return S_OK;
1526 }
1527
1528 /******************************************************************************
1529  * _create_istorage16 [INTERNAL]
1530  */
1531 static void _create_istorage16(LPSTORAGE16 *stg) {
1532         IStorage16Impl* lpst;
1533
1534         if (!stvt16.QueryInterface) {
1535                 HMODULE16       wp = GetModuleHandle16("STORAGE");
1536                 if (wp>=32) {
1537 #define VTENT(xfn)  stvt16.xfn = (void*)GetProcAddress16(wp,"IStorage16_"#xfn);
1538                         VTENT(QueryInterface)
1539                         VTENT(AddRef)
1540                         VTENT(Release)
1541                         VTENT(CreateStream)
1542                         VTENT(OpenStream)
1543                         VTENT(CreateStorage)
1544                         VTENT(OpenStorage)
1545                         VTENT(CopyTo)
1546                         VTENT(MoveElementTo)
1547                         VTENT(Commit)
1548                         VTENT(Revert)
1549                         VTENT(EnumElements)
1550                         VTENT(DestroyElement)
1551                         VTENT(RenameElement)
1552                         VTENT(SetElementTimes)
1553                         VTENT(SetClass)
1554                         VTENT(SetStateBits)
1555                         VTENT(Stat)
1556 #undef VTENT
1557                         segstvt16 = (ICOM_VTABLE(IStorage16)*)MapLS( &stvt16 );
1558                 } else {
1559 #define VTENT(xfn) stvt16.xfn = IStorage16_fn##xfn;
1560                         VTENT(QueryInterface)
1561                         VTENT(AddRef)
1562                         VTENT(Release)
1563                         VTENT(CreateStream)
1564                         VTENT(OpenStream)
1565                         VTENT(CreateStorage)
1566                         VTENT(OpenStorage)
1567                         VTENT(CopyTo)
1568                         VTENT(Commit)
1569         /*  not (yet) implemented ...
1570                         VTENT(MoveElementTo)
1571                         VTENT(Revert)
1572                         VTENT(EnumElements)
1573                         VTENT(DestroyElement)
1574                         VTENT(RenameElement)
1575                         VTENT(SetElementTimes)
1576                         VTENT(SetClass)
1577                         VTENT(SetStateBits)
1578                         VTENT(Stat)
1579         */
1580 #undef VTENT
1581                         segstvt16 = &stvt16;
1582                 }
1583         }
1584         lpst = HeapAlloc( GetProcessHeap(), 0, sizeof(*lpst) );
1585         ICOM_VTBL(lpst) = segstvt16;
1586         lpst->ref       = 1;
1587         lpst->thisptr   = MapLS(lpst);
1588         *stg = (void*)lpst->thisptr;
1589 }
1590
1591 /******************************************************************************
1592  *      Storage API functions
1593  */
1594
1595 /******************************************************************************
1596  *              StgCreateDocFileA       [STORAGE.1]
1597  */
1598 HRESULT WINAPI StgCreateDocFile16(
1599         LPCOLESTR16 pwcsName,DWORD grfMode,DWORD reserved,IStorage16 **ppstgOpen
1600 ) {
1601         HANDLE          hf;
1602         int             i,ret;
1603         IStorage16Impl* lpstg;
1604         struct storage_pps_entry        stde;
1605
1606         TRACE("(%s,0x%08lx,0x%08lx,%p)\n",
1607                 pwcsName,grfMode,reserved,ppstgOpen
1608         );
1609         _create_istorage16(ppstgOpen);
1610         hf = CreateFileA(pwcsName,GENERIC_READ|GENERIC_WRITE,0,NULL,CREATE_NEW,0,0);
1611         if (hf==INVALID_HANDLE_VALUE) {
1612                 WARN("couldn't open file for storage:%ld\n",GetLastError());
1613                 return E_FAIL;
1614         }
1615         lpstg = MapSL((SEGPTR)*ppstgOpen);
1616         lpstg->hf = hf;
1617         /* FIXME: check for existence before overwriting? */
1618         if (!STORAGE_init_storage(hf)) {
1619                 CloseHandle(hf);
1620                 return E_FAIL;
1621         }
1622         i=0;ret=0;
1623         while (!ret) { /* neither 1 nor <0 */
1624                 ret=STORAGE_get_pps_entry(hf,i,&stde);
1625                 if ((ret==1) && (stde.pps_type==5)) {
1626                         lpstg->stde     = stde;
1627                         lpstg->ppsent   = i;
1628                         break;
1629                 }
1630                 i++;
1631         }
1632         if (ret!=1) {
1633                 IStorage16_fnRelease((IStorage16*)lpstg); /* will remove it */
1634                 return E_FAIL;
1635         }
1636
1637         return S_OK;
1638 }
1639
1640 /******************************************************************************
1641  * StgIsStorageFile [STORAGE.5]
1642  */
1643 HRESULT WINAPI StgIsStorageFile16(LPCOLESTR16 fn) {
1644         UNICODE_STRING strW;
1645         HRESULT ret;
1646
1647         RtlCreateUnicodeStringFromAsciiz(&strW, fn);
1648         ret = StgIsStorageFile( strW.Buffer );
1649         RtlFreeUnicodeString( &strW );
1650
1651         return ret;
1652 }
1653
1654 /******************************************************************************
1655  * StgOpenStorage [STORAGE.3]
1656  */
1657 HRESULT WINAPI StgOpenStorage16(
1658         LPCOLESTR16 pwcsName,IStorage16 *pstgPriority,DWORD grfMode,
1659         SNB16 snbExclude,DWORD reserved, IStorage16 **ppstgOpen
1660 ) {
1661         HANDLE          hf;
1662         int             ret,i;
1663         IStorage16Impl* lpstg;
1664         struct storage_pps_entry        stde;
1665
1666         TRACE("(%s,%p,0x%08lx,%p,%ld,%p)\n",
1667               pwcsName,pstgPriority,grfMode,snbExclude,reserved,ppstgOpen
1668         );
1669         _create_istorage16(ppstgOpen);
1670         hf = CreateFileA(pwcsName,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
1671         if (hf==INVALID_HANDLE_VALUE) {
1672                 WARN("Couldn't open file for storage\n");
1673                 return E_FAIL;
1674         }
1675         lpstg = MapSL((SEGPTR)*ppstgOpen);
1676         lpstg->hf = hf;
1677
1678         i=0;ret=0;
1679         while (!ret) { /* neither 1 nor <0 */
1680                 ret=STORAGE_get_pps_entry(hf,i,&stde);
1681                 if ((ret==1) && (stde.pps_type==5)) {
1682                         lpstg->stde=stde;
1683                         break;
1684                 }
1685                 i++;
1686         }
1687         if (ret!=1) {
1688                 IStorage16_fnRelease((IStorage16*)lpstg); /* will remove it */
1689                 return E_FAIL;
1690         }
1691         return S_OK;
1692
1693 }