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