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