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