d2d1/tests: Fix stroke style object leak (Valgrind).
[wine.git] / dlls / storage.dll16 / storage.c
blob865411f9cb0d645a2575c55efbb8eb0b8d5f1b1e
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
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; /* 6C: 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 IStorage16 IStorage16_iface;
330 LONG ref;
331 /* IStorage16 fields */
332 SEGPTR thisptr; /* pointer to this struct as segmented */
333 struct storage_pps_entry stde;
334 int ppsent;
335 stream_access16 str;
336 } IStorage16Impl;
339 /******************************************************************************
340 * STORAGE_get_big_block [Internal]
342 * Reading OLE compound storage
344 static BOOL
345 STORAGE_get_big_block(stream_access16 *str,int n,BYTE *block)
347 DWORD result;
349 assert(n>=-1);
350 if (str->hf) {
351 if ((SetFilePointer( str->hf, (n+1)*BIGSIZE, NULL,
352 SEEK_SET ) == INVALID_SET_FILE_POINTER) && GetLastError())
354 WARN("(%p,%d,%p), seek failed (%d)\n",str->hf, n, block, GetLastError());
355 return FALSE;
357 if (!ReadFile( str->hf, block, BIGSIZE, &result, NULL ) || result != BIGSIZE)
359 WARN("(hf=%p, block size %d): read didn't read (%d)\n",str->hf,n,GetLastError());
360 return FALSE;
362 } else {
363 DWORD args[6];
364 HRESULT hres;
365 HANDLE16 hsig;
367 args[0] = (DWORD)str->lockbytes; /* iface */
368 args[1] = (n+1)*BIGSIZE;
369 args[2] = 0; /* ULARGE_INTEGER offset */
370 args[3] = WOWGlobalAllocLock16( 0, BIGSIZE, &hsig ); /* sig */
371 args[4] = BIGSIZE;
372 args[5] = 0;
374 if (!WOWCallback16Ex(
375 (DWORD)((const ILockBytes16Vtbl*)MapSL(
376 (SEGPTR)((LPLOCKBYTES16)MapSL(str->lockbytes))->lpVtbl)
377 )->ReadAt,
378 WCB16_PASCAL,
379 6*sizeof(DWORD),
380 (LPVOID)args,
381 (LPDWORD)&hres
382 )) {
383 ERR("CallTo16 ILockBytes16::ReadAt() failed, hres %x\n",hres);
384 return FALSE;
386 memcpy(block, MapSL(args[3]), BIGSIZE);
387 WOWGlobalUnlockFree16(args[3]);
389 return TRUE;
392 static BOOL
393 _ilockbytes16_writeat(SEGPTR lockbytes, DWORD offset, DWORD length, void *buffer) {
394 DWORD args[6];
395 HRESULT hres;
397 args[0] = (DWORD)lockbytes; /* iface */
398 args[1] = offset;
399 args[2] = 0; /* ULARGE_INTEGER offset */
400 args[3] = (DWORD)MapLS( buffer );
401 args[4] = length;
402 args[5] = 0;
404 /* THIS_ ULARGE_INTEGER ulOffset, const void *pv, ULONG cb, ULONG *pcbWritten); */
406 if (!WOWCallback16Ex(
407 (DWORD)((const ILockBytes16Vtbl*)MapSL(
408 (SEGPTR)((LPLOCKBYTES16)MapSL(lockbytes))->lpVtbl)
409 )->WriteAt,
410 WCB16_PASCAL,
411 6*sizeof(DWORD),
412 (LPVOID)args,
413 (LPDWORD)&hres
414 )) {
415 ERR("CallTo16 ILockBytes16::WriteAt() failed, hres %x\n",hres);
416 return FALSE;
418 UnMapLS(args[3]);
419 return TRUE;
422 /******************************************************************************
423 * STORAGE_put_big_block [INTERNAL]
425 static BOOL
426 STORAGE_put_big_block(stream_access16 *str,int n,BYTE *block)
428 DWORD result;
430 assert(n>=-1);
431 if (str->hf) {
432 if ((SetFilePointer( str->hf, (n+1)*BIGSIZE, NULL,
433 SEEK_SET ) == INVALID_SET_FILE_POINTER) && GetLastError())
435 WARN("seek failed (%d)\n",GetLastError());
436 return FALSE;
438 if (!WriteFile( str->hf, block, BIGSIZE, &result, NULL ) || result != BIGSIZE)
440 WARN(" write failed (%d)\n",GetLastError());
441 return FALSE;
443 return TRUE;
444 } else {
445 _ilockbytes16_writeat(str->lockbytes, (n+1)*BIGSIZE, BIGSIZE, block);
446 return TRUE;
450 /******************************************************************************
451 * STORAGE_get_next_big_blocknr [INTERNAL]
453 static int
454 STORAGE_get_next_big_blocknr(stream_access16 *str,int blocknr) {
455 INT bbs[BIGSIZE/sizeof(INT)];
456 struct storage_header sth;
458 READ_HEADER(str);
460 assert(blocknr>>7<sth.num_of_bbd_blocks);
461 if (sth.bbd_list[blocknr>>7]==0xffffffff)
462 return -5;
463 if (!STORAGE_get_big_block(str,sth.bbd_list[blocknr>>7],(LPBYTE)bbs))
464 return -5;
465 assert(bbs[blocknr&0x7f]!=STORAGE_CHAINENTRY_FREE);
466 return bbs[blocknr&0x7f];
469 /******************************************************************************
470 * STORAGE_get_nth_next_big_blocknr [INTERNAL]
472 static int
473 STORAGE_get_nth_next_big_blocknr(stream_access16 *str,int blocknr,int nr) {
474 INT bbs[BIGSIZE/sizeof(INT)];
475 int lastblock = -1;
476 struct storage_header sth;
478 TRACE("(blocknr=%d, nr=%d)\n", blocknr, nr);
479 READ_HEADER(str);
481 assert(blocknr>=0);
482 while (nr--) {
483 assert((blocknr>>7)<sth.num_of_bbd_blocks);
484 assert(sth.bbd_list[blocknr>>7]!=0xffffffff);
486 /* simple caching... */
487 if (lastblock!=sth.bbd_list[blocknr>>7]) {
488 BOOL ret = STORAGE_get_big_block(str,sth.bbd_list[blocknr>>7],(LPBYTE)bbs);
489 assert(ret);
490 lastblock = sth.bbd_list[blocknr>>7];
492 blocknr = bbs[blocknr&0x7f];
494 return blocknr;
497 /******************************************************************************
498 * STORAGE_get_root_pps_entry [Internal]
500 static BOOL
501 STORAGE_get_root_pps_entry(stream_access16* str,struct storage_pps_entry *pstde) {
502 int blocknr,i;
503 BYTE block[BIGSIZE];
504 struct storage_pps_entry *stde=(struct storage_pps_entry*)block;
505 struct storage_header sth;
507 READ_HEADER(str);
508 blocknr = sth.root_startblock;
509 TRACE("startblock is %d\n", blocknr);
510 while (blocknr>=0) {
511 BOOL ret = STORAGE_get_big_block(str,blocknr,block);
512 assert(ret);
513 for (i=0;i<4;i++) {
514 if (!stde[i].pps_sizeofname)
515 continue;
516 if (stde[i].pps_type==5) {
517 *pstde=stde[i];
518 return TRUE;
521 blocknr=STORAGE_get_next_big_blocknr(str,blocknr);
522 TRACE("next block is %d\n", blocknr);
524 return FALSE;
527 /******************************************************************************
528 * STORAGE_get_small_block [INTERNAL]
530 static BOOL
531 STORAGE_get_small_block(stream_access16 *str,int blocknr,BYTE *sblock) {
532 BYTE block[BIGSIZE];
533 int bigblocknr;
534 struct storage_pps_entry root;
535 BOOL ret;
537 TRACE("(blocknr=%d)\n", blocknr);
538 assert(blocknr>=0);
539 ret = STORAGE_get_root_pps_entry(str,&root);
540 assert(ret);
541 bigblocknr = STORAGE_get_nth_next_big_blocknr(str,root.pps_sb,blocknr/SMALLBLOCKS_PER_BIGBLOCK);
542 assert(bigblocknr>=0);
543 ret = STORAGE_get_big_block(str,bigblocknr,block);
544 assert(ret);
546 memcpy(sblock,((LPBYTE)block)+SMALLSIZE*(blocknr&(SMALLBLOCKS_PER_BIGBLOCK-1)),SMALLSIZE);
547 return TRUE;
550 /******************************************************************************
551 * STORAGE_put_small_block [INTERNAL]
553 static BOOL
554 STORAGE_put_small_block(stream_access16 *str,int blocknr,const BYTE *sblock) {
555 BYTE block[BIGSIZE];
556 int bigblocknr;
557 struct storage_pps_entry root;
558 BOOL ret;
560 assert(blocknr>=0);
561 TRACE("(blocknr=%d)\n", blocknr);
563 ret = STORAGE_get_root_pps_entry(str,&root);
564 assert(ret);
565 bigblocknr = STORAGE_get_nth_next_big_blocknr(str,root.pps_sb,blocknr/SMALLBLOCKS_PER_BIGBLOCK);
566 assert(bigblocknr>=0);
567 ret = STORAGE_get_big_block(str,bigblocknr,block);
568 assert(ret);
570 memcpy(((LPBYTE)block)+SMALLSIZE*(blocknr&(SMALLBLOCKS_PER_BIGBLOCK-1)),sblock,SMALLSIZE);
571 ret = STORAGE_put_big_block(str,bigblocknr,block);
572 assert(ret);
573 return TRUE;
576 /******************************************************************************
577 * STORAGE_get_next_small_blocknr [INTERNAL]
579 static int
580 STORAGE_get_next_small_blocknr(stream_access16 *str,int blocknr) {
581 BYTE block[BIGSIZE];
582 LPINT sbd = (LPINT)block;
583 int bigblocknr;
584 struct storage_header sth;
585 BOOL ret;
587 TRACE("(blocknr=%d)\n", blocknr);
588 READ_HEADER(str);
589 assert(blocknr>=0);
590 bigblocknr = STORAGE_get_nth_next_big_blocknr(str,sth.sbd_startblock,blocknr/128);
591 assert(bigblocknr>=0);
592 ret = STORAGE_get_big_block(str,bigblocknr,block);
593 assert(ret);
594 assert(sbd[blocknr & 127]!=STORAGE_CHAINENTRY_FREE);
595 return sbd[blocknr & (128-1)];
598 /******************************************************************************
599 * STORAGE_get_nth_next_small_blocknr [INTERNAL]
601 static int
602 STORAGE_get_nth_next_small_blocknr(stream_access16*str,int blocknr,int nr) {
603 int lastblocknr=-129;
604 BYTE block[BIGSIZE];
605 LPINT sbd = (LPINT)block;
606 struct storage_header sth;
607 BOOL ret;
609 TRACE("(blocknr=%d, nr=%d)\n", blocknr, nr);
610 READ_HEADER(str);
611 assert(blocknr>=0);
612 while ((nr--) && (blocknr>=0)) {
613 if (lastblocknr/128!=blocknr/128) {
614 int bigblocknr;
615 bigblocknr = STORAGE_get_nth_next_big_blocknr(str,sth.sbd_startblock,blocknr/128);
616 assert(bigblocknr>=0);
617 ret = STORAGE_get_big_block(str,bigblocknr,block);
618 assert(ret);
619 lastblocknr = blocknr;
621 assert(lastblocknr>=0);
622 lastblocknr=blocknr;
623 blocknr=sbd[blocknr & (128-1)];
624 assert(blocknr!=STORAGE_CHAINENTRY_FREE);
626 return blocknr;
629 /******************************************************************************
630 * STORAGE_get_pps_entry [INTERNAL]
632 static int
633 STORAGE_get_pps_entry(stream_access16*str,int n,struct storage_pps_entry *pstde) {
634 int blocknr;
635 BYTE block[BIGSIZE];
636 struct storage_pps_entry *stde = (struct storage_pps_entry*)(((LPBYTE)block)+128*(n&3));
637 struct storage_header sth;
638 BOOL ret;
640 TRACE("(n=%d)\n", n);
641 READ_HEADER(str);
642 /* we have 4 pps entries per big block */
643 blocknr = STORAGE_get_nth_next_big_blocknr(str,sth.root_startblock,n/4);
644 assert(blocknr>=0);
645 ret = STORAGE_get_big_block(str,blocknr,block);
646 assert(ret);
648 *pstde=*stde;
649 return 1;
652 /******************************************************************************
653 * STORAGE_put_pps_entry [Internal]
655 static int
656 STORAGE_put_pps_entry(stream_access16*str,int n,const struct storage_pps_entry *pstde) {
657 int blocknr;
658 BYTE block[BIGSIZE];
659 struct storage_pps_entry *stde = (struct storage_pps_entry*)(((LPBYTE)block)+128*(n&3));
660 struct storage_header sth;
661 BOOL ret;
663 TRACE("(n=%d)\n", n);
664 READ_HEADER(str);
665 /* we have 4 pps entries per big block */
666 blocknr = STORAGE_get_nth_next_big_blocknr(str,sth.root_startblock,n/4);
667 assert(blocknr>=0);
668 ret = STORAGE_get_big_block(str,blocknr,block);
669 assert(ret);
670 *stde=*pstde;
671 ret = STORAGE_put_big_block(str,blocknr,block);
672 assert(ret);
673 return 1;
676 /******************************************************************************
677 * STORAGE_look_for_named_pps [Internal]
679 static int
680 STORAGE_look_for_named_pps(stream_access16*str,int n,LPOLESTR name) {
681 struct storage_pps_entry stde;
682 int ret;
684 TRACE("(n=%d,name=%s)\n", n, debugstr_w(name));
685 if (n==-1)
686 return -1;
687 if (1!=STORAGE_get_pps_entry(str,n,&stde))
688 return -1;
690 if (!lstrcmpW(name,stde.pps_rawname))
691 return n;
692 if (stde.pps_prev != -1) {
693 ret=STORAGE_look_for_named_pps(str,stde.pps_prev,name);
694 if (ret!=-1)
695 return ret;
697 if (stde.pps_next != -1) {
698 ret=STORAGE_look_for_named_pps(str,stde.pps_next,name);
699 if (ret!=-1)
700 return ret;
702 return -1;
705 /******************************************************************************
706 * STORAGE_dump_pps_entry [Internal]
708 * This function is there to simplify debugging. It is otherwise unused.
710 void
711 STORAGE_dump_pps_entry(struct storage_pps_entry *stde) {
712 char name[33];
714 WideCharToMultiByte( CP_ACP, 0, stde->pps_rawname, -1, name, sizeof(name), NULL, NULL);
715 if (!stde->pps_sizeofname)
716 return;
717 TRACE("name: %s\n",name);
718 TRACE("type: %d\n",stde->pps_type);
719 TRACE("prev pps: %d\n",stde->pps_prev);
720 TRACE("next pps: %d\n",stde->pps_next);
721 TRACE("dir pps: %d\n",stde->pps_dir);
722 TRACE("guid: %s\n",debugstr_guid(&(stde->pps_guid)));
723 if (stde->pps_type !=2) {
724 time_t t;
725 DWORD dw;
726 RtlTimeToSecondsSince1970((LARGE_INTEGER *)&(stde->pps_ft1),&dw);
727 t = dw;
728 TRACE("ts1: %s\n",ctime(&t));
729 RtlTimeToSecondsSince1970((LARGE_INTEGER *)&(stde->pps_ft2),&dw);
730 t = dw;
731 TRACE("ts2: %s\n",ctime(&t));
733 TRACE("startblock: %d\n",stde->pps_sb);
734 TRACE("size: %d\n",stde->pps_size);
737 /******************************************************************************
738 * STORAGE_init_storage [INTERNAL]
740 static BOOL
741 STORAGE_init_storage(stream_access16 *str) {
742 BYTE block[BIGSIZE];
743 LPDWORD bbs;
744 struct storage_header *sth;
745 struct storage_pps_entry *stde;
746 DWORD result;
748 if (str->hf)
749 SetFilePointer( str->hf, 0, NULL, SEEK_SET );
750 /* block -1 is the storage header */
751 sth = (struct storage_header*)block;
752 memcpy(sth->magic,STORAGE_magic,8);
753 memset(sth->unknown1,0,sizeof(sth->unknown1));
754 memset(sth->unknown2,0,sizeof(sth->unknown2));
755 memset(sth->unknown3,0,sizeof(sth->unknown3));
756 sth->num_of_bbd_blocks = 1;
757 sth->root_startblock = 1;
758 sth->sbd_startblock = 0xffffffff;
759 memset(sth->bbd_list,0xff,sizeof(sth->bbd_list));
760 sth->bbd_list[0] = 0;
761 if (str->hf) {
762 if (!WriteFile( str->hf, block, BIGSIZE, &result, NULL ) || result != BIGSIZE) return FALSE;
763 } else {
764 if (!_ilockbytes16_writeat(str->lockbytes, 0, BIGSIZE, block)) return FALSE;
766 /* block 0 is the big block directory */
767 bbs=(LPDWORD)block;
768 memset(block,0xff,sizeof(block)); /* mark all blocks as free */
769 bbs[0]=STORAGE_CHAINENTRY_ENDOFCHAIN; /* for this block */
770 bbs[1]=STORAGE_CHAINENTRY_ENDOFCHAIN; /* for directory entry */
771 if (str->hf) {
772 if (!WriteFile( str->hf, block, BIGSIZE, &result, NULL ) || result != BIGSIZE) return FALSE;
773 } else {
774 if (!_ilockbytes16_writeat(str->lockbytes, BIGSIZE, BIGSIZE, block)) return FALSE;
776 /* block 1 is the root directory entry */
777 memset(block,0x00,sizeof(block));
778 stde = (struct storage_pps_entry*)block;
779 MultiByteToWideChar( CP_ACP, 0, "RootEntry", -1, stde->pps_rawname,
780 sizeof(stde->pps_rawname)/sizeof(WCHAR));
781 stde->pps_sizeofname = (strlenW(stde->pps_rawname)+1) * sizeof(WCHAR);
782 stde->pps_type = 5;
783 stde->pps_dir = -1;
784 stde->pps_next = -1;
785 stde->pps_prev = -1;
786 stde->pps_sb = 0xffffffff;
787 stde->pps_size = 0;
788 if (str->hf) {
789 return (WriteFile( str->hf, block, BIGSIZE, &result, NULL ) && result == BIGSIZE);
790 } else {
791 return _ilockbytes16_writeat(str->lockbytes, BIGSIZE, BIGSIZE, block);
795 /******************************************************************************
796 * STORAGE_set_big_chain [Internal]
798 static BOOL
799 STORAGE_set_big_chain(stream_access16*str,int blocknr,INT type) {
800 BYTE block[BIGSIZE];
801 LPINT bbd = (LPINT)block;
802 int nextblocknr,bigblocknr;
803 struct storage_header sth;
804 BOOL ret;
806 READ_HEADER(str);
807 assert(blocknr!=type);
808 while (blocknr>=0) {
809 bigblocknr = sth.bbd_list[blocknr/128];
810 assert(bigblocknr>=0);
811 ret = STORAGE_get_big_block(str,bigblocknr,block);
812 assert(ret);
814 nextblocknr = bbd[blocknr&(128-1)];
815 bbd[blocknr&(128-1)] = type;
816 if (type>=0)
817 return TRUE;
818 ret = STORAGE_put_big_block(str,bigblocknr,block);
819 assert(ret);
820 type = STORAGE_CHAINENTRY_FREE;
821 blocknr = nextblocknr;
823 return TRUE;
826 /******************************************************************************
827 * STORAGE_set_small_chain [Internal]
829 static BOOL
830 STORAGE_set_small_chain(stream_access16*str,int blocknr,INT type) {
831 BYTE block[BIGSIZE];
832 LPINT sbd = (LPINT)block;
833 int lastblocknr,nextsmallblocknr,bigblocknr;
834 struct storage_header sth;
835 BOOL ret;
837 READ_HEADER(str);
839 assert(blocknr!=type);
840 lastblocknr=-129;bigblocknr=-2;
841 while (blocknr>=0) {
842 /* cache block ... */
843 if (lastblocknr/128!=blocknr/128) {
844 bigblocknr = STORAGE_get_nth_next_big_blocknr(str,sth.sbd_startblock,blocknr/128);
845 assert(bigblocknr>=0);
846 ret = STORAGE_get_big_block(str,bigblocknr,block);
847 assert(ret);
849 lastblocknr = blocknr;
850 nextsmallblocknr = sbd[blocknr&(128-1)];
851 sbd[blocknr&(128-1)] = type;
852 ret = STORAGE_put_big_block(str,bigblocknr,block);
853 assert(ret);
854 if (type>=0)
855 return TRUE;
856 type = STORAGE_CHAINENTRY_FREE;
857 blocknr = nextsmallblocknr;
859 return TRUE;
862 /******************************************************************************
863 * STORAGE_get_free_big_blocknr [Internal]
865 static int
866 STORAGE_get_free_big_blocknr(stream_access16 *str) {
867 BYTE block[BIGSIZE];
868 LPINT sbd = (LPINT)block;
869 int lastbigblocknr,i,bigblocknr;
870 unsigned int curblock;
871 struct storage_header sth;
872 BOOL ret;
874 READ_HEADER(str);
875 curblock = 0;
876 lastbigblocknr = -1;
877 bigblocknr = sth.bbd_list[curblock];
878 while (curblock<sth.num_of_bbd_blocks) {
879 assert(bigblocknr>=0);
880 ret = STORAGE_get_big_block(str,bigblocknr,block);
881 assert(ret);
882 for (i=0;i<128;i++)
883 if (sbd[i]==STORAGE_CHAINENTRY_FREE) {
884 sbd[i] = STORAGE_CHAINENTRY_ENDOFCHAIN;
885 ret = STORAGE_put_big_block(str,bigblocknr,block);
886 assert(ret);
887 memset(block,0x42,sizeof(block));
888 ret = STORAGE_put_big_block(str,i+curblock*128,block);
889 assert(ret);
890 return i+curblock*128;
892 lastbigblocknr = bigblocknr;
893 bigblocknr = sth.bbd_list[++curblock];
895 bigblocknr = curblock*128;
896 /* since we have marked all blocks from 0 up to curblock*128-1
897 * the next free one is curblock*128, where we happily put our
898 * next large block depot.
900 memset(block,0xff,sizeof(block));
901 /* mark the block allocated and returned by this function */
902 sbd[1] = STORAGE_CHAINENTRY_ENDOFCHAIN;
903 ret = STORAGE_put_big_block(str,bigblocknr,block);
904 assert(ret);
906 /* if we had a bbd block already (most likely) we need
907 * to link the new one into the chain
909 if (lastbigblocknr!=-1) {
910 ret = STORAGE_set_big_chain(str,lastbigblocknr,bigblocknr);
911 assert(ret);
913 sth.bbd_list[curblock]=bigblocknr;
914 sth.num_of_bbd_blocks++;
915 assert(sth.num_of_bbd_blocks==curblock+1);
916 ret = STORAGE_put_big_block(str,-1,(LPBYTE)&sth);
917 assert(ret);
919 /* Set the end of the chain for the bigblockdepots */
920 ret = STORAGE_set_big_chain(str,bigblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN);
921 assert(ret);
922 /* add 1, for the first entry is used for the additional big block
923 * depot. (means we already used bigblocknr) */
924 memset(block,0x42,sizeof(block));
925 /* allocate this block (filled with 0x42) */
926 ret = STORAGE_put_big_block(str,bigblocknr+1,block);
927 assert(ret);
928 return bigblocknr+1;
932 /******************************************************************************
933 * STORAGE_get_free_small_blocknr [Internal]
935 static int
936 STORAGE_get_free_small_blocknr(stream_access16 *str) {
937 BYTE block[BIGSIZE];
938 LPINT sbd = (LPINT)block;
939 int lastbigblocknr,newblocknr,i,curblock,bigblocknr;
940 struct storage_pps_entry root;
941 struct storage_header sth;
943 READ_HEADER(str);
944 bigblocknr = sth.sbd_startblock;
945 curblock = 0;
946 lastbigblocknr = -1;
947 newblocknr = -1;
948 while (bigblocknr>=0) {
949 if (!STORAGE_get_big_block(str,bigblocknr,block))
950 return -1;
951 for (i=0;i<128;i++)
952 if (sbd[i]==STORAGE_CHAINENTRY_FREE) {
953 sbd[i]=STORAGE_CHAINENTRY_ENDOFCHAIN;
954 newblocknr = i+curblock*128;
955 break;
957 if (i!=128)
958 break;
959 lastbigblocknr = bigblocknr;
960 bigblocknr = STORAGE_get_next_big_blocknr(str,bigblocknr);
961 curblock++;
963 if (newblocknr==-1) {
964 bigblocknr = STORAGE_get_free_big_blocknr(str);
965 if (bigblocknr<0)
966 return -1;
967 READ_HEADER(str);
968 memset(block,0xff,sizeof(block));
969 sbd[0]=STORAGE_CHAINENTRY_ENDOFCHAIN;
970 if (!STORAGE_put_big_block(str,bigblocknr,block))
971 return -1;
972 if (lastbigblocknr==-1) {
973 sth.sbd_startblock = bigblocknr;
974 if (!STORAGE_put_big_block(str,-1,(LPBYTE)&sth)) /* need to write it */
975 return -1;
976 } else {
977 if (!STORAGE_set_big_chain(str,lastbigblocknr,bigblocknr))
978 return -1;
980 if (!STORAGE_set_big_chain(str,bigblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
981 return -1;
982 newblocknr = curblock*128;
984 /* allocate enough big blocks for storing the allocated small block */
985 if (!STORAGE_get_root_pps_entry(str,&root))
986 return -1;
987 if (root.pps_sb==-1)
988 lastbigblocknr = -1;
989 else
990 lastbigblocknr = STORAGE_get_nth_next_big_blocknr(str,root.pps_sb,(root.pps_size-1)/BIGSIZE);
991 while (root.pps_size < (newblocknr*SMALLSIZE+SMALLSIZE-1)) {
992 /* we need to allocate more stuff */
993 bigblocknr = STORAGE_get_free_big_blocknr(str);
994 if (bigblocknr<0)
995 return -1;
996 READ_HEADER(str);
997 if (root.pps_sb==-1) {
998 root.pps_sb = bigblocknr;
999 root.pps_size += BIGSIZE;
1000 } else {
1001 if (!STORAGE_set_big_chain(str,lastbigblocknr,bigblocknr))
1002 return -1;
1003 root.pps_size += BIGSIZE;
1005 lastbigblocknr = bigblocknr;
1007 if (!STORAGE_set_big_chain(str,lastbigblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1008 return -1;
1009 if (!STORAGE_put_pps_entry(str,0,&root))
1010 return -1;
1011 return newblocknr;
1014 /******************************************************************************
1015 * STORAGE_get_free_pps_entry [Internal]
1017 static int
1018 STORAGE_get_free_pps_entry(stream_access16*str) {
1019 int blocknr, i, curblock, lastblocknr=-1;
1020 BYTE block[BIGSIZE];
1021 struct storage_pps_entry *stde = (struct storage_pps_entry*)block;
1022 struct storage_header sth;
1024 READ_HEADER(str);
1025 blocknr = sth.root_startblock;
1026 assert(blocknr>=0);
1027 curblock=0;
1028 while (blocknr>=0) {
1029 if (!STORAGE_get_big_block(str,blocknr,block))
1030 return -1;
1031 for (i=0;i<4;i++)
1032 if (stde[i].pps_sizeofname==0) /* free */
1033 return curblock*4+i;
1034 lastblocknr = blocknr;
1035 blocknr = STORAGE_get_next_big_blocknr(str,blocknr);
1036 curblock++;
1038 assert(blocknr==STORAGE_CHAINENTRY_ENDOFCHAIN);
1039 blocknr = STORAGE_get_free_big_blocknr(str);
1040 /* sth invalidated */
1041 if (blocknr<0)
1042 return -1;
1044 if (!STORAGE_set_big_chain(str,lastblocknr,blocknr))
1045 return -1;
1046 if (!STORAGE_set_big_chain(str,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1047 return -1;
1048 memset(block,0,sizeof(block));
1049 STORAGE_put_big_block(str,blocknr,block);
1050 return curblock*4;
1053 /* --- IStream16 implementation */
1055 typedef struct
1057 IStream16 IStream16_iface;
1058 LONG ref;
1059 /* IStream16 fields */
1060 SEGPTR thisptr; /* pointer to this struct as segmented */
1061 struct storage_pps_entry stde;
1062 int ppsent;
1063 ULARGE_INTEGER offset;
1064 stream_access16 str;
1065 } IStream16Impl;
1067 static inline IStream16Impl *impl_from_IStream16(IStream16 *iface)
1069 return CONTAINING_RECORD(iface, IStream16Impl, IStream16_iface);
1072 /******************************************************************************
1073 * IStream16_QueryInterface [STORAGE.518]
1075 HRESULT CDECL IStream16_fnQueryInterface(IStream16 *iface, REFIID refiid, void **obj)
1077 IStream16Impl *This = impl_from_IStream16(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)
1092 IStream16Impl *This = impl_from_IStream16(iface);
1093 return InterlockedIncrement(&This->ref);
1096 static void
1097 _ilockbytes16_addref(SEGPTR lockbytes) {
1098 DWORD args[1];
1099 HRESULT hres;
1101 args[0] = (DWORD)lockbytes; /* iface */
1102 if (!WOWCallback16Ex(
1103 (DWORD)((const ILockBytes16Vtbl*)MapSL(
1104 (SEGPTR)((LPLOCKBYTES16)MapSL(lockbytes))->lpVtbl)
1105 )->AddRef,
1106 WCB16_PASCAL,
1107 1*sizeof(DWORD),
1108 (LPVOID)args,
1109 (LPDWORD)&hres
1111 ERR("CallTo16 ILockBytes16::AddRef() failed, hres %x\n",hres);
1114 static void
1115 _ilockbytes16_release(SEGPTR lockbytes) {
1116 DWORD args[1];
1117 HRESULT hres;
1119 args[0] = (DWORD)lockbytes; /* iface */
1120 if (!WOWCallback16Ex(
1121 (DWORD)((const ILockBytes16Vtbl*)MapSL(
1122 (SEGPTR)((LPLOCKBYTES16)MapSL(lockbytes))->lpVtbl)
1123 )->Release,
1124 WCB16_PASCAL,
1125 1*sizeof(DWORD),
1126 (LPVOID)args,
1127 (LPDWORD)&hres
1129 ERR("CallTo16 ILockBytes16::Release() failed, hres %x\n",hres);
1132 static void
1133 _ilockbytes16_flush(SEGPTR lockbytes) {
1134 DWORD args[1];
1135 HRESULT hres;
1137 args[0] = (DWORD)lockbytes; /* iface */
1138 if (!WOWCallback16Ex(
1139 (DWORD)((const ILockBytes16Vtbl*)MapSL(
1140 (SEGPTR)((LPLOCKBYTES16)MapSL(lockbytes))->lpVtbl)
1141 )->Flush,
1142 WCB16_PASCAL,
1143 1*sizeof(DWORD),
1144 (LPVOID)args,
1145 (LPDWORD)&hres
1147 ERR("CallTo16 ILockBytes16::Flush() failed, hres %x\n",hres);
1150 /******************************************************************************
1151 * IStream16_Release [STORAGE.520]
1153 ULONG CDECL IStream16_fnRelease(IStream16 *iface)
1155 IStream16Impl *This = impl_from_IStream16(iface);
1156 ULONG ref;
1158 if (This->str.hf)
1159 FlushFileBuffers(This->str.hf);
1160 else
1161 _ilockbytes16_flush(This->str.lockbytes);
1162 ref = InterlockedDecrement(&This->ref);
1163 if (ref)
1164 return ref;
1166 if (This->str.hf)
1167 CloseHandle(This->str.hf);
1168 else
1169 _ilockbytes16_release(This->str.lockbytes);
1170 UnMapLS( This->thisptr );
1171 HeapFree( GetProcessHeap(), 0, This );
1172 return 0;
1175 /******************************************************************************
1176 * IStream16_Seek [STORAGE.523]
1178 * FIXME
1179 * Does not handle 64 bits
1181 HRESULT CDECL IStream16_fnSeek(IStream16 *iface, LARGE_INTEGER offset, DWORD whence,
1182 ULARGE_INTEGER *newpos)
1184 IStream16Impl *This = impl_from_IStream16(iface);
1185 TRACE_(relay)("(%p)->([%d.%d],%d,%p)\n",This,offset.u.HighPart,offset.u.LowPart,whence,newpos);
1187 switch (whence) {
1188 case STREAM_SEEK_SET:
1189 This->offset.QuadPart = offset.QuadPart;
1190 break;
1191 case STREAM_SEEK_CUR:
1192 if ((offset.QuadPart < 0 && -offset.QuadPart > This->offset.QuadPart) ||
1193 (offset.QuadPart > 0 && -offset.QuadPart <= This->offset.QuadPart))
1194 return STG_E_INVALIDFUNCTION;
1195 This->offset.QuadPart += offset.QuadPart;
1196 break;
1197 case STREAM_SEEK_END:
1198 if (-offset.QuadPart > This->stde.pps_size)
1199 return STG_E_INVALIDFUNCTION;
1201 This->offset.QuadPart = This->stde.pps_size + offset.QuadPart;
1202 break;
1205 if (This->offset.QuadPart>This->stde.pps_size)
1206 This->offset.QuadPart=This->stde.pps_size;
1207 if (newpos) *newpos = This->offset;
1208 return S_OK;
1211 /******************************************************************************
1212 * IStream16_Read [STORAGE.521]
1214 HRESULT CDECL IStream16_fnRead(IStream16 *iface, void *pv, ULONG cb, ULONG *pcbRead)
1216 IStream16Impl *This = impl_from_IStream16(iface);
1217 BYTE block[BIGSIZE];
1218 ULONG *bytesread=pcbRead,xxread;
1219 int blocknr;
1220 LPBYTE pbv = pv;
1222 TRACE_(relay)("(%p)->(%p,%d,%p)\n",This,pv,cb,pcbRead);
1223 if (!pcbRead) bytesread=&xxread;
1224 *bytesread = 0;
1226 if (cb>This->stde.pps_size-This->offset.u.LowPart)
1227 cb=This->stde.pps_size-This->offset.u.LowPart;
1228 if (This->stde.pps_size < 0x1000) {
1229 /* use small block reader */
1230 blocknr = STORAGE_get_nth_next_small_blocknr(&This->str,This->stde.pps_sb,This->offset.u.LowPart/SMALLSIZE);
1231 while (cb) {
1232 unsigned int cc;
1234 if (!STORAGE_get_small_block(&This->str,blocknr,block)) {
1235 WARN("small block read failed!!!\n");
1236 return E_FAIL;
1238 cc = cb;
1239 if (cc>SMALLSIZE-(This->offset.u.LowPart&(SMALLSIZE-1)))
1240 cc=SMALLSIZE-(This->offset.u.LowPart&(SMALLSIZE-1));
1241 memcpy(pbv,block+(This->offset.u.LowPart&(SMALLSIZE-1)),cc);
1242 This->offset.u.LowPart+=cc;
1243 pbv+=cc;
1244 *bytesread+=cc;
1245 cb-=cc;
1246 blocknr = STORAGE_get_next_small_blocknr(&This->str,blocknr);
1248 } else {
1249 /* use big block reader */
1250 blocknr = STORAGE_get_nth_next_big_blocknr(&This->str,This->stde.pps_sb,This->offset.u.LowPart/BIGSIZE);
1251 while (cb) {
1252 unsigned int cc;
1254 if (!STORAGE_get_big_block(&This->str,blocknr,block)) {
1255 WARN("big block read failed!!!\n");
1256 return E_FAIL;
1258 cc = cb;
1259 if (cc>BIGSIZE-(This->offset.u.LowPart&(BIGSIZE-1)))
1260 cc=BIGSIZE-(This->offset.u.LowPart&(BIGSIZE-1));
1261 memcpy(pbv,block+(This->offset.u.LowPart&(BIGSIZE-1)),cc);
1262 This->offset.u.LowPart+=cc;
1263 pbv+=cc;
1264 *bytesread+=cc;
1265 cb-=cc;
1266 blocknr=STORAGE_get_next_big_blocknr(&This->str,blocknr);
1269 return S_OK;
1272 /******************************************************************************
1273 * IStream16_Write [STORAGE.522]
1275 HRESULT CDECL IStream16_fnWrite(IStream16 *iface, const void *pv, ULONG cb, ULONG *pcbWrite)
1277 IStream16Impl *This = impl_from_IStream16(iface);
1278 BYTE block[BIGSIZE];
1279 ULONG *byteswritten=pcbWrite,xxwritten;
1280 int oldsize,newsize,i,curoffset=0,lastblocknr,blocknr,cc;
1281 const BYTE* pbv = pv;
1283 if (!pcbWrite) byteswritten=&xxwritten;
1284 *byteswritten = 0;
1286 TRACE_(relay)("(%p)->(%p,%d,%p)\n",This,pv,cb,pcbWrite);
1287 /* do we need to junk some blocks? */
1288 newsize = This->offset.u.LowPart+cb;
1289 oldsize = This->stde.pps_size;
1290 if (newsize < oldsize) {
1291 if (oldsize < 0x1000) {
1292 /* only small blocks */
1293 blocknr=STORAGE_get_nth_next_small_blocknr(&This->str,This->stde.pps_sb,newsize/SMALLSIZE);
1295 assert(blocknr>=0);
1297 /* will set the rest of the chain to 'free' */
1298 if (!STORAGE_set_small_chain(&This->str,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1299 return E_FAIL;
1300 } else {
1301 if (newsize >= 0x1000) {
1302 blocknr=STORAGE_get_nth_next_big_blocknr(&This->str,This->stde.pps_sb,newsize/BIGSIZE);
1303 assert(blocknr>=0);
1305 /* will set the rest of the chain to 'free' */
1306 if (!STORAGE_set_big_chain(&This->str,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1307 return E_FAIL;
1308 } else {
1309 /* Migrate large blocks to small blocks
1310 * (we just migrate newsize bytes)
1312 LPBYTE curdata,data = HeapAlloc(GetProcessHeap(),0,newsize+BIGSIZE);
1313 HRESULT r = E_FAIL;
1315 cc = newsize;
1316 blocknr = This->stde.pps_sb;
1317 curdata = data;
1318 while (cc>0) {
1319 if (!STORAGE_get_big_block(&This->str,blocknr,curdata)) {
1320 HeapFree(GetProcessHeap(),0,data);
1321 return E_FAIL;
1323 curdata += BIGSIZE;
1324 cc -= BIGSIZE;
1325 blocknr = STORAGE_get_next_big_blocknr(&This->str,blocknr);
1327 /* frees complete chain for this stream */
1328 if (!STORAGE_set_big_chain(&This->str,This->stde.pps_sb,STORAGE_CHAINENTRY_FREE))
1329 goto err;
1330 curdata = data;
1331 blocknr = This->stde.pps_sb = STORAGE_get_free_small_blocknr(&This->str);
1332 if (blocknr<0)
1333 goto err;
1334 cc = newsize;
1335 while (cc>0) {
1336 if (!STORAGE_put_small_block(&This->str,blocknr,curdata))
1337 goto err;
1338 cc -= SMALLSIZE;
1339 if (cc<=0) {
1340 if (!STORAGE_set_small_chain(&This->str,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1341 goto err;
1342 break;
1343 } else {
1344 int newblocknr = STORAGE_get_free_small_blocknr(&This->str);
1345 if (newblocknr<0)
1346 goto err;
1347 if (!STORAGE_set_small_chain(&This->str,blocknr,newblocknr))
1348 goto err;
1349 blocknr = newblocknr;
1351 curdata += SMALLSIZE;
1353 r = S_OK;
1354 err:
1355 HeapFree(GetProcessHeap(),0,data);
1356 if(r != S_OK)
1357 return r;
1360 This->stde.pps_size = newsize;
1363 if (newsize > oldsize) {
1364 if (oldsize >= 0x1000) {
1365 /* should return the block right before the 'endofchain' */
1366 blocknr = STORAGE_get_nth_next_big_blocknr(&This->str,This->stde.pps_sb,This->stde.pps_size/BIGSIZE);
1367 assert(blocknr>=0);
1368 lastblocknr = blocknr;
1369 for (i=oldsize/BIGSIZE;i<newsize/BIGSIZE;i++) {
1370 blocknr = STORAGE_get_free_big_blocknr(&This->str);
1371 if (blocknr<0)
1372 return E_FAIL;
1373 if (!STORAGE_set_big_chain(&This->str,lastblocknr,blocknr))
1374 return E_FAIL;
1375 lastblocknr = blocknr;
1377 if (!STORAGE_set_big_chain(&This->str,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1378 return E_FAIL;
1379 } else {
1380 if (newsize < 0x1000) {
1381 /* find startblock */
1382 if (!oldsize)
1383 This->stde.pps_sb = blocknr = STORAGE_get_free_small_blocknr(&This->str);
1384 else
1385 blocknr = STORAGE_get_nth_next_small_blocknr(&This->str,This->stde.pps_sb,This->stde.pps_size/SMALLSIZE);
1386 if (blocknr<0)
1387 return E_FAIL;
1389 /* allocate required new small blocks */
1390 lastblocknr = blocknr;
1391 for (i=oldsize/SMALLSIZE;i<newsize/SMALLSIZE;i++) {
1392 blocknr = STORAGE_get_free_small_blocknr(&This->str);
1393 if (blocknr<0)
1394 return E_FAIL;
1395 if (!STORAGE_set_small_chain(&This->str,lastblocknr,blocknr))
1396 return E_FAIL;
1397 lastblocknr = blocknr;
1399 /* and terminate the chain */
1400 if (!STORAGE_set_small_chain(&This->str,lastblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1401 return E_FAIL;
1402 } else {
1403 if (!oldsize) {
1404 /* no single block allocated yet */
1405 blocknr=STORAGE_get_free_big_blocknr(&This->str);
1406 if (blocknr<0)
1407 return E_FAIL;
1408 This->stde.pps_sb = blocknr;
1409 } else {
1410 /* Migrate small blocks to big blocks */
1411 LPBYTE curdata,data = HeapAlloc(GetProcessHeap(),0,oldsize+BIGSIZE);
1412 HRESULT r = E_FAIL;
1414 cc = oldsize;
1415 blocknr = This->stde.pps_sb;
1416 curdata = data;
1417 /* slurp in */
1418 while (cc>0) {
1419 if (!STORAGE_get_small_block(&This->str,blocknr,curdata))
1420 goto err2;
1421 curdata += SMALLSIZE;
1422 cc -= SMALLSIZE;
1423 blocknr = STORAGE_get_next_small_blocknr(&This->str,blocknr);
1425 /* free small block chain */
1426 if (!STORAGE_set_small_chain(&This->str,This->stde.pps_sb,STORAGE_CHAINENTRY_FREE))
1427 goto err2;
1428 curdata = data;
1429 blocknr = This->stde.pps_sb = STORAGE_get_free_big_blocknr(&This->str);
1430 if (blocknr<0)
1431 goto err2;
1432 /* put the data into the big blocks */
1433 cc = This->stde.pps_size;
1434 while (cc>0) {
1435 if (!STORAGE_put_big_block(&This->str,blocknr,curdata))
1436 goto err2;
1437 cc -= BIGSIZE;
1438 if (cc<=0) {
1439 if (!STORAGE_set_big_chain(&This->str,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1440 goto err2;
1441 break;
1442 } else {
1443 int newblocknr = STORAGE_get_free_big_blocknr(&This->str);
1444 if (newblocknr<0)
1445 goto err2;
1446 if (!STORAGE_set_big_chain(&This->str,blocknr,newblocknr))
1447 goto err2;
1448 blocknr = newblocknr;
1450 curdata += BIGSIZE;
1452 r = S_OK;
1453 err2:
1454 HeapFree(GetProcessHeap(),0,data);
1455 if(r != S_OK)
1456 return r;
1458 /* generate big blocks to fit the new data */
1459 lastblocknr = blocknr;
1460 for (i=oldsize/BIGSIZE;i<newsize/BIGSIZE;i++) {
1461 blocknr = STORAGE_get_free_big_blocknr(&This->str);
1462 if (blocknr<0)
1463 return E_FAIL;
1464 if (!STORAGE_set_big_chain(&This->str,lastblocknr,blocknr))
1465 return E_FAIL;
1466 lastblocknr = blocknr;
1468 /* terminate chain */
1469 if (!STORAGE_set_big_chain(&This->str,lastblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1470 return E_FAIL;
1473 This->stde.pps_size = newsize;
1476 /* There are just some cases where we didn't modify it, we write it out
1477 * every time
1479 if (!STORAGE_put_pps_entry(&This->str,This->ppsent,&(This->stde)))
1480 return E_FAIL;
1482 /* finally the write pass */
1483 if (This->stde.pps_size < 0x1000) {
1484 blocknr = STORAGE_get_nth_next_small_blocknr(&This->str,This->stde.pps_sb,This->offset.u.LowPart/SMALLSIZE);
1485 assert(blocknr>=0);
1486 while (cb>0) {
1487 /* we ensured that it is allocated above */
1488 assert(blocknr>=0);
1489 /* Read old block every time, since we can have
1490 * overlapping data at START and END of the write
1492 if (!STORAGE_get_small_block(&This->str,blocknr,block))
1493 return E_FAIL;
1495 cc = SMALLSIZE-(This->offset.u.LowPart&(SMALLSIZE-1));
1496 if (cc>cb)
1497 cc=cb;
1498 memcpy( ((LPBYTE)block)+(This->offset.u.LowPart&(SMALLSIZE-1)),
1499 pbv+curoffset,
1502 if (!STORAGE_put_small_block(&This->str,blocknr,block))
1503 return E_FAIL;
1504 cb -= cc;
1505 curoffset += cc;
1506 pbv += cc;
1507 This->offset.u.LowPart += cc;
1508 *byteswritten += cc;
1509 blocknr = STORAGE_get_next_small_blocknr(&This->str,blocknr);
1511 } else {
1512 blocknr = STORAGE_get_nth_next_big_blocknr(&This->str,This->stde.pps_sb,This->offset.u.LowPart/BIGSIZE);
1513 assert(blocknr>=0);
1514 while (cb>0) {
1515 /* we ensured that it is allocated above, so it better is */
1516 assert(blocknr>=0);
1517 /* read old block every time, since we can have
1518 * overlapping data at START and END of the write
1520 if (!STORAGE_get_big_block(&This->str,blocknr,block))
1521 return E_FAIL;
1523 cc = BIGSIZE-(This->offset.u.LowPart&(BIGSIZE-1));
1524 if (cc>cb)
1525 cc=cb;
1526 memcpy( ((LPBYTE)block)+(This->offset.u.LowPart&(BIGSIZE-1)),
1527 pbv+curoffset,
1530 if (!STORAGE_put_big_block(&This->str,blocknr,block))
1531 return E_FAIL;
1532 cb -= cc;
1533 curoffset += cc;
1534 pbv += cc;
1535 This->offset.u.LowPart += cc;
1536 *byteswritten += cc;
1537 blocknr = STORAGE_get_next_big_blocknr(&This->str,blocknr);
1540 return S_OK;
1543 /******************************************************************************
1544 * _create_istream16 [Internal]
1546 static void _create_istream16(LPSTREAM16 *str) {
1547 IStream16Impl* lpst;
1549 if (!strvt16.QueryInterface) {
1550 HMODULE16 wp = GetModuleHandle16("STORAGE");
1551 if (wp>=32) {
1552 /* FIXME: what is This GetProcAddress16. Should the name be IStream16_QueryInterface of IStream16_fnQueryInterface */
1553 #define VTENT(xfn) strvt16.xfn = (void*)GetProcAddress16(wp,"IStream16_"#xfn);assert(strvt16.xfn)
1554 VTENT(QueryInterface);
1555 VTENT(AddRef);
1556 VTENT(Release);
1557 VTENT(Read);
1558 VTENT(Write);
1559 VTENT(Seek);
1560 VTENT(SetSize);
1561 VTENT(CopyTo);
1562 VTENT(Commit);
1563 VTENT(Revert);
1564 VTENT(LockRegion);
1565 VTENT(UnlockRegion);
1566 VTENT(Stat);
1567 VTENT(Clone);
1568 #undef VTENT
1569 segstrvt16 = (const IStream16Vtbl*)MapLS( &strvt16 );
1570 } else {
1571 #define VTENT(xfn) strvt16.xfn = IStream16_fn##xfn;
1572 VTENT(QueryInterface);
1573 VTENT(AddRef);
1574 VTENT(Release);
1575 VTENT(Read);
1576 VTENT(Write);
1577 VTENT(Seek);
1579 VTENT(CopyTo);
1580 VTENT(Commit);
1581 VTENT(SetSize);
1582 VTENT(Revert);
1583 VTENT(LockRegion);
1584 VTENT(UnlockRegion);
1585 VTENT(Stat);
1586 VTENT(Clone);
1588 #undef VTENT
1589 segstrvt16 = &strvt16;
1592 lpst = HeapAlloc( GetProcessHeap(), 0, sizeof(*lpst) );
1593 lpst->IStream16_iface.lpVtbl = segstrvt16;
1594 lpst->ref = 1;
1595 lpst->thisptr = MapLS( lpst );
1596 lpst->str.hf = NULL;
1597 lpst->str.lockbytes = 0;
1598 *str = (void*)lpst->thisptr;
1601 static inline IStorage16Impl *impl_from_IStorage16(IStorage16 *iface)
1603 return CONTAINING_RECORD(iface, IStorage16Impl, IStorage16_iface);
1606 /******************************************************************************
1607 * IStorage16_QueryInterface [STORAGE.500]
1609 HRESULT CDECL IStorage16_fnQueryInterface(IStorage16 *iface, REFIID refiid, void **obj)
1611 IStorage16Impl *This = impl_from_IStorage16(iface);
1613 TRACE_(relay)("(%p)->(%s,%p)\n",This,debugstr_guid(refiid),obj);
1615 if (!memcmp(&IID_IUnknown,refiid,sizeof(IID_IUnknown))) {
1616 *obj = This;
1617 return 0;
1619 return OLE_E_ENUM_NOMORE;
1622 /******************************************************************************
1623 * IStorage16_AddRef [STORAGE.501]
1625 ULONG CDECL IStorage16_fnAddRef(IStorage16 *iface)
1627 IStorage16Impl *This = impl_from_IStorage16(iface);
1628 return InterlockedIncrement(&This->ref);
1631 /******************************************************************************
1632 * IStorage16_Release [STORAGE.502]
1634 ULONG CDECL IStorage16_fnRelease(IStorage16 *iface)
1636 IStorage16Impl *This = impl_from_IStorage16(iface);
1637 ULONG ref;
1638 ref = InterlockedDecrement(&This->ref);
1639 if (!ref)
1641 UnMapLS( This->thisptr );
1642 HeapFree( GetProcessHeap(), 0, This );
1644 return ref;
1647 /******************************************************************************
1648 * IStorage16_Stat [STORAGE.517]
1650 HRESULT CDECL IStorage16_fnStat(IStorage16 *iface, STATSTG16 *pstatstg, DWORD grfStatFlag)
1652 IStorage16Impl *This = impl_from_IStorage16(iface);
1653 DWORD len = WideCharToMultiByte( CP_ACP, 0, This->stde.pps_rawname, -1, NULL, 0, NULL, NULL );
1654 LPSTR nameA = HeapAlloc( GetProcessHeap(), 0, len );
1656 TRACE("(%p)->(%p,0x%08x)\n",
1657 This,pstatstg,grfStatFlag
1659 WideCharToMultiByte( CP_ACP, 0, This->stde.pps_rawname, -1, nameA, len, NULL, NULL );
1660 pstatstg->pwcsName=(LPOLESTR16)MapLS( nameA );
1661 pstatstg->type = This->stde.pps_type;
1662 pstatstg->cbSize.u.LowPart = This->stde.pps_size;
1663 pstatstg->mtime = This->stde.pps_ft1; /* FIXME */ /* why? */
1664 pstatstg->atime = This->stde.pps_ft2; /* FIXME */
1665 pstatstg->ctime = This->stde.pps_ft2; /* FIXME */
1666 pstatstg->grfMode = 0; /* FIXME */
1667 pstatstg->grfLocksSupported = 0; /* FIXME */
1668 pstatstg->clsid = This->stde.pps_guid;
1669 pstatstg->grfStateBits = 0; /* FIXME */
1670 pstatstg->reserved = 0;
1671 return S_OK;
1674 /******************************************************************************
1675 * IStorage16_Commit [STORAGE.509]
1677 HRESULT CDECL IStorage16_fnCommit(IStorage16 *iface, DWORD commitflags)
1679 IStorage16Impl *This = impl_from_IStorage16(iface);
1680 FIXME("(%p)->(0x%08x),STUB!\n",
1681 This,commitflags
1683 return S_OK;
1686 /******************************************************************************
1687 * IStorage16_CopyTo [STORAGE.507]
1689 HRESULT CDECL IStorage16_fnCopyTo(IStorage16 *iface, DWORD ciidExclude, const IID *rgiidExclude,
1690 SNB16 SNB16Exclude, IStorage16 *pstgDest)
1692 IStorage16Impl *This = impl_from_IStorage16(iface);
1693 FIXME("IStorage16(%p)->(0x%08x,%s,%p,%p),stub!\n",
1694 This,ciidExclude,debugstr_guid(rgiidExclude),SNB16Exclude,pstgDest
1696 return S_OK;
1700 /******************************************************************************
1701 * IStorage16_CreateStorage [STORAGE.505]
1703 HRESULT CDECL IStorage16_fnCreateStorage(IStorage16 *iface, LPCOLESTR16 pwcsName, DWORD grfMode,
1704 DWORD dwStgFormat, DWORD reserved2, IStorage16 **ppstg)
1706 IStorage16Impl *This = impl_from_IStorage16(iface);
1707 IStorage16Impl* lpstg;
1708 int ppsent,x;
1709 struct storage_pps_entry stde;
1710 struct storage_header sth;
1711 BOOL ret;
1712 int nPPSEntries;
1714 READ_HEADER(&This->str);
1715 TRACE("(%p)->(%s,0x%08x,0x%08x,0x%08x,%p)\n",
1716 This,pwcsName,grfMode,dwStgFormat,reserved2,ppstg
1718 if (grfMode & STGM_TRANSACTED)
1719 FIXME("We do not support transacted Compound Storage. Using direct mode.\n");
1720 _create_istorage16(ppstg);
1721 lpstg = MapSL((SEGPTR)*ppstg);
1722 if (This->str.hf) {
1723 DuplicateHandle( GetCurrentProcess(), This->str.hf, GetCurrentProcess(),
1724 &lpstg->str.hf, 0, TRUE, DUPLICATE_SAME_ACCESS );
1725 } else {
1726 lpstg->str.lockbytes = This->str.lockbytes;
1727 _ilockbytes16_addref(This->str.lockbytes);
1730 ppsent=STORAGE_get_free_pps_entry(&lpstg->str);
1731 if (ppsent<0)
1732 return E_FAIL;
1733 stde=This->stde;
1734 if (stde.pps_dir==-1) {
1735 stde.pps_dir = ppsent;
1736 x = This->ppsent;
1737 } else {
1738 FIXME(" use prev chain too ?\n");
1739 x=stde.pps_dir;
1740 if (1!=STORAGE_get_pps_entry(&lpstg->str,x,&stde))
1741 return E_FAIL;
1742 while (stde.pps_next!=-1) {
1743 x=stde.pps_next;
1744 if (1!=STORAGE_get_pps_entry(&lpstg->str,x,&stde))
1745 return E_FAIL;
1747 stde.pps_next = ppsent;
1749 ret = STORAGE_put_pps_entry(&lpstg->str,x,&stde);
1750 assert(ret);
1751 nPPSEntries = STORAGE_get_pps_entry(&lpstg->str,ppsent,&(lpstg->stde));
1752 assert(nPPSEntries == 1);
1753 MultiByteToWideChar( CP_ACP, 0, pwcsName, -1, lpstg->stde.pps_rawname,
1754 sizeof(lpstg->stde.pps_rawname)/sizeof(WCHAR));
1755 lpstg->stde.pps_sizeofname = (strlenW(lpstg->stde.pps_rawname)+1)*sizeof(WCHAR);
1756 lpstg->stde.pps_next = -1;
1757 lpstg->stde.pps_prev = -1;
1758 lpstg->stde.pps_dir = -1;
1759 lpstg->stde.pps_sb = -1;
1760 lpstg->stde.pps_size = 0;
1761 lpstg->stde.pps_type = 1;
1762 lpstg->ppsent = ppsent;
1763 /* FIXME: timestamps? */
1764 if (!STORAGE_put_pps_entry(&lpstg->str,ppsent,&(lpstg->stde)))
1765 return E_FAIL;
1766 return S_OK;
1769 /******************************************************************************
1770 * IStorage16_CreateStream [STORAGE.503]
1772 HRESULT CDECL IStorage16_fnCreateStream(IStorage16 *iface, LPCOLESTR16 pwcsName, DWORD grfMode,
1773 DWORD reserved1, DWORD reserved2, IStream16 **ppstm)
1775 IStorage16Impl *This = impl_from_IStorage16(iface);
1776 IStream16Impl* lpstr;
1777 int ppsent,x;
1778 struct storage_pps_entry stde;
1779 BOOL ret;
1780 int nPPSEntries;
1782 TRACE("(%p)->(%s,0x%08x,0x%08x,0x%08x,%p)\n",
1783 This,pwcsName,grfMode,reserved1,reserved2,ppstm
1785 if (grfMode & STGM_TRANSACTED)
1786 FIXME("We do not support transacted Compound Storage. Using direct mode.\n");
1787 _create_istream16(ppstm);
1788 lpstr = MapSL((SEGPTR)*ppstm);
1789 if (This->str.hf) {
1790 DuplicateHandle( GetCurrentProcess(), This->str.hf, GetCurrentProcess(),
1791 &lpstr->str.hf, 0, TRUE, DUPLICATE_SAME_ACCESS );
1792 } else {
1793 lpstr->str.lockbytes = This->str.lockbytes;
1794 _ilockbytes16_addref(This->str.lockbytes);
1796 lpstr->offset.u.LowPart = 0;
1797 lpstr->offset.u.HighPart= 0;
1799 ppsent=STORAGE_get_free_pps_entry(&lpstr->str);
1800 if (ppsent<0)
1801 return E_FAIL;
1802 stde=This->stde;
1803 if (stde.pps_next==-1)
1804 x=This->ppsent;
1805 else
1806 while (stde.pps_next!=-1) {
1807 x=stde.pps_next;
1808 if (1!=STORAGE_get_pps_entry(&lpstr->str,x,&stde))
1809 return E_FAIL;
1811 stde.pps_next = ppsent;
1812 ret = STORAGE_put_pps_entry(&lpstr->str,x,&stde);
1813 assert(ret);
1814 nPPSEntries = STORAGE_get_pps_entry(&lpstr->str,ppsent,&(lpstr->stde));
1815 assert(nPPSEntries == 1);
1816 MultiByteToWideChar( CP_ACP, 0, pwcsName, -1, lpstr->stde.pps_rawname,
1817 sizeof(lpstr->stde.pps_rawname)/sizeof(WCHAR));
1818 lpstr->stde.pps_sizeofname = (strlenW(lpstr->stde.pps_rawname)+1) * sizeof(WCHAR);
1819 lpstr->stde.pps_next = -1;
1820 lpstr->stde.pps_prev = -1;
1821 lpstr->stde.pps_dir = -1;
1822 lpstr->stde.pps_sb = -1;
1823 lpstr->stde.pps_size = 0;
1824 lpstr->stde.pps_type = 2;
1825 lpstr->ppsent = ppsent;
1827 /* FIXME: timestamps? */
1828 if (!STORAGE_put_pps_entry(&lpstr->str,ppsent,&(lpstr->stde)))
1829 return E_FAIL;
1830 return S_OK;
1833 /******************************************************************************
1834 * IStorage16_OpenStorage [STORAGE.506]
1836 HRESULT CDECL IStorage16_fnOpenStorage(IStorage16 *iface, LPCOLESTR16 pwcsName,
1837 IStorage16 *pstgPrio, DWORD grfMode, SNB16 snbExclude, DWORD reserved, IStorage16 **ppstg)
1839 IStorage16Impl *This = impl_from_IStorage16(iface);
1840 IStorage16Impl *lpstg;
1841 WCHAR name[33];
1842 int newpps;
1844 TRACE("(%p)->(%s,%p,0x%08x,%p,0x%08x,%p)\n",
1845 This,pwcsName,pstgPrio,grfMode,snbExclude,reserved,ppstg
1847 if (grfMode & STGM_TRANSACTED)
1848 FIXME("We do not support transacted Compound Storage. Using direct mode.\n");
1849 _create_istorage16(ppstg);
1850 lpstg = MapSL((SEGPTR)*ppstg);
1851 if (This->str.hf) {
1852 DuplicateHandle( GetCurrentProcess(), This->str.hf, GetCurrentProcess(),
1853 &lpstg->str.hf, 0, TRUE, DUPLICATE_SAME_ACCESS );
1854 } else {
1855 lpstg->str.lockbytes = This->str.lockbytes;
1856 _ilockbytes16_addref(This->str.lockbytes);
1858 MultiByteToWideChar( CP_ACP, 0, pwcsName, -1, name, sizeof(name)/sizeof(WCHAR));
1859 newpps = STORAGE_look_for_named_pps(&lpstg->str,This->stde.pps_dir,name);
1860 if (newpps==-1) {
1861 IStorage16_fnRelease(&lpstg->IStorage16_iface);
1862 *ppstg = NULL;
1863 return E_FAIL;
1866 if (1!=STORAGE_get_pps_entry(&lpstg->str,newpps,&(lpstg->stde))) {
1867 IStorage16_fnRelease(&lpstg->IStorage16_iface);
1868 *ppstg = NULL;
1869 return E_FAIL;
1871 lpstg->ppsent = newpps;
1872 return S_OK;
1875 /******************************************************************************
1876 * IStorage16_OpenStream [STORAGE.504]
1878 HRESULT CDECL IStorage16_fnOpenStream(IStorage16 *iface, LPCOLESTR16 pwcsName, void *reserved1,
1879 DWORD grfMode, DWORD reserved2, IStream16 **ppstm)
1881 IStorage16Impl *This = impl_from_IStorage16(iface);
1882 IStream16Impl* lpstr;
1883 WCHAR name[33];
1884 int newpps;
1886 TRACE("(%p)->(%s,%p,0x%08x,0x%08x,%p)\n",
1887 This,pwcsName,reserved1,grfMode,reserved2,ppstm
1889 if (grfMode & STGM_TRANSACTED)
1890 FIXME("We do not support transacted Compound Storage. Using direct mode.\n");
1891 _create_istream16(ppstm);
1892 lpstr = MapSL((SEGPTR)*ppstm);
1893 if (This->str.hf) {
1894 DuplicateHandle( GetCurrentProcess(), This->str.hf, GetCurrentProcess(),
1895 &lpstr->str.hf, 0, TRUE, DUPLICATE_SAME_ACCESS );
1896 } else {
1897 lpstr->str.lockbytes = This->str.lockbytes;
1898 _ilockbytes16_addref(This->str.lockbytes);
1900 MultiByteToWideChar( CP_ACP, 0, pwcsName, -1, name, sizeof(name)/sizeof(WCHAR));
1901 newpps = STORAGE_look_for_named_pps(&lpstr->str,This->stde.pps_dir,name);
1902 if (newpps==-1) {
1903 IStream16_fnRelease(&lpstr->IStream16_iface);
1904 *ppstm = NULL;
1905 return E_FAIL;
1908 if (1!=STORAGE_get_pps_entry(&lpstr->str,newpps,&(lpstr->stde))) {
1909 IStream16_fnRelease(&lpstr->IStream16_iface);
1910 *ppstm = NULL;
1911 return E_FAIL;
1913 lpstr->offset.u.LowPart = 0;
1914 lpstr->offset.u.HighPart = 0;
1915 lpstr->ppsent = newpps;
1916 return S_OK;
1919 /******************************************************************************
1920 * _create_istorage16 [INTERNAL]
1922 static void _create_istorage16(LPSTORAGE16 *stg) {
1923 IStorage16Impl* lpst;
1925 if (!stvt16.QueryInterface) {
1926 HMODULE16 wp = GetModuleHandle16("STORAGE");
1927 if (wp>=32) {
1928 #define VTENT(xfn) stvt16.xfn = (void*)GetProcAddress16(wp,"IStorage16_"#xfn);
1929 VTENT(QueryInterface)
1930 VTENT(AddRef)
1931 VTENT(Release)
1932 VTENT(CreateStream)
1933 VTENT(OpenStream)
1934 VTENT(CreateStorage)
1935 VTENT(OpenStorage)
1936 VTENT(CopyTo)
1937 VTENT(MoveElementTo)
1938 VTENT(Commit)
1939 VTENT(Revert)
1940 VTENT(EnumElements)
1941 VTENT(DestroyElement)
1942 VTENT(RenameElement)
1943 VTENT(SetElementTimes)
1944 VTENT(SetClass)
1945 VTENT(SetStateBits)
1946 VTENT(Stat)
1947 #undef VTENT
1948 segstvt16 = (const IStorage16Vtbl*)MapLS( &stvt16 );
1949 } else {
1950 #define VTENT(xfn) stvt16.xfn = IStorage16_fn##xfn;
1951 VTENT(QueryInterface)
1952 VTENT(AddRef)
1953 VTENT(Release)
1954 VTENT(CreateStream)
1955 VTENT(OpenStream)
1956 VTENT(CreateStorage)
1957 VTENT(OpenStorage)
1958 VTENT(CopyTo)
1959 VTENT(Commit)
1960 /* not (yet) implemented ...
1961 VTENT(MoveElementTo)
1962 VTENT(Revert)
1963 VTENT(EnumElements)
1964 VTENT(DestroyElement)
1965 VTENT(RenameElement)
1966 VTENT(SetElementTimes)
1967 VTENT(SetClass)
1968 VTENT(SetStateBits)
1969 VTENT(Stat)
1971 #undef VTENT
1972 segstvt16 = &stvt16;
1975 lpst = HeapAlloc( GetProcessHeap(), 0, sizeof(*lpst) );
1976 lpst->IStorage16_iface.lpVtbl = segstvt16;
1977 lpst->str.hf = NULL;
1978 lpst->str.lockbytes = 0;
1979 lpst->ref = 1;
1980 lpst->thisptr = MapLS(lpst);
1981 *stg = (void*)lpst->thisptr;
1984 /******************************************************************************
1985 * Storage API functions
1988 /******************************************************************************
1989 * StgCreateDocFileA [STORAGE.1]
1991 HRESULT WINAPI StgCreateDocFile16(
1992 LPCOLESTR16 pwcsName,DWORD grfMode,DWORD reserved,IStorage16 **ppstgOpen
1994 HANDLE hf;
1995 int i,ret;
1996 IStorage16Impl* lpstg;
1997 struct storage_pps_entry stde;
1999 TRACE("(%s,0x%08x,0x%08x,%p)\n",
2000 pwcsName,grfMode,reserved,ppstgOpen
2002 _create_istorage16(ppstgOpen);
2003 hf = CreateFileA(pwcsName,GENERIC_READ|GENERIC_WRITE,0,NULL,CREATE_NEW,0,0);
2004 if (hf==INVALID_HANDLE_VALUE) {
2005 WARN("couldn't open file for storage:%d\n",GetLastError());
2006 return E_FAIL;
2008 lpstg = MapSL((SEGPTR)*ppstgOpen);
2009 lpstg->str.hf = hf;
2010 lpstg->str.lockbytes = 0;
2011 /* FIXME: check for existence before overwriting? */
2012 if (!STORAGE_init_storage(&lpstg->str)) {
2013 CloseHandle(hf);
2014 return E_FAIL;
2016 i=0;ret=0;
2017 while (!ret) { /* neither 1 nor <0 */
2018 ret=STORAGE_get_pps_entry(&lpstg->str,i,&stde);
2019 if ((ret==1) && (stde.pps_type==5)) {
2020 lpstg->stde = stde;
2021 lpstg->ppsent = i;
2022 break;
2024 i++;
2026 if (ret!=1) {
2027 IStorage16_fnRelease(&lpstg->IStorage16_iface);
2028 return E_FAIL;
2031 return S_OK;
2034 /******************************************************************************
2035 * StgIsStorageFile [STORAGE.5]
2037 HRESULT WINAPI StgIsStorageFile16(LPCOLESTR16 fn) {
2038 UNICODE_STRING strW;
2039 HRESULT ret;
2041 RtlCreateUnicodeStringFromAsciiz(&strW, fn);
2042 ret = StgIsStorageFile( strW.Buffer );
2043 RtlFreeUnicodeString( &strW );
2045 return ret;
2048 /******************************************************************************
2049 * StgOpenStorage [STORAGE.3]
2051 HRESULT WINAPI StgOpenStorage16(
2052 LPCOLESTR16 pwcsName,IStorage16 *pstgPriority,DWORD grfMode,
2053 SNB16 snbExclude,DWORD reserved, IStorage16 **ppstgOpen
2055 HANDLE hf;
2056 int ret,i;
2057 IStorage16Impl* lpstg;
2058 struct storage_pps_entry stde;
2060 TRACE("(%s,%p,0x%08x,%p,%d,%p)\n",
2061 pwcsName,pstgPriority,grfMode,snbExclude,reserved,ppstgOpen
2063 _create_istorage16(ppstgOpen);
2064 hf = CreateFileA(pwcsName,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
2065 if (hf==INVALID_HANDLE_VALUE) {
2066 WARN("Couldn't open file for storage\n");
2067 return E_FAIL;
2069 lpstg = MapSL((SEGPTR)*ppstgOpen);
2070 lpstg->str.hf = hf;
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 break;
2079 i++;
2081 if (ret!=1) {
2082 IStorage16_fnRelease(&lpstg->IStorage16_iface);
2083 return E_FAIL;
2085 return S_OK;
2089 /******************************************************************************
2090 * StgIsStorageILockBytes [STORAGE.6]
2092 * Determines if the ILockBytes contains a storage object.
2094 HRESULT WINAPI StgIsStorageILockBytes16(SEGPTR plkbyt)
2096 DWORD args[6];
2097 HRESULT hres;
2098 HANDLE16 hsig;
2100 args[0] = (DWORD)plkbyt; /* iface */
2101 args[1] = args[2] = 0; /* ULARGE_INTEGER offset */
2102 args[3] = WOWGlobalAllocLock16( 0, 8, &hsig ); /* sig */
2103 args[4] = 8;
2104 args[5] = 0;
2106 if (!WOWCallback16Ex(
2107 (DWORD)((const ILockBytes16Vtbl*)MapSL(
2108 (SEGPTR)((LPLOCKBYTES16)MapSL(plkbyt))->lpVtbl)
2109 )->ReadAt,
2110 WCB16_PASCAL,
2111 6*sizeof(DWORD),
2112 (LPVOID)args,
2113 (LPDWORD)&hres
2114 )) {
2115 ERR("CallTo16 ILockBytes16::ReadAt() failed, hres %x\n",hres);
2116 return hres;
2118 if (memcmp(MapSL(args[3]), STORAGE_magic, sizeof(STORAGE_magic)) == 0) {
2119 WOWGlobalUnlockFree16(args[3]);
2120 return S_OK;
2122 WOWGlobalUnlockFree16(args[3]);
2123 return S_FALSE;
2126 /******************************************************************************
2127 * StgOpenStorageOnILockBytes [STORAGE.4]
2129 * PARAMS
2130 * plkbyt FIXME: Should probably be an ILockBytes16 *.
2132 HRESULT WINAPI StgOpenStorageOnILockBytes16(
2133 SEGPTR plkbyt,
2134 IStorage16 *pstgPriority,
2135 DWORD grfMode,
2136 SNB16 snbExclude,
2137 DWORD reserved,
2138 IStorage16 **ppstgOpen)
2140 IStorage16Impl* lpstg;
2141 int i,ret;
2142 struct storage_pps_entry stde;
2144 FIXME("(%x, %p, 0x%08x, %d, %x, %p)\n", plkbyt, pstgPriority, grfMode, (int)snbExclude, reserved, ppstgOpen);
2145 if ((plkbyt == 0) || (ppstgOpen == 0))
2146 return STG_E_INVALIDPOINTER;
2148 *ppstgOpen = 0;
2150 _create_istorage16(ppstgOpen);
2151 lpstg = MapSL((SEGPTR)*ppstgOpen);
2152 lpstg->str.hf = NULL;
2153 lpstg->str.lockbytes = plkbyt;
2154 i=0;ret=0;
2155 while (!ret) { /* neither 1 nor <0 */
2156 ret=STORAGE_get_pps_entry(&lpstg->str,i,&stde);
2157 if ((ret==1) && (stde.pps_type==5)) {
2158 lpstg->stde=stde;
2159 break;
2161 i++;
2163 if (ret!=1) {
2164 IStorage16_fnRelease(&lpstg->IStorage16_iface);
2165 return E_FAIL;
2167 return S_OK;