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