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