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