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