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