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