push 337eb2e2d902d84a5d689451984c5832d7e04fc4
[wine/hacks.git] / dlls / ole32 / storage.c
blob2e5203f689a43ec39d59afe7926e8b9f6eec5d07
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 * IStorage16_QueryInterface [STORAGE.500]
1629 HRESULT CDECL IStorage16_fnQueryInterface(
1630 IStorage16* iface,REFIID refiid,LPVOID *obj
1632 IStorage16Impl *This = (IStorage16Impl *)iface;
1634 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;
1640 return OLE_E_ENUM_NOMORE;
1643 /******************************************************************************
1644 * IStorage16_AddRef [STORAGE.501]
1646 ULONG CDECL IStorage16_fnAddRef(IStorage16* iface) {
1647 IStorage16Impl *This = (IStorage16Impl *)iface;
1648 return InterlockedIncrement(&This->ref);
1651 /******************************************************************************
1652 * IStorage16_Release [STORAGE.502]
1654 ULONG CDECL IStorage16_fnRelease(IStorage16* iface) {
1655 IStorage16Impl *This = (IStorage16Impl *)iface;
1656 ULONG ref;
1657 ref = InterlockedDecrement(&This->ref);
1658 if (!ref)
1660 UnMapLS( This->thisptr );
1661 HeapFree( GetProcessHeap(), 0, This );
1663 return ref;
1666 /******************************************************************************
1667 * IStorage16_Stat [STORAGE.517]
1669 HRESULT CDECL IStorage16_fnStat(
1670 LPSTORAGE16 iface,STATSTG16 *pstatstg, DWORD grfStatFlag
1672 IStorage16Impl *This = (IStorage16Impl *)iface;
1673 DWORD len = WideCharToMultiByte( CP_ACP, 0, This->stde.pps_rawname, -1, NULL, 0, NULL, NULL );
1674 LPSTR nameA = HeapAlloc( GetProcessHeap(), 0, len );
1676 TRACE("(%p)->(%p,0x%08x)\n",
1677 This,pstatstg,grfStatFlag
1679 WideCharToMultiByte( CP_ACP, 0, This->stde.pps_rawname, -1, nameA, len, NULL, NULL );
1680 pstatstg->pwcsName=(LPOLESTR16)MapLS( nameA );
1681 pstatstg->type = This->stde.pps_type;
1682 pstatstg->cbSize.u.LowPart = This->stde.pps_size;
1683 pstatstg->mtime = This->stde.pps_ft1; /* FIXME */ /* why? */
1684 pstatstg->atime = This->stde.pps_ft2; /* FIXME */
1685 pstatstg->ctime = This->stde.pps_ft2; /* FIXME */
1686 pstatstg->grfMode = 0; /* FIXME */
1687 pstatstg->grfLocksSupported = 0; /* FIXME */
1688 pstatstg->clsid = This->stde.pps_guid;
1689 pstatstg->grfStateBits = 0; /* FIXME */
1690 pstatstg->reserved = 0;
1691 return S_OK;
1694 /******************************************************************************
1695 * IStorage16_Commit [STORAGE.509]
1697 HRESULT CDECL IStorage16_fnCommit(
1698 LPSTORAGE16 iface,DWORD commitflags
1700 IStorage16Impl *This = (IStorage16Impl *)iface;
1701 FIXME("(%p)->(0x%08x),STUB!\n",
1702 This,commitflags
1704 return S_OK;
1707 /******************************************************************************
1708 * IStorage16_CopyTo [STORAGE.507]
1710 HRESULT CDECL IStorage16_fnCopyTo(LPSTORAGE16 iface,DWORD ciidExclude,const IID *rgiidExclude,SNB16 SNB16Exclude,IStorage16 *pstgDest) {
1711 IStorage16Impl *This = (IStorage16Impl *)iface;
1712 FIXME("IStorage16(%p)->(0x%08x,%s,%p,%p),stub!\n",
1713 This,ciidExclude,debugstr_guid(rgiidExclude),SNB16Exclude,pstgDest
1715 return S_OK;
1719 /******************************************************************************
1720 * IStorage16_CreateStorage [STORAGE.505]
1722 HRESULT CDECL IStorage16_fnCreateStorage(
1723 LPSTORAGE16 iface,LPCOLESTR16 pwcsName,DWORD grfMode,DWORD dwStgFormat,DWORD reserved2, IStorage16 **ppstg
1725 IStorage16Impl *This = (IStorage16Impl *)iface;
1726 IStorage16Impl* lpstg;
1727 int ppsent,x;
1728 struct storage_pps_entry stde;
1729 struct storage_header sth;
1730 BOOL ret;
1731 int nPPSEntries;
1733 READ_HEADER(&This->str);
1734 TRACE("(%p)->(%s,0x%08x,0x%08x,0x%08x,%p)\n",
1735 This,pwcsName,grfMode,dwStgFormat,reserved2,ppstg
1737 if (grfMode & STGM_TRANSACTED)
1738 FIXME("We do not support transacted Compound Storage. Using direct mode.\n");
1739 _create_istorage16(ppstg);
1740 lpstg = MapSL((SEGPTR)*ppstg);
1741 if (This->str.hf) {
1742 DuplicateHandle( GetCurrentProcess(), This->str.hf, GetCurrentProcess(),
1743 &lpstg->str.hf, 0, TRUE, DUPLICATE_SAME_ACCESS );
1744 } else {
1745 lpstg->str.lockbytes = This->str.lockbytes;
1746 _ilockbytes16_addref(This->str.lockbytes);
1749 ppsent=STORAGE_get_free_pps_entry(&lpstg->str);
1750 if (ppsent<0)
1751 return E_FAIL;
1752 stde=This->stde;
1753 if (stde.pps_dir==-1) {
1754 stde.pps_dir = ppsent;
1755 x = This->ppsent;
1756 } else {
1757 FIXME(" use prev chain too ?\n");
1758 x=stde.pps_dir;
1759 if (1!=STORAGE_get_pps_entry(&lpstg->str,x,&stde))
1760 return E_FAIL;
1761 while (stde.pps_next!=-1) {
1762 x=stde.pps_next;
1763 if (1!=STORAGE_get_pps_entry(&lpstg->str,x,&stde))
1764 return E_FAIL;
1766 stde.pps_next = ppsent;
1768 ret = STORAGE_put_pps_entry(&lpstg->str,x,&stde);
1769 assert(ret);
1770 nPPSEntries = STORAGE_get_pps_entry(&lpstg->str,ppsent,&(lpstg->stde));
1771 assert(nPPSEntries == 1);
1772 MultiByteToWideChar( CP_ACP, 0, pwcsName, -1, lpstg->stde.pps_rawname,
1773 sizeof(lpstg->stde.pps_rawname)/sizeof(WCHAR));
1774 lpstg->stde.pps_sizeofname = (strlenW(lpstg->stde.pps_rawname)+1)*sizeof(WCHAR);
1775 lpstg->stde.pps_next = -1;
1776 lpstg->stde.pps_prev = -1;
1777 lpstg->stde.pps_dir = -1;
1778 lpstg->stde.pps_sb = -1;
1779 lpstg->stde.pps_size = 0;
1780 lpstg->stde.pps_type = 1;
1781 lpstg->ppsent = ppsent;
1782 /* FIXME: timestamps? */
1783 if (!STORAGE_put_pps_entry(&lpstg->str,ppsent,&(lpstg->stde)))
1784 return E_FAIL;
1785 return S_OK;
1788 /******************************************************************************
1789 * IStorage16_CreateStream [STORAGE.503]
1791 HRESULT CDECL IStorage16_fnCreateStream(
1792 LPSTORAGE16 iface,LPCOLESTR16 pwcsName,DWORD grfMode,DWORD reserved1,DWORD reserved2, IStream16 **ppstm
1794 IStorage16Impl *This = (IStorage16Impl *)iface;
1795 IStream16Impl* lpstr;
1796 int ppsent,x;
1797 struct storage_pps_entry stde;
1798 BOOL ret;
1799 int nPPSEntries;
1801 TRACE("(%p)->(%s,0x%08x,0x%08x,0x%08x,%p)\n",
1802 This,pwcsName,grfMode,reserved1,reserved2,ppstm
1804 if (grfMode & STGM_TRANSACTED)
1805 FIXME("We do not support transacted Compound Storage. Using direct mode.\n");
1806 _create_istream16(ppstm);
1807 lpstr = MapSL((SEGPTR)*ppstm);
1808 if (This->str.hf) {
1809 DuplicateHandle( GetCurrentProcess(), This->str.hf, GetCurrentProcess(),
1810 &lpstr->str.hf, 0, TRUE, DUPLICATE_SAME_ACCESS );
1811 } else {
1812 lpstr->str.lockbytes = This->str.lockbytes;
1813 _ilockbytes16_addref(This->str.lockbytes);
1815 lpstr->offset.u.LowPart = 0;
1816 lpstr->offset.u.HighPart= 0;
1818 ppsent=STORAGE_get_free_pps_entry(&lpstr->str);
1819 if (ppsent<0)
1820 return E_FAIL;
1821 stde=This->stde;
1822 if (stde.pps_next==-1)
1823 x=This->ppsent;
1824 else
1825 while (stde.pps_next!=-1) {
1826 x=stde.pps_next;
1827 if (1!=STORAGE_get_pps_entry(&lpstr->str,x,&stde))
1828 return E_FAIL;
1830 stde.pps_next = ppsent;
1831 ret = STORAGE_put_pps_entry(&lpstr->str,x,&stde);
1832 assert(ret);
1833 nPPSEntries = STORAGE_get_pps_entry(&lpstr->str,ppsent,&(lpstr->stde));
1834 assert(nPPSEntries == 1);
1835 MultiByteToWideChar( CP_ACP, 0, pwcsName, -1, lpstr->stde.pps_rawname,
1836 sizeof(lpstr->stde.pps_rawname)/sizeof(WCHAR));
1837 lpstr->stde.pps_sizeofname = (strlenW(lpstr->stde.pps_rawname)+1) * sizeof(WCHAR);
1838 lpstr->stde.pps_next = -1;
1839 lpstr->stde.pps_prev = -1;
1840 lpstr->stde.pps_dir = -1;
1841 lpstr->stde.pps_sb = -1;
1842 lpstr->stde.pps_size = 0;
1843 lpstr->stde.pps_type = 2;
1844 lpstr->ppsent = ppsent;
1846 /* FIXME: timestamps? */
1847 if (!STORAGE_put_pps_entry(&lpstr->str,ppsent,&(lpstr->stde)))
1848 return E_FAIL;
1849 return S_OK;
1852 /******************************************************************************
1853 * IStorage16_OpenStorage [STORAGE.506]
1855 HRESULT CDECL IStorage16_fnOpenStorage(
1856 LPSTORAGE16 iface,LPCOLESTR16 pwcsName, IStorage16 *pstgPrio, DWORD grfMode, SNB16 snbExclude, DWORD reserved, IStorage16 **ppstg
1858 IStorage16Impl *This = (IStorage16Impl *)iface;
1859 IStream16Impl* lpstg;
1860 WCHAR name[33];
1861 int newpps;
1863 TRACE("(%p)->(%s,%p,0x%08x,%p,0x%08x,%p)\n",
1864 This,pwcsName,pstgPrio,grfMode,snbExclude,reserved,ppstg
1866 if (grfMode & STGM_TRANSACTED)
1867 FIXME("We do not support transacted Compound Storage. Using direct mode.\n");
1868 _create_istorage16(ppstg);
1869 lpstg = MapSL((SEGPTR)*ppstg);
1870 if (This->str.hf) {
1871 DuplicateHandle( GetCurrentProcess(), This->str.hf, GetCurrentProcess(),
1872 &lpstg->str.hf, 0, TRUE, DUPLICATE_SAME_ACCESS );
1873 } else {
1874 lpstg->str.lockbytes = This->str.lockbytes;
1875 _ilockbytes16_addref(This->str.lockbytes);
1877 MultiByteToWideChar( CP_ACP, 0, pwcsName, -1, name, sizeof(name)/sizeof(WCHAR));
1878 newpps = STORAGE_look_for_named_pps(&lpstg->str,This->stde.pps_dir,name);
1879 if (newpps==-1) {
1880 IStream16_fnRelease((IStream16*)lpstg);
1881 return E_FAIL;
1884 if (1!=STORAGE_get_pps_entry(&lpstg->str,newpps,&(lpstg->stde))) {
1885 IStream16_fnRelease((IStream16*)lpstg);
1886 return E_FAIL;
1888 lpstg->ppsent = newpps;
1889 return S_OK;
1892 /******************************************************************************
1893 * IStorage16_OpenStream [STORAGE.504]
1895 HRESULT CDECL IStorage16_fnOpenStream(
1896 LPSTORAGE16 iface,LPCOLESTR16 pwcsName, void *reserved1, DWORD grfMode, DWORD reserved2, IStream16 **ppstm
1898 IStorage16Impl *This = (IStorage16Impl *)iface;
1899 IStream16Impl* lpstr;
1900 WCHAR name[33];
1901 int newpps;
1903 TRACE("(%p)->(%s,%p,0x%08x,0x%08x,%p)\n",
1904 This,pwcsName,reserved1,grfMode,reserved2,ppstm
1906 if (grfMode & STGM_TRANSACTED)
1907 FIXME("We do not support transacted Compound Storage. Using direct mode.\n");
1908 _create_istream16(ppstm);
1909 lpstr = MapSL((SEGPTR)*ppstm);
1910 if (This->str.hf) {
1911 DuplicateHandle( GetCurrentProcess(), This->str.hf, GetCurrentProcess(),
1912 &lpstr->str.hf, 0, TRUE, DUPLICATE_SAME_ACCESS );
1913 } else {
1914 lpstr->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(&lpstr->str,This->stde.pps_dir,name);
1919 if (newpps==-1) {
1920 IStream16_fnRelease((IStream16*)lpstr);
1921 return E_FAIL;
1924 if (1!=STORAGE_get_pps_entry(&lpstr->str,newpps,&(lpstr->stde))) {
1925 IStream16_fnRelease((IStream16*)lpstr);
1926 return E_FAIL;
1928 lpstr->offset.u.LowPart = 0;
1929 lpstr->offset.u.HighPart = 0;
1930 lpstr->ppsent = newpps;
1931 return S_OK;
1934 /******************************************************************************
1935 * _create_istorage16 [INTERNAL]
1937 static void _create_istorage16(LPSTORAGE16 *stg) {
1938 IStorage16Impl* lpst;
1940 if (!stvt16.QueryInterface) {
1941 HMODULE16 wp = GetModuleHandle16("STORAGE");
1942 if (wp>=32) {
1943 #define VTENT(xfn) stvt16.xfn = (void*)GetProcAddress16(wp,"IStorage16_"#xfn);
1944 VTENT(QueryInterface)
1945 VTENT(AddRef)
1946 VTENT(Release)
1947 VTENT(CreateStream)
1948 VTENT(OpenStream)
1949 VTENT(CreateStorage)
1950 VTENT(OpenStorage)
1951 VTENT(CopyTo)
1952 VTENT(MoveElementTo)
1953 VTENT(Commit)
1954 VTENT(Revert)
1955 VTENT(EnumElements)
1956 VTENT(DestroyElement)
1957 VTENT(RenameElement)
1958 VTENT(SetElementTimes)
1959 VTENT(SetClass)
1960 VTENT(SetStateBits)
1961 VTENT(Stat)
1962 #undef VTENT
1963 segstvt16 = (const IStorage16Vtbl*)MapLS( &stvt16 );
1964 } else {
1965 #define VTENT(xfn) stvt16.xfn = IStorage16_fn##xfn;
1966 VTENT(QueryInterface)
1967 VTENT(AddRef)
1968 VTENT(Release)
1969 VTENT(CreateStream)
1970 VTENT(OpenStream)
1971 VTENT(CreateStorage)
1972 VTENT(OpenStorage)
1973 VTENT(CopyTo)
1974 VTENT(Commit)
1975 /* not (yet) implemented ...
1976 VTENT(MoveElementTo)
1977 VTENT(Revert)
1978 VTENT(EnumElements)
1979 VTENT(DestroyElement)
1980 VTENT(RenameElement)
1981 VTENT(SetElementTimes)
1982 VTENT(SetClass)
1983 VTENT(SetStateBits)
1984 VTENT(Stat)
1986 #undef VTENT
1987 segstvt16 = &stvt16;
1990 lpst = HeapAlloc( GetProcessHeap(), 0, sizeof(*lpst) );
1991 lpst->lpVtbl = segstvt16;
1992 lpst->str.hf = NULL;
1993 lpst->str.lockbytes = 0;
1994 lpst->ref = 1;
1995 lpst->thisptr = MapLS(lpst);
1996 *stg = (void*)lpst->thisptr;
1999 /******************************************************************************
2000 * Storage API functions
2003 /******************************************************************************
2004 * StgCreateDocFileA [STORAGE.1]
2006 HRESULT WINAPI StgCreateDocFile16(
2007 LPCOLESTR16 pwcsName,DWORD grfMode,DWORD reserved,IStorage16 **ppstgOpen
2009 HANDLE hf;
2010 int i,ret;
2011 IStorage16Impl* lpstg;
2012 struct storage_pps_entry stde;
2014 TRACE("(%s,0x%08x,0x%08x,%p)\n",
2015 pwcsName,grfMode,reserved,ppstgOpen
2017 _create_istorage16(ppstgOpen);
2018 hf = CreateFileA(pwcsName,GENERIC_READ|GENERIC_WRITE,0,NULL,CREATE_NEW,0,0);
2019 if (hf==INVALID_HANDLE_VALUE) {
2020 WARN("couldn't open file for storage:%d\n",GetLastError());
2021 return E_FAIL;
2023 lpstg = MapSL((SEGPTR)*ppstgOpen);
2024 lpstg->str.hf = hf;
2025 lpstg->str.lockbytes = 0;
2026 /* FIXME: check for existence before overwriting? */
2027 if (!STORAGE_init_storage(&lpstg->str)) {
2028 CloseHandle(hf);
2029 return E_FAIL;
2031 i=0;ret=0;
2032 while (!ret) { /* neither 1 nor <0 */
2033 ret=STORAGE_get_pps_entry(&lpstg->str,i,&stde);
2034 if ((ret==1) && (stde.pps_type==5)) {
2035 lpstg->stde = stde;
2036 lpstg->ppsent = i;
2037 break;
2039 i++;
2041 if (ret!=1) {
2042 IStorage16_fnRelease((IStorage16*)lpstg); /* will remove it */
2043 return E_FAIL;
2046 return S_OK;
2049 /******************************************************************************
2050 * StgIsStorageFile [STORAGE.5]
2052 HRESULT WINAPI StgIsStorageFile16(LPCOLESTR16 fn) {
2053 UNICODE_STRING strW;
2054 HRESULT ret;
2056 RtlCreateUnicodeStringFromAsciiz(&strW, fn);
2057 ret = StgIsStorageFile( strW.Buffer );
2058 RtlFreeUnicodeString( &strW );
2060 return ret;
2063 /******************************************************************************
2064 * StgOpenStorage [STORAGE.3]
2066 HRESULT WINAPI StgOpenStorage16(
2067 LPCOLESTR16 pwcsName,IStorage16 *pstgPriority,DWORD grfMode,
2068 SNB16 snbExclude,DWORD reserved, IStorage16 **ppstgOpen
2070 HANDLE hf;
2071 int ret,i;
2072 IStorage16Impl* lpstg;
2073 struct storage_pps_entry stde;
2075 TRACE("(%s,%p,0x%08x,%p,%d,%p)\n",
2076 pwcsName,pstgPriority,grfMode,snbExclude,reserved,ppstgOpen
2078 _create_istorage16(ppstgOpen);
2079 hf = CreateFileA(pwcsName,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
2080 if (hf==INVALID_HANDLE_VALUE) {
2081 WARN("Couldn't open file for storage\n");
2082 return E_FAIL;
2084 lpstg = MapSL((SEGPTR)*ppstgOpen);
2085 lpstg->str.hf = hf;
2087 i=0;ret=0;
2088 while (!ret) { /* neither 1 nor <0 */
2089 ret=STORAGE_get_pps_entry(&lpstg->str,i,&stde);
2090 if ((ret==1) && (stde.pps_type==5)) {
2091 lpstg->stde=stde;
2092 break;
2094 i++;
2096 if (ret!=1) {
2097 IStorage16_fnRelease((IStorage16*)lpstg); /* will remove it */
2098 return E_FAIL;
2100 return S_OK;
2104 /******************************************************************************
2105 * StgIsStorageILockBytes [STORAGE.6]
2107 * Determines if the ILockBytes contains a storage object.
2109 HRESULT WINAPI StgIsStorageILockBytes16(SEGPTR plkbyt)
2111 DWORD args[6];
2112 HRESULT hres;
2113 HANDLE16 hsig;
2115 args[0] = (DWORD)plkbyt; /* iface */
2116 args[1] = args[2] = 0; /* ULARGE_INTEGER offset */
2117 args[3] = WOWGlobalAllocLock16( 0, 8, &hsig ); /* sig */
2118 args[4] = 8;
2119 args[5] = 0;
2121 if (!WOWCallback16Ex(
2122 (DWORD)((const ILockBytes16Vtbl*)MapSL(
2123 (SEGPTR)((LPLOCKBYTES16)MapSL(plkbyt))->lpVtbl)
2124 )->ReadAt,
2125 WCB16_PASCAL,
2126 6*sizeof(DWORD),
2127 (LPVOID)args,
2128 (LPDWORD)&hres
2129 )) {
2130 ERR("CallTo16 ILockBytes16::ReadAt() failed, hres %x\n",hres);
2131 return hres;
2133 if (memcmp(MapSL(args[3]), STORAGE_magic, sizeof(STORAGE_magic)) == 0) {
2134 WOWGlobalUnlockFree16(args[3]);
2135 return S_OK;
2137 WOWGlobalUnlockFree16(args[3]);
2138 return S_FALSE;
2141 /******************************************************************************
2142 * StgOpenStorageOnILockBytes [STORAGE.4]
2144 * PARAMS
2145 * plkbyt FIXME: Should probably be an ILockBytes16 *.
2147 HRESULT WINAPI StgOpenStorageOnILockBytes16(
2148 SEGPTR plkbyt,
2149 IStorage16 *pstgPriority,
2150 DWORD grfMode,
2151 SNB16 snbExclude,
2152 DWORD reserved,
2153 IStorage16 **ppstgOpen)
2155 IStorage16Impl* lpstg;
2156 int i,ret;
2157 struct storage_pps_entry stde;
2159 FIXME("(%x, %p, 0x%08x, %d, %x, %p)\n", plkbyt, pstgPriority, grfMode, (int)snbExclude, reserved, ppstgOpen);
2160 if ((plkbyt == 0) || (ppstgOpen == 0))
2161 return STG_E_INVALIDPOINTER;
2163 *ppstgOpen = 0;
2165 _create_istorage16(ppstgOpen);
2166 lpstg = MapSL((SEGPTR)*ppstgOpen);
2167 lpstg->str.hf = NULL;
2168 lpstg->str.lockbytes = plkbyt;
2169 i=0;ret=0;
2170 while (!ret) { /* neither 1 nor <0 */
2171 ret=STORAGE_get_pps_entry(&lpstg->str,i,&stde);
2172 if ((ret==1) && (stde.pps_type==5)) {
2173 lpstg->stde=stde;
2174 break;
2176 i++;
2178 if (ret!=1) {
2179 IStorage16_fnRelease((IStorage16*)lpstg); /* will remove it */
2180 return E_FAIL;
2182 return S_OK;
2185 /***********************************************************************
2186 * ReadClassStg (OLE2.18)
2188 * This method reads the CLSID previously written to a storage object with
2189 * the WriteClassStg.
2191 * PARAMS
2192 * pstg [I] Segmented LPSTORAGE pointer.
2193 * pclsid [O] Pointer to where the CLSID is written
2195 * RETURNS
2196 * Success: S_OK.
2197 * Failure: HRESULT code.
2199 HRESULT WINAPI ReadClassStg16(SEGPTR pstg, CLSID *pclsid)
2201 STATSTG16 statstg;
2202 HANDLE16 hstatstg;
2203 HRESULT hres;
2204 DWORD args[3];
2206 TRACE("(%x, %p)\n", pstg, pclsid);
2208 if(pclsid==NULL)
2209 return E_POINTER;
2211 * read a STATSTG structure (contains the clsid) from the storage
2213 args[0] = (DWORD)pstg; /* iface */
2214 args[1] = WOWGlobalAllocLock16( 0, sizeof(STATSTG16), &hstatstg );
2215 args[2] = STATFLAG_DEFAULT;
2217 if (!WOWCallback16Ex(
2218 (DWORD)((const IStorage16Vtbl*)MapSL(
2219 (SEGPTR)((LPSTORAGE16)MapSL(pstg))->lpVtbl)
2220 )->Stat,
2221 WCB16_PASCAL,
2222 3*sizeof(DWORD),
2223 (LPVOID)args,
2224 (LPDWORD)&hres
2225 )) {
2226 WOWGlobalUnlockFree16(args[1]);
2227 ERR("CallTo16 IStorage16::Stat() failed, hres %x\n",hres);
2228 return hres;
2230 memcpy(&statstg, MapSL(args[1]), sizeof(STATSTG16));
2231 WOWGlobalUnlockFree16(args[1]);
2233 if(SUCCEEDED(hres)) {
2234 *pclsid=statstg.clsid;
2235 TRACE("clsid is %s\n", debugstr_guid(&statstg.clsid));
2237 return hres;
2240 /***********************************************************************
2241 * GetConvertStg (OLE2.82)
2243 HRESULT WINAPI GetConvertStg16(LPSTORAGE stg) {
2244 FIXME("unimplemented stub!\n");
2245 return E_FAIL;