mshtml: Don't create element object for document node.
[wine] / dlls / ole32 / storage.c
1 /* Compound Storage
2  *
3  * Implemented using the documentation of the LAOLA project at
4  * <URL:http://wwwwbs.cs.tu-berlin.de/~schwartz/pmh/index.html>
5  * (Thanks to Martin Schwartz <schwartz@cs.tu-berlin.de>)
6  *
7  * Copyright 1998 Marcus Meissner
8  *
9  * This library is free software; you can redistribute it and/or
10  * modify it under the terms of the GNU Lesser General Public
11  * License as published by the Free Software Foundation; either
12  * version 2.1 of the License, or (at your option) any later version.
13  *
14  * This library is distributed in the hope that it will be useful,
15  * but WITHOUT ANY WARRANTY; without even the implied warranty of
16  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
17  * Lesser General Public License for more details.
18  *
19  * You should have received a copy of the GNU Lesser General Public
20  * License along with this library; if not, write to the Free Software
21  * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
22  */
23
24 #include "config.h"
25
26 #include <assert.h>
27 #include <time.h>
28 #include <stdarg.h>
29 #include <string.h>
30 #include <sys/types.h>
31 #ifdef HAVE_UNISTD_H
32 # include <unistd.h>
33 #endif
34
35 #define NONAMELESSUNION
36 #define NONAMELESSSTRUCT
37 #include "windef.h"
38 #include "winbase.h"
39 #include "winternl.h"
40 #include "winerror.h"
41 #include "wine/winbase16.h"
42 #include "wownt32.h"
43 #include "wine/unicode.h"
44 #include "objbase.h"
45 #include "wine/debug.h"
46
47 #include "ifs.h"
48
49 WINE_DEFAULT_DEBUG_CHANNEL(ole);
50 WINE_DECLARE_DEBUG_CHANNEL(relay);
51
52 struct storage_header {
53         BYTE    magic[8];       /* 00: magic */
54         BYTE    unknown1[36];   /* 08: unknown */
55         DWORD   num_of_bbd_blocks;/* 2C: length of big datablocks */
56         DWORD   root_startblock;/* 30: root storage first big block */
57         DWORD   unknown2[2];    /* 34: unknown */
58         DWORD   sbd_startblock; /* 3C: small block depot first big block */
59         DWORD   unknown3[3];    /* 40: unknown */
60         DWORD   bbd_list[109];  /* 4C: big data block list (up to end of sector)*/
61 };
62 struct storage_pps_entry {
63         WCHAR   pps_rawname[32];/* 00: \0 terminated widechar name */
64         WORD    pps_sizeofname; /* 40: namelength in bytes */
65         BYTE    pps_type;       /* 42: flags, 1 storage/dir, 2 stream, 5 root */
66         BYTE    pps_unknown0;   /* 43: unknown */
67         DWORD   pps_prev;       /* 44: previous pps */
68         DWORD   pps_next;       /* 48: next pps */
69         DWORD   pps_dir;        /* 4C: directory pps */
70         GUID    pps_guid;       /* 50: class ID */
71         DWORD   pps_unknown1;   /* 60: unknown */
72         FILETIME pps_ft1;       /* 64: filetime1 */
73         FILETIME pps_ft2;       /* 70: filetime2 */
74         DWORD   pps_sb;         /* 74: data startblock */
75         DWORD   pps_size;       /* 78: datalength. (<0x1000)?small:big blocks*/
76         DWORD   pps_unknown2;   /* 7C: unknown */
77 };
78
79 #define STORAGE_CHAINENTRY_FAT          0xfffffffd
80 #define STORAGE_CHAINENTRY_ENDOFCHAIN   0xfffffffe
81 #define STORAGE_CHAINENTRY_FREE         0xffffffff
82
83
84 static const BYTE STORAGE_magic[8]   ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
85
86 #define BIGSIZE         512
87 #define SMALLSIZE               64
88
89 #define SMALLBLOCKS_PER_BIGBLOCK        (BIGSIZE/SMALLSIZE)
90
91 #define READ_HEADER(str)        STORAGE_get_big_block(str,-1,(LPBYTE)&sth);assert(!memcmp(STORAGE_magic,sth.magic,sizeof(STORAGE_magic)));
92 static IStorage16Vtbl stvt16;
93 static const IStorage16Vtbl *segstvt16 = NULL;
94 static IStream16Vtbl strvt16;
95 static const IStream16Vtbl *segstrvt16 = NULL;
96
97 /*ULONG WINAPI IStorage16_AddRef(LPSTORAGE16 this);*/
98 static void _create_istorage16(LPSTORAGE16 *stg);
99 static void _create_istream16(LPSTREAM16 *str);
100
101 #define IMPLEMENTED 1
102
103 /* The following is taken from the CorVu implementation of docfiles, and
104  * documents things about the file format that are not implemented here, and
105  * not documented by the LAOLA project. The CorVu implementation was posted
106  * to wine-devel in February 2004, and released under the LGPL at the same
107  * time. Because that implementation is in C++, it's not directly usable in
108  * Wine, but does have documentation value.
109  *
110  *
111  * #define DF_EXT_VTOC          -4
112  * #define DF_VTOC_VTOC         -3
113  * #define DF_VTOC_EOF          -2
114  * #define DF_VTOC_FREE         -1
115  * #define DF_NAMELEN   0x20    // Maximum entry name length - 31 characters plus
116  *                              // a NUL terminator
117  * 
118  * #define DF_FT_STORAGE        1
119  * #define DF_FT_STREAM         2
120  * #define DF_FT_LOCKBYTES      3       // Not used -- How the bloody hell did I manage
121  * #define DF_FT_PROPERTY       4       // Not Used -- to figure these two out?
122  * #define DF_FT_ROOT           5
123  * 
124  * #define DF_BLOCK_SIZE        0x200
125  * #define DF_VTOC_SIZE         0x80
126  * #define DF_DE_PER_BLOCK      4
127  * #define DF_STREAM_BLOCK_SIZE 0x40
128  * 
129  * A DocFile is divided into blocks of 512 bytes.
130  * The first block contains the header.
131  *
132  * The file header contains The first 109 entries in the VTOC of VTOCs.
133  *
134  * Each block pointed to by a VTOC of VTOCs contains a VTOC, which
135  * includes block chains - just like FAT. This is a somewhat poor
136  * design for the following reasons:
137  *
138  *      1. FAT was a poor file system design to begin with, and
139  *         has long been known to be horrendously inefficient
140  *         for day to day operations.
141  *
142  *      2. The problem is compounded here, since the file
143  *         level streams are generally *not* read sequentially.
144  *         This means that a significant percentage of reads
145  *         require seeking from the start of the chain.
146  *
147  * Data chains also contain an internal VTOC. The block size for
148  * the standard VTOC is 512. The block size for the internal VTOC
149  * is 64.
150  *
151  * Now, the 109 blocks in the VTOC of VTOCs allows for files of
152  * up to around 7MB. So what do you think happens if that's
153  * exceeded? Well, there's an entry in the header block which
154  * points to the first block used as additional storage for
155  * the VTOC of VTOCs.
156  *
157  * Now we can get up to around 15MB. Now, guess how the file
158  * format adds in another block to the VTOC of VTOCs. Come on,
159  * it's no big surprise. That's right - the last entry in each
160  * block extending the VTOC of VTOCs is, you guessed it, the
161  * block number of the next block containing an extension to
162  * the VTOC of VTOCs. The VTOC of VTOCs is chained!!!!
163  *
164  * So, to review:
165  *
166  * 1. If you are using a FAT file system, the location of
167  *    your file's blocks is stored in chains.
168  *
169  * 2. At the abstract level, the file contains a VTOC of VTOCs,
170  *    which is stored in the most inefficient possible format for
171  *    random access - a chain (AKA list).
172  *
173  * 3. The VTOC of VTOCs contains descriptions of three file level
174  *    streams:
175  *
176  *    a. The Directory stream
177  *    b. The Data stream
178  *    c. The Data VTOC stream
179  *
180  *    These are, of course, represented as chains.
181  *
182  * 4. The Data VTOC contains data describing the chains of blocks
183  *    within the Data stream.
184  *
185  * That's right - we have a total of four levels of block chains!
186  *
187  * Now, is that complicated enough for you? No? OK, there's another
188  * complication. If an individual stream (ie. an IStream) reaches
189  * 4096 bytes in size, it gets moved from the Data Stream to
190  * a new file level stream. Now, if the stream then gets truncated
191  * back to less than 4096 bytes, it returns to the data stream.
192  *
193  * The effect of using this format can be seen very easily. Pick
194  * an arbitrary application with a grid data representation that
195  * can export to both Lotus 123 and Excel 5 or higher. Export
196  * a large file to Lotus 123 and time it. Export the same thing
197  * to Excel 5 and time that. The difference is the inefficiency
198  * of the Microsoft DocFile format.
199  *
200  *
201  * #define TOTAL_SIMPLE_VTOCS   109
202  * 
203  * struct       DocFile_Header
204  * {
205  *      df_byte iMagic1;        // 0xd0 
206  *      df_byte iMagic2;        // 0xcf 
207  *      df_byte iMagic3;        // 0x11 
208  *      df_byte iMagic4;        // 0xe0 - Spells D0CF11E0, or DocFile 
209  *      df_byte iMagic5;        // 161  (igi upside down) 
210  *      df_byte iMagic6;        // 177  (lli upside down - see below 
211  *      df_byte iMagic7;        // 26 (gz upside down) 
212  *      df_byte iMagic8;        // 225 (szz upside down) - see below 
213  *      df_int4 aiUnknown1[4];
214  *      df_int4 iVersion;       // DocFile Version - 0x03003E   
215  *      df_int4 aiUnknown2[4];
216  *      df_int4 nVTOCs;         // Number of VTOCs 
217  *      df_int4 iFirstDirBlock; // First Directory Block 
218  *      df_int4 aiUnknown3[2];
219  *      df_int4 iFirstDataVTOC; // First data VTOC block 
220  *      df_int4 iHasData;       // 1 if there is data in the file - yes, this is important
221  *      df_int4 iExtendedVTOC;  // Extended VTOC location 
222  *      df_int4 iExtendedVTOCSize; // Size of extended VTOC (+1?) 
223  *      df_int4 aiVTOCofVTOCs[TOTAL_SIMPLE_VTOCS];
224  * };
225  * 
226  * struct       DocFile_VTOC
227  * {
228  *      df_int4 aiBlocks[DF_VTOC_SIZE];
229  * };
230  * 
231  * 
232  * The meaning of the magic numbers
233  *
234  * 0xd0cf11e0 is DocFile with a zero on the end (sort of)
235  *
236  * If you key 177161 into a calculator, then turn the calculator
237  * upside down, you get igilli, which may be a reference to
238  * somebody's name, or to the Hebrew word for "angel".
239  *
240  * If you key 26225 into a calculator, then turn it upside down, you
241  * get szzgz. Microsoft has a tradition of creating nonsense words
242  * using the letters s, g, z and y. We think szzgz may be one of the
243  * Microsoft placeholder variables, along the lines of foo, bar and baz.
244  * Alternatively, it could be 22526, which would be gzszz.
245  *
246  * 
247  * struct       DocFile_DirEnt
248  * {
249  *      df_char achEntryName[DF_NAMELEN];       // Entry Name 
250  *      df_int2 iNameLen;                       // Name length in bytes, including NUL terminator 
251  *      df_byte iFileType;                      // Entry type 
252  *      df_byte iColour;                        // 1 = Black, 0 = Red 
253  *      df_int4 iLeftSibling;                   // Next Left Sibling Entry - See below 
254  *      df_int4 iRightSibling;                  // Next Right Sibling Entry 
255  *      df_int4 iFirstChild;                    // First Child Entry 
256  *      df_byte achClassID[16];                 // Class ID 
257  *      df_int4 iStateBits;                     // [GS]etStateBits value 
258  *      df_int4 iCreatedLow;                    // Low DWORD of creation time 
259  *      df_int4 iCreatedHigh;                   // High DWORD of creation time 
260  *      df_int4 iModifiedLow;                   // Low DWORD of modification time 
261  *      df_int4 iModifiedHigh;                  // High DWORD of modification time 
262  *      df_int4 iVTOCPosition;                  // VTOC Position 
263  *      df_int4 iFileSize;                      // Size of the stream 
264  *      df_int4 iZero;                          // We think this is part of the 64 bit stream size - must be 0 
265  * };
266  * 
267  * Siblings
268  * ========
269  *
270  * Siblings are stored in an obscure but incredibly elegant
271  * data structure called a red-black tree. This is generally
272  * defined as a 2-3-4 tree stored in a binary tree.
273  *
274  * A red-black tree can always be balanced very easily. The rules
275  * for a red-black tree are as follows:
276  *
277  *      1. The root node is always black.
278  *      2. The parent of a red node is always black.
279  *
280  * There is a Java demo of red-black trees at:
281  *
282  *      http://langevin.usc.edu/BST/RedBlackTree-Example.html
283  *
284  * This demo is an excellent tool for learning how red-black
285  * trees work, without having to go through the process of
286  * learning how they were derived.
287  *
288  * Within the tree, elements are ordered by the length of the
289  * name and within that, ASCII order by name. This causes the
290  * apparently bizarre reordering you see when you use dfview.
291  *
292  * This is a somewhat bizarre choice. It suggests that the
293  * designer of the DocFile format was trying to optimise
294  * searching through the directory entries. However searching
295  * through directory entries is a relatively rare operation.
296  * Reading and seeking within a stream are much more common
297  * operations, especially within the file level streams, yet
298  * these use the horrendously inefficient FAT chains.
299  *
300  * This suggests that the designer was probably somebody
301  * fresh out of university, who had some basic knowledge of
302  * basic data structures, but little knowledge of anything
303  * more practical. It is bizarre to attempt to optimise
304  * directory searches while not using a more efficient file
305  * block locating system than FAT (seedling/sapling/tree
306  * would result in a massive improvement - in fact we have
307  * an alternative to DocFiles that we use internally that
308  * uses seedling/sapling/tree and *is* far more efficient).
309  *
310  * It is worth noting that the MS implementation of red-black
311  * trees is incorrect (I can tell you're surprised) and
312  * actually causes more operations to occur than are really
313  * needed. Fortunately the fact that our implementation is
314  * correct will not cause any problems - the MS implementation
315  * still appears to cause the tree to satisfy the rules, albeit
316  * a sequence of the same insertions in the different
317  * implementations may result in a different, and possibly
318  * deeper (but never shallower) tree.
319  */
320
321 typedef struct {
322         HANDLE          hf;
323         SEGPTR          lockbytes;
324 } stream_access16;
325 /* --- IStorage16 implementation struct */
326
327 typedef struct
328 {
329         /* IUnknown fields */
330         const IStorage16Vtbl           *lpVtbl;
331         LONG                            ref;
332         /* IStorage16 fields */
333         SEGPTR                          thisptr; /* pointer to this struct as segmented */
334         struct storage_pps_entry        stde;
335         int                             ppsent;
336         stream_access16                 str;
337 } IStorage16Impl;
338
339
340 /******************************************************************************
341  *              STORAGE_get_big_block   [Internal]
342  *
343  * Reading OLE compound storage
344  */
345 static BOOL
346 STORAGE_get_big_block(stream_access16 *str,int n,BYTE *block)
347 {
348     DWORD result;
349
350     assert(n>=-1);
351     if (str->hf) {
352         if ((SetFilePointer( str->hf, (n+1)*BIGSIZE, NULL,
353                              SEEK_SET ) == INVALID_SET_FILE_POINTER) && GetLastError())
354         {
355             WARN("(%p,%d,%p), seek failed (%d)\n",str->hf, n, block, GetLastError());
356             return FALSE;
357         }
358         if (!ReadFile( str->hf, block, BIGSIZE, &result, NULL ) || result != BIGSIZE)
359         {
360             WARN("(hf=%p, block size %d): read didn't read (%d)\n",str->hf,n,GetLastError());
361             return FALSE;
362         }
363     } else {
364         DWORD args[6];
365         HRESULT hres;
366         HANDLE16 hsig;
367         
368         args[0] = (DWORD)str->lockbytes;        /* iface */
369         args[1] = (n+1)*BIGSIZE;
370         args[2] = 0;    /* ULARGE_INTEGER offset */
371         args[3] = WOWGlobalAllocLock16( 0, BIGSIZE, &hsig ); /* sig */
372         args[4] = BIGSIZE;
373         args[5] = 0;
374
375         if (!WOWCallback16Ex(
376             (DWORD)((const ILockBytes16Vtbl*)MapSL(
377                         (SEGPTR)((LPLOCKBYTES16)MapSL(str->lockbytes))->lpVtbl)
378             )->ReadAt,
379             WCB16_PASCAL,
380             6*sizeof(DWORD),
381             (LPVOID)args,
382             (LPDWORD)&hres
383         )) {
384             ERR("CallTo16 ILockBytes16::ReadAt() failed, hres %x\n",hres);
385             return FALSE;
386         }
387         memcpy(block, MapSL(args[3]), BIGSIZE);
388         WOWGlobalUnlockFree16(args[3]);
389     }
390     return TRUE;
391 }
392
393 static BOOL
394 _ilockbytes16_writeat(SEGPTR lockbytes, DWORD offset, DWORD length, void *buffer) {
395     DWORD args[6];
396     HRESULT hres;
397
398     args[0] = (DWORD)lockbytes; /* iface */
399     args[1] = offset;
400     args[2] = 0;        /* ULARGE_INTEGER offset */
401     args[3] = (DWORD)MapLS( buffer );
402     args[4] = length;
403     args[5] = 0;
404
405     /* THIS_ ULARGE_INTEGER ulOffset, const void *pv, ULONG cb, ULONG *pcbWritten); */
406
407     if (!WOWCallback16Ex(
408         (DWORD)((const ILockBytes16Vtbl*)MapSL(
409                     (SEGPTR)((LPLOCKBYTES16)MapSL(lockbytes))->lpVtbl)
410         )->WriteAt,
411         WCB16_PASCAL,
412         6*sizeof(DWORD),
413         (LPVOID)args,
414         (LPDWORD)&hres
415     )) {
416         ERR("CallTo16 ILockBytes16::WriteAt() failed, hres %x\n",hres);
417         return FALSE;
418     }
419     UnMapLS(args[3]);
420     return TRUE;
421 }
422
423 /******************************************************************************
424  * STORAGE_put_big_block [INTERNAL]
425  */
426 static BOOL
427 STORAGE_put_big_block(stream_access16 *str,int n,BYTE *block)
428 {
429     DWORD result;
430
431     assert(n>=-1);
432     if (str->hf) {
433         if ((SetFilePointer( str->hf, (n+1)*BIGSIZE, NULL,
434                              SEEK_SET ) == INVALID_SET_FILE_POINTER) && GetLastError())
435         {
436             WARN("seek failed (%d)\n",GetLastError());
437             return FALSE;
438         }
439         if (!WriteFile( str->hf, block, BIGSIZE, &result, NULL ) || result != BIGSIZE)
440         {
441             WARN(" write failed (%d)\n",GetLastError());
442             return FALSE;
443         }
444         return TRUE;
445     } else {
446         _ilockbytes16_writeat(str->lockbytes, (n+1)*BIGSIZE, BIGSIZE, block);
447         return TRUE;
448     }
449 }
450
451 /******************************************************************************
452  * STORAGE_get_next_big_blocknr [INTERNAL]
453  */
454 static int
455 STORAGE_get_next_big_blocknr(stream_access16 *str,int blocknr) {
456         INT     bbs[BIGSIZE/sizeof(INT)];
457         struct  storage_header  sth;
458
459         READ_HEADER(str);
460
461         assert(blocknr>>7<sth.num_of_bbd_blocks);
462         if (sth.bbd_list[blocknr>>7]==0xffffffff)
463                 return -5;
464         if (!STORAGE_get_big_block(str,sth.bbd_list[blocknr>>7],(LPBYTE)bbs))
465                 return -5;
466         assert(bbs[blocknr&0x7f]!=STORAGE_CHAINENTRY_FREE);
467         return bbs[blocknr&0x7f];
468 }
469
470 /******************************************************************************
471  * STORAGE_get_nth_next_big_blocknr [INTERNAL]
472  */
473 static int
474 STORAGE_get_nth_next_big_blocknr(stream_access16 *str,int blocknr,int nr) {
475         INT     bbs[BIGSIZE/sizeof(INT)];
476         int     lastblock = -1;
477         struct storage_header sth;
478
479         TRACE("(blocknr=%d, nr=%d)\n", blocknr, nr);
480         READ_HEADER(str);
481
482         assert(blocknr>=0);
483         while (nr--) {
484                 assert((blocknr>>7)<sth.num_of_bbd_blocks);
485                 assert(sth.bbd_list[blocknr>>7]!=0xffffffff);
486
487                 /* simple caching... */
488                 if (lastblock!=sth.bbd_list[blocknr>>7]) {
489                         BOOL ret = STORAGE_get_big_block(str,sth.bbd_list[blocknr>>7],(LPBYTE)bbs);
490                         assert(ret);
491                         lastblock = sth.bbd_list[blocknr>>7];
492                 }
493                 blocknr = bbs[blocknr&0x7f];
494         }
495         return blocknr;
496 }
497
498 /******************************************************************************
499  *              STORAGE_get_root_pps_entry      [Internal]
500  */
501 static BOOL
502 STORAGE_get_root_pps_entry(stream_access16* str,struct storage_pps_entry *pstde) {
503         int     blocknr,i;
504         BYTE    block[BIGSIZE];
505         struct storage_pps_entry        *stde=(struct storage_pps_entry*)block;
506         struct storage_header sth;
507
508         READ_HEADER(str);
509         blocknr = sth.root_startblock;
510         TRACE("startblock is %d\n", blocknr);
511         while (blocknr>=0) {
512                 BOOL ret = STORAGE_get_big_block(str,blocknr,block);
513                 assert(ret);
514                 for (i=0;i<4;i++) {
515                         if (!stde[i].pps_sizeofname)
516                                 continue;
517                         if (stde[i].pps_type==5) {
518                                 *pstde=stde[i];
519                                 return TRUE;
520                         }
521                 }
522                 blocknr=STORAGE_get_next_big_blocknr(str,blocknr);
523                 TRACE("next block is %d\n", blocknr);
524         }
525         return FALSE;
526 }
527
528 /******************************************************************************
529  * STORAGE_get_small_block [INTERNAL]
530  */
531 static BOOL
532 STORAGE_get_small_block(stream_access16 *str,int blocknr,BYTE *sblock) {
533         BYTE                            block[BIGSIZE];
534         int                             bigblocknr;
535         struct storage_pps_entry        root;
536         BOOL ret;
537
538         TRACE("(blocknr=%d)\n", blocknr);
539         assert(blocknr>=0);
540         ret = STORAGE_get_root_pps_entry(str,&root);
541         assert(ret);
542         bigblocknr = STORAGE_get_nth_next_big_blocknr(str,root.pps_sb,blocknr/SMALLBLOCKS_PER_BIGBLOCK);
543         assert(bigblocknr>=0);
544         ret = STORAGE_get_big_block(str,bigblocknr,block);
545         assert(ret);
546
547         memcpy(sblock,((LPBYTE)block)+SMALLSIZE*(blocknr&(SMALLBLOCKS_PER_BIGBLOCK-1)),SMALLSIZE);
548         return TRUE;
549 }
550
551 /******************************************************************************
552  * STORAGE_put_small_block [INTERNAL]
553  */
554 static BOOL
555 STORAGE_put_small_block(stream_access16 *str,int blocknr,const BYTE *sblock) {
556         BYTE                            block[BIGSIZE];
557         int                             bigblocknr;
558         struct storage_pps_entry        root;
559         BOOL ret;
560
561         assert(blocknr>=0);
562         TRACE("(blocknr=%d)\n", blocknr);
563
564         ret = STORAGE_get_root_pps_entry(str,&root);
565         assert(ret);
566         bigblocknr = STORAGE_get_nth_next_big_blocknr(str,root.pps_sb,blocknr/SMALLBLOCKS_PER_BIGBLOCK);
567         assert(bigblocknr>=0);
568         ret = STORAGE_get_big_block(str,bigblocknr,block);
569         assert(ret);
570
571         memcpy(((LPBYTE)block)+SMALLSIZE*(blocknr&(SMALLBLOCKS_PER_BIGBLOCK-1)),sblock,SMALLSIZE);
572         ret = STORAGE_put_big_block(str,bigblocknr,block);
573         assert(ret);
574         return TRUE;
575 }
576
577 /******************************************************************************
578  * STORAGE_get_next_small_blocknr [INTERNAL]
579  */
580 static int
581 STORAGE_get_next_small_blocknr(stream_access16 *str,int blocknr) {
582         BYTE                            block[BIGSIZE];
583         LPINT                           sbd = (LPINT)block;
584         int                             bigblocknr;
585         struct storage_header           sth;
586         BOOL ret;
587
588         TRACE("(blocknr=%d)\n", blocknr);
589         READ_HEADER(str);
590         assert(blocknr>=0);
591         bigblocknr = STORAGE_get_nth_next_big_blocknr(str,sth.sbd_startblock,blocknr/128);
592         assert(bigblocknr>=0);
593         ret = STORAGE_get_big_block(str,bigblocknr,block);
594         assert(ret);
595         assert(sbd[blocknr & 127]!=STORAGE_CHAINENTRY_FREE);
596         return sbd[blocknr & (128-1)];
597 }
598
599 /******************************************************************************
600  * STORAGE_get_nth_next_small_blocknr [INTERNAL]
601  */
602 static int
603 STORAGE_get_nth_next_small_blocknr(stream_access16*str,int blocknr,int nr) {
604         int     lastblocknr=-1;
605         BYTE    block[BIGSIZE];
606         LPINT   sbd = (LPINT)block;
607         struct storage_header sth;
608         BOOL ret;
609
610         TRACE("(blocknr=%d, nr=%d)\n", blocknr, nr);
611         READ_HEADER(str);
612         assert(blocknr>=0);
613         while ((nr--) && (blocknr>=0)) {
614                 if (lastblocknr/128!=blocknr/128) {
615                         int     bigblocknr;
616                         bigblocknr = STORAGE_get_nth_next_big_blocknr(str,sth.sbd_startblock,blocknr/128);
617                         assert(bigblocknr>=0);
618                         ret = STORAGE_get_big_block(str,bigblocknr,block);
619                         assert(ret);
620                         lastblocknr = blocknr;
621                 }
622                 assert(lastblocknr>=0);
623                 lastblocknr=blocknr;
624                 blocknr=sbd[blocknr & (128-1)];
625                 assert(blocknr!=STORAGE_CHAINENTRY_FREE);
626         }
627         return blocknr;
628 }
629
630 /******************************************************************************
631  * STORAGE_get_pps_entry [INTERNAL]
632  */
633 static int
634 STORAGE_get_pps_entry(stream_access16*str,int n,struct storage_pps_entry *pstde) {
635         int     blocknr;
636         BYTE    block[BIGSIZE];
637         struct storage_pps_entry *stde = (struct storage_pps_entry*)(((LPBYTE)block)+128*(n&3));
638         struct storage_header sth;
639         BOOL ret;
640
641         TRACE("(n=%d)\n", n);
642         READ_HEADER(str);
643         /* we have 4 pps entries per big block */
644         blocknr = STORAGE_get_nth_next_big_blocknr(str,sth.root_startblock,n/4);
645         assert(blocknr>=0);
646         ret = STORAGE_get_big_block(str,blocknr,block);
647         assert(ret);
648
649         *pstde=*stde;
650         return 1;
651 }
652
653 /******************************************************************************
654  *              STORAGE_put_pps_entry   [Internal]
655  */
656 static int
657 STORAGE_put_pps_entry(stream_access16*str,int n,const struct storage_pps_entry *pstde) {
658         int     blocknr;
659         BYTE    block[BIGSIZE];
660         struct storage_pps_entry *stde = (struct storage_pps_entry*)(((LPBYTE)block)+128*(n&3));
661         struct storage_header sth;
662         BOOL ret;
663
664         TRACE("(n=%d)\n", n);
665         READ_HEADER(str);
666         /* we have 4 pps entries per big block */
667         blocknr = STORAGE_get_nth_next_big_blocknr(str,sth.root_startblock,n/4);
668         assert(blocknr>=0);
669         ret = STORAGE_get_big_block(str,blocknr,block);
670         assert(ret);
671         *stde=*pstde;
672         ret = STORAGE_put_big_block(str,blocknr,block);
673         assert(ret);
674         return 1;
675 }
676
677 /******************************************************************************
678  *              STORAGE_look_for_named_pps      [Internal]
679  */
680 static int
681 STORAGE_look_for_named_pps(stream_access16*str,int n,LPOLESTR name) {
682         struct storage_pps_entry        stde;
683         int                             ret;
684
685         TRACE("(n=%d,name=%s)\n", n, debugstr_w(name));
686         if (n==-1)
687                 return -1;
688         if (1!=STORAGE_get_pps_entry(str,n,&stde))
689                 return -1;
690
691         if (!lstrcmpW(name,stde.pps_rawname))
692                 return n;
693         if (stde.pps_prev != -1) {
694                 ret=STORAGE_look_for_named_pps(str,stde.pps_prev,name);
695                 if (ret!=-1)
696                         return ret;
697         }
698         if (stde.pps_next != -1) {
699                 ret=STORAGE_look_for_named_pps(str,stde.pps_next,name);
700                 if (ret!=-1)
701                         return ret;
702         }
703         return -1;
704 }
705
706 /******************************************************************************
707  *              STORAGE_dump_pps_entry  [Internal]
708  *
709  * FIXME
710  *    Function is unused
711  */
712 void
713 STORAGE_dump_pps_entry(struct storage_pps_entry *stde) {
714     char        name[33];
715
716     WideCharToMultiByte( CP_ACP, 0, stde->pps_rawname, -1, name, sizeof(name), NULL, NULL);
717         if (!stde->pps_sizeofname)
718                 return;
719         DPRINTF("name: %s\n",name);
720         DPRINTF("type: %d\n",stde->pps_type);
721         DPRINTF("prev pps: %d\n",stde->pps_prev);
722         DPRINTF("next pps: %d\n",stde->pps_next);
723         DPRINTF("dir pps: %d\n",stde->pps_dir);
724         DPRINTF("guid: %s\n",debugstr_guid(&(stde->pps_guid)));
725         if (stde->pps_type !=2) {
726                 time_t  t;
727                 DWORD dw;
728                 RtlTimeToSecondsSince1970((LARGE_INTEGER *)&(stde->pps_ft1),&dw);
729                 t = dw;
730                 DPRINTF("ts1: %s\n",ctime(&t));
731                 RtlTimeToSecondsSince1970((LARGE_INTEGER *)&(stde->pps_ft2),&dw);
732                 t = dw;
733                 DPRINTF("ts2: %s\n",ctime(&t));
734         }
735         DPRINTF("startblock: %d\n",stde->pps_sb);
736         DPRINTF("size: %d\n",stde->pps_size);
737 }
738
739 /******************************************************************************
740  * STORAGE_init_storage [INTERNAL]
741  */
742 static BOOL
743 STORAGE_init_storage(stream_access16 *str) {
744         BYTE    block[BIGSIZE];
745         LPDWORD bbs;
746         struct storage_header *sth;
747         struct storage_pps_entry *stde;
748         DWORD result;
749
750         if (str->hf)
751             SetFilePointer( str->hf, 0, NULL, SEEK_SET );
752         /* block -1 is the storage header */
753         sth = (struct storage_header*)block;
754         memcpy(sth->magic,STORAGE_magic,8);
755         memset(sth->unknown1,0,sizeof(sth->unknown1));
756         memset(sth->unknown2,0,sizeof(sth->unknown2));
757         memset(sth->unknown3,0,sizeof(sth->unknown3));
758         sth->num_of_bbd_blocks  = 1;
759         sth->root_startblock    = 1;
760         sth->sbd_startblock     = 0xffffffff;
761         memset(sth->bbd_list,0xff,sizeof(sth->bbd_list));
762         sth->bbd_list[0]        = 0;
763         if (str->hf) {
764             if (!WriteFile( str->hf, block, BIGSIZE, &result, NULL ) || result != BIGSIZE) return FALSE;
765         } else {
766             if (!_ilockbytes16_writeat(str->lockbytes, 0, BIGSIZE, block)) return FALSE;
767         }
768         /* block 0 is the big block directory */
769         bbs=(LPDWORD)block;
770         memset(block,0xff,sizeof(block)); /* mark all blocks as free */
771         bbs[0]=STORAGE_CHAINENTRY_ENDOFCHAIN; /* for this block */
772         bbs[1]=STORAGE_CHAINENTRY_ENDOFCHAIN; /* for directory entry */
773         if (str->hf) {
774             if (!WriteFile( str->hf, block, BIGSIZE, &result, NULL ) || result != BIGSIZE) return FALSE;
775         } else {
776             if (!_ilockbytes16_writeat(str->lockbytes, BIGSIZE, BIGSIZE, block)) return FALSE;
777         }
778         /* block 1 is the root directory entry */
779         memset(block,0x00,sizeof(block));
780         stde = (struct storage_pps_entry*)block;
781         MultiByteToWideChar( CP_ACP, 0, "RootEntry", -1, stde->pps_rawname,
782                              sizeof(stde->pps_rawname)/sizeof(WCHAR));
783         stde->pps_sizeofname    = (strlenW(stde->pps_rawname)+1) * sizeof(WCHAR);
784         stde->pps_type          = 5;
785         stde->pps_dir           = -1;
786         stde->pps_next          = -1;
787         stde->pps_prev          = -1;
788         stde->pps_sb            = 0xffffffff;
789         stde->pps_size          = 0;
790         if (str->hf) {
791             return (WriteFile( str->hf, block, BIGSIZE, &result, NULL ) && result == BIGSIZE);
792         } else {
793             return _ilockbytes16_writeat(str->lockbytes, BIGSIZE, BIGSIZE, block);
794         }
795 }
796
797 /******************************************************************************
798  *              STORAGE_set_big_chain   [Internal]
799  */
800 static BOOL
801 STORAGE_set_big_chain(stream_access16*str,int blocknr,INT type) {
802         BYTE    block[BIGSIZE];
803         LPINT   bbd = (LPINT)block;
804         int     nextblocknr,bigblocknr;
805         struct storage_header sth;
806         BOOL ret;
807
808         READ_HEADER(str);
809         assert(blocknr!=type);
810         while (blocknr>=0) {
811                 bigblocknr = sth.bbd_list[blocknr/128];
812                 assert(bigblocknr>=0);
813                 ret = STORAGE_get_big_block(str,bigblocknr,block);
814                 assert(ret);
815
816                 nextblocknr = bbd[blocknr&(128-1)];
817                 bbd[blocknr&(128-1)] = type;
818                 if (type>=0)
819                         return TRUE;
820                 ret = STORAGE_put_big_block(str,bigblocknr,block);
821                 assert(ret);
822                 type = STORAGE_CHAINENTRY_FREE;
823                 blocknr = nextblocknr;
824         }
825         return TRUE;
826 }
827
828 /******************************************************************************
829  * STORAGE_set_small_chain [Internal]
830  */
831 static BOOL
832 STORAGE_set_small_chain(stream_access16*str,int blocknr,INT type) {
833         BYTE    block[BIGSIZE];
834         LPINT   sbd = (LPINT)block;
835         int     lastblocknr,nextsmallblocknr,bigblocknr;
836         struct storage_header sth;
837         BOOL ret;
838
839         READ_HEADER(str);
840
841         assert(blocknr!=type);
842         lastblocknr=-129;bigblocknr=-2;
843         while (blocknr>=0) {
844                 /* cache block ... */
845                 if (lastblocknr/128!=blocknr/128) {
846                         bigblocknr = STORAGE_get_nth_next_big_blocknr(str,sth.sbd_startblock,blocknr/128);
847                         assert(bigblocknr>=0);
848                         ret = STORAGE_get_big_block(str,bigblocknr,block);
849                         assert(ret);
850                 }
851                 lastblocknr = blocknr;
852                 nextsmallblocknr = sbd[blocknr&(128-1)];
853                 sbd[blocknr&(128-1)] = type;
854                 ret = STORAGE_put_big_block(str,bigblocknr,block);
855                 assert(ret);
856                 if (type>=0)
857                         return TRUE;
858                 type = STORAGE_CHAINENTRY_FREE;
859                 blocknr = nextsmallblocknr;
860         }
861         return TRUE;
862 }
863
864 /******************************************************************************
865  *              STORAGE_get_free_big_blocknr    [Internal]
866  */
867 static int
868 STORAGE_get_free_big_blocknr(stream_access16 *str) {
869         BYTE    block[BIGSIZE];
870         LPINT   sbd = (LPINT)block;
871         int     lastbigblocknr,i,bigblocknr;
872         unsigned int curblock;
873         struct storage_header sth;
874         BOOL ret;
875
876         READ_HEADER(str);
877         curblock        = 0;
878         lastbigblocknr  = -1;
879         bigblocknr      = sth.bbd_list[curblock];
880         while (curblock<sth.num_of_bbd_blocks) {
881                 assert(bigblocknr>=0);
882                 ret = STORAGE_get_big_block(str,bigblocknr,block);
883                 assert(ret);
884                 for (i=0;i<128;i++)
885                         if (sbd[i]==STORAGE_CHAINENTRY_FREE) {
886                                 sbd[i] = STORAGE_CHAINENTRY_ENDOFCHAIN;
887                                 ret = STORAGE_put_big_block(str,bigblocknr,block);
888                                 assert(ret);
889                                 memset(block,0x42,sizeof(block));
890                                 ret = STORAGE_put_big_block(str,i+curblock*128,block);
891                                 assert(ret);
892                                 return i+curblock*128;
893                         }
894                 lastbigblocknr = bigblocknr;
895                 bigblocknr = sth.bbd_list[++curblock];
896         }
897         bigblocknr = curblock*128;
898         /* since we have marked all blocks from 0 up to curblock*128-1
899          * the next free one is curblock*128, where we happily put our
900          * next large block depot.
901          */
902         memset(block,0xff,sizeof(block));
903         /* mark the block allocated and returned by this function */
904         sbd[1] = STORAGE_CHAINENTRY_ENDOFCHAIN;
905         ret = STORAGE_put_big_block(str,bigblocknr,block);
906         assert(ret);
907
908         /* if we had a bbd block already (mostlikely) we need
909          * to link the new one into the chain
910          */
911         if (lastbigblocknr!=-1) {
912                 ret = STORAGE_set_big_chain(str,lastbigblocknr,bigblocknr);
913                 assert(ret);
914         }
915         sth.bbd_list[curblock]=bigblocknr;
916         sth.num_of_bbd_blocks++;
917         assert(sth.num_of_bbd_blocks==curblock+1);
918         ret = STORAGE_put_big_block(str,-1,(LPBYTE)&sth);
919         assert(ret);
920
921         /* Set the end of the chain for the bigblockdepots */
922         ret = STORAGE_set_big_chain(str,bigblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN);
923         assert(ret);
924         /* add 1, for the first entry is used for the additional big block
925          * depot. (means we already used bigblocknr) */
926         memset(block,0x42,sizeof(block));
927         /* allocate this block (filled with 0x42) */
928         ret = STORAGE_put_big_block(str,bigblocknr+1,block);
929         assert(ret);
930         return bigblocknr+1;
931 }
932
933
934 /******************************************************************************
935  *              STORAGE_get_free_small_blocknr  [Internal]
936  */
937 static int
938 STORAGE_get_free_small_blocknr(stream_access16 *str) {
939         BYTE    block[BIGSIZE];
940         LPINT   sbd = (LPINT)block;
941         int     lastbigblocknr,newblocknr,i,curblock,bigblocknr;
942         struct storage_pps_entry        root;
943         struct storage_header sth;
944
945         READ_HEADER(str);
946         bigblocknr      = sth.sbd_startblock;
947         curblock        = 0;
948         lastbigblocknr  = -1;
949         newblocknr      = -1;
950         while (bigblocknr>=0) {
951                 if (!STORAGE_get_big_block(str,bigblocknr,block))
952                         return -1;
953                 for (i=0;i<128;i++)
954                         if (sbd[i]==STORAGE_CHAINENTRY_FREE) {
955                                 sbd[i]=STORAGE_CHAINENTRY_ENDOFCHAIN;
956                                 newblocknr = i+curblock*128;
957                                 break;
958                         }
959                 if (i!=128)
960                         break;
961                 lastbigblocknr = bigblocknr;
962                 bigblocknr = STORAGE_get_next_big_blocknr(str,bigblocknr);
963                 curblock++;
964         }
965         if (newblocknr==-1) {
966                 bigblocknr = STORAGE_get_free_big_blocknr(str);
967                 if (bigblocknr<0)
968                         return -1;
969                 READ_HEADER(str);
970                 memset(block,0xff,sizeof(block));
971                 sbd[0]=STORAGE_CHAINENTRY_ENDOFCHAIN;
972                 if (!STORAGE_put_big_block(str,bigblocknr,block))
973                         return -1;
974                 if (lastbigblocknr==-1) {
975                         sth.sbd_startblock = bigblocknr;
976                         if (!STORAGE_put_big_block(str,-1,(LPBYTE)&sth)) /* need to write it */
977                                 return -1;
978                 } else {
979                         if (!STORAGE_set_big_chain(str,lastbigblocknr,bigblocknr))
980                                 return -1;
981                 }
982                 if (!STORAGE_set_big_chain(str,bigblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
983                         return -1;
984                 newblocknr = curblock*128;
985         }
986         /* allocate enough big blocks for storing the allocated small block */
987         if (!STORAGE_get_root_pps_entry(str,&root))
988                 return -1;
989         if (root.pps_sb==-1)
990                 lastbigblocknr  = -1;
991         else
992                 lastbigblocknr  = STORAGE_get_nth_next_big_blocknr(str,root.pps_sb,(root.pps_size-1)/BIGSIZE);
993         while (root.pps_size < (newblocknr*SMALLSIZE+SMALLSIZE-1)) {
994                 /* we need to allocate more stuff */
995                 bigblocknr = STORAGE_get_free_big_blocknr(str);
996                 if (bigblocknr<0)
997                         return -1;
998                 READ_HEADER(str);
999                 if (root.pps_sb==-1) {
1000                         root.pps_sb      = bigblocknr;
1001                         root.pps_size   += BIGSIZE;
1002                 } else {
1003                         if (!STORAGE_set_big_chain(str,lastbigblocknr,bigblocknr))
1004                                 return -1;
1005                         root.pps_size   += BIGSIZE;
1006                 }
1007                 lastbigblocknr = bigblocknr;
1008         }
1009         if (!STORAGE_set_big_chain(str,lastbigblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1010                 return -1;
1011         if (!STORAGE_put_pps_entry(str,0,&root))
1012                 return -1;
1013         return newblocknr;
1014 }
1015
1016 /******************************************************************************
1017  *              STORAGE_get_free_pps_entry      [Internal]
1018  */
1019 static int
1020 STORAGE_get_free_pps_entry(stream_access16*str) {
1021         int     blocknr, i, curblock, lastblocknr=-1;
1022         BYTE    block[BIGSIZE];
1023         struct storage_pps_entry *stde = (struct storage_pps_entry*)block;
1024         struct storage_header sth;
1025
1026         READ_HEADER(str);
1027         blocknr = sth.root_startblock;
1028         assert(blocknr>=0);
1029         curblock=0;
1030         while (blocknr>=0) {
1031                 if (!STORAGE_get_big_block(str,blocknr,block))
1032                         return -1;
1033                 for (i=0;i<4;i++)
1034                         if (stde[i].pps_sizeofname==0) /* free */
1035                                 return curblock*4+i;
1036                 lastblocknr = blocknr;
1037                 blocknr = STORAGE_get_next_big_blocknr(str,blocknr);
1038                 curblock++;
1039         }
1040         assert(blocknr==STORAGE_CHAINENTRY_ENDOFCHAIN);
1041         blocknr = STORAGE_get_free_big_blocknr(str);
1042         /* sth invalidated */
1043         if (blocknr<0)
1044                 return -1;
1045
1046         if (!STORAGE_set_big_chain(str,lastblocknr,blocknr))
1047                 return -1;
1048         if (!STORAGE_set_big_chain(str,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1049                 return -1;
1050         memset(block,0,sizeof(block));
1051         STORAGE_put_big_block(str,blocknr,block);
1052         return curblock*4;
1053 }
1054
1055 /* --- IStream16 implementation */
1056
1057 typedef struct
1058 {
1059         /* IUnknown fields */
1060         const IStream16Vtbl            *lpVtbl;
1061         LONG                            ref;
1062         /* IStream16 fields */
1063         SEGPTR                          thisptr; /* pointer to this struct as segmented */
1064         struct storage_pps_entry        stde;
1065         int                             ppsent;
1066         ULARGE_INTEGER                  offset;
1067         stream_access16                 str;
1068 } IStream16Impl;
1069
1070 /******************************************************************************
1071  *              IStream16_QueryInterface        [STORAGE.518]
1072  */
1073 HRESULT CDECL IStream16_fnQueryInterface(
1074         IStream16* iface,REFIID refiid,LPVOID *obj
1075 ) {
1076         IStream16Impl *This = (IStream16Impl *)iface;
1077         TRACE_(relay)("(%p)->(%s,%p)\n",This,debugstr_guid(refiid),obj);
1078         if (!memcmp(&IID_IUnknown,refiid,sizeof(IID_IUnknown))) {
1079                 *obj = This;
1080                 return 0;
1081         }
1082         return OLE_E_ENUM_NOMORE;
1083
1084 }
1085
1086 /******************************************************************************
1087  * IStream16_AddRef [STORAGE.519]
1088  */
1089 ULONG CDECL IStream16_fnAddRef(IStream16* iface) {
1090         IStream16Impl *This = (IStream16Impl *)iface;
1091         return InterlockedIncrement(&This->ref);
1092 }
1093
1094 static void
1095 _ilockbytes16_addref(SEGPTR lockbytes) {
1096     DWORD args[1];
1097     HRESULT hres;
1098     
1099     args[0] = (DWORD)lockbytes; /* iface */
1100     if (!WOWCallback16Ex(
1101         (DWORD)((const ILockBytes16Vtbl*)MapSL(
1102                     (SEGPTR)((LPLOCKBYTES16)MapSL(lockbytes))->lpVtbl)
1103         )->AddRef,
1104         WCB16_PASCAL,
1105         1*sizeof(DWORD),
1106         (LPVOID)args,
1107         (LPDWORD)&hres
1108     ))
1109         ERR("CallTo16 ILockBytes16::AddRef() failed, hres %x\n",hres);
1110 }
1111
1112 static void
1113 _ilockbytes16_release(SEGPTR lockbytes) {
1114     DWORD args[1];
1115     HRESULT hres;
1116     
1117     args[0] = (DWORD)lockbytes; /* iface */
1118     if (!WOWCallback16Ex(
1119         (DWORD)((const ILockBytes16Vtbl*)MapSL(
1120                     (SEGPTR)((LPLOCKBYTES16)MapSL(lockbytes))->lpVtbl)
1121         )->Release,
1122         WCB16_PASCAL,
1123         1*sizeof(DWORD),
1124         (LPVOID)args,
1125         (LPDWORD)&hres
1126     ))
1127         ERR("CallTo16 ILockBytes16::Release() failed, hres %x\n",hres);
1128 }
1129
1130 static void
1131 _ilockbytes16_flush(SEGPTR lockbytes) {
1132     DWORD args[1];
1133     HRESULT hres;
1134     
1135     args[0] = (DWORD)lockbytes; /* iface */
1136     if (!WOWCallback16Ex(
1137         (DWORD)((const ILockBytes16Vtbl*)MapSL(
1138                     (SEGPTR)((LPLOCKBYTES16)MapSL(lockbytes))->lpVtbl)
1139         )->Flush,
1140         WCB16_PASCAL,
1141         1*sizeof(DWORD),
1142         (LPVOID)args,
1143         (LPDWORD)&hres
1144     ))
1145         ERR("CallTo16 ILockBytes16::Flush() failed, hres %x\n",hres);
1146 }
1147
1148 /******************************************************************************
1149  * IStream16_Release [STORAGE.520]
1150  */
1151 ULONG CDECL IStream16_fnRelease(IStream16* iface) {
1152         IStream16Impl *This = (IStream16Impl *)iface;
1153         ULONG ref;
1154
1155         if (This->str.hf)
1156             FlushFileBuffers(This->str.hf);
1157         else
1158             _ilockbytes16_flush(This->str.lockbytes);
1159         ref = InterlockedDecrement(&This->ref);
1160         if (ref)
1161             return ref;
1162
1163         if (This->str.hf)
1164             CloseHandle(This->str.hf);
1165         else
1166             _ilockbytes16_release(This->str.lockbytes);
1167         UnMapLS( This->thisptr );
1168         HeapFree( GetProcessHeap(), 0, This );
1169         return 0;
1170 }
1171
1172 /******************************************************************************
1173  *              IStream16_Seek  [STORAGE.523]
1174  *
1175  * FIXME
1176  *    Does not handle 64 bits
1177  */
1178 HRESULT CDECL IStream16_fnSeek(
1179         IStream16* iface,LARGE_INTEGER offset,DWORD whence,ULARGE_INTEGER *newpos
1180 ) {
1181         IStream16Impl *This = (IStream16Impl *)iface;
1182         TRACE_(relay)("(%p)->([%d.%d],%d,%p)\n",This,offset.u.HighPart,offset.u.LowPart,whence,newpos);
1183
1184         switch (whence) {
1185         /* unix SEEK_xx should be the same as win95 ones */
1186         case SEEK_SET:
1187                 /* offset must be ==0 (<0 is invalid, and >0 cannot be handled
1188                  * right now.
1189                  */
1190                 assert(offset.u.HighPart==0);
1191                 This->offset.u.HighPart = offset.u.HighPart;
1192                 This->offset.u.LowPart = offset.u.LowPart;
1193                 break;
1194         case SEEK_CUR:
1195                 if (offset.u.HighPart < 0) {
1196                         /* FIXME: is this negation correct ? */
1197                         offset.u.HighPart = -offset.u.HighPart;
1198                         offset.u.LowPart = (0xffffffff ^ offset.u.LowPart)+1;
1199
1200                         assert(offset.u.HighPart==0);
1201                         assert(This->offset.u.LowPart >= offset.u.LowPart);
1202                         This->offset.u.LowPart -= offset.u.LowPart;
1203                 } else {
1204                         assert(offset.u.HighPart==0);
1205                         This->offset.u.LowPart+= offset.u.LowPart;
1206                 }
1207                 break;
1208         case SEEK_END:
1209                 assert(offset.u.HighPart==0);
1210                 This->offset.u.LowPart = This->stde.pps_size-offset.u.LowPart;
1211                 break;
1212         }
1213         if (This->offset.u.LowPart>This->stde.pps_size)
1214                 This->offset.u.LowPart=This->stde.pps_size;
1215         if (newpos) *newpos = This->offset;
1216         return S_OK;
1217 }
1218
1219 /******************************************************************************
1220  *              IStream16_Read  [STORAGE.521]
1221  */
1222 HRESULT CDECL IStream16_fnRead(
1223         IStream16* iface,void  *pv,ULONG cb,ULONG  *pcbRead
1224 ) {
1225         IStream16Impl *This = (IStream16Impl *)iface;
1226         BYTE    block[BIGSIZE];
1227         ULONG   *bytesread=pcbRead,xxread;
1228         int     blocknr;
1229         LPBYTE  pbv = pv;
1230
1231         TRACE_(relay)("(%p)->(%p,%d,%p)\n",This,pv,cb,pcbRead);
1232         if (!pcbRead) bytesread=&xxread;
1233         *bytesread = 0;
1234
1235         if (cb>This->stde.pps_size-This->offset.u.LowPart)
1236                 cb=This->stde.pps_size-This->offset.u.LowPart;
1237         if (This->stde.pps_size < 0x1000) {
1238                 /* use small block reader */
1239                 blocknr = STORAGE_get_nth_next_small_blocknr(&This->str,This->stde.pps_sb,This->offset.u.LowPart/SMALLSIZE);
1240                 while (cb) {
1241                         unsigned int cc;
1242
1243                         if (!STORAGE_get_small_block(&This->str,blocknr,block)) {
1244                            WARN("small block read failed!!!\n");
1245                                 return E_FAIL;
1246                         }
1247                         cc = cb;
1248                         if (cc>SMALLSIZE-(This->offset.u.LowPart&(SMALLSIZE-1)))
1249                                 cc=SMALLSIZE-(This->offset.u.LowPart&(SMALLSIZE-1));
1250                         memcpy(pbv,block+(This->offset.u.LowPart&(SMALLSIZE-1)),cc);
1251                         This->offset.u.LowPart+=cc;
1252                         pbv+=cc;
1253                         *bytesread+=cc;
1254                         cb-=cc;
1255                         blocknr = STORAGE_get_next_small_blocknr(&This->str,blocknr);
1256                 }
1257         } else {
1258                 /* use big block reader */
1259                 blocknr = STORAGE_get_nth_next_big_blocknr(&This->str,This->stde.pps_sb,This->offset.u.LowPart/BIGSIZE);
1260                 while (cb) {
1261                         unsigned int cc;
1262
1263                         if (!STORAGE_get_big_block(&This->str,blocknr,block)) {
1264                                 WARN("big block read failed!!!\n");
1265                                 return E_FAIL;
1266                         }
1267                         cc = cb;
1268                         if (cc>BIGSIZE-(This->offset.u.LowPart&(BIGSIZE-1)))
1269                                 cc=BIGSIZE-(This->offset.u.LowPart&(BIGSIZE-1));
1270                         memcpy(pbv,block+(This->offset.u.LowPart&(BIGSIZE-1)),cc);
1271                         This->offset.u.LowPart+=cc;
1272                         pbv+=cc;
1273                         *bytesread+=cc;
1274                         cb-=cc;
1275                         blocknr=STORAGE_get_next_big_blocknr(&This->str,blocknr);
1276                 }
1277         }
1278         return S_OK;
1279 }
1280
1281 /******************************************************************************
1282  *              IStream16_Write [STORAGE.522]
1283  */
1284 HRESULT CDECL IStream16_fnWrite(
1285         IStream16* iface,const void *pv,ULONG cb,ULONG *pcbWrite
1286 ) {
1287         IStream16Impl *This = (IStream16Impl *)iface;
1288         BYTE    block[BIGSIZE];
1289         ULONG   *byteswritten=pcbWrite,xxwritten;
1290         int     oldsize,newsize,i,curoffset=0,lastblocknr,blocknr,cc;
1291         const BYTE*     pbv = (const BYTE*)pv;
1292
1293         if (!pcbWrite) byteswritten=&xxwritten;
1294         *byteswritten = 0;
1295
1296         TRACE_(relay)("(%p)->(%p,%d,%p)\n",This,pv,cb,pcbWrite);
1297         /* do we need to junk some blocks? */
1298         newsize = This->offset.u.LowPart+cb;
1299         oldsize = This->stde.pps_size;
1300         if (newsize < oldsize) {
1301                 if (oldsize < 0x1000) {
1302                         /* only small blocks */
1303                         blocknr=STORAGE_get_nth_next_small_blocknr(&This->str,This->stde.pps_sb,newsize/SMALLSIZE);
1304
1305                         assert(blocknr>=0);
1306
1307                         /* will set the rest of the chain to 'free' */
1308                         if (!STORAGE_set_small_chain(&This->str,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1309                                 return E_FAIL;
1310                 } else {
1311                         if (newsize >= 0x1000) {
1312                                 blocknr=STORAGE_get_nth_next_big_blocknr(&This->str,This->stde.pps_sb,newsize/BIGSIZE);
1313                                 assert(blocknr>=0);
1314
1315                                 /* will set the rest of the chain to 'free' */
1316                                 if (!STORAGE_set_big_chain(&This->str,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1317                                         return E_FAIL;
1318                         } else {
1319                                 /* Migrate large blocks to small blocks
1320                                  * (we just migrate newsize bytes)
1321                                  */
1322                                 LPBYTE  curdata,data = HeapAlloc(GetProcessHeap(),0,newsize+BIGSIZE);
1323                                 HRESULT r = E_FAIL;
1324
1325                                 cc      = newsize;
1326                                 blocknr = This->stde.pps_sb;
1327                                 curdata = data;
1328                                 while (cc>0) {
1329                                         if (!STORAGE_get_big_block(&This->str,blocknr,curdata)) {
1330                                                 HeapFree(GetProcessHeap(),0,data);
1331                                                 return E_FAIL;
1332                                         }
1333                                         curdata += BIGSIZE;
1334                                         cc      -= BIGSIZE;
1335                                         blocknr  = STORAGE_get_next_big_blocknr(&This->str,blocknr);
1336                                 }
1337                                 /* frees complete chain for this stream */
1338                                 if (!STORAGE_set_big_chain(&This->str,This->stde.pps_sb,STORAGE_CHAINENTRY_FREE))
1339                                         goto err;
1340                                 curdata = data;
1341                                 blocknr = This->stde.pps_sb = STORAGE_get_free_small_blocknr(&This->str);
1342                                 if (blocknr<0)
1343                                         goto err;
1344                                 cc      = newsize;
1345                                 while (cc>0) {
1346                                         if (!STORAGE_put_small_block(&This->str,blocknr,curdata))
1347                                                 goto err;
1348                                         cc      -= SMALLSIZE;
1349                                         if (cc<=0) {
1350                                                 if (!STORAGE_set_small_chain(&This->str,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1351                                                         goto err;
1352                                                 break;
1353                                         } else {
1354                                                 int newblocknr = STORAGE_get_free_small_blocknr(&This->str);
1355                                                 if (newblocknr<0)
1356                                                         goto err;
1357                                                 if (!STORAGE_set_small_chain(&This->str,blocknr,newblocknr))
1358                                                         goto err;
1359                                                 blocknr = newblocknr;
1360                                         }
1361                                         curdata += SMALLSIZE;
1362                                 }
1363                                 r = S_OK;
1364                         err:
1365                                 HeapFree(GetProcessHeap(),0,data);
1366                                 if(r != S_OK)
1367                                         return r;
1368                         }
1369                 }
1370                 This->stde.pps_size = newsize;
1371         }
1372
1373         if (newsize > oldsize) {
1374                 if (oldsize >= 0x1000) {
1375                         /* should return the block right before the 'endofchain' */
1376                         blocknr = STORAGE_get_nth_next_big_blocknr(&This->str,This->stde.pps_sb,This->stde.pps_size/BIGSIZE);
1377                         assert(blocknr>=0);
1378                         lastblocknr     = blocknr;
1379                         for (i=oldsize/BIGSIZE;i<newsize/BIGSIZE;i++) {
1380                                 blocknr = STORAGE_get_free_big_blocknr(&This->str);
1381                                 if (blocknr<0)
1382                                         return E_FAIL;
1383                                 if (!STORAGE_set_big_chain(&This->str,lastblocknr,blocknr))
1384                                         return E_FAIL;
1385                                 lastblocknr = blocknr;
1386                         }
1387                         if (!STORAGE_set_big_chain(&This->str,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1388                                 return E_FAIL;
1389                 } else {
1390                         if (newsize < 0x1000) {
1391                                 /* find startblock */
1392                                 if (!oldsize)
1393                                         This->stde.pps_sb = blocknr = STORAGE_get_free_small_blocknr(&This->str);
1394                                 else
1395                                         blocknr = STORAGE_get_nth_next_small_blocknr(&This->str,This->stde.pps_sb,This->stde.pps_size/SMALLSIZE);
1396                                 if (blocknr<0)
1397                                         return E_FAIL;
1398
1399                                 /* allocate required new small blocks */
1400                                 lastblocknr = blocknr;
1401                                 for (i=oldsize/SMALLSIZE;i<newsize/SMALLSIZE;i++) {
1402                                         blocknr = STORAGE_get_free_small_blocknr(&This->str);
1403                                         if (blocknr<0)
1404                                                 return E_FAIL;
1405                                         if (!STORAGE_set_small_chain(&This->str,lastblocknr,blocknr))
1406                                                 return E_FAIL;
1407                                         lastblocknr = blocknr;
1408                                 }
1409                                 /* and terminate the chain */
1410                                 if (!STORAGE_set_small_chain(&This->str,lastblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1411                                         return E_FAIL;
1412                         } else {
1413                                 if (!oldsize) {
1414                                         /* no single block allocated yet */
1415                                         blocknr=STORAGE_get_free_big_blocknr(&This->str);
1416                                         if (blocknr<0)
1417                                                 return E_FAIL;
1418                                         This->stde.pps_sb = blocknr;
1419                                 } else {
1420                                         /* Migrate small blocks to big blocks */
1421                                         LPBYTE  curdata,data = HeapAlloc(GetProcessHeap(),0,oldsize+BIGSIZE);
1422                                         HRESULT r = E_FAIL;
1423
1424                                         cc      = oldsize;
1425                                         blocknr = This->stde.pps_sb;
1426                                         curdata = data;
1427                                         /* slurp in */
1428                                         while (cc>0) {
1429                                                 if (!STORAGE_get_small_block(&This->str,blocknr,curdata))
1430                                                         goto err2;
1431                                                 curdata += SMALLSIZE;
1432                                                 cc      -= SMALLSIZE;
1433                                                 blocknr  = STORAGE_get_next_small_blocknr(&This->str,blocknr);
1434                                         }
1435                                         /* free small block chain */
1436                                         if (!STORAGE_set_small_chain(&This->str,This->stde.pps_sb,STORAGE_CHAINENTRY_FREE))
1437                                                 goto err2;
1438                                         curdata = data;
1439                                         blocknr = This->stde.pps_sb = STORAGE_get_free_big_blocknr(&This->str);
1440                                         if (blocknr<0)
1441                                                 goto err2;
1442                                         /* put the data into the big blocks */
1443                                         cc      = This->stde.pps_size;
1444                                         while (cc>0) {
1445                                                 if (!STORAGE_put_big_block(&This->str,blocknr,curdata))
1446                                                         goto err2;
1447                                                 cc      -= BIGSIZE;
1448                                                 if (cc<=0) {
1449                                                         if (!STORAGE_set_big_chain(&This->str,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1450                                                                 goto err2;
1451                                                         break;
1452                                                 } else {
1453                                                         int newblocknr = STORAGE_get_free_big_blocknr(&This->str);
1454                                                         if (newblocknr<0)
1455                                                                 goto err2;
1456                                                         if (!STORAGE_set_big_chain(&This->str,blocknr,newblocknr))
1457                                                                 goto err2;
1458                                                         blocknr = newblocknr;
1459                                                 }
1460                                                 curdata += BIGSIZE;
1461                                         }
1462                                         r = S_OK;
1463                                 err2:
1464                                         HeapFree(GetProcessHeap(),0,data);
1465                                         if(r != S_OK)
1466                                                 return r;
1467                                 }
1468                                 /* generate big blocks to fit the new data */
1469                                 lastblocknr     = blocknr;
1470                                 for (i=oldsize/BIGSIZE;i<newsize/BIGSIZE;i++) {
1471                                         blocknr = STORAGE_get_free_big_blocknr(&This->str);
1472                                         if (blocknr<0)
1473                                                 return E_FAIL;
1474                                         if (!STORAGE_set_big_chain(&This->str,lastblocknr,blocknr))
1475                                                 return E_FAIL;
1476                                         lastblocknr = blocknr;
1477                                 }
1478                                 /* terminate chain */
1479                                 if (!STORAGE_set_big_chain(&This->str,lastblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1480                                         return E_FAIL;
1481                         }
1482                 }
1483                 This->stde.pps_size = newsize;
1484         }
1485
1486         /* There are just some cases where we didn't modify it, we write it out
1487          * everytime
1488          */
1489         if (!STORAGE_put_pps_entry(&This->str,This->ppsent,&(This->stde)))
1490                 return E_FAIL;
1491
1492         /* finally the write pass */
1493         if (This->stde.pps_size < 0x1000) {
1494                 blocknr = STORAGE_get_nth_next_small_blocknr(&This->str,This->stde.pps_sb,This->offset.u.LowPart/SMALLSIZE);
1495                 assert(blocknr>=0);
1496                 while (cb>0) {
1497                         /* we ensured that it is allocated above */
1498                         assert(blocknr>=0);
1499                         /* Read old block everytime, since we can have
1500                          * overlapping data at START and END of the write
1501                          */
1502                         if (!STORAGE_get_small_block(&This->str,blocknr,block))
1503                                 return E_FAIL;
1504
1505                         cc = SMALLSIZE-(This->offset.u.LowPart&(SMALLSIZE-1));
1506                         if (cc>cb)
1507                                 cc=cb;
1508                         memcpy( ((LPBYTE)block)+(This->offset.u.LowPart&(SMALLSIZE-1)),
1509                                 pbv+curoffset,
1510                                 cc
1511                         );
1512                         if (!STORAGE_put_small_block(&This->str,blocknr,block))
1513                                 return E_FAIL;
1514                         cb                      -= cc;
1515                         curoffset               += cc;
1516                         pbv                     += cc;
1517                         This->offset.u.LowPart  += cc;
1518                         *byteswritten           += cc;
1519                         blocknr = STORAGE_get_next_small_blocknr(&This->str,blocknr);
1520                 }
1521         } else {
1522                 blocknr = STORAGE_get_nth_next_big_blocknr(&This->str,This->stde.pps_sb,This->offset.u.LowPart/BIGSIZE);
1523                 assert(blocknr>=0);
1524                 while (cb>0) {
1525                         /* we ensured that it is allocated above, so it better is */
1526                         assert(blocknr>=0);
1527                         /* read old block everytime, since we can have
1528                          * overlapping data at START and END of the write
1529                          */
1530                         if (!STORAGE_get_big_block(&This->str,blocknr,block))
1531                                 return E_FAIL;
1532
1533                         cc = BIGSIZE-(This->offset.u.LowPart&(BIGSIZE-1));
1534                         if (cc>cb)
1535                                 cc=cb;
1536                         memcpy( ((LPBYTE)block)+(This->offset.u.LowPart&(BIGSIZE-1)),
1537                                 pbv+curoffset,
1538                                 cc
1539                         );
1540                         if (!STORAGE_put_big_block(&This->str,blocknr,block))
1541                                 return E_FAIL;
1542                         cb                      -= cc;
1543                         curoffset               += cc;
1544                         pbv                     += cc;
1545                         This->offset.u.LowPart  += cc;
1546                         *byteswritten           += cc;
1547                         blocknr = STORAGE_get_next_big_blocknr(&This->str,blocknr);
1548                 }
1549         }
1550         return S_OK;
1551 }
1552
1553 /******************************************************************************
1554  *              _create_istream16       [Internal]
1555  */
1556 static void _create_istream16(LPSTREAM16 *str) {
1557         IStream16Impl*  lpst;
1558
1559         if (!strvt16.QueryInterface) {
1560                 HMODULE16       wp = GetModuleHandle16("STORAGE");
1561                 if (wp>=32) {
1562                   /* FIXME: what is This GetProcAddress16. Should the name be IStream16_QueryInterface of IStream16_fnQueryInterface */
1563 #define VTENT(xfn)  strvt16.xfn = (void*)GetProcAddress16(wp,"IStream16_"#xfn);assert(strvt16.xfn)
1564                         VTENT(QueryInterface);
1565                         VTENT(AddRef);
1566                         VTENT(Release);
1567                         VTENT(Read);
1568                         VTENT(Write);
1569                         VTENT(Seek);
1570                         VTENT(SetSize);
1571                         VTENT(CopyTo);
1572                         VTENT(Commit);
1573                         VTENT(Revert);
1574                         VTENT(LockRegion);
1575                         VTENT(UnlockRegion);
1576                         VTENT(Stat);
1577                         VTENT(Clone);
1578 #undef VTENT
1579                         segstrvt16 = (const IStream16Vtbl*)MapLS( &strvt16 );
1580                 } else {
1581 #define VTENT(xfn) strvt16.xfn = IStream16_fn##xfn;
1582                         VTENT(QueryInterface);
1583                         VTENT(AddRef);
1584                         VTENT(Release);
1585                         VTENT(Read);
1586                         VTENT(Write);
1587                         VTENT(Seek);
1588         /*
1589                         VTENT(CopyTo);
1590                         VTENT(Commit);
1591                         VTENT(SetSize);
1592                         VTENT(Revert);
1593                         VTENT(LockRegion);
1594                         VTENT(UnlockRegion);
1595                         VTENT(Stat);
1596                         VTENT(Clone);
1597         */
1598 #undef VTENT
1599                         segstrvt16 = &strvt16;
1600                 }
1601         }
1602         lpst = HeapAlloc( GetProcessHeap(), 0, sizeof(*lpst) );
1603         lpst->lpVtbl    = segstrvt16;
1604         lpst->ref       = 1;
1605         lpst->thisptr   = MapLS( lpst );
1606         lpst->str.hf    = NULL;
1607         lpst->str.lockbytes     = 0;
1608         *str = (void*)lpst->thisptr;
1609 }
1610
1611
1612 /* --- IStream32 implementation */
1613
1614 typedef struct
1615 {
1616         /* IUnknown fields */
1617         const IStreamVtbl              *lpVtbl;
1618         LONG                            ref;
1619         /* IStream32 fields */
1620         struct storage_pps_entry        stde;
1621         int                             ppsent;
1622         HANDLE                          hf;
1623         ULARGE_INTEGER                  offset;
1624 } IStream32Impl;
1625
1626 /*****************************************************************************
1627  *              IStream32_QueryInterface        [VTABLE]
1628  */
1629 HRESULT WINAPI IStream_fnQueryInterface(
1630         IStream* iface,REFIID refiid,LPVOID *obj
1631 ) {
1632         IStream32Impl *This = (IStream32Impl *)iface;
1633
1634         TRACE_(relay)("(%p)->(%s,%p)\n",This,debugstr_guid(refiid),obj);
1635         if (!memcmp(&IID_IUnknown,refiid,sizeof(IID_IUnknown))) {
1636                 *obj = This;
1637                 return 0;
1638         }
1639         return OLE_E_ENUM_NOMORE;
1640
1641 }
1642
1643 /******************************************************************************
1644  * IStream32_AddRef [VTABLE]
1645  */
1646 ULONG WINAPI IStream_fnAddRef(IStream* iface) {
1647         IStream32Impl *This = (IStream32Impl *)iface;
1648         return InterlockedIncrement(&This->ref);
1649 }
1650
1651 /******************************************************************************
1652  * IStream32_Release [VTABLE]
1653  */
1654 ULONG WINAPI IStream_fnRelease(IStream* iface) {
1655         IStream32Impl *This = (IStream32Impl *)iface;
1656         ULONG ref;
1657         FlushFileBuffers(This->hf);
1658         ref = InterlockedDecrement(&This->ref);
1659         if (!ref) {
1660                 CloseHandle(This->hf);
1661                 HeapFree( GetProcessHeap(), 0, This );
1662         }
1663         return ref;
1664 }
1665
1666 /******************************************************************************
1667  *              IStorage16_QueryInterface       [STORAGE.500]
1668  */
1669 HRESULT CDECL IStorage16_fnQueryInterface(
1670         IStorage16* iface,REFIID refiid,LPVOID *obj
1671 ) {
1672         IStorage16Impl *This = (IStorage16Impl *)iface;
1673
1674         TRACE_(relay)("(%p)->(%s,%p)\n",This,debugstr_guid(refiid),obj);
1675
1676         if (!memcmp(&IID_IUnknown,refiid,sizeof(IID_IUnknown))) {
1677                 *obj = This;
1678                 return 0;
1679         }
1680         return OLE_E_ENUM_NOMORE;
1681 }
1682
1683 /******************************************************************************
1684  * IStorage16_AddRef [STORAGE.501]
1685  */
1686 ULONG CDECL IStorage16_fnAddRef(IStorage16* iface) {
1687         IStorage16Impl *This = (IStorage16Impl *)iface;
1688         return InterlockedIncrement(&This->ref);
1689 }
1690
1691 /******************************************************************************
1692  * IStorage16_Release [STORAGE.502]
1693  */
1694 ULONG CDECL IStorage16_fnRelease(IStorage16* iface) {
1695         IStorage16Impl *This = (IStorage16Impl *)iface;
1696         ULONG ref;
1697         ref = InterlockedDecrement(&This->ref);
1698         if (!ref)
1699         {
1700             UnMapLS( This->thisptr );
1701             HeapFree( GetProcessHeap(), 0, This );
1702         }
1703         return ref;
1704 }
1705
1706 /******************************************************************************
1707  * IStorage16_Stat [STORAGE.517]
1708  */
1709 HRESULT CDECL IStorage16_fnStat(
1710         LPSTORAGE16 iface,STATSTG16 *pstatstg, DWORD grfStatFlag
1711 ) {
1712         IStorage16Impl *This = (IStorage16Impl *)iface;
1713         DWORD len = WideCharToMultiByte( CP_ACP, 0, This->stde.pps_rawname, -1, NULL, 0, NULL, NULL );
1714         LPSTR nameA = HeapAlloc( GetProcessHeap(), 0, len );
1715
1716         TRACE("(%p)->(%p,0x%08x)\n",
1717                 This,pstatstg,grfStatFlag
1718         );
1719         WideCharToMultiByte( CP_ACP, 0, This->stde.pps_rawname, -1, nameA, len, NULL, NULL );
1720         pstatstg->pwcsName=(LPOLESTR16)MapLS( nameA );
1721         pstatstg->type = This->stde.pps_type;
1722         pstatstg->cbSize.u.LowPart = This->stde.pps_size;
1723         pstatstg->mtime = This->stde.pps_ft1; /* FIXME */ /* why? */
1724         pstatstg->atime = This->stde.pps_ft2; /* FIXME */
1725         pstatstg->ctime = This->stde.pps_ft2; /* FIXME */
1726         pstatstg->grfMode       = 0; /* FIXME */
1727         pstatstg->grfLocksSupported = 0; /* FIXME */
1728         pstatstg->clsid         = This->stde.pps_guid;
1729         pstatstg->grfStateBits  = 0; /* FIXME */
1730         pstatstg->reserved      = 0;
1731         return S_OK;
1732 }
1733
1734 /******************************************************************************
1735  *              IStorage16_Commit       [STORAGE.509]
1736  */
1737 HRESULT CDECL IStorage16_fnCommit(
1738         LPSTORAGE16 iface,DWORD commitflags
1739 ) {
1740         IStorage16Impl *This = (IStorage16Impl *)iface;
1741         FIXME("(%p)->(0x%08x),STUB!\n",
1742                 This,commitflags
1743         );
1744         return S_OK;
1745 }
1746
1747 /******************************************************************************
1748  * IStorage16_CopyTo [STORAGE.507]
1749  */
1750 HRESULT CDECL IStorage16_fnCopyTo(LPSTORAGE16 iface,DWORD ciidExclude,const IID *rgiidExclude,SNB16 SNB16Exclude,IStorage16 *pstgDest) {
1751         IStorage16Impl *This = (IStorage16Impl *)iface;
1752         FIXME("IStorage16(%p)->(0x%08x,%s,%p,%p),stub!\n",
1753                 This,ciidExclude,debugstr_guid(rgiidExclude),SNB16Exclude,pstgDest
1754         );
1755         return S_OK;
1756 }
1757
1758
1759 /******************************************************************************
1760  * IStorage16_CreateStorage [STORAGE.505]
1761  */
1762 HRESULT CDECL IStorage16_fnCreateStorage(
1763         LPSTORAGE16 iface,LPCOLESTR16 pwcsName,DWORD grfMode,DWORD dwStgFormat,DWORD reserved2, IStorage16 **ppstg
1764 ) {
1765         IStorage16Impl *This = (IStorage16Impl *)iface;
1766         IStorage16Impl* lpstg;
1767         int             ppsent,x;
1768         struct storage_pps_entry        stde;
1769         struct storage_header sth;
1770         BOOL ret;
1771         int      nPPSEntries;
1772
1773         READ_HEADER(&This->str);
1774         TRACE("(%p)->(%s,0x%08x,0x%08x,0x%08x,%p)\n",
1775                 This,pwcsName,grfMode,dwStgFormat,reserved2,ppstg
1776         );
1777         if (grfMode & STGM_TRANSACTED)
1778                 FIXME("We do not support transacted Compound Storage. Using direct mode.\n");
1779         _create_istorage16(ppstg);
1780         lpstg = MapSL((SEGPTR)*ppstg);
1781         if (This->str.hf) {
1782             DuplicateHandle( GetCurrentProcess(), This->str.hf, GetCurrentProcess(),
1783                              &lpstg->str.hf, 0, TRUE, DUPLICATE_SAME_ACCESS );
1784         } else {
1785             lpstg->str.lockbytes = This->str.lockbytes;
1786             _ilockbytes16_addref(This->str.lockbytes);
1787         }
1788
1789         ppsent=STORAGE_get_free_pps_entry(&lpstg->str);
1790         if (ppsent<0)
1791                 return E_FAIL;
1792         stde=This->stde;
1793         if (stde.pps_dir==-1) {
1794                 stde.pps_dir = ppsent;
1795                 x = This->ppsent;
1796         } else {
1797                 FIXME(" use prev chain too ?\n");
1798                 x=stde.pps_dir;
1799                 if (1!=STORAGE_get_pps_entry(&lpstg->str,x,&stde))
1800                         return E_FAIL;
1801                 while (stde.pps_next!=-1) {
1802                         x=stde.pps_next;
1803                         if (1!=STORAGE_get_pps_entry(&lpstg->str,x,&stde))
1804                                 return E_FAIL;
1805                 }
1806                 stde.pps_next = ppsent;
1807         }
1808         ret = STORAGE_put_pps_entry(&lpstg->str,x,&stde);
1809         assert(ret);
1810         nPPSEntries = STORAGE_get_pps_entry(&lpstg->str,ppsent,&(lpstg->stde));
1811         assert(nPPSEntries == 1);
1812         MultiByteToWideChar( CP_ACP, 0, pwcsName, -1, lpstg->stde.pps_rawname,
1813                              sizeof(lpstg->stde.pps_rawname)/sizeof(WCHAR));
1814         lpstg->stde.pps_sizeofname = (strlenW(lpstg->stde.pps_rawname)+1)*sizeof(WCHAR);
1815         lpstg->stde.pps_next    = -1;
1816         lpstg->stde.pps_prev    = -1;
1817         lpstg->stde.pps_dir     = -1;
1818         lpstg->stde.pps_sb      = -1;
1819         lpstg->stde.pps_size    =  0;
1820         lpstg->stde.pps_type    =  1;
1821         lpstg->ppsent           = ppsent;
1822         /* FIXME: timestamps? */
1823         if (!STORAGE_put_pps_entry(&lpstg->str,ppsent,&(lpstg->stde)))
1824                 return E_FAIL;
1825         return S_OK;
1826 }
1827
1828 /******************************************************************************
1829  *              IStorage16_CreateStream [STORAGE.503]
1830  */
1831 HRESULT CDECL IStorage16_fnCreateStream(
1832         LPSTORAGE16 iface,LPCOLESTR16 pwcsName,DWORD grfMode,DWORD reserved1,DWORD reserved2, IStream16 **ppstm
1833 ) {
1834         IStorage16Impl *This = (IStorage16Impl *)iface;
1835         IStream16Impl*  lpstr;
1836         int             ppsent,x;
1837         struct storage_pps_entry        stde;
1838         BOOL ret;
1839         int      nPPSEntries;
1840
1841         TRACE("(%p)->(%s,0x%08x,0x%08x,0x%08x,%p)\n",
1842                 This,pwcsName,grfMode,reserved1,reserved2,ppstm
1843         );
1844         if (grfMode & STGM_TRANSACTED)
1845                 FIXME("We do not support transacted Compound Storage. Using direct mode.\n");
1846         _create_istream16(ppstm);
1847         lpstr = MapSL((SEGPTR)*ppstm);
1848         if (This->str.hf) {
1849             DuplicateHandle( GetCurrentProcess(), This->str.hf, GetCurrentProcess(),
1850                              &lpstr->str.hf, 0, TRUE, DUPLICATE_SAME_ACCESS );
1851         } else {
1852             lpstr->str.lockbytes = This->str.lockbytes;
1853             _ilockbytes16_addref(This->str.lockbytes);
1854         }
1855         lpstr->offset.u.LowPart = 0;
1856         lpstr->offset.u.HighPart= 0;
1857
1858         ppsent=STORAGE_get_free_pps_entry(&lpstr->str);
1859         if (ppsent<0)
1860                 return E_FAIL;
1861         stde=This->stde;
1862         if (stde.pps_next==-1)
1863                 x=This->ppsent;
1864         else
1865                 while (stde.pps_next!=-1) {
1866                         x=stde.pps_next;
1867                         if (1!=STORAGE_get_pps_entry(&lpstr->str,x,&stde))
1868                                 return E_FAIL;
1869                 }
1870         stde.pps_next = ppsent;
1871         ret = STORAGE_put_pps_entry(&lpstr->str,x,&stde);
1872         assert(ret);
1873         nPPSEntries = STORAGE_get_pps_entry(&lpstr->str,ppsent,&(lpstr->stde));
1874         assert(nPPSEntries == 1);
1875         MultiByteToWideChar( CP_ACP, 0, pwcsName, -1, lpstr->stde.pps_rawname,
1876                              sizeof(lpstr->stde.pps_rawname)/sizeof(WCHAR));
1877         lpstr->stde.pps_sizeofname = (strlenW(lpstr->stde.pps_rawname)+1) * sizeof(WCHAR);
1878         lpstr->stde.pps_next    = -1;
1879         lpstr->stde.pps_prev    = -1;
1880         lpstr->stde.pps_dir     = -1;
1881         lpstr->stde.pps_sb      = -1;
1882         lpstr->stde.pps_size    =  0;
1883         lpstr->stde.pps_type    =  2;
1884         lpstr->ppsent           = ppsent;
1885
1886         /* FIXME: timestamps? */
1887         if (!STORAGE_put_pps_entry(&lpstr->str,ppsent,&(lpstr->stde)))
1888                 return E_FAIL;
1889         return S_OK;
1890 }
1891
1892 /******************************************************************************
1893  *              IStorage16_OpenStorage  [STORAGE.506]
1894  */
1895 HRESULT CDECL IStorage16_fnOpenStorage(
1896         LPSTORAGE16 iface,LPCOLESTR16 pwcsName, IStorage16 *pstgPrio, DWORD grfMode, SNB16 snbExclude, DWORD reserved, IStorage16 **ppstg
1897 ) {
1898         IStorage16Impl *This = (IStorage16Impl *)iface;
1899         IStream16Impl*  lpstg;
1900         WCHAR           name[33];
1901         int             newpps;
1902
1903         TRACE("(%p)->(%s,%p,0x%08x,%p,0x%08x,%p)\n",
1904                 This,pwcsName,pstgPrio,grfMode,snbExclude,reserved,ppstg
1905         );
1906         if (grfMode & STGM_TRANSACTED)
1907                 FIXME("We do not support transacted Compound Storage. Using direct mode.\n");
1908         _create_istorage16(ppstg);
1909         lpstg = MapSL((SEGPTR)*ppstg);
1910         if (This->str.hf) {
1911             DuplicateHandle( GetCurrentProcess(), This->str.hf, GetCurrentProcess(),
1912                              &lpstg->str.hf, 0, TRUE, DUPLICATE_SAME_ACCESS );
1913         } else {
1914             lpstg->str.lockbytes = This->str.lockbytes;
1915             _ilockbytes16_addref(This->str.lockbytes);
1916         }
1917         MultiByteToWideChar( CP_ACP, 0, pwcsName, -1, name, sizeof(name)/sizeof(WCHAR));
1918         newpps = STORAGE_look_for_named_pps(&lpstg->str,This->stde.pps_dir,name);
1919         if (newpps==-1) {
1920                 IStream16_fnRelease((IStream16*)lpstg);
1921                 return E_FAIL;
1922         }
1923
1924         if (1!=STORAGE_get_pps_entry(&lpstg->str,newpps,&(lpstg->stde))) {
1925                 IStream16_fnRelease((IStream16*)lpstg);
1926                 return E_FAIL;
1927         }
1928         lpstg->ppsent           = newpps;
1929         return S_OK;
1930 }
1931
1932 /******************************************************************************
1933  * IStorage16_OpenStream [STORAGE.504]
1934  */
1935 HRESULT CDECL IStorage16_fnOpenStream(
1936         LPSTORAGE16 iface,LPCOLESTR16 pwcsName, void *reserved1, DWORD grfMode, DWORD reserved2, IStream16 **ppstm
1937 ) {
1938         IStorage16Impl *This = (IStorage16Impl *)iface;
1939         IStream16Impl*  lpstr;
1940         WCHAR           name[33];
1941         int             newpps;
1942
1943         TRACE("(%p)->(%s,%p,0x%08x,0x%08x,%p)\n",
1944                 This,pwcsName,reserved1,grfMode,reserved2,ppstm
1945         );
1946         if (grfMode & STGM_TRANSACTED)
1947                 FIXME("We do not support transacted Compound Storage. Using direct mode.\n");
1948         _create_istream16(ppstm);
1949         lpstr = MapSL((SEGPTR)*ppstm);
1950         if (This->str.hf) {
1951             DuplicateHandle( GetCurrentProcess(), This->str.hf, GetCurrentProcess(),
1952                              &lpstr->str.hf, 0, TRUE, DUPLICATE_SAME_ACCESS );
1953         } else {
1954             lpstr->str.lockbytes = This->str.lockbytes;
1955             _ilockbytes16_addref(This->str.lockbytes);
1956         }
1957         MultiByteToWideChar( CP_ACP, 0, pwcsName, -1, name, sizeof(name)/sizeof(WCHAR));
1958         newpps = STORAGE_look_for_named_pps(&lpstr->str,This->stde.pps_dir,name);
1959         if (newpps==-1) {
1960                 IStream16_fnRelease((IStream16*)lpstr);
1961                 return E_FAIL;
1962         }
1963
1964         if (1!=STORAGE_get_pps_entry(&lpstr->str,newpps,&(lpstr->stde))) {
1965                 IStream16_fnRelease((IStream16*)lpstr);
1966                 return E_FAIL;
1967         }
1968         lpstr->offset.u.LowPart         = 0;
1969         lpstr->offset.u.HighPart        = 0;
1970         lpstr->ppsent                   = newpps;
1971         return S_OK;
1972 }
1973
1974 /******************************************************************************
1975  * _create_istorage16 [INTERNAL]
1976  */
1977 static void _create_istorage16(LPSTORAGE16 *stg) {
1978         IStorage16Impl* lpst;
1979
1980         if (!stvt16.QueryInterface) {
1981                 HMODULE16       wp = GetModuleHandle16("STORAGE");
1982                 if (wp>=32) {
1983 #define VTENT(xfn)  stvt16.xfn = (void*)GetProcAddress16(wp,"IStorage16_"#xfn);
1984                         VTENT(QueryInterface)
1985                         VTENT(AddRef)
1986                         VTENT(Release)
1987                         VTENT(CreateStream)
1988                         VTENT(OpenStream)
1989                         VTENT(CreateStorage)
1990                         VTENT(OpenStorage)
1991                         VTENT(CopyTo)
1992                         VTENT(MoveElementTo)
1993                         VTENT(Commit)
1994                         VTENT(Revert)
1995                         VTENT(EnumElements)
1996                         VTENT(DestroyElement)
1997                         VTENT(RenameElement)
1998                         VTENT(SetElementTimes)
1999                         VTENT(SetClass)
2000                         VTENT(SetStateBits)
2001                         VTENT(Stat)
2002 #undef VTENT
2003                         segstvt16 = (const IStorage16Vtbl*)MapLS( &stvt16 );
2004                 } else {
2005 #define VTENT(xfn) stvt16.xfn = IStorage16_fn##xfn;
2006                         VTENT(QueryInterface)
2007                         VTENT(AddRef)
2008                         VTENT(Release)
2009                         VTENT(CreateStream)
2010                         VTENT(OpenStream)
2011                         VTENT(CreateStorage)
2012                         VTENT(OpenStorage)
2013                         VTENT(CopyTo)
2014                         VTENT(Commit)
2015         /*  not (yet) implemented ...
2016                         VTENT(MoveElementTo)
2017                         VTENT(Revert)
2018                         VTENT(EnumElements)
2019                         VTENT(DestroyElement)
2020                         VTENT(RenameElement)
2021                         VTENT(SetElementTimes)
2022                         VTENT(SetClass)
2023                         VTENT(SetStateBits)
2024                         VTENT(Stat)
2025         */
2026 #undef VTENT
2027                         segstvt16 = &stvt16;
2028                 }
2029         }
2030         lpst = HeapAlloc( GetProcessHeap(), 0, sizeof(*lpst) );
2031         lpst->lpVtbl    = segstvt16;
2032         lpst->str.hf    = NULL;
2033         lpst->str.lockbytes     = 0;
2034         lpst->ref       = 1;
2035         lpst->thisptr   = MapLS(lpst);
2036         *stg = (void*)lpst->thisptr;
2037 }
2038
2039 /******************************************************************************
2040  *      Storage API functions
2041  */
2042
2043 /******************************************************************************
2044  *              StgCreateDocFileA       [STORAGE.1]
2045  */
2046 HRESULT WINAPI StgCreateDocFile16(
2047         LPCOLESTR16 pwcsName,DWORD grfMode,DWORD reserved,IStorage16 **ppstgOpen
2048 ) {
2049         HANDLE          hf;
2050         int             i,ret;
2051         IStorage16Impl* lpstg;
2052         struct storage_pps_entry        stde;
2053
2054         TRACE("(%s,0x%08x,0x%08x,%p)\n",
2055                 pwcsName,grfMode,reserved,ppstgOpen
2056         );
2057         _create_istorage16(ppstgOpen);
2058         hf = CreateFileA(pwcsName,GENERIC_READ|GENERIC_WRITE,0,NULL,CREATE_NEW,0,0);
2059         if (hf==INVALID_HANDLE_VALUE) {
2060                 WARN("couldn't open file for storage:%d\n",GetLastError());
2061                 return E_FAIL;
2062         }
2063         lpstg = MapSL((SEGPTR)*ppstgOpen);
2064         lpstg->str.hf = hf;
2065         lpstg->str.lockbytes = 0;
2066         /* FIXME: check for existence before overwriting? */
2067         if (!STORAGE_init_storage(&lpstg->str)) {
2068                 CloseHandle(hf);
2069                 return E_FAIL;
2070         }
2071         i=0;ret=0;
2072         while (!ret) { /* neither 1 nor <0 */
2073                 ret=STORAGE_get_pps_entry(&lpstg->str,i,&stde);
2074                 if ((ret==1) && (stde.pps_type==5)) {
2075                         lpstg->stde     = stde;
2076                         lpstg->ppsent   = i;
2077                         break;
2078                 }
2079                 i++;
2080         }
2081         if (ret!=1) {
2082                 IStorage16_fnRelease((IStorage16*)lpstg); /* will remove it */
2083                 return E_FAIL;
2084         }
2085
2086         return S_OK;
2087 }
2088
2089 /******************************************************************************
2090  * StgIsStorageFile [STORAGE.5]
2091  */
2092 HRESULT WINAPI StgIsStorageFile16(LPCOLESTR16 fn) {
2093         UNICODE_STRING strW;
2094         HRESULT ret;
2095
2096         RtlCreateUnicodeStringFromAsciiz(&strW, fn);
2097         ret = StgIsStorageFile( strW.Buffer );
2098         RtlFreeUnicodeString( &strW );
2099
2100         return ret;
2101 }
2102
2103 /******************************************************************************
2104  * StgOpenStorage [STORAGE.3]
2105  */
2106 HRESULT WINAPI StgOpenStorage16(
2107         LPCOLESTR16 pwcsName,IStorage16 *pstgPriority,DWORD grfMode,
2108         SNB16 snbExclude,DWORD reserved, IStorage16 **ppstgOpen
2109 ) {
2110         HANDLE          hf;
2111         int             ret,i;
2112         IStorage16Impl* lpstg;
2113         struct storage_pps_entry        stde;
2114
2115         TRACE("(%s,%p,0x%08x,%p,%d,%p)\n",
2116               pwcsName,pstgPriority,grfMode,snbExclude,reserved,ppstgOpen
2117         );
2118         _create_istorage16(ppstgOpen);
2119         hf = CreateFileA(pwcsName,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
2120         if (hf==INVALID_HANDLE_VALUE) {
2121                 WARN("Couldn't open file for storage\n");
2122                 return E_FAIL;
2123         }
2124         lpstg = MapSL((SEGPTR)*ppstgOpen);
2125         lpstg->str.hf = hf;
2126
2127         i=0;ret=0;
2128         while (!ret) { /* neither 1 nor <0 */
2129                 ret=STORAGE_get_pps_entry(&lpstg->str,i,&stde);
2130                 if ((ret==1) && (stde.pps_type==5)) {
2131                         lpstg->stde=stde;
2132                         break;
2133                 }
2134                 i++;
2135         }
2136         if (ret!=1) {
2137                 IStorage16_fnRelease((IStorage16*)lpstg); /* will remove it */
2138                 return E_FAIL;
2139         }
2140         return S_OK;
2141
2142 }
2143
2144 /******************************************************************************
2145  *              StgIsStorageILockBytes        [STORAGE.6]
2146  *
2147  * Determines if the ILockBytes contains a storage object.
2148  */
2149 HRESULT WINAPI StgIsStorageILockBytes16(SEGPTR plkbyt)
2150 {
2151   DWORD args[6];
2152   HRESULT hres;
2153   HANDLE16 hsig;
2154   
2155   args[0] = (DWORD)plkbyt;      /* iface */
2156   args[1] = args[2] = 0;        /* ULARGE_INTEGER offset */
2157   args[3] = WOWGlobalAllocLock16( 0, 8, &hsig ); /* sig */
2158   args[4] = 8;
2159   args[5] = 0;
2160
2161   if (!WOWCallback16Ex(
2162       (DWORD)((const ILockBytes16Vtbl*)MapSL(
2163                   (SEGPTR)((LPLOCKBYTES16)MapSL(plkbyt))->lpVtbl)
2164       )->ReadAt,
2165       WCB16_PASCAL,
2166       6*sizeof(DWORD),
2167       (LPVOID)args,
2168       (LPDWORD)&hres
2169   )) {
2170       ERR("CallTo16 ILockBytes16::ReadAt() failed, hres %x\n",hres);
2171       return hres;
2172   }
2173   if (memcmp(MapSL(args[3]), STORAGE_magic, sizeof(STORAGE_magic)) == 0) {
2174     WOWGlobalUnlockFree16(args[3]);
2175     return S_OK;
2176   }
2177   WOWGlobalUnlockFree16(args[3]);
2178   return S_FALSE;
2179 }
2180
2181 /******************************************************************************
2182  *    StgOpenStorageOnILockBytes    [STORAGE.4]
2183  *
2184  * PARAMS
2185  *  plkbyt  FIXME: Should probably be an ILockBytes16 *.
2186  */
2187 HRESULT WINAPI StgOpenStorageOnILockBytes16(
2188         SEGPTR plkbyt,
2189         IStorage16 *pstgPriority,
2190         DWORD grfMode,
2191         SNB16 snbExclude,
2192         DWORD reserved,
2193         IStorage16 **ppstgOpen)
2194 {
2195         IStorage16Impl* lpstg;
2196         int i,ret;
2197         struct storage_pps_entry        stde;
2198
2199         FIXME("(%x, %p, 0x%08x, %d, %x, %p)\n", plkbyt, pstgPriority, grfMode, (int)snbExclude, reserved, ppstgOpen);
2200         if ((plkbyt == 0) || (ppstgOpen == 0))
2201                 return STG_E_INVALIDPOINTER;
2202
2203         *ppstgOpen = 0;
2204
2205         _create_istorage16(ppstgOpen);
2206         lpstg = MapSL((SEGPTR)*ppstgOpen);
2207         lpstg->str.hf = NULL;
2208         lpstg->str.lockbytes = plkbyt;
2209         i=0;ret=0;
2210         while (!ret) { /* neither 1 nor <0 */
2211                 ret=STORAGE_get_pps_entry(&lpstg->str,i,&stde);
2212                 if ((ret==1) && (stde.pps_type==5)) {
2213                         lpstg->stde=stde;
2214                         break;
2215                 }
2216                 i++;
2217         }
2218         if (ret!=1) {
2219                 IStorage16_fnRelease((IStorage16*)lpstg); /* will remove it */
2220                 return E_FAIL;
2221         }
2222         return S_OK;
2223 }
2224
2225 /***********************************************************************
2226  *    ReadClassStg (OLE2.18)
2227  *
2228  * This method reads the CLSID previously written to a storage object with
2229  * the WriteClassStg.
2230  *
2231  * PARAMS
2232  *  pstg    [I] Segmented LPSTORAGE pointer.
2233  *  pclsid  [O] Pointer to where the CLSID is written
2234  *
2235  * RETURNS
2236  *  Success: S_OK.
2237  *  Failure: HRESULT code.
2238  */
2239 HRESULT WINAPI ReadClassStg16(SEGPTR pstg, CLSID *pclsid)
2240 {
2241         STATSTG16 statstg;
2242         HANDLE16 hstatstg;
2243         HRESULT hres;
2244         DWORD args[3];
2245
2246         TRACE("(%x, %p)\n", pstg, pclsid);
2247
2248         if(pclsid==NULL)
2249                 return E_POINTER;
2250         /*
2251          * read a STATSTG structure (contains the clsid) from the storage
2252          */
2253         args[0] = (DWORD)pstg;  /* iface */
2254         args[1] = WOWGlobalAllocLock16( 0, sizeof(STATSTG16), &hstatstg );
2255         args[2] = STATFLAG_DEFAULT;
2256
2257         if (!WOWCallback16Ex(
2258             (DWORD)((const IStorage16Vtbl*)MapSL(
2259                         (SEGPTR)((LPSTORAGE16)MapSL(pstg))->lpVtbl)
2260             )->Stat,
2261             WCB16_PASCAL,
2262             3*sizeof(DWORD),
2263             (LPVOID)args,
2264             (LPDWORD)&hres
2265         )) {
2266             WOWGlobalUnlockFree16(args[1]);
2267             ERR("CallTo16 IStorage16::Stat() failed, hres %x\n",hres);
2268             return hres;
2269         }
2270         memcpy(&statstg, MapSL(args[1]), sizeof(STATSTG16));
2271         WOWGlobalUnlockFree16(args[1]);
2272
2273         if(SUCCEEDED(hres)) {
2274                 *pclsid=statstg.clsid;
2275                 TRACE("clsid is %s\n", debugstr_guid(&statstg.clsid));
2276         }
2277         return hres;
2278 }
2279
2280 /***********************************************************************
2281  *              GetConvertStg (OLE2.82)
2282  */
2283 HRESULT WINAPI GetConvertStg16(LPSTORAGE stg) {
2284     FIXME("unimplemented stub!\n");
2285     return E_FAIL;
2286 }