- Implemented IMallocSpy hooks in IMalloc.
[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                                 HRESULT r = E_FAIL;
889
890                                 cc      = newsize;
891                                 blocknr = This->stde.pps_sb;
892                                 curdata = data;
893                                 while (cc>0) {
894                                         if (!STORAGE_get_big_block(hf,blocknr,curdata)) {
895                                                 HeapFree(GetProcessHeap(),0,data);
896                                                 return E_FAIL;
897                                         }
898                                         curdata += BIGSIZE;
899                                         cc      -= BIGSIZE;
900                                         blocknr  = STORAGE_get_next_big_blocknr(hf,blocknr);
901                                 }
902                                 /* frees complete chain for this stream */
903                                 if (!STORAGE_set_big_chain(hf,This->stde.pps_sb,STORAGE_CHAINENTRY_FREE))
904                                         goto err;
905                                 curdata = data;
906                                 blocknr = This->stde.pps_sb = STORAGE_get_free_small_blocknr(hf);
907                                 if (blocknr<0)
908                                         goto err;
909                                 cc      = newsize;
910                                 while (cc>0) {
911                                         if (!STORAGE_put_small_block(hf,blocknr,curdata))
912                                                 goto err;
913                                         cc      -= SMALLSIZE;
914                                         if (cc<=0) {
915                                                 if (!STORAGE_set_small_chain(hf,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
916                                                         goto err;
917                                                 break;
918                                         } else {
919                                                 int newblocknr = STORAGE_get_free_small_blocknr(hf);
920                                                 if (newblocknr<0)
921                                                         goto err;
922                                                 if (!STORAGE_set_small_chain(hf,blocknr,newblocknr))
923                                                         goto err;
924                                                 blocknr = newblocknr;
925                                         }
926                                         curdata += SMALLSIZE;
927                                 }
928                                 r = S_OK;
929                         err:
930                                 HeapFree(GetProcessHeap(),0,data);
931                                 if(r != S_OK)
932                                         return r;
933                         }
934                 }
935                 This->stde.pps_size = newsize;
936         }
937
938         if (newsize > oldsize) {
939                 if (oldsize >= 0x1000) {
940                         /* should return the block right before the 'endofchain' */
941                         blocknr = STORAGE_get_nth_next_big_blocknr(hf,This->stde.pps_sb,This->stde.pps_size/BIGSIZE);
942                         assert(blocknr>=0);
943                         lastblocknr     = blocknr;
944                         for (i=oldsize/BIGSIZE;i<newsize/BIGSIZE;i++) {
945                                 blocknr = STORAGE_get_free_big_blocknr(hf);
946                                 if (blocknr<0)
947                                         return E_FAIL;
948                                 if (!STORAGE_set_big_chain(hf,lastblocknr,blocknr))
949                                         return E_FAIL;
950                                 lastblocknr = blocknr;
951                         }
952                         if (!STORAGE_set_big_chain(hf,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
953                                 return E_FAIL;
954                 } else {
955                         if (newsize < 0x1000) {
956                                 /* find startblock */
957                                 if (!oldsize)
958                                         This->stde.pps_sb = blocknr = STORAGE_get_free_small_blocknr(hf);
959                                 else
960                                         blocknr = STORAGE_get_nth_next_small_blocknr(hf,This->stde.pps_sb,This->stde.pps_size/SMALLSIZE);
961                                 if (blocknr<0)
962                                         return E_FAIL;
963
964                                 /* allocate required new small blocks */
965                                 lastblocknr = blocknr;
966                                 for (i=oldsize/SMALLSIZE;i<newsize/SMALLSIZE;i++) {
967                                         blocknr = STORAGE_get_free_small_blocknr(hf);
968                                         if (blocknr<0)
969                                                 return E_FAIL;
970                                         if (!STORAGE_set_small_chain(hf,lastblocknr,blocknr))
971                                                 return E_FAIL;
972                                         lastblocknr = blocknr;
973                                 }
974                                 /* and terminate the chain */
975                                 if (!STORAGE_set_small_chain(hf,lastblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
976                                         return E_FAIL;
977                         } else {
978                                 if (!oldsize) {
979                                         /* no single block allocated yet */
980                                         blocknr=STORAGE_get_free_big_blocknr(hf);
981                                         if (blocknr<0)
982                                                 return E_FAIL;
983                                         This->stde.pps_sb = blocknr;
984                                 } else {
985                                         /* Migrate small blocks to big blocks */
986                                         LPBYTE  curdata,data = HeapAlloc(GetProcessHeap(),0,oldsize+BIGSIZE);
987                                         HRESULT r = E_FAIL;
988
989                                         cc      = oldsize;
990                                         blocknr = This->stde.pps_sb;
991                                         curdata = data;
992                                         /* slurp in */
993                                         while (cc>0) {
994                                                 if (!STORAGE_get_small_block(hf,blocknr,curdata))
995                                                         goto err2;
996                                                 curdata += SMALLSIZE;
997                                                 cc      -= SMALLSIZE;
998                                                 blocknr  = STORAGE_get_next_small_blocknr(hf,blocknr);
999                                         }
1000                                         /* free small block chain */
1001                                         if (!STORAGE_set_small_chain(hf,This->stde.pps_sb,STORAGE_CHAINENTRY_FREE))
1002                                                 goto err2;
1003                                         curdata = data;
1004                                         blocknr = This->stde.pps_sb = STORAGE_get_free_big_blocknr(hf);
1005                                         if (blocknr<0)
1006                                                 goto err2;
1007                                         /* put the data into the big blocks */
1008                                         cc      = This->stde.pps_size;
1009                                         while (cc>0) {
1010                                                 if (!STORAGE_put_big_block(hf,blocknr,curdata))
1011                                                         goto err2;
1012                                                 cc      -= BIGSIZE;
1013                                                 if (cc<=0) {
1014                                                         if (!STORAGE_set_big_chain(hf,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1015                                                                 goto err2;
1016                                                         break;
1017                                                 } else {
1018                                                         int newblocknr = STORAGE_get_free_big_blocknr(hf);
1019                                                         if (newblocknr<0)
1020                                                                 goto err2;
1021                                                         if (!STORAGE_set_big_chain(hf,blocknr,newblocknr))
1022                                                                 goto err2;
1023                                                         blocknr = newblocknr;
1024                                                 }
1025                                                 curdata += BIGSIZE;
1026                                         }
1027                                         r = S_OK;
1028                                 err2:
1029                                         HeapFree(GetProcessHeap(),0,data);
1030                                         if(r != S_OK)
1031                                                 return r;
1032                                 }
1033                                 /* generate big blocks to fit the new data */
1034                                 lastblocknr     = blocknr;
1035                                 for (i=oldsize/BIGSIZE;i<newsize/BIGSIZE;i++) {
1036                                         blocknr = STORAGE_get_free_big_blocknr(hf);
1037                                         if (blocknr<0)
1038                                                 return E_FAIL;
1039                                         if (!STORAGE_set_big_chain(hf,lastblocknr,blocknr))
1040                                                 return E_FAIL;
1041                                         lastblocknr = blocknr;
1042                                 }
1043                                 /* terminate chain */
1044                                 if (!STORAGE_set_big_chain(hf,lastblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1045                                         return E_FAIL;
1046                         }
1047                 }
1048                 This->stde.pps_size = newsize;
1049         }
1050
1051         /* There are just some cases where we didn't modify it, we write it out
1052          * everytime
1053          */
1054         if (!STORAGE_put_pps_entry(hf,This->ppsent,&(This->stde)))
1055                 return E_FAIL;
1056
1057         /* finally the write pass */
1058         if (This->stde.pps_size < 0x1000) {
1059                 blocknr = STORAGE_get_nth_next_small_blocknr(hf,This->stde.pps_sb,This->offset.s.LowPart/SMALLSIZE);
1060                 assert(blocknr>=0);
1061                 while (cb>0) {
1062                         /* we ensured that it is allocated above */
1063                         assert(blocknr>=0);
1064                         /* Read old block everytime, since we can have
1065                          * overlapping data at START and END of the write
1066                          */
1067                         if (!STORAGE_get_small_block(hf,blocknr,block))
1068                                 return E_FAIL;
1069
1070                         cc = SMALLSIZE-(This->offset.s.LowPart&(SMALLSIZE-1));
1071                         if (cc>cb)
1072                                 cc=cb;
1073                         memcpy( ((LPBYTE)block)+(This->offset.s.LowPart&(SMALLSIZE-1)),
1074                                 (LPBYTE)((char *) pv+curoffset),
1075                                 cc
1076                         );
1077                         if (!STORAGE_put_small_block(hf,blocknr,block))
1078                                 return E_FAIL;
1079                         cb                      -= cc;
1080                         curoffset               += cc;
1081                         (LPBYTE)pv              += cc;
1082                         This->offset.s.LowPart  += cc;
1083                         *byteswritten           += cc;
1084                         blocknr = STORAGE_get_next_small_blocknr(hf,blocknr);
1085                 }
1086         } else {
1087                 blocknr = STORAGE_get_nth_next_big_blocknr(hf,This->stde.pps_sb,This->offset.s.LowPart/BIGSIZE);
1088                 assert(blocknr>=0);
1089                 while (cb>0) {
1090                         /* we ensured that it is allocated above, so it better is */
1091                         assert(blocknr>=0);
1092                         /* read old block everytime, since we can have
1093                          * overlapping data at START and END of the write
1094                          */
1095                         if (!STORAGE_get_big_block(hf,blocknr,block))
1096                                 return E_FAIL;
1097
1098                         cc = BIGSIZE-(This->offset.s.LowPart&(BIGSIZE-1));
1099                         if (cc>cb)
1100                                 cc=cb;
1101                         memcpy( ((LPBYTE)block)+(This->offset.s.LowPart&(BIGSIZE-1)),
1102                                 (LPBYTE)((char *) pv+curoffset),
1103                                 cc
1104                         );
1105                         if (!STORAGE_put_big_block(hf,blocknr,block))
1106                                 return E_FAIL;
1107                         cb                      -= cc;
1108                         curoffset               += cc;
1109                         (LPBYTE)pv              += cc;
1110                         This->offset.s.LowPart  += cc;
1111                         *byteswritten           += cc;
1112                         blocknr = STORAGE_get_next_big_blocknr(hf,blocknr);
1113                 }
1114         }
1115         return S_OK;
1116 }
1117
1118 /******************************************************************************
1119  *              _create_istream16       [Internal]
1120  */
1121 static void _create_istream16(LPSTREAM16 *str) {
1122         IStream16Impl*  lpst;
1123
1124         if (!strvt16.QueryInterface) {
1125                 HMODULE16       wp = GetModuleHandle16("STORAGE");
1126                 if (wp>=32) {
1127                   /* FIXME: what is This GetProcAddress16. Should the name be IStream16_QueryInterface of IStream16_fnQueryInterface */
1128 #define VTENT(xfn)  strvt16.xfn = (void*)GetProcAddress16(wp,"IStream16_"#xfn);assert(strvt16.xfn)
1129                         VTENT(QueryInterface);
1130                         VTENT(AddRef);
1131                         VTENT(Release);
1132                         VTENT(Read);
1133                         VTENT(Write);
1134                         VTENT(Seek);
1135                         VTENT(SetSize);
1136                         VTENT(CopyTo);
1137                         VTENT(Commit);
1138                         VTENT(Revert);
1139                         VTENT(LockRegion);
1140                         VTENT(UnlockRegion);
1141                         VTENT(Stat);
1142                         VTENT(Clone);
1143 #undef VTENT
1144                         segstrvt16 = (ICOM_VTABLE(IStream16)*)MapLS( &strvt16 );
1145                 } else {
1146 #define VTENT(xfn) strvt16.xfn = IStream16_fn##xfn;
1147                         VTENT(QueryInterface);
1148                         VTENT(AddRef);
1149                         VTENT(Release);
1150                         VTENT(Read);
1151                         VTENT(Write);
1152                         VTENT(Seek);
1153         /*
1154                         VTENT(CopyTo);
1155                         VTENT(Commit);
1156                         VTENT(SetSize);
1157                         VTENT(Revert);
1158                         VTENT(LockRegion);
1159                         VTENT(UnlockRegion);
1160                         VTENT(Stat);
1161                         VTENT(Clone);
1162         */
1163 #undef VTENT
1164                         segstrvt16 = &strvt16;
1165                 }
1166         }
1167         lpst = HeapAlloc( GetProcessHeap(), 0, sizeof(*lpst) );
1168         ICOM_VTBL(lpst) = segstrvt16;
1169         lpst->ref       = 1;
1170         lpst->thisptr   = MapLS( lpst );
1171         *str = (void*)lpst->thisptr;
1172 }
1173
1174
1175 /* --- IStream32 implementation */
1176
1177 typedef struct
1178 {
1179         /* IUnknown fields */
1180         ICOM_VFIELD(IStream);
1181         DWORD                           ref;
1182         /* IStream32 fields */
1183         struct storage_pps_entry        stde;
1184         int                             ppsent;
1185         HFILE                         hf;
1186         ULARGE_INTEGER                  offset;
1187 } IStream32Impl;
1188
1189 /*****************************************************************************
1190  *              IStream32_QueryInterface        [VTABLE]
1191  */
1192 HRESULT WINAPI IStream_fnQueryInterface(
1193         IStream* iface,REFIID refiid,LPVOID *obj
1194 ) {
1195         ICOM_THIS(IStream32Impl,iface);
1196
1197         TRACE_(relay)("(%p)->(%s,%p)\n",This,debugstr_guid(refiid),obj);
1198         if (!memcmp(&IID_IUnknown,refiid,sizeof(IID_IUnknown))) {
1199                 *obj = This;
1200                 return 0;
1201         }
1202         return OLE_E_ENUM_NOMORE;
1203
1204 }
1205
1206 /******************************************************************************
1207  * IStream32_AddRef [VTABLE]
1208  */
1209 ULONG WINAPI IStream_fnAddRef(IStream* iface) {
1210         ICOM_THIS(IStream32Impl,iface);
1211         return ++(This->ref);
1212 }
1213
1214 /******************************************************************************
1215  * IStream32_Release [VTABLE]
1216  */
1217 ULONG WINAPI IStream_fnRelease(IStream* iface) {
1218         ICOM_THIS(IStream32Impl,iface);
1219         FlushFileBuffers(This->hf);
1220         This->ref--;
1221         if (!This->ref) {
1222                 CloseHandle(This->hf);
1223                 HeapFree( GetProcessHeap(), 0, This );
1224                 return 0;
1225         }
1226         return This->ref;
1227 }
1228
1229 /* --- IStorage16 implementation */
1230
1231 typedef struct
1232 {
1233         /* IUnknown fields */
1234         ICOM_VFIELD(IStorage16);
1235         DWORD                           ref;
1236         /* IStorage16 fields */
1237         SEGPTR                          thisptr; /* pointer to this struct as segmented */
1238         struct storage_pps_entry        stde;
1239         int                             ppsent;
1240         HFILE                         hf;
1241 } IStorage16Impl;
1242
1243 /******************************************************************************
1244  *              IStorage16_QueryInterface       [STORAGE.500]
1245  */
1246 HRESULT WINAPI IStorage16_fnQueryInterface(
1247         IStorage16* iface,REFIID refiid,LPVOID *obj
1248 ) {
1249         ICOM_THIS(IStorage16Impl,iface);
1250
1251         TRACE_(relay)("(%p)->(%s,%p)\n",This,debugstr_guid(refiid),obj);
1252
1253         if (!memcmp(&IID_IUnknown,refiid,sizeof(IID_IUnknown))) {
1254                 *obj = This;
1255                 return 0;
1256         }
1257         return OLE_E_ENUM_NOMORE;
1258 }
1259
1260 /******************************************************************************
1261  * IStorage16_AddRef [STORAGE.501]
1262  */
1263 ULONG WINAPI IStorage16_fnAddRef(IStorage16* iface) {
1264         ICOM_THIS(IStorage16Impl,iface);
1265         return ++(This->ref);
1266 }
1267
1268 /******************************************************************************
1269  * IStorage16_Release [STORAGE.502]
1270  */
1271 ULONG WINAPI IStorage16_fnRelease(IStorage16* iface) {
1272         ICOM_THIS(IStorage16Impl,iface);
1273         This->ref--;
1274         if (This->ref)
1275                 return This->ref;
1276         UnMapLS( This->thisptr );
1277         HeapFree( GetProcessHeap(), 0, This );
1278         return 0;
1279 }
1280
1281 /******************************************************************************
1282  * IStorage16_Stat [STORAGE.517]
1283  */
1284 HRESULT WINAPI IStorage16_fnStat(
1285         LPSTORAGE16 iface,STATSTG16 *pstatstg, DWORD grfStatFlag
1286 ) {
1287         ICOM_THIS(IStorage16Impl,iface);
1288         DWORD len = WideCharToMultiByte( CP_ACP, 0, This->stde.pps_rawname, -1, NULL, 0, NULL, NULL );
1289         LPSTR nameA = HeapAlloc( GetProcessHeap(), 0, len );
1290
1291         TRACE("(%p)->(%p,0x%08lx)\n",
1292                 This,pstatstg,grfStatFlag
1293         );
1294         WideCharToMultiByte( CP_ACP, 0, This->stde.pps_rawname, -1, nameA, len, NULL, NULL );
1295         pstatstg->pwcsName=(LPOLESTR16)MapLS( nameA );
1296         pstatstg->type = This->stde.pps_type;
1297         pstatstg->cbSize.s.LowPart = This->stde.pps_size;
1298         pstatstg->mtime = This->stde.pps_ft1; /* FIXME */ /* why? */
1299         pstatstg->atime = This->stde.pps_ft2; /* FIXME */
1300         pstatstg->ctime = This->stde.pps_ft2; /* FIXME */
1301         pstatstg->grfMode       = 0; /* FIXME */
1302         pstatstg->grfLocksSupported = 0; /* FIXME */
1303         pstatstg->clsid         = This->stde.pps_guid;
1304         pstatstg->grfStateBits  = 0; /* FIXME */
1305         pstatstg->reserved      = 0;
1306         return S_OK;
1307 }
1308
1309 /******************************************************************************
1310  *              IStorage16_Commit       [STORAGE.509]
1311  */
1312 HRESULT WINAPI IStorage16_fnCommit(
1313         LPSTORAGE16 iface,DWORD commitflags
1314 ) {
1315         ICOM_THIS(IStorage16Impl,iface);
1316         FIXME("(%p)->(0x%08lx),STUB!\n",
1317                 This,commitflags
1318         );
1319         return S_OK;
1320 }
1321
1322 /******************************************************************************
1323  * IStorage16_CopyTo [STORAGE.507]
1324  */
1325 HRESULT WINAPI IStorage16_fnCopyTo(LPSTORAGE16 iface,DWORD ciidExclude,const IID *rgiidExclude,SNB16 SNB16Exclude,IStorage16 *pstgDest) {
1326         ICOM_THIS(IStorage16Impl,iface);
1327         FIXME("IStorage16(%p)->(0x%08lx,%s,%p,%p),stub!\n",
1328                 This,ciidExclude,debugstr_guid(rgiidExclude),SNB16Exclude,pstgDest
1329         );
1330         return S_OK;
1331 }
1332
1333
1334 /******************************************************************************
1335  * IStorage16_CreateStorage [STORAGE.505]
1336  */
1337 HRESULT WINAPI IStorage16_fnCreateStorage(
1338         LPSTORAGE16 iface,LPCOLESTR16 pwcsName,DWORD grfMode,DWORD dwStgFormat,DWORD reserved2, IStorage16 **ppstg
1339 ) {
1340         ICOM_THIS(IStorage16Impl,iface);
1341         IStorage16Impl* lpstg;
1342         int             ppsent,x;
1343         struct storage_pps_entry        stde;
1344         struct storage_header sth;
1345         HFILE           hf=This->hf;
1346
1347         READ_HEADER;
1348
1349         TRACE("(%p)->(%s,0x%08lx,0x%08lx,0x%08lx,%p)\n",
1350                 This,pwcsName,grfMode,dwStgFormat,reserved2,ppstg
1351         );
1352         if (grfMode & STGM_TRANSACTED)
1353                 FIXME("We do not support transacted Compound Storage. Using direct mode.\n");
1354         _create_istorage16(ppstg);
1355         lpstg = MapSL((SEGPTR)*ppstg);
1356         lpstg->hf               = This->hf;
1357
1358         ppsent=STORAGE_get_free_pps_entry(lpstg->hf);
1359         if (ppsent<0)
1360                 return E_FAIL;
1361         stde=This->stde;
1362         if (stde.pps_dir==-1) {
1363                 stde.pps_dir = ppsent;
1364                 x = This->ppsent;
1365         } else {
1366                 FIXME(" use prev chain too ?\n");
1367                 x=stde.pps_dir;
1368                 if (1!=STORAGE_get_pps_entry(lpstg->hf,x,&stde))
1369                         return E_FAIL;
1370                 while (stde.pps_next!=-1) {
1371                         x=stde.pps_next;
1372                         if (1!=STORAGE_get_pps_entry(lpstg->hf,x,&stde))
1373                                 return E_FAIL;
1374                 }
1375                 stde.pps_next = ppsent;
1376         }
1377         assert(STORAGE_put_pps_entry(lpstg->hf,x,&stde));
1378         assert(1==STORAGE_get_pps_entry(lpstg->hf,ppsent,&(lpstg->stde)));
1379         MultiByteToWideChar( CP_ACP, 0, pwcsName, -1, lpstg->stde.pps_rawname,
1380                              sizeof(lpstg->stde.pps_rawname)/sizeof(WCHAR));
1381         lpstg->stde.pps_sizeofname = (strlenW(lpstg->stde.pps_rawname)+1)*sizeof(WCHAR);
1382         lpstg->stde.pps_next    = -1;
1383         lpstg->stde.pps_prev    = -1;
1384         lpstg->stde.pps_dir     = -1;
1385         lpstg->stde.pps_sb      = -1;
1386         lpstg->stde.pps_size    =  0;
1387         lpstg->stde.pps_type    =  1;
1388         lpstg->ppsent           = ppsent;
1389         /* FIXME: timestamps? */
1390         if (!STORAGE_put_pps_entry(lpstg->hf,ppsent,&(lpstg->stde)))
1391                 return E_FAIL;
1392         return S_OK;
1393 }
1394
1395 /******************************************************************************
1396  *              IStorage16_CreateStream [STORAGE.503]
1397  */
1398 HRESULT WINAPI IStorage16_fnCreateStream(
1399         LPSTORAGE16 iface,LPCOLESTR16 pwcsName,DWORD grfMode,DWORD reserved1,DWORD reserved2, IStream16 **ppstm
1400 ) {
1401         ICOM_THIS(IStorage16Impl,iface);
1402         IStream16Impl*  lpstr;
1403         int             ppsent,x;
1404         struct storage_pps_entry        stde;
1405
1406         TRACE("(%p)->(%s,0x%08lx,0x%08lx,0x%08lx,%p)\n",
1407                 This,pwcsName,grfMode,reserved1,reserved2,ppstm
1408         );
1409         if (grfMode & STGM_TRANSACTED)
1410                 FIXME("We do not support transacted Compound Storage. Using direct mode.\n");
1411         _create_istream16(ppstm);
1412         lpstr = MapSL((SEGPTR)*ppstm);
1413         DuplicateHandle( GetCurrentProcess(), This->hf, GetCurrentProcess(),
1414                          &lpstr->hf, 0, TRUE, DUPLICATE_SAME_ACCESS );
1415         lpstr->offset.s.LowPart = 0;
1416         lpstr->offset.s.HighPart        = 0;
1417
1418         ppsent=STORAGE_get_free_pps_entry(lpstr->hf);
1419         if (ppsent<0)
1420                 return E_FAIL;
1421         stde=This->stde;
1422         if (stde.pps_next==-1)
1423                 x=This->ppsent;
1424         else
1425                 while (stde.pps_next!=-1) {
1426                         x=stde.pps_next;
1427                         if (1!=STORAGE_get_pps_entry(lpstr->hf,x,&stde))
1428                                 return E_FAIL;
1429                 }
1430         stde.pps_next = ppsent;
1431         assert(STORAGE_put_pps_entry(lpstr->hf,x,&stde));
1432         assert(1==STORAGE_get_pps_entry(lpstr->hf,ppsent,&(lpstr->stde)));
1433         MultiByteToWideChar( CP_ACP, 0, pwcsName, -1, lpstr->stde.pps_rawname,
1434                              sizeof(lpstr->stde.pps_rawname)/sizeof(WCHAR));
1435         lpstr->stde.pps_sizeofname = (strlenW(lpstr->stde.pps_rawname)+1) * sizeof(WCHAR);
1436         lpstr->stde.pps_next    = -1;
1437         lpstr->stde.pps_prev    = -1;
1438         lpstr->stde.pps_dir     = -1;
1439         lpstr->stde.pps_sb      = -1;
1440         lpstr->stde.pps_size    =  0;
1441         lpstr->stde.pps_type    =  2;
1442         lpstr->ppsent           = ppsent;
1443         /* FIXME: timestamps? */
1444         if (!STORAGE_put_pps_entry(lpstr->hf,ppsent,&(lpstr->stde)))
1445                 return E_FAIL;
1446         return S_OK;
1447 }
1448
1449 /******************************************************************************
1450  *              IStorage16_OpenStorage  [STORAGE.506]
1451  */
1452 HRESULT WINAPI IStorage16_fnOpenStorage(
1453         LPSTORAGE16 iface,LPCOLESTR16 pwcsName, IStorage16 *pstgPrio, DWORD grfMode, SNB16 snbExclude, DWORD reserved, IStorage16 **ppstg
1454 ) {
1455         ICOM_THIS(IStorage16Impl,iface);
1456         IStream16Impl*  lpstg;
1457         WCHAR           name[33];
1458         int             newpps;
1459
1460         TRACE_(relay)("(%p)->(%s,%p,0x%08lx,%p,0x%08lx,%p)\n",
1461                 This,pwcsName,pstgPrio,grfMode,snbExclude,reserved,ppstg
1462         );
1463         if (grfMode & STGM_TRANSACTED)
1464                 FIXME("We do not support transacted Compound Storage. Using direct mode.\n");
1465         _create_istorage16(ppstg);
1466         lpstg = MapSL((SEGPTR)*ppstg);
1467         DuplicateHandle( GetCurrentProcess(), This->hf, GetCurrentProcess(),
1468                          &lpstg->hf, 0, TRUE, DUPLICATE_SAME_ACCESS );
1469         MultiByteToWideChar( CP_ACP, 0, pwcsName, -1, name, sizeof(name)/sizeof(WCHAR));
1470         newpps = STORAGE_look_for_named_pps(lpstg->hf,This->stde.pps_dir,name);
1471         if (newpps==-1) {
1472                 IStream16_fnRelease((IStream16*)lpstg);
1473                 return E_FAIL;
1474         }
1475
1476         if (1!=STORAGE_get_pps_entry(lpstg->hf,newpps,&(lpstg->stde))) {
1477                 IStream16_fnRelease((IStream16*)lpstg);
1478                 return E_FAIL;
1479         }
1480         lpstg->ppsent           = newpps;
1481         return S_OK;
1482 }
1483
1484 /******************************************************************************
1485  * IStorage16_OpenStream [STORAGE.504]
1486  */
1487 HRESULT WINAPI IStorage16_fnOpenStream(
1488         LPSTORAGE16 iface,LPCOLESTR16 pwcsName, void *reserved1, DWORD grfMode, DWORD reserved2, IStream16 **ppstm
1489 ) {
1490         ICOM_THIS(IStorage16Impl,iface);
1491         IStream16Impl*  lpstr;
1492         WCHAR           name[33];
1493         int             newpps;
1494
1495         TRACE_(relay)("(%p)->(%s,%p,0x%08lx,0x%08lx,%p)\n",
1496                 This,pwcsName,reserved1,grfMode,reserved2,ppstm
1497         );
1498         if (grfMode & STGM_TRANSACTED)
1499                 FIXME("We do not support transacted Compound Storage. Using direct mode.\n");
1500         _create_istream16(ppstm);
1501         lpstr = MapSL((SEGPTR)*ppstm);
1502         DuplicateHandle( GetCurrentProcess(), This->hf, GetCurrentProcess(),
1503                          &lpstr->hf, 0, TRUE, DUPLICATE_SAME_ACCESS );
1504         MultiByteToWideChar( CP_ACP, 0, pwcsName, -1, name, sizeof(name)/sizeof(WCHAR));
1505         newpps = STORAGE_look_for_named_pps(lpstr->hf,This->stde.pps_dir,name);
1506         if (newpps==-1) {
1507                 IStream16_fnRelease((IStream16*)lpstr);
1508                 return E_FAIL;
1509         }
1510
1511         if (1!=STORAGE_get_pps_entry(lpstr->hf,newpps,&(lpstr->stde))) {
1512                 IStream16_fnRelease((IStream16*)lpstr);
1513                 return E_FAIL;
1514         }
1515         lpstr->offset.s.LowPart = 0;
1516         lpstr->offset.s.HighPart        = 0;
1517         lpstr->ppsent           = newpps;
1518         return S_OK;
1519 }
1520
1521 /******************************************************************************
1522  * _create_istorage16 [INTERNAL]
1523  */
1524 static void _create_istorage16(LPSTORAGE16 *stg) {
1525         IStorage16Impl* lpst;
1526
1527         if (!stvt16.QueryInterface) {
1528                 HMODULE16       wp = GetModuleHandle16("STORAGE");
1529                 if (wp>=32) {
1530 #define VTENT(xfn)  stvt16.xfn = (void*)GetProcAddress16(wp,"IStorage16_"#xfn);
1531                         VTENT(QueryInterface)
1532                         VTENT(AddRef)
1533                         VTENT(Release)
1534                         VTENT(CreateStream)
1535                         VTENT(OpenStream)
1536                         VTENT(CreateStorage)
1537                         VTENT(OpenStorage)
1538                         VTENT(CopyTo)
1539                         VTENT(MoveElementTo)
1540                         VTENT(Commit)
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 #undef VTENT
1550                         segstvt16 = (ICOM_VTABLE(IStorage16)*)MapLS( &stvt16 );
1551                 } else {
1552 #define VTENT(xfn) stvt16.xfn = IStorage16_fn##xfn;
1553                         VTENT(QueryInterface)
1554                         VTENT(AddRef)
1555                         VTENT(Release)
1556                         VTENT(CreateStream)
1557                         VTENT(OpenStream)
1558                         VTENT(CreateStorage)
1559                         VTENT(OpenStorage)
1560                         VTENT(CopyTo)
1561                         VTENT(Commit)
1562         /*  not (yet) implemented ...
1563                         VTENT(MoveElementTo)
1564                         VTENT(Revert)
1565                         VTENT(EnumElements)
1566                         VTENT(DestroyElement)
1567                         VTENT(RenameElement)
1568                         VTENT(SetElementTimes)
1569                         VTENT(SetClass)
1570                         VTENT(SetStateBits)
1571                         VTENT(Stat)
1572         */
1573 #undef VTENT
1574                         segstvt16 = &stvt16;
1575                 }
1576         }
1577         lpst = HeapAlloc( GetProcessHeap(), 0, sizeof(*lpst) );
1578         ICOM_VTBL(lpst) = segstvt16;
1579         lpst->ref       = 1;
1580         lpst->thisptr   = MapLS(lpst);
1581         *stg = (void*)lpst->thisptr;
1582 }
1583
1584 /******************************************************************************
1585  *      Storage API functions
1586  */
1587
1588 /******************************************************************************
1589  *              StgCreateDocFileA       [STORAGE.1]
1590  */
1591 HRESULT WINAPI StgCreateDocFile16(
1592         LPCOLESTR16 pwcsName,DWORD grfMode,DWORD reserved,IStorage16 **ppstgOpen
1593 ) {
1594         HFILE           hf;
1595         int             i,ret;
1596         IStorage16Impl* lpstg;
1597         struct storage_pps_entry        stde;
1598
1599         TRACE("(%s,0x%08lx,0x%08lx,%p)\n",
1600                 pwcsName,grfMode,reserved,ppstgOpen
1601         );
1602         _create_istorage16(ppstgOpen);
1603         hf = CreateFileA(pwcsName,GENERIC_READ|GENERIC_WRITE,0,NULL,CREATE_NEW,0,0);
1604         if (hf==INVALID_HANDLE_VALUE) {
1605                 WARN("couldn't open file for storage:%ld\n",GetLastError());
1606                 return E_FAIL;
1607         }
1608         lpstg = MapSL((SEGPTR)*ppstgOpen);
1609         lpstg->hf = hf;
1610         /* FIXME: check for existence before overwriting? */
1611         if (!STORAGE_init_storage(hf)) {
1612                 CloseHandle(hf);
1613                 return E_FAIL;
1614         }
1615         i=0;ret=0;
1616         while (!ret) { /* neither 1 nor <0 */
1617                 ret=STORAGE_get_pps_entry(hf,i,&stde);
1618                 if ((ret==1) && (stde.pps_type==5)) {
1619                         lpstg->stde     = stde;
1620                         lpstg->ppsent   = i;
1621                         break;
1622                 }
1623                 i++;
1624         }
1625         if (ret!=1) {
1626                 IStorage16_fnRelease((IStorage16*)lpstg); /* will remove it */
1627                 return E_FAIL;
1628         }
1629
1630         return S_OK;
1631 }
1632
1633 /******************************************************************************
1634  * StgIsStorageFile [STORAGE.5]
1635  */
1636 HRESULT WINAPI StgIsStorageFile16(LPCOLESTR16 fn) {
1637         HFILE           hf;
1638         OFSTRUCT        ofs;
1639         BYTE            magic[24];
1640
1641         TRACE("(\'%s\')\n",fn);
1642         hf = OpenFile(fn,&ofs,OF_SHARE_DENY_NONE);
1643         if (hf==HFILE_ERROR)
1644                 return STG_E_FILENOTFOUND;
1645         if (24!=_lread(hf,magic,24)) {
1646                 WARN(" too short\n");
1647                 _lclose(hf);
1648                 return S_FALSE;
1649         }
1650         if (!memcmp(magic,STORAGE_magic,8)) {
1651                 WARN(" -> YES\n");
1652                 _lclose(hf);
1653                 return S_OK;
1654         }
1655         if (!memcmp(magic,STORAGE_notmagic,8)) {
1656                 WARN(" -> NO\n");
1657                 _lclose(hf);
1658                 return S_FALSE;
1659         }
1660         if (!memcmp(magic,STORAGE_oldmagic,8)) {
1661                 WARN(" -> old format\n");
1662                 _lclose(hf);
1663                 return STG_E_OLDFORMAT;
1664         }
1665         WARN(" -> Invalid header.\n");
1666         _lclose(hf);
1667         return STG_E_INVALIDHEADER;
1668 }
1669
1670 /******************************************************************************
1671  * StgIsStorageFile [OLE32.146]
1672  */
1673 HRESULT WINAPI
1674 StgIsStorageFile(LPCOLESTR fn)
1675 {
1676     HRESULT ret;
1677     DWORD len = WideCharToMultiByte( CP_ACP, 0, fn, -1, NULL, 0, NULL, NULL );
1678     LPSTR strA = HeapAlloc( GetProcessHeap(), 0, len );
1679
1680     WideCharToMultiByte( CP_ACP, 0, fn, -1, strA, len, NULL, NULL );
1681     ret = StgIsStorageFile16(strA);
1682     HeapFree( GetProcessHeap(), 0, strA );
1683     return ret;
1684 }
1685
1686
1687 /******************************************************************************
1688  * StgOpenStorage [STORAGE.3]
1689  */
1690 HRESULT WINAPI StgOpenStorage16(
1691         LPCOLESTR16 pwcsName,IStorage16 *pstgPriority,DWORD grfMode,
1692         SNB16 snbExclude,DWORD reserved, IStorage16 **ppstgOpen
1693 ) {
1694         HFILE           hf;
1695         int             ret,i;
1696         IStorage16Impl* lpstg;
1697         struct storage_pps_entry        stde;
1698
1699         TRACE("(%s,%p,0x%08lx,%p,%ld,%p)\n",
1700               pwcsName,pstgPriority,grfMode,snbExclude,reserved,ppstgOpen
1701         );
1702         _create_istorage16(ppstgOpen);
1703         hf = CreateFileA(pwcsName,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
1704         if (hf==INVALID_HANDLE_VALUE) {
1705                 WARN("Couldn't open file for storage\n");
1706                 return E_FAIL;
1707         }
1708         lpstg = MapSL((SEGPTR)*ppstgOpen);
1709         lpstg->hf = hf;
1710
1711         i=0;ret=0;
1712         while (!ret) { /* neither 1 nor <0 */
1713                 ret=STORAGE_get_pps_entry(hf,i,&stde);
1714                 if ((ret==1) && (stde.pps_type==5)) {
1715                         lpstg->stde=stde;
1716                         break;
1717                 }
1718                 i++;
1719         }
1720         if (ret!=1) {
1721                 IStorage16_fnRelease((IStorage16*)lpstg); /* will remove it */
1722                 return E_FAIL;
1723         }
1724         return S_OK;
1725
1726 }
1727
1728