msiexec: Add support for /update.
[wine.git] / dlls / storage.dll16 / storage.c
blob4807bcd436950dbcf0716449e4530223b3c69073
1 /* Compound Storage
3 * Implemented using the documentation of the LAOLA project at
4 * <URL:http://wwwwbs.cs.tu-berlin.de/~schwartz/pmh/index.html>
5 * (Thanks to Martin Schwartz <schwartz@cs.tu-berlin.de>)
7 * Copyright 1998 Marcus Meissner
9 * This library is free software; you can redistribute it and/or
10 * modify it under the terms of the GNU Lesser General Public
11 * License as published by the Free Software Foundation; either
12 * version 2.1 of the License, or (at your option) any later version.
14 * This library is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
17 * Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public
20 * License along with this library; if not, write to the Free Software
21 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301, USA
24 #include "config.h"
26 #include <assert.h>
27 #include <time.h>
28 #include <stdarg.h>
29 #include <string.h>
30 #include <sys/types.h>
31 #ifdef HAVE_UNISTD_H
32 # include <unistd.h>
33 #endif
35 #define NONAMELESSUNION
36 #define NONAMELESSSTRUCT
37 #include "windef.h"
38 #include "winbase.h"
39 #include "winternl.h"
40 #include "winerror.h"
41 #include "wine/winbase16.h"
42 #include "wownt32.h"
43 #include "wine/unicode.h"
44 #include "objbase.h"
45 #include "wine/debug.h"
47 #include "ifs.h"
49 WINE_DEFAULT_DEBUG_CHANNEL(ole);
50 WINE_DECLARE_DEBUG_CHANNEL(relay);
52 struct storage_header {
53 BYTE magic[8]; /* 00: magic */
54 BYTE unknown1[36]; /* 08: unknown */
55 DWORD num_of_bbd_blocks;/* 2C: length of big datablocks */
56 DWORD root_startblock;/* 30: root storage first big block */
57 DWORD unknown2[2]; /* 34: unknown */
58 DWORD sbd_startblock; /* 3C: small block depot first big block */
59 DWORD unknown3[3]; /* 40: unknown */
60 DWORD bbd_list[109]; /* 4C: big data block list (up to end of sector)*/
62 struct storage_pps_entry {
63 WCHAR pps_rawname[32];/* 00: \0 terminated widechar name */
64 WORD pps_sizeofname; /* 40: namelength in bytes */
65 BYTE pps_type; /* 42: flags, 1 storage/dir, 2 stream, 5 root */
66 BYTE pps_unknown0; /* 43: unknown */
67 DWORD pps_prev; /* 44: previous pps */
68 DWORD pps_next; /* 48: next pps */
69 DWORD pps_dir; /* 4C: directory pps */
70 GUID pps_guid; /* 50: class ID */
71 DWORD pps_unknown1; /* 60: unknown */
72 FILETIME pps_ft1; /* 64: filetime1 */
73 FILETIME pps_ft2; /* 70: filetime2 */
74 DWORD pps_sb; /* 74: data startblock */
75 DWORD pps_size; /* 78: datalength. (<0x1000)?small:big blocks*/
76 DWORD pps_unknown2; /* 7C: unknown */
79 #define STORAGE_CHAINENTRY_FAT 0xfffffffd
80 #define STORAGE_CHAINENTRY_ENDOFCHAIN 0xfffffffe
81 #define STORAGE_CHAINENTRY_FREE 0xffffffff
84 static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
86 #define BIGSIZE 512
87 #define SMALLSIZE 64
89 #define SMALLBLOCKS_PER_BIGBLOCK (BIGSIZE/SMALLSIZE)
91 #define READ_HEADER(str) STORAGE_get_big_block(str,-1,(LPBYTE)&sth);assert(!memcmp(STORAGE_magic,sth.magic,sizeof(STORAGE_magic)));
92 static IStorage16Vtbl stvt16;
93 static const IStorage16Vtbl *segstvt16 = NULL;
94 static IStream16Vtbl strvt16;
95 static const IStream16Vtbl *segstrvt16 = NULL;
97 /*ULONG WINAPI IStorage16_AddRef(LPSTORAGE16 this);*/
98 static void _create_istorage16(LPSTORAGE16 *stg);
99 static void _create_istream16(LPSTREAM16 *str);
101 #define IMPLEMENTED 1
103 /* The following is taken from the CorVu implementation of docfiles, and
104 * documents things about the file format that are not implemented here, and
105 * not documented by the LAOLA project. The CorVu implementation was posted
106 * to wine-devel in February 2004, and released under the LGPL at the same
107 * time. Because that implementation is in C++, it's not directly usable in
108 * Wine, but does have documentation value.
111 * #define DF_EXT_VTOC -4
112 * #define DF_VTOC_VTOC -3
113 * #define DF_VTOC_EOF -2
114 * #define DF_VTOC_FREE -1
115 * #define DF_NAMELEN 0x20 // Maximum entry name length - 31 characters plus
116 * // a NUL terminator
118 * #define DF_FT_STORAGE 1
119 * #define DF_FT_STREAM 2
120 * #define DF_FT_LOCKBYTES 3 // Not used -- How the bloody hell did I manage
121 * #define DF_FT_PROPERTY 4 // Not Used -- to figure these two out?
122 * #define DF_FT_ROOT 5
124 * #define DF_BLOCK_SIZE 0x200
125 * #define DF_VTOC_SIZE 0x80
126 * #define DF_DE_PER_BLOCK 4
127 * #define DF_STREAM_BLOCK_SIZE 0x40
129 * A DocFile is divided into blocks of 512 bytes.
130 * The first block contains the header.
132 * The file header contains The first 109 entries in the VTOC of VTOCs.
134 * Each block pointed to by a VTOC of VTOCs contains a VTOC, which
135 * includes block chains - just like FAT. This is a somewhat poor
136 * design for the following reasons:
138 * 1. FAT was a poor file system design to begin with, and
139 * has long been known to be horrendously inefficient
140 * for day to day operations.
142 * 2. The problem is compounded here, since the file
143 * level streams are generally *not* read sequentially.
144 * This means that a significant percentage of reads
145 * require seeking from the start of the chain.
147 * Data chains also contain an internal VTOC. The block size for
148 * the standard VTOC is 512. The block size for the internal VTOC
149 * is 64.
151 * Now, the 109 blocks in the VTOC of VTOCs allows for files of
152 * up to around 7MB. So what do you think happens if that's
153 * exceeded? Well, there's an entry in the header block which
154 * points to the first block used as additional storage for
155 * the VTOC of VTOCs.
157 * Now we can get up to around 15MB. Now, guess how the file
158 * format adds in another block to the VTOC of VTOCs. Come on,
159 * it's no big surprise. That's right - the last entry in each
160 * block extending the VTOC of VTOCs is, you guessed it, the
161 * block number of the next block containing an extension to
162 * the VTOC of VTOCs. The VTOC of VTOCs is chained!!!!
164 * So, to review:
166 * 1. If you are using a FAT file system, the location of
167 * your file's blocks is stored in chains.
169 * 2. At the abstract level, the file contains a VTOC of VTOCs,
170 * which is stored in the most inefficient possible format for
171 * random access - a chain (AKA list).
173 * 3. The VTOC of VTOCs contains descriptions of three file level
174 * streams:
176 * a. The Directory stream
177 * b. The Data stream
178 * c. The Data VTOC stream
180 * These are, of course, represented as chains.
182 * 4. The Data VTOC contains data describing the chains of blocks
183 * within the Data stream.
185 * That's right - we have a total of four levels of block chains!
187 * Now, is that complicated enough for you? No? OK, there's another
188 * complication. If an individual stream (ie. an IStream) reaches
189 * 4096 bytes in size, it gets moved from the Data Stream to
190 * a new file level stream. Now, if the stream then gets truncated
191 * back to less than 4096 bytes, it returns to the data stream.
193 * The effect of using this format can be seen very easily. Pick
194 * an arbitrary application with a grid data representation that
195 * can export to both Lotus 123 and Excel 5 or higher. Export
196 * a large file to Lotus 123 and time it. Export the same thing
197 * to Excel 5 and time that. The difference is the inefficiency
198 * of the Microsoft DocFile format.
201 * #define TOTAL_SIMPLE_VTOCS 109
203 * struct DocFile_Header
205 * df_byte iMagic1; // 0xd0
206 * df_byte iMagic2; // 0xcf
207 * df_byte iMagic3; // 0x11
208 * df_byte iMagic4; // 0xe0 - Spells D0CF11E0, or DocFile
209 * df_byte iMagic5; // 161 (igi upside down)
210 * df_byte iMagic6; // 177 (lli upside down - see below
211 * df_byte iMagic7; // 26 (gz upside down)
212 * df_byte iMagic8; // 225 (szz upside down) - see below
213 * df_int4 aiUnknown1[4];
214 * df_int4 iVersion; // DocFile Version - 0x03003E
215 * df_int4 aiUnknown2[4];
216 * df_int4 nVTOCs; // Number of VTOCs
217 * df_int4 iFirstDirBlock; // First Directory Block
218 * df_int4 aiUnknown3[2];
219 * df_int4 iFirstDataVTOC; // First data VTOC block
220 * df_int4 iHasData; // 1 if there is data in the file - yes, this is important
221 * df_int4 iExtendedVTOC; // Extended VTOC location
222 * df_int4 iExtendedVTOCSize; // Size of extended VTOC (+1?)
223 * df_int4 aiVTOCofVTOCs[TOTAL_SIMPLE_VTOCS];
224 * };
226 * struct DocFile_VTOC
228 * df_int4 aiBlocks[DF_VTOC_SIZE];
229 * };
232 * The meaning of the magic numbers
234 * 0xd0cf11e0 is DocFile with a zero on the end (sort of)
236 * If you key 177161 into a calculator, then turn the calculator
237 * upside down, you get igilli, which may be a reference to
238 * somebody's name, or to the Hebrew word for "angel".
240 * If you key 26225 into a calculator, then turn it upside down, you
241 * get szzgz. Microsoft has a tradition of creating nonsense words
242 * using the letters s, g, z and y. We think szzgz may be one of the
243 * Microsoft placeholder variables, along the lines of foo, bar and baz.
244 * Alternatively, it could be 22526, which would be gzszz.
247 * struct DocFile_DirEnt
249 * df_char achEntryName[DF_NAMELEN]; // Entry Name
250 * df_int2 iNameLen; // Name length in bytes, including NUL terminator
251 * df_byte iFileType; // Entry type
252 * df_byte iColour; // 1 = Black, 0 = Red
253 * df_int4 iLeftSibling; // Next Left Sibling Entry - See below
254 * df_int4 iRightSibling; // Next Right Sibling Entry
255 * df_int4 iFirstChild; // First Child Entry
256 * df_byte achClassID[16]; // Class ID
257 * df_int4 iStateBits; // [GS]etStateBits value
258 * df_int4 iCreatedLow; // Low DWORD of creation time
259 * df_int4 iCreatedHigh; // High DWORD of creation time
260 * df_int4 iModifiedLow; // Low DWORD of modification time
261 * df_int4 iModifiedHigh; // High DWORD of modification time
262 * df_int4 iVTOCPosition; // VTOC Position
263 * df_int4 iFileSize; // Size of the stream
264 * df_int4 iZero; // We think this is part of the 64 bit stream size - must be 0
265 * };
267 * Siblings
268 * ========
270 * Siblings are stored in an obscure but incredibly elegant
271 * data structure called a red-black tree. This is generally
272 * defined as a 2-3-4 tree stored in a binary tree.
274 * A red-black tree can always be balanced very easily. The rules
275 * for a red-black tree are as follows:
277 * 1. The root node is always black.
278 * 2. The parent of a red node is always black.
280 * There is a Java demo of red-black trees at:
282 * http://langevin.usc.edu/BST/RedBlackTree-Example.html
284 * This demo is an excellent tool for learning how red-black
285 * trees work, without having to go through the process of
286 * learning how they were derived.
288 * Within the tree, elements are ordered by the length of the
289 * name and within that, ASCII order by name. This causes the
290 * apparently bizarre reordering you see when you use dfview.
292 * This is a somewhat bizarre choice. It suggests that the
293 * designer of the DocFile format was trying to optimise
294 * searching through the directory entries. However searching
295 * through directory entries is a relatively rare operation.
296 * Reading and seeking within a stream are much more common
297 * operations, especially within the file level streams, yet
298 * these use the horrendously inefficient FAT chains.
300 * This suggests that the designer was probably somebody
301 * fresh out of university, who had some basic knowledge of
302 * basic data structures, but little knowledge of anything
303 * more practical. It is bizarre to attempt to optimise
304 * directory searches while not using a more efficient file
305 * block locating system than FAT (seedling/sapling/tree
306 * would result in a massive improvement - in fact we have
307 * an alternative to docfiles that we use internally that
308 * uses seedling/sapling/tree and *is* far more efficient).
310 * It is worth noting that the MS implementation of red-black
311 * trees is incorrect (I can tell you're surprised) and
312 * actually causes more operations to occur than are really
313 * needed. Fortunately the fact that our implementation is
314 * correct will not cause any problems - the MS implementation
315 * still appears to cause the tree to satisfy the rules, albeit
316 * a sequence of the same insertions in the different
317 * implementations may result in a different, and possibly
318 * deeper (but never shallower) tree.
321 typedef struct {
322 HANDLE hf;
323 SEGPTR lockbytes;
324 } stream_access16;
325 /* --- IStorage16 implementation struct */
327 typedef struct
329 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=-1;
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 /* unix SEEK_xx should be the same as win95 ones */
1189 case SEEK_SET:
1190 /* offset must be ==0 (<0 is invalid, and >0 cannot be handled
1191 * right now.
1193 assert(offset.u.HighPart==0);
1194 This->offset.u.HighPart = offset.u.HighPart;
1195 This->offset.u.LowPart = offset.u.LowPart;
1196 break;
1197 case SEEK_CUR:
1198 if (offset.u.HighPart < 0) {
1199 /* FIXME: is this negation correct ? */
1200 offset.u.HighPart = -offset.u.HighPart;
1201 offset.u.LowPart = (0xffffffff ^ offset.u.LowPart)+1;
1203 assert(offset.u.HighPart==0);
1204 assert(This->offset.u.LowPart >= offset.u.LowPart);
1205 This->offset.u.LowPart -= offset.u.LowPart;
1206 } else {
1207 assert(offset.u.HighPart==0);
1208 This->offset.u.LowPart+= offset.u.LowPart;
1210 break;
1211 case SEEK_END:
1212 assert(offset.u.HighPart==0);
1213 This->offset.u.LowPart = This->stde.pps_size-offset.u.LowPart;
1214 break;
1216 if (This->offset.u.LowPart>This->stde.pps_size)
1217 This->offset.u.LowPart=This->stde.pps_size;
1218 if (newpos) *newpos = This->offset;
1219 return S_OK;
1222 /******************************************************************************
1223 * IStream16_Read [STORAGE.521]
1225 HRESULT CDECL IStream16_fnRead(IStream16 *iface, void *pv, ULONG cb, ULONG *pcbRead)
1227 IStream16Impl *This = impl_from_IStream16(iface);
1228 BYTE block[BIGSIZE];
1229 ULONG *bytesread=pcbRead,xxread;
1230 int blocknr;
1231 LPBYTE pbv = pv;
1233 TRACE_(relay)("(%p)->(%p,%d,%p)\n",This,pv,cb,pcbRead);
1234 if (!pcbRead) bytesread=&xxread;
1235 *bytesread = 0;
1237 if (cb>This->stde.pps_size-This->offset.u.LowPart)
1238 cb=This->stde.pps_size-This->offset.u.LowPart;
1239 if (This->stde.pps_size < 0x1000) {
1240 /* use small block reader */
1241 blocknr = STORAGE_get_nth_next_small_blocknr(&This->str,This->stde.pps_sb,This->offset.u.LowPart/SMALLSIZE);
1242 while (cb) {
1243 unsigned int cc;
1245 if (!STORAGE_get_small_block(&This->str,blocknr,block)) {
1246 WARN("small block read failed!!!\n");
1247 return E_FAIL;
1249 cc = cb;
1250 if (cc>SMALLSIZE-(This->offset.u.LowPart&(SMALLSIZE-1)))
1251 cc=SMALLSIZE-(This->offset.u.LowPart&(SMALLSIZE-1));
1252 memcpy(pbv,block+(This->offset.u.LowPart&(SMALLSIZE-1)),cc);
1253 This->offset.u.LowPart+=cc;
1254 pbv+=cc;
1255 *bytesread+=cc;
1256 cb-=cc;
1257 blocknr = STORAGE_get_next_small_blocknr(&This->str,blocknr);
1259 } else {
1260 /* use big block reader */
1261 blocknr = STORAGE_get_nth_next_big_blocknr(&This->str,This->stde.pps_sb,This->offset.u.LowPart/BIGSIZE);
1262 while (cb) {
1263 unsigned int cc;
1265 if (!STORAGE_get_big_block(&This->str,blocknr,block)) {
1266 WARN("big block read failed!!!\n");
1267 return E_FAIL;
1269 cc = cb;
1270 if (cc>BIGSIZE-(This->offset.u.LowPart&(BIGSIZE-1)))
1271 cc=BIGSIZE-(This->offset.u.LowPart&(BIGSIZE-1));
1272 memcpy(pbv,block+(This->offset.u.LowPart&(BIGSIZE-1)),cc);
1273 This->offset.u.LowPart+=cc;
1274 pbv+=cc;
1275 *bytesread+=cc;
1276 cb-=cc;
1277 blocknr=STORAGE_get_next_big_blocknr(&This->str,blocknr);
1280 return S_OK;
1283 /******************************************************************************
1284 * IStream16_Write [STORAGE.522]
1286 HRESULT CDECL IStream16_fnWrite(IStream16 *iface, const void *pv, ULONG cb, ULONG *pcbWrite)
1288 IStream16Impl *This = impl_from_IStream16(iface);
1289 BYTE block[BIGSIZE];
1290 ULONG *byteswritten=pcbWrite,xxwritten;
1291 int oldsize,newsize,i,curoffset=0,lastblocknr,blocknr,cc;
1292 const BYTE* pbv = 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 * every time
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 every time, 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 every time, 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->IStream16_iface.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;
1612 static inline IStorage16Impl *impl_from_IStorage16(IStorage16 *iface)
1614 return CONTAINING_RECORD(iface, IStorage16Impl, IStorage16_iface);
1617 /******************************************************************************
1618 * IStorage16_QueryInterface [STORAGE.500]
1620 HRESULT CDECL IStorage16_fnQueryInterface(IStorage16 *iface, REFIID refiid, void **obj)
1622 IStorage16Impl *This = impl_from_IStorage16(iface);
1624 TRACE_(relay)("(%p)->(%s,%p)\n",This,debugstr_guid(refiid),obj);
1626 if (!memcmp(&IID_IUnknown,refiid,sizeof(IID_IUnknown))) {
1627 *obj = This;
1628 return 0;
1630 return OLE_E_ENUM_NOMORE;
1633 /******************************************************************************
1634 * IStorage16_AddRef [STORAGE.501]
1636 ULONG CDECL IStorage16_fnAddRef(IStorage16 *iface)
1638 IStorage16Impl *This = impl_from_IStorage16(iface);
1639 return InterlockedIncrement(&This->ref);
1642 /******************************************************************************
1643 * IStorage16_Release [STORAGE.502]
1645 ULONG CDECL IStorage16_fnRelease(IStorage16 *iface)
1647 IStorage16Impl *This = impl_from_IStorage16(iface);
1648 ULONG ref;
1649 ref = InterlockedDecrement(&This->ref);
1650 if (!ref)
1652 UnMapLS( This->thisptr );
1653 HeapFree( GetProcessHeap(), 0, This );
1655 return ref;
1658 /******************************************************************************
1659 * IStorage16_Stat [STORAGE.517]
1661 HRESULT CDECL IStorage16_fnStat(IStorage16 *iface, STATSTG16 *pstatstg, DWORD grfStatFlag)
1663 IStorage16Impl *This = impl_from_IStorage16(iface);
1664 DWORD len = WideCharToMultiByte( CP_ACP, 0, This->stde.pps_rawname, -1, NULL, 0, NULL, NULL );
1665 LPSTR nameA = HeapAlloc( GetProcessHeap(), 0, len );
1667 TRACE("(%p)->(%p,0x%08x)\n",
1668 This,pstatstg,grfStatFlag
1670 WideCharToMultiByte( CP_ACP, 0, This->stde.pps_rawname, -1, nameA, len, NULL, NULL );
1671 pstatstg->pwcsName=(LPOLESTR16)MapLS( nameA );
1672 pstatstg->type = This->stde.pps_type;
1673 pstatstg->cbSize.u.LowPart = This->stde.pps_size;
1674 pstatstg->mtime = This->stde.pps_ft1; /* FIXME */ /* why? */
1675 pstatstg->atime = This->stde.pps_ft2; /* FIXME */
1676 pstatstg->ctime = This->stde.pps_ft2; /* FIXME */
1677 pstatstg->grfMode = 0; /* FIXME */
1678 pstatstg->grfLocksSupported = 0; /* FIXME */
1679 pstatstg->clsid = This->stde.pps_guid;
1680 pstatstg->grfStateBits = 0; /* FIXME */
1681 pstatstg->reserved = 0;
1682 return S_OK;
1685 /******************************************************************************
1686 * IStorage16_Commit [STORAGE.509]
1688 HRESULT CDECL IStorage16_fnCommit(IStorage16 *iface, DWORD commitflags)
1690 IStorage16Impl *This = impl_from_IStorage16(iface);
1691 FIXME("(%p)->(0x%08x),STUB!\n",
1692 This,commitflags
1694 return S_OK;
1697 /******************************************************************************
1698 * IStorage16_CopyTo [STORAGE.507]
1700 HRESULT CDECL IStorage16_fnCopyTo(IStorage16 *iface, DWORD ciidExclude, const IID *rgiidExclude,
1701 SNB16 SNB16Exclude, IStorage16 *pstgDest)
1703 IStorage16Impl *This = impl_from_IStorage16(iface);
1704 FIXME("IStorage16(%p)->(0x%08x,%s,%p,%p),stub!\n",
1705 This,ciidExclude,debugstr_guid(rgiidExclude),SNB16Exclude,pstgDest
1707 return S_OK;
1711 /******************************************************************************
1712 * IStorage16_CreateStorage [STORAGE.505]
1714 HRESULT CDECL IStorage16_fnCreateStorage(IStorage16 *iface, LPCOLESTR16 pwcsName, DWORD grfMode,
1715 DWORD dwStgFormat, DWORD reserved2, IStorage16 **ppstg)
1717 IStorage16Impl *This = impl_from_IStorage16(iface);
1718 IStorage16Impl* lpstg;
1719 int ppsent,x;
1720 struct storage_pps_entry stde;
1721 struct storage_header sth;
1722 BOOL ret;
1723 int nPPSEntries;
1725 READ_HEADER(&This->str);
1726 TRACE("(%p)->(%s,0x%08x,0x%08x,0x%08x,%p)\n",
1727 This,pwcsName,grfMode,dwStgFormat,reserved2,ppstg
1729 if (grfMode & STGM_TRANSACTED)
1730 FIXME("We do not support transacted Compound Storage. Using direct mode.\n");
1731 _create_istorage16(ppstg);
1732 lpstg = MapSL((SEGPTR)*ppstg);
1733 if (This->str.hf) {
1734 DuplicateHandle( GetCurrentProcess(), This->str.hf, GetCurrentProcess(),
1735 &lpstg->str.hf, 0, TRUE, DUPLICATE_SAME_ACCESS );
1736 } else {
1737 lpstg->str.lockbytes = This->str.lockbytes;
1738 _ilockbytes16_addref(This->str.lockbytes);
1741 ppsent=STORAGE_get_free_pps_entry(&lpstg->str);
1742 if (ppsent<0)
1743 return E_FAIL;
1744 stde=This->stde;
1745 if (stde.pps_dir==-1) {
1746 stde.pps_dir = ppsent;
1747 x = This->ppsent;
1748 } else {
1749 FIXME(" use prev chain too ?\n");
1750 x=stde.pps_dir;
1751 if (1!=STORAGE_get_pps_entry(&lpstg->str,x,&stde))
1752 return E_FAIL;
1753 while (stde.pps_next!=-1) {
1754 x=stde.pps_next;
1755 if (1!=STORAGE_get_pps_entry(&lpstg->str,x,&stde))
1756 return E_FAIL;
1758 stde.pps_next = ppsent;
1760 ret = STORAGE_put_pps_entry(&lpstg->str,x,&stde);
1761 assert(ret);
1762 nPPSEntries = STORAGE_get_pps_entry(&lpstg->str,ppsent,&(lpstg->stde));
1763 assert(nPPSEntries == 1);
1764 MultiByteToWideChar( CP_ACP, 0, pwcsName, -1, lpstg->stde.pps_rawname,
1765 sizeof(lpstg->stde.pps_rawname)/sizeof(WCHAR));
1766 lpstg->stde.pps_sizeofname = (strlenW(lpstg->stde.pps_rawname)+1)*sizeof(WCHAR);
1767 lpstg->stde.pps_next = -1;
1768 lpstg->stde.pps_prev = -1;
1769 lpstg->stde.pps_dir = -1;
1770 lpstg->stde.pps_sb = -1;
1771 lpstg->stde.pps_size = 0;
1772 lpstg->stde.pps_type = 1;
1773 lpstg->ppsent = ppsent;
1774 /* FIXME: timestamps? */
1775 if (!STORAGE_put_pps_entry(&lpstg->str,ppsent,&(lpstg->stde)))
1776 return E_FAIL;
1777 return S_OK;
1780 /******************************************************************************
1781 * IStorage16_CreateStream [STORAGE.503]
1783 HRESULT CDECL IStorage16_fnCreateStream(IStorage16 *iface, LPCOLESTR16 pwcsName, DWORD grfMode,
1784 DWORD reserved1, DWORD reserved2, IStream16 **ppstm)
1786 IStorage16Impl *This = impl_from_IStorage16(iface);
1787 IStream16Impl* lpstr;
1788 int ppsent,x;
1789 struct storage_pps_entry stde;
1790 BOOL ret;
1791 int nPPSEntries;
1793 TRACE("(%p)->(%s,0x%08x,0x%08x,0x%08x,%p)\n",
1794 This,pwcsName,grfMode,reserved1,reserved2,ppstm
1796 if (grfMode & STGM_TRANSACTED)
1797 FIXME("We do not support transacted Compound Storage. Using direct mode.\n");
1798 _create_istream16(ppstm);
1799 lpstr = MapSL((SEGPTR)*ppstm);
1800 if (This->str.hf) {
1801 DuplicateHandle( GetCurrentProcess(), This->str.hf, GetCurrentProcess(),
1802 &lpstr->str.hf, 0, TRUE, DUPLICATE_SAME_ACCESS );
1803 } else {
1804 lpstr->str.lockbytes = This->str.lockbytes;
1805 _ilockbytes16_addref(This->str.lockbytes);
1807 lpstr->offset.u.LowPart = 0;
1808 lpstr->offset.u.HighPart= 0;
1810 ppsent=STORAGE_get_free_pps_entry(&lpstr->str);
1811 if (ppsent<0)
1812 return E_FAIL;
1813 stde=This->stde;
1814 if (stde.pps_next==-1)
1815 x=This->ppsent;
1816 else
1817 while (stde.pps_next!=-1) {
1818 x=stde.pps_next;
1819 if (1!=STORAGE_get_pps_entry(&lpstr->str,x,&stde))
1820 return E_FAIL;
1822 stde.pps_next = ppsent;
1823 ret = STORAGE_put_pps_entry(&lpstr->str,x,&stde);
1824 assert(ret);
1825 nPPSEntries = STORAGE_get_pps_entry(&lpstr->str,ppsent,&(lpstr->stde));
1826 assert(nPPSEntries == 1);
1827 MultiByteToWideChar( CP_ACP, 0, pwcsName, -1, lpstr->stde.pps_rawname,
1828 sizeof(lpstr->stde.pps_rawname)/sizeof(WCHAR));
1829 lpstr->stde.pps_sizeofname = (strlenW(lpstr->stde.pps_rawname)+1) * sizeof(WCHAR);
1830 lpstr->stde.pps_next = -1;
1831 lpstr->stde.pps_prev = -1;
1832 lpstr->stde.pps_dir = -1;
1833 lpstr->stde.pps_sb = -1;
1834 lpstr->stde.pps_size = 0;
1835 lpstr->stde.pps_type = 2;
1836 lpstr->ppsent = ppsent;
1838 /* FIXME: timestamps? */
1839 if (!STORAGE_put_pps_entry(&lpstr->str,ppsent,&(lpstr->stde)))
1840 return E_FAIL;
1841 return S_OK;
1844 /******************************************************************************
1845 * IStorage16_OpenStorage [STORAGE.506]
1847 HRESULT CDECL IStorage16_fnOpenStorage(IStorage16 *iface, LPCOLESTR16 pwcsName,
1848 IStorage16 *pstgPrio, DWORD grfMode, SNB16 snbExclude, DWORD reserved, IStorage16 **ppstg)
1850 IStorage16Impl *This = impl_from_IStorage16(iface);
1851 IStream16Impl* lpstg;
1852 WCHAR name[33];
1853 int newpps;
1855 TRACE("(%p)->(%s,%p,0x%08x,%p,0x%08x,%p)\n",
1856 This,pwcsName,pstgPrio,grfMode,snbExclude,reserved,ppstg
1858 if (grfMode & STGM_TRANSACTED)
1859 FIXME("We do not support transacted Compound Storage. Using direct mode.\n");
1860 _create_istorage16(ppstg);
1861 lpstg = MapSL((SEGPTR)*ppstg);
1862 if (This->str.hf) {
1863 DuplicateHandle( GetCurrentProcess(), This->str.hf, GetCurrentProcess(),
1864 &lpstg->str.hf, 0, TRUE, DUPLICATE_SAME_ACCESS );
1865 } else {
1866 lpstg->str.lockbytes = This->str.lockbytes;
1867 _ilockbytes16_addref(This->str.lockbytes);
1869 MultiByteToWideChar( CP_ACP, 0, pwcsName, -1, name, sizeof(name)/sizeof(WCHAR));
1870 newpps = STORAGE_look_for_named_pps(&lpstg->str,This->stde.pps_dir,name);
1871 if (newpps==-1) {
1872 IStream16_fnRelease(&lpstg->IStream16_iface);
1873 return E_FAIL;
1876 if (1!=STORAGE_get_pps_entry(&lpstg->str,newpps,&(lpstg->stde))) {
1877 IStream16_fnRelease(&lpstg->IStream16_iface);
1878 return E_FAIL;
1880 lpstg->ppsent = newpps;
1881 return S_OK;
1884 /******************************************************************************
1885 * IStorage16_OpenStream [STORAGE.504]
1887 HRESULT CDECL IStorage16_fnOpenStream(IStorage16 *iface, LPCOLESTR16 pwcsName, void *reserved1,
1888 DWORD grfMode, DWORD reserved2, IStream16 **ppstm)
1890 IStorage16Impl *This = impl_from_IStorage16(iface);
1891 IStream16Impl* lpstr;
1892 WCHAR name[33];
1893 int newpps;
1895 TRACE("(%p)->(%s,%p,0x%08x,0x%08x,%p)\n",
1896 This,pwcsName,reserved1,grfMode,reserved2,ppstm
1898 if (grfMode & STGM_TRANSACTED)
1899 FIXME("We do not support transacted Compound Storage. Using direct mode.\n");
1900 _create_istream16(ppstm);
1901 lpstr = MapSL((SEGPTR)*ppstm);
1902 if (This->str.hf) {
1903 DuplicateHandle( GetCurrentProcess(), This->str.hf, GetCurrentProcess(),
1904 &lpstr->str.hf, 0, TRUE, DUPLICATE_SAME_ACCESS );
1905 } else {
1906 lpstr->str.lockbytes = This->str.lockbytes;
1907 _ilockbytes16_addref(This->str.lockbytes);
1909 MultiByteToWideChar( CP_ACP, 0, pwcsName, -1, name, sizeof(name)/sizeof(WCHAR));
1910 newpps = STORAGE_look_for_named_pps(&lpstr->str,This->stde.pps_dir,name);
1911 if (newpps==-1) {
1912 IStream16_fnRelease(&lpstr->IStream16_iface);
1913 return E_FAIL;
1916 if (1!=STORAGE_get_pps_entry(&lpstr->str,newpps,&(lpstr->stde))) {
1917 IStream16_fnRelease(&lpstr->IStream16_iface);
1918 return E_FAIL;
1920 lpstr->offset.u.LowPart = 0;
1921 lpstr->offset.u.HighPart = 0;
1922 lpstr->ppsent = newpps;
1923 return S_OK;
1926 /******************************************************************************
1927 * _create_istorage16 [INTERNAL]
1929 static void _create_istorage16(LPSTORAGE16 *stg) {
1930 IStorage16Impl* lpst;
1932 if (!stvt16.QueryInterface) {
1933 HMODULE16 wp = GetModuleHandle16("STORAGE");
1934 if (wp>=32) {
1935 #define VTENT(xfn) stvt16.xfn = (void*)GetProcAddress16(wp,"IStorage16_"#xfn);
1936 VTENT(QueryInterface)
1937 VTENT(AddRef)
1938 VTENT(Release)
1939 VTENT(CreateStream)
1940 VTENT(OpenStream)
1941 VTENT(CreateStorage)
1942 VTENT(OpenStorage)
1943 VTENT(CopyTo)
1944 VTENT(MoveElementTo)
1945 VTENT(Commit)
1946 VTENT(Revert)
1947 VTENT(EnumElements)
1948 VTENT(DestroyElement)
1949 VTENT(RenameElement)
1950 VTENT(SetElementTimes)
1951 VTENT(SetClass)
1952 VTENT(SetStateBits)
1953 VTENT(Stat)
1954 #undef VTENT
1955 segstvt16 = (const IStorage16Vtbl*)MapLS( &stvt16 );
1956 } else {
1957 #define VTENT(xfn) stvt16.xfn = IStorage16_fn##xfn;
1958 VTENT(QueryInterface)
1959 VTENT(AddRef)
1960 VTENT(Release)
1961 VTENT(CreateStream)
1962 VTENT(OpenStream)
1963 VTENT(CreateStorage)
1964 VTENT(OpenStorage)
1965 VTENT(CopyTo)
1966 VTENT(Commit)
1967 /* not (yet) implemented ...
1968 VTENT(MoveElementTo)
1969 VTENT(Revert)
1970 VTENT(EnumElements)
1971 VTENT(DestroyElement)
1972 VTENT(RenameElement)
1973 VTENT(SetElementTimes)
1974 VTENT(SetClass)
1975 VTENT(SetStateBits)
1976 VTENT(Stat)
1978 #undef VTENT
1979 segstvt16 = &stvt16;
1982 lpst = HeapAlloc( GetProcessHeap(), 0, sizeof(*lpst) );
1983 lpst->IStorage16_iface.lpVtbl = segstvt16;
1984 lpst->str.hf = NULL;
1985 lpst->str.lockbytes = 0;
1986 lpst->ref = 1;
1987 lpst->thisptr = MapLS(lpst);
1988 *stg = (void*)lpst->thisptr;
1991 /******************************************************************************
1992 * Storage API functions
1995 /******************************************************************************
1996 * StgCreateDocFileA [STORAGE.1]
1998 HRESULT WINAPI StgCreateDocFile16(
1999 LPCOLESTR16 pwcsName,DWORD grfMode,DWORD reserved,IStorage16 **ppstgOpen
2001 HANDLE hf;
2002 int i,ret;
2003 IStorage16Impl* lpstg;
2004 struct storage_pps_entry stde;
2006 TRACE("(%s,0x%08x,0x%08x,%p)\n",
2007 pwcsName,grfMode,reserved,ppstgOpen
2009 _create_istorage16(ppstgOpen);
2010 hf = CreateFileA(pwcsName,GENERIC_READ|GENERIC_WRITE,0,NULL,CREATE_NEW,0,0);
2011 if (hf==INVALID_HANDLE_VALUE) {
2012 WARN("couldn't open file for storage:%d\n",GetLastError());
2013 return E_FAIL;
2015 lpstg = MapSL((SEGPTR)*ppstgOpen);
2016 lpstg->str.hf = hf;
2017 lpstg->str.lockbytes = 0;
2018 /* FIXME: check for existence before overwriting? */
2019 if (!STORAGE_init_storage(&lpstg->str)) {
2020 CloseHandle(hf);
2021 return E_FAIL;
2023 i=0;ret=0;
2024 while (!ret) { /* neither 1 nor <0 */
2025 ret=STORAGE_get_pps_entry(&lpstg->str,i,&stde);
2026 if ((ret==1) && (stde.pps_type==5)) {
2027 lpstg->stde = stde;
2028 lpstg->ppsent = i;
2029 break;
2031 i++;
2033 if (ret!=1) {
2034 IStorage16_fnRelease(&lpstg->IStorage16_iface);
2035 return E_FAIL;
2038 return S_OK;
2041 /******************************************************************************
2042 * StgIsStorageFile [STORAGE.5]
2044 HRESULT WINAPI StgIsStorageFile16(LPCOLESTR16 fn) {
2045 UNICODE_STRING strW;
2046 HRESULT ret;
2048 RtlCreateUnicodeStringFromAsciiz(&strW, fn);
2049 ret = StgIsStorageFile( strW.Buffer );
2050 RtlFreeUnicodeString( &strW );
2052 return ret;
2055 /******************************************************************************
2056 * StgOpenStorage [STORAGE.3]
2058 HRESULT WINAPI StgOpenStorage16(
2059 LPCOLESTR16 pwcsName,IStorage16 *pstgPriority,DWORD grfMode,
2060 SNB16 snbExclude,DWORD reserved, IStorage16 **ppstgOpen
2062 HANDLE hf;
2063 int ret,i;
2064 IStorage16Impl* lpstg;
2065 struct storage_pps_entry stde;
2067 TRACE("(%s,%p,0x%08x,%p,%d,%p)\n",
2068 pwcsName,pstgPriority,grfMode,snbExclude,reserved,ppstgOpen
2070 _create_istorage16(ppstgOpen);
2071 hf = CreateFileA(pwcsName,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
2072 if (hf==INVALID_HANDLE_VALUE) {
2073 WARN("Couldn't open file for storage\n");
2074 return E_FAIL;
2076 lpstg = MapSL((SEGPTR)*ppstgOpen);
2077 lpstg->str.hf = hf;
2079 i=0;ret=0;
2080 while (!ret) { /* neither 1 nor <0 */
2081 ret=STORAGE_get_pps_entry(&lpstg->str,i,&stde);
2082 if ((ret==1) && (stde.pps_type==5)) {
2083 lpstg->stde=stde;
2084 break;
2086 i++;
2088 if (ret!=1) {
2089 IStorage16_fnRelease(&lpstg->IStorage16_iface);
2090 return E_FAIL;
2092 return S_OK;
2096 /******************************************************************************
2097 * StgIsStorageILockBytes [STORAGE.6]
2099 * Determines if the ILockBytes contains a storage object.
2101 HRESULT WINAPI StgIsStorageILockBytes16(SEGPTR plkbyt)
2103 DWORD args[6];
2104 HRESULT hres;
2105 HANDLE16 hsig;
2107 args[0] = (DWORD)plkbyt; /* iface */
2108 args[1] = args[2] = 0; /* ULARGE_INTEGER offset */
2109 args[3] = WOWGlobalAllocLock16( 0, 8, &hsig ); /* sig */
2110 args[4] = 8;
2111 args[5] = 0;
2113 if (!WOWCallback16Ex(
2114 (DWORD)((const ILockBytes16Vtbl*)MapSL(
2115 (SEGPTR)((LPLOCKBYTES16)MapSL(plkbyt))->lpVtbl)
2116 )->ReadAt,
2117 WCB16_PASCAL,
2118 6*sizeof(DWORD),
2119 (LPVOID)args,
2120 (LPDWORD)&hres
2121 )) {
2122 ERR("CallTo16 ILockBytes16::ReadAt() failed, hres %x\n",hres);
2123 return hres;
2125 if (memcmp(MapSL(args[3]), STORAGE_magic, sizeof(STORAGE_magic)) == 0) {
2126 WOWGlobalUnlockFree16(args[3]);
2127 return S_OK;
2129 WOWGlobalUnlockFree16(args[3]);
2130 return S_FALSE;
2133 /******************************************************************************
2134 * StgOpenStorageOnILockBytes [STORAGE.4]
2136 * PARAMS
2137 * plkbyt FIXME: Should probably be an ILockBytes16 *.
2139 HRESULT WINAPI StgOpenStorageOnILockBytes16(
2140 SEGPTR plkbyt,
2141 IStorage16 *pstgPriority,
2142 DWORD grfMode,
2143 SNB16 snbExclude,
2144 DWORD reserved,
2145 IStorage16 **ppstgOpen)
2147 IStorage16Impl* lpstg;
2148 int i,ret;
2149 struct storage_pps_entry stde;
2151 FIXME("(%x, %p, 0x%08x, %d, %x, %p)\n", plkbyt, pstgPriority, grfMode, (int)snbExclude, reserved, ppstgOpen);
2152 if ((plkbyt == 0) || (ppstgOpen == 0))
2153 return STG_E_INVALIDPOINTER;
2155 *ppstgOpen = 0;
2157 _create_istorage16(ppstgOpen);
2158 lpstg = MapSL((SEGPTR)*ppstgOpen);
2159 lpstg->str.hf = NULL;
2160 lpstg->str.lockbytes = plkbyt;
2161 i=0;ret=0;
2162 while (!ret) { /* neither 1 nor <0 */
2163 ret=STORAGE_get_pps_entry(&lpstg->str,i,&stde);
2164 if ((ret==1) && (stde.pps_type==5)) {
2165 lpstg->stde=stde;
2166 break;
2168 i++;
2170 if (ret!=1) {
2171 IStorage16_fnRelease(&lpstg->IStorage16_iface);
2172 return E_FAIL;
2174 return S_OK;