dmime/tests: Check more notification / dirty messages fields.
[wine.git] / dlls / storage.dll16 / storage.c
blob3eb6f228c802b0b5fa44affbc4aaed965f042f4f
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 <assert.h>
25 #include <time.h>
26 #include <stdarg.h>
27 #include <string.h>
28 #include <sys/types.h>
30 #include "windef.h"
31 #include "winbase.h"
32 #include "winternl.h"
33 #include "winerror.h"
34 #include "wine/winbase16.h"
35 #include "wownt32.h"
36 #include "objbase.h"
37 #include "wine/debug.h"
39 #include "ifs.h"
41 WINE_DEFAULT_DEBUG_CHANNEL(ole);
42 WINE_DECLARE_DEBUG_CHANNEL(relay);
44 struct storage_header {
45 BYTE magic[8]; /* 00: magic */
46 BYTE unknown1[36]; /* 08: unknown */
47 DWORD num_of_bbd_blocks;/* 2C: length of big datablocks */
48 DWORD root_startblock;/* 30: root storage first big block */
49 DWORD unknown2[2]; /* 34: unknown */
50 DWORD sbd_startblock; /* 3C: small block depot first big block */
51 DWORD unknown3[3]; /* 40: unknown */
52 DWORD bbd_list[109]; /* 4C: big data block list (up to end of sector)*/
54 struct storage_pps_entry {
55 WCHAR pps_rawname[32];/* 00: \0 terminated widechar name */
56 WORD pps_sizeofname; /* 40: namelength in bytes */
57 BYTE pps_type; /* 42: flags, 1 storage/dir, 2 stream, 5 root */
58 BYTE pps_unknown0; /* 43: unknown */
59 DWORD pps_prev; /* 44: previous pps */
60 DWORD pps_next; /* 48: next pps */
61 DWORD pps_dir; /* 4C: directory pps */
62 GUID pps_guid; /* 50: class ID */
63 DWORD pps_unknown1; /* 60: unknown */
64 FILETIME pps_ft1; /* 64: filetime1 */
65 FILETIME pps_ft2; /* 6C: filetime2 */
66 DWORD pps_sb; /* 74: data startblock */
67 DWORD pps_size; /* 78: datalength. (<0x1000)?small:big blocks*/
68 DWORD pps_unknown2; /* 7C: unknown */
71 #define STORAGE_CHAINENTRY_FAT 0xfffffffd
72 #define STORAGE_CHAINENTRY_ENDOFCHAIN 0xfffffffe
73 #define STORAGE_CHAINENTRY_FREE 0xffffffff
76 static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
78 #define BIGSIZE 512
79 #define SMALLSIZE 64
81 #define SMALLBLOCKS_PER_BIGBLOCK (BIGSIZE/SMALLSIZE)
83 #define READ_HEADER(str) STORAGE_get_big_block(str,-1,(LPBYTE)&sth);assert(!memcmp(STORAGE_magic,sth.magic,sizeof(STORAGE_magic)));
84 static IStorage16Vtbl stvt16;
85 static const IStorage16Vtbl *segstvt16 = NULL;
86 static IStream16Vtbl strvt16;
87 static const IStream16Vtbl *segstrvt16 = NULL;
89 /*ULONG WINAPI IStorage16_AddRef(LPSTORAGE16 this);*/
90 static void _create_istorage16(LPSTORAGE16 *stg);
91 static void _create_istream16(LPSTREAM16 *str);
93 #define IMPLEMENTED 1
95 /* The following is taken from the CorVu implementation of docfiles, and
96 * documents things about the file format that are not implemented here, and
97 * not documented by the LAOLA project. The CorVu implementation was posted
98 * to wine-devel in February 2004, and released under the LGPL at the same
99 * time. Because that implementation is in C++, it's not directly usable in
100 * Wine, but does have documentation value.
103 * #define DF_EXT_VTOC -4
104 * #define DF_VTOC_VTOC -3
105 * #define DF_VTOC_EOF -2
106 * #define DF_VTOC_FREE -1
107 * #define DF_NAMELEN 0x20 // Maximum entry name length - 31 characters plus
108 * // a NUL terminator
110 * #define DF_FT_STORAGE 1
111 * #define DF_FT_STREAM 2
112 * #define DF_FT_LOCKBYTES 3 // Not used -- How the bloody hell did I manage
113 * #define DF_FT_PROPERTY 4 // Not Used -- to figure these two out?
114 * #define DF_FT_ROOT 5
116 * #define DF_BLOCK_SIZE 0x200
117 * #define DF_VTOC_SIZE 0x80
118 * #define DF_DE_PER_BLOCK 4
119 * #define DF_STREAM_BLOCK_SIZE 0x40
121 * A DocFile is divided into blocks of 512 bytes.
122 * The first block contains the header.
124 * The file header contains The first 109 entries in the VTOC of VTOCs.
126 * Each block pointed to by a VTOC of VTOCs contains a VTOC, which
127 * includes block chains - just like FAT. This is a somewhat poor
128 * design for the following reasons:
130 * 1. FAT was a poor file system design to begin with, and
131 * has long been known to be horrendously inefficient
132 * for day to day operations.
134 * 2. The problem is compounded here, since the file
135 * level streams are generally *not* read sequentially.
136 * This means that a significant percentage of reads
137 * require seeking from the start of the chain.
139 * Data chains also contain an internal VTOC. The block size for
140 * the standard VTOC is 512. The block size for the internal VTOC
141 * is 64.
143 * Now, the 109 blocks in the VTOC of VTOCs allows for files of
144 * up to around 7MB. So what do you think happens if that's
145 * exceeded? Well, there's an entry in the header block which
146 * points to the first block used as additional storage for
147 * the VTOC of VTOCs.
149 * Now we can get up to around 15MB. Now, guess how the file
150 * format adds in another block to the VTOC of VTOCs. Come on,
151 * it's no big surprise. That's right - the last entry in each
152 * block extending the VTOC of VTOCs is, you guessed it, the
153 * block number of the next block containing an extension to
154 * the VTOC of VTOCs. The VTOC of VTOCs is chained!!!!
156 * So, to review:
158 * 1. If you are using a FAT file system, the location of
159 * your file's blocks is stored in chains.
161 * 2. At the abstract level, the file contains a VTOC of VTOCs,
162 * which is stored in the most inefficient possible format for
163 * random access - a chain (AKA list).
165 * 3. The VTOC of VTOCs contains descriptions of three file level
166 * streams:
168 * a. The Directory stream
169 * b. The Data stream
170 * c. The Data VTOC stream
172 * These are, of course, represented as chains.
174 * 4. The Data VTOC contains data describing the chains of blocks
175 * within the Data stream.
177 * That's right - we have a total of four levels of block chains!
179 * Now, is that complicated enough for you? No? OK, there's another
180 * complication. If an individual stream (ie. an IStream) reaches
181 * 4096 bytes in size, it gets moved from the Data Stream to
182 * a new file level stream. Now, if the stream then gets truncated
183 * back to less than 4096 bytes, it returns to the data stream.
185 * The effect of using this format can be seen very easily. Pick
186 * an arbitrary application with a grid data representation that
187 * can export to both Lotus 123 and Excel 5 or higher. Export
188 * a large file to Lotus 123 and time it. Export the same thing
189 * to Excel 5 and time that. The difference is the inefficiency
190 * of the Microsoft DocFile format.
193 * #define TOTAL_SIMPLE_VTOCS 109
195 * struct DocFile_Header
197 * df_byte iMagic1; // 0xd0
198 * df_byte iMagic2; // 0xcf
199 * df_byte iMagic3; // 0x11
200 * df_byte iMagic4; // 0xe0 - Spells D0CF11E0, or DocFile
201 * df_byte iMagic5; // 161 (igi upside down)
202 * df_byte iMagic6; // 177 (lli upside down - see below
203 * df_byte iMagic7; // 26 (gz upside down)
204 * df_byte iMagic8; // 225 (szz upside down) - see below
205 * df_int4 aiUnknown1[4];
206 * df_int4 iVersion; // DocFile Version - 0x03003E
207 * df_int4 aiUnknown2[4];
208 * df_int4 nVTOCs; // Number of VTOCs
209 * df_int4 iFirstDirBlock; // First Directory Block
210 * df_int4 aiUnknown3[2];
211 * df_int4 iFirstDataVTOC; // First data VTOC block
212 * df_int4 iHasData; // 1 if there is data in the file - yes, this is important
213 * df_int4 iExtendedVTOC; // Extended VTOC location
214 * df_int4 iExtendedVTOCSize; // Size of extended VTOC (+1?)
215 * df_int4 aiVTOCofVTOCs[TOTAL_SIMPLE_VTOCS];
216 * };
218 * struct DocFile_VTOC
220 * df_int4 aiBlocks[DF_VTOC_SIZE];
221 * };
224 * The meaning of the magic numbers
226 * 0xd0cf11e0 is DocFile with a zero on the end (sort of)
228 * If you key 177161 into a calculator, then turn the calculator
229 * upside down, you get igilli, which may be a reference to
230 * somebody's name, or to the Hebrew word for "angel".
232 * If you key 26225 into a calculator, then turn it upside down, you
233 * get szzgz. Microsoft has a tradition of creating nonsense words
234 * using the letters s, g, z and y. We think szzgz may be one of the
235 * Microsoft placeholder variables, along the lines of foo, bar and baz.
236 * Alternatively, it could be 22526, which would be gzszz.
239 * struct DocFile_DirEnt
241 * df_char achEntryName[DF_NAMELEN]; // Entry Name
242 * df_int2 iNameLen; // Name length in bytes, including NUL terminator
243 * df_byte iFileType; // Entry type
244 * df_byte iColour; // 1 = Black, 0 = Red
245 * df_int4 iLeftSibling; // Next Left Sibling Entry - See below
246 * df_int4 iRightSibling; // Next Right Sibling Entry
247 * df_int4 iFirstChild; // First Child Entry
248 * df_byte achClassID[16]; // Class ID
249 * df_int4 iStateBits; // [GS]etStateBits value
250 * df_int4 iCreatedLow; // Low DWORD of creation time
251 * df_int4 iCreatedHigh; // High DWORD of creation time
252 * df_int4 iModifiedLow; // Low DWORD of modification time
253 * df_int4 iModifiedHigh; // High DWORD of modification time
254 * df_int4 iVTOCPosition; // VTOC Position
255 * df_int4 iFileSize; // Size of the stream
256 * df_int4 iZero; // We think this is part of the 64 bit stream size - must be 0
257 * };
259 * Siblings
260 * ========
262 * Siblings are stored in an obscure but incredibly elegant
263 * data structure called a red-black tree. This is generally
264 * defined as a 2-3-4 tree stored in a binary tree.
266 * A red-black tree can always be balanced very easily. The rules
267 * for a red-black tree are as follows:
269 * 1. The root node is always black.
270 * 2. The parent of a red node is always black.
272 * There is a Java demo of red-black trees at:
274 * http://langevin.usc.edu/BST/RedBlackTree-Example.html
276 * This demo is an excellent tool for learning how red-black
277 * trees work, without having to go through the process of
278 * learning how they were derived.
280 * Within the tree, elements are ordered by the length of the
281 * name and within that, ASCII order by name. This causes the
282 * apparently bizarre reordering you see when you use dfview.
284 * This is a somewhat bizarre choice. It suggests that the
285 * designer of the DocFile format was trying to optimise
286 * searching through the directory entries. However searching
287 * through directory entries is a relatively rare operation.
288 * Reading and seeking within a stream are much more common
289 * operations, especially within the file level streams, yet
290 * these use the horrendously inefficient FAT chains.
292 * This suggests that the designer was probably somebody
293 * fresh out of university, who had some basic knowledge of
294 * basic data structures, but little knowledge of anything
295 * more practical. It is bizarre to attempt to optimise
296 * directory searches while not using a more efficient file
297 * block locating system than FAT (seedling/sapling/tree
298 * would result in a massive improvement - in fact we have
299 * an alternative to docfiles that we use internally that
300 * uses seedling/sapling/tree and *is* far more efficient).
302 * It is worth noting that the MS implementation of red-black
303 * trees is incorrect (I can tell you're surprised) and
304 * actually causes more operations to occur than are really
305 * needed. Fortunately the fact that our implementation is
306 * correct will not cause any problems - the MS implementation
307 * still appears to cause the tree to satisfy the rules, albeit
308 * a sequence of the same insertions in the different
309 * implementations may result in a different, and possibly
310 * deeper (but never shallower) tree.
313 typedef struct {
314 HANDLE hf;
315 SEGPTR lockbytes;
316 } stream_access16;
317 /* --- IStorage16 implementation struct */
319 typedef struct
321 IStorage16 IStorage16_iface;
322 LONG ref;
323 /* IStorage16 fields */
324 SEGPTR thisptr; /* pointer to this struct as segmented */
325 struct storage_pps_entry stde;
326 int ppsent;
327 stream_access16 str;
328 } IStorage16Impl;
331 /******************************************************************************
332 * STORAGE_get_big_block [Internal]
334 * Reading OLE compound storage
336 static BOOL
337 STORAGE_get_big_block(stream_access16 *str,int n,BYTE *block)
339 DWORD result;
341 assert(n>=-1);
342 if (str->hf) {
343 if ((SetFilePointer( str->hf, (n+1)*BIGSIZE, NULL,
344 SEEK_SET ) == INVALID_SET_FILE_POINTER) && GetLastError())
346 WARN("(%p,%d,%p), seek failed (%ld)\n",str->hf, n, block, GetLastError());
347 return FALSE;
349 if (!ReadFile( str->hf, block, BIGSIZE, &result, NULL ) || result != BIGSIZE)
351 WARN("(hf=%p, block size %d): read didn't read (%ld)\n",str->hf,n,GetLastError());
352 return FALSE;
354 } else {
355 DWORD args[6];
356 HRESULT hres;
357 HANDLE16 hsig;
359 args[0] = (DWORD)str->lockbytes; /* iface */
360 args[1] = (n+1)*BIGSIZE;
361 args[2] = 0; /* ULARGE_INTEGER offset */
362 args[3] = WOWGlobalAllocLock16( 0, BIGSIZE, &hsig ); /* sig */
363 args[4] = BIGSIZE;
364 args[5] = 0;
366 if (!WOWCallback16Ex(
367 (DWORD)((const ILockBytes16Vtbl*)MapSL(
368 (SEGPTR)((LPLOCKBYTES16)MapSL(str->lockbytes))->lpVtbl)
369 )->ReadAt,
370 WCB16_PASCAL,
371 6*sizeof(DWORD),
372 (LPVOID)args,
373 (LPDWORD)&hres
374 )) {
375 ERR("CallTo16 ILockBytes16::ReadAt() failed, hres %lx\n",hres);
376 return FALSE;
378 memcpy(block, MapSL(args[3]), BIGSIZE);
379 WOWGlobalUnlockFree16(args[3]);
381 return TRUE;
384 static BOOL
385 _ilockbytes16_writeat(SEGPTR lockbytes, DWORD offset, DWORD length, void *buffer) {
386 DWORD args[6];
387 HRESULT hres;
389 args[0] = (DWORD)lockbytes; /* iface */
390 args[1] = offset;
391 args[2] = 0; /* ULARGE_INTEGER offset */
392 args[3] = (DWORD)MapLS( buffer );
393 args[4] = length;
394 args[5] = 0;
396 /* THIS_ ULARGE_INTEGER ulOffset, const void *pv, ULONG cb, ULONG *pcbWritten); */
398 if (!WOWCallback16Ex(
399 (DWORD)((const ILockBytes16Vtbl*)MapSL(
400 (SEGPTR)((LPLOCKBYTES16)MapSL(lockbytes))->lpVtbl)
401 )->WriteAt,
402 WCB16_PASCAL,
403 6*sizeof(DWORD),
404 (LPVOID)args,
405 (LPDWORD)&hres
406 )) {
407 ERR("CallTo16 ILockBytes16::WriteAt() failed, hres %lx\n",hres);
408 return FALSE;
410 UnMapLS(args[3]);
411 return TRUE;
414 /******************************************************************************
415 * STORAGE_put_big_block [INTERNAL]
417 static BOOL
418 STORAGE_put_big_block(stream_access16 *str,int n,BYTE *block)
420 DWORD result;
422 assert(n>=-1);
423 if (str->hf) {
424 if ((SetFilePointer( str->hf, (n+1)*BIGSIZE, NULL,
425 SEEK_SET ) == INVALID_SET_FILE_POINTER) && GetLastError())
427 WARN("seek failed (%ld)\n",GetLastError());
428 return FALSE;
430 if (!WriteFile( str->hf, block, BIGSIZE, &result, NULL ) || result != BIGSIZE)
432 WARN(" write failed (%ld)\n",GetLastError());
433 return FALSE;
435 return TRUE;
436 } else {
437 _ilockbytes16_writeat(str->lockbytes, (n+1)*BIGSIZE, BIGSIZE, block);
438 return TRUE;
442 /******************************************************************************
443 * STORAGE_get_next_big_blocknr [INTERNAL]
445 static int
446 STORAGE_get_next_big_blocknr(stream_access16 *str,int blocknr) {
447 INT bbs[BIGSIZE/sizeof(INT)];
448 struct storage_header sth;
450 READ_HEADER(str);
452 assert(blocknr>>7<sth.num_of_bbd_blocks);
453 if (sth.bbd_list[blocknr>>7]==0xffffffff)
454 return -5;
455 if (!STORAGE_get_big_block(str,sth.bbd_list[blocknr>>7],(LPBYTE)bbs))
456 return -5;
457 assert(bbs[blocknr&0x7f]!=STORAGE_CHAINENTRY_FREE);
458 return bbs[blocknr&0x7f];
461 /******************************************************************************
462 * STORAGE_get_nth_next_big_blocknr [INTERNAL]
464 static int
465 STORAGE_get_nth_next_big_blocknr(stream_access16 *str,int blocknr,int nr) {
466 INT bbs[BIGSIZE/sizeof(INT)];
467 int lastblock = -1;
468 struct storage_header sth;
470 TRACE("(blocknr=%d, nr=%d)\n", blocknr, nr);
471 READ_HEADER(str);
473 assert(blocknr>=0);
474 while (nr--) {
475 assert((blocknr>>7)<sth.num_of_bbd_blocks);
476 assert(sth.bbd_list[blocknr>>7]!=0xffffffff);
478 /* simple caching... */
479 if (lastblock!=sth.bbd_list[blocknr>>7]) {
480 BOOL ret = STORAGE_get_big_block(str,sth.bbd_list[blocknr>>7],(LPBYTE)bbs);
481 assert(ret);
482 lastblock = sth.bbd_list[blocknr>>7];
484 blocknr = bbs[blocknr&0x7f];
486 return blocknr;
489 /******************************************************************************
490 * STORAGE_get_root_pps_entry [Internal]
492 static BOOL
493 STORAGE_get_root_pps_entry(stream_access16* str,struct storage_pps_entry *pstde) {
494 int blocknr,i;
495 BYTE block[BIGSIZE];
496 struct storage_pps_entry *stde=(struct storage_pps_entry*)block;
497 struct storage_header sth;
499 READ_HEADER(str);
500 blocknr = sth.root_startblock;
501 TRACE("startblock is %d\n", blocknr);
502 while (blocknr>=0) {
503 BOOL ret = STORAGE_get_big_block(str,blocknr,block);
504 assert(ret);
505 for (i=0;i<4;i++) {
506 if (!stde[i].pps_sizeofname)
507 continue;
508 if (stde[i].pps_type==5) {
509 *pstde=stde[i];
510 return TRUE;
513 blocknr=STORAGE_get_next_big_blocknr(str,blocknr);
514 TRACE("next block is %d\n", blocknr);
516 return FALSE;
519 /******************************************************************************
520 * STORAGE_get_small_block [INTERNAL]
522 static BOOL
523 STORAGE_get_small_block(stream_access16 *str,int blocknr,BYTE *sblock) {
524 BYTE block[BIGSIZE];
525 int bigblocknr;
526 struct storage_pps_entry root;
527 BOOL ret;
529 TRACE("(blocknr=%d)\n", blocknr);
530 assert(blocknr>=0);
531 ret = STORAGE_get_root_pps_entry(str,&root);
532 assert(ret);
533 bigblocknr = STORAGE_get_nth_next_big_blocknr(str,root.pps_sb,blocknr/SMALLBLOCKS_PER_BIGBLOCK);
534 assert(bigblocknr>=0);
535 ret = STORAGE_get_big_block(str,bigblocknr,block);
536 assert(ret);
538 memcpy(sblock,((LPBYTE)block)+SMALLSIZE*(blocknr&(SMALLBLOCKS_PER_BIGBLOCK-1)),SMALLSIZE);
539 return TRUE;
542 /******************************************************************************
543 * STORAGE_put_small_block [INTERNAL]
545 static BOOL
546 STORAGE_put_small_block(stream_access16 *str,int blocknr,const BYTE *sblock) {
547 BYTE block[BIGSIZE];
548 int bigblocknr;
549 struct storage_pps_entry root;
550 BOOL ret;
552 assert(blocknr>=0);
553 TRACE("(blocknr=%d)\n", blocknr);
555 ret = STORAGE_get_root_pps_entry(str,&root);
556 assert(ret);
557 bigblocknr = STORAGE_get_nth_next_big_blocknr(str,root.pps_sb,blocknr/SMALLBLOCKS_PER_BIGBLOCK);
558 assert(bigblocknr>=0);
559 ret = STORAGE_get_big_block(str,bigblocknr,block);
560 assert(ret);
562 memcpy(((LPBYTE)block)+SMALLSIZE*(blocknr&(SMALLBLOCKS_PER_BIGBLOCK-1)),sblock,SMALLSIZE);
563 ret = STORAGE_put_big_block(str,bigblocknr,block);
564 assert(ret);
565 return TRUE;
568 /******************************************************************************
569 * STORAGE_get_next_small_blocknr [INTERNAL]
571 static int
572 STORAGE_get_next_small_blocknr(stream_access16 *str,int blocknr) {
573 BYTE block[BIGSIZE];
574 LPINT sbd = (LPINT)block;
575 int bigblocknr;
576 struct storage_header sth;
577 BOOL ret;
579 TRACE("(blocknr=%d)\n", blocknr);
580 READ_HEADER(str);
581 assert(blocknr>=0);
582 bigblocknr = STORAGE_get_nth_next_big_blocknr(str,sth.sbd_startblock,blocknr/128);
583 assert(bigblocknr>=0);
584 ret = STORAGE_get_big_block(str,bigblocknr,block);
585 assert(ret);
586 assert(sbd[blocknr & 127]!=STORAGE_CHAINENTRY_FREE);
587 return sbd[blocknr & (128-1)];
590 /******************************************************************************
591 * STORAGE_get_nth_next_small_blocknr [INTERNAL]
593 static int
594 STORAGE_get_nth_next_small_blocknr(stream_access16*str,int blocknr,int nr) {
595 int lastblocknr=-129;
596 BYTE block[BIGSIZE];
597 LPINT sbd = (LPINT)block;
598 struct storage_header sth;
599 BOOL ret;
601 TRACE("(blocknr=%d, nr=%d)\n", blocknr, nr);
602 READ_HEADER(str);
603 assert(blocknr>=0);
604 while ((nr--) && (blocknr>=0)) {
605 if (lastblocknr/128!=blocknr/128) {
606 int bigblocknr;
607 bigblocknr = STORAGE_get_nth_next_big_blocknr(str,sth.sbd_startblock,blocknr/128);
608 assert(bigblocknr>=0);
609 ret = STORAGE_get_big_block(str,bigblocknr,block);
610 assert(ret);
611 lastblocknr = blocknr;
613 assert(lastblocknr>=0);
614 lastblocknr=blocknr;
615 blocknr=sbd[blocknr & (128-1)];
616 assert(blocknr!=STORAGE_CHAINENTRY_FREE);
618 return blocknr;
621 /******************************************************************************
622 * STORAGE_get_pps_entry [INTERNAL]
624 static int
625 STORAGE_get_pps_entry(stream_access16*str,int n,struct storage_pps_entry *pstde) {
626 int blocknr;
627 BYTE block[BIGSIZE];
628 struct storage_pps_entry *stde = (struct storage_pps_entry*)(((LPBYTE)block)+128*(n&3));
629 struct storage_header sth;
630 BOOL ret;
632 TRACE("(n=%d)\n", n);
633 READ_HEADER(str);
634 /* we have 4 pps entries per big block */
635 blocknr = STORAGE_get_nth_next_big_blocknr(str,sth.root_startblock,n/4);
636 assert(blocknr>=0);
637 ret = STORAGE_get_big_block(str,blocknr,block);
638 assert(ret);
640 *pstde=*stde;
641 return 1;
644 /******************************************************************************
645 * STORAGE_put_pps_entry [Internal]
647 static int
648 STORAGE_put_pps_entry(stream_access16*str,int n,const struct storage_pps_entry *pstde) {
649 int blocknr;
650 BYTE block[BIGSIZE];
651 struct storage_pps_entry *stde = (struct storage_pps_entry*)(((LPBYTE)block)+128*(n&3));
652 struct storage_header sth;
653 BOOL ret;
655 TRACE("(n=%d)\n", n);
656 READ_HEADER(str);
657 /* we have 4 pps entries per big block */
658 blocknr = STORAGE_get_nth_next_big_blocknr(str,sth.root_startblock,n/4);
659 assert(blocknr>=0);
660 ret = STORAGE_get_big_block(str,blocknr,block);
661 assert(ret);
662 *stde=*pstde;
663 ret = STORAGE_put_big_block(str,blocknr,block);
664 assert(ret);
665 return 1;
668 /******************************************************************************
669 * STORAGE_look_for_named_pps [Internal]
671 static int
672 STORAGE_look_for_named_pps(stream_access16*str,int n,LPOLESTR name) {
673 struct storage_pps_entry stde;
674 int ret;
676 TRACE("(n=%d,name=%s)\n", n, debugstr_w(name));
677 if (n==-1)
678 return -1;
679 if (1!=STORAGE_get_pps_entry(str,n,&stde))
680 return -1;
682 if (!wcscmp(name,stde.pps_rawname))
683 return n;
684 if (stde.pps_prev != -1) {
685 ret=STORAGE_look_for_named_pps(str,stde.pps_prev,name);
686 if (ret!=-1)
687 return ret;
689 if (stde.pps_next != -1) {
690 ret=STORAGE_look_for_named_pps(str,stde.pps_next,name);
691 if (ret!=-1)
692 return ret;
694 return -1;
697 /******************************************************************************
698 * STORAGE_dump_pps_entry [Internal]
700 * This function is there to simplify debugging. It is otherwise unused.
702 void
703 STORAGE_dump_pps_entry(struct storage_pps_entry *stde) {
704 char name[33];
706 WideCharToMultiByte( CP_ACP, 0, stde->pps_rawname, -1, name, sizeof(name), NULL, NULL);
707 if (!stde->pps_sizeofname)
708 return;
709 TRACE("name: %s\n",name);
710 TRACE("type: %d\n",stde->pps_type);
711 TRACE("prev pps: %ld\n",stde->pps_prev);
712 TRACE("next pps: %ld\n",stde->pps_next);
713 TRACE("dir pps: %ld\n",stde->pps_dir);
714 TRACE("guid: %s\n",debugstr_guid(&(stde->pps_guid)));
715 if (stde->pps_type !=2) {
716 time_t t;
717 DWORD dw;
718 RtlTimeToSecondsSince1970((LARGE_INTEGER *)&(stde->pps_ft1),&dw);
719 t = dw;
720 TRACE("ts1: %s\n",ctime(&t));
721 RtlTimeToSecondsSince1970((LARGE_INTEGER *)&(stde->pps_ft2),&dw);
722 t = dw;
723 TRACE("ts2: %s\n",ctime(&t));
725 TRACE("startblock: %ld\n",stde->pps_sb);
726 TRACE("size: %ld\n",stde->pps_size);
729 /******************************************************************************
730 * STORAGE_init_storage [INTERNAL]
732 static BOOL
733 STORAGE_init_storage(stream_access16 *str) {
734 BYTE block[BIGSIZE];
735 LPDWORD bbs;
736 struct storage_header *sth;
737 struct storage_pps_entry *stde;
738 DWORD result;
740 if (str->hf)
741 SetFilePointer( str->hf, 0, NULL, SEEK_SET );
742 /* block -1 is the storage header */
743 sth = (struct storage_header*)block;
744 memcpy(sth->magic,STORAGE_magic,8);
745 memset(sth->unknown1,0,sizeof(sth->unknown1));
746 memset(sth->unknown2,0,sizeof(sth->unknown2));
747 memset(sth->unknown3,0,sizeof(sth->unknown3));
748 sth->num_of_bbd_blocks = 1;
749 sth->root_startblock = 1;
750 sth->sbd_startblock = 0xffffffff;
751 memset(sth->bbd_list,0xff,sizeof(sth->bbd_list));
752 sth->bbd_list[0] = 0;
753 if (str->hf) {
754 if (!WriteFile( str->hf, block, BIGSIZE, &result, NULL ) || result != BIGSIZE) return FALSE;
755 } else {
756 if (!_ilockbytes16_writeat(str->lockbytes, 0, BIGSIZE, block)) return FALSE;
758 /* block 0 is the big block directory */
759 bbs=(LPDWORD)block;
760 memset(block,0xff,sizeof(block)); /* mark all blocks as free */
761 bbs[0]=STORAGE_CHAINENTRY_ENDOFCHAIN; /* for this block */
762 bbs[1]=STORAGE_CHAINENTRY_ENDOFCHAIN; /* for directory entry */
763 if (str->hf) {
764 if (!WriteFile( str->hf, block, BIGSIZE, &result, NULL ) || result != BIGSIZE) return FALSE;
765 } else {
766 if (!_ilockbytes16_writeat(str->lockbytes, BIGSIZE, BIGSIZE, block)) return FALSE;
768 /* block 1 is the root directory entry */
769 memset(block,0x00,sizeof(block));
770 stde = (struct storage_pps_entry*)block;
771 MultiByteToWideChar(CP_ACP, 0, "RootEntry", -1, stde->pps_rawname,
772 ARRAY_SIZE(stde->pps_rawname));
773 stde->pps_sizeofname = (lstrlenW(stde->pps_rawname)+1) * sizeof(WCHAR);
774 stde->pps_type = 5;
775 stde->pps_dir = -1;
776 stde->pps_next = -1;
777 stde->pps_prev = -1;
778 stde->pps_sb = 0xffffffff;
779 stde->pps_size = 0;
780 if (str->hf) {
781 return (WriteFile( str->hf, block, BIGSIZE, &result, NULL ) && result == BIGSIZE);
782 } else {
783 return _ilockbytes16_writeat(str->lockbytes, BIGSIZE, BIGSIZE, block);
787 /******************************************************************************
788 * STORAGE_set_big_chain [Internal]
790 static BOOL
791 STORAGE_set_big_chain(stream_access16*str,int blocknr,INT type) {
792 BYTE block[BIGSIZE];
793 LPINT bbd = (LPINT)block;
794 int nextblocknr,bigblocknr;
795 struct storage_header sth;
796 BOOL ret;
798 READ_HEADER(str);
799 assert(blocknr!=type);
800 while (blocknr>=0) {
801 bigblocknr = sth.bbd_list[blocknr/128];
802 assert(bigblocknr>=0);
803 ret = STORAGE_get_big_block(str,bigblocknr,block);
804 assert(ret);
806 nextblocknr = bbd[blocknr&(128-1)];
807 bbd[blocknr&(128-1)] = type;
808 if (type>=0)
809 return TRUE;
810 ret = STORAGE_put_big_block(str,bigblocknr,block);
811 assert(ret);
812 type = STORAGE_CHAINENTRY_FREE;
813 blocknr = nextblocknr;
815 return TRUE;
818 /******************************************************************************
819 * STORAGE_set_small_chain [Internal]
821 static BOOL
822 STORAGE_set_small_chain(stream_access16*str,int blocknr,INT type) {
823 BYTE block[BIGSIZE];
824 LPINT sbd = (LPINT)block;
825 int lastblocknr,nextsmallblocknr,bigblocknr;
826 struct storage_header sth;
827 BOOL ret;
829 READ_HEADER(str);
831 assert(blocknr!=type);
832 lastblocknr=-129;bigblocknr=-2;
833 while (blocknr>=0) {
834 /* cache block ... */
835 if (lastblocknr/128!=blocknr/128) {
836 bigblocknr = STORAGE_get_nth_next_big_blocknr(str,sth.sbd_startblock,blocknr/128);
837 assert(bigblocknr>=0);
838 ret = STORAGE_get_big_block(str,bigblocknr,block);
839 assert(ret);
841 lastblocknr = blocknr;
842 nextsmallblocknr = sbd[blocknr&(128-1)];
843 sbd[blocknr&(128-1)] = type;
844 ret = STORAGE_put_big_block(str,bigblocknr,block);
845 assert(ret);
846 if (type>=0)
847 return TRUE;
848 type = STORAGE_CHAINENTRY_FREE;
849 blocknr = nextsmallblocknr;
851 return TRUE;
854 /******************************************************************************
855 * STORAGE_get_free_big_blocknr [Internal]
857 static int
858 STORAGE_get_free_big_blocknr(stream_access16 *str) {
859 BYTE block[BIGSIZE];
860 LPINT sbd = (LPINT)block;
861 int lastbigblocknr,i,bigblocknr;
862 unsigned int curblock;
863 struct storage_header sth;
864 BOOL ret;
866 READ_HEADER(str);
867 curblock = 0;
868 lastbigblocknr = -1;
869 bigblocknr = sth.bbd_list[curblock];
870 while (curblock<sth.num_of_bbd_blocks) {
871 assert(bigblocknr>=0);
872 ret = STORAGE_get_big_block(str,bigblocknr,block);
873 assert(ret);
874 for (i=0;i<128;i++)
875 if (sbd[i]==STORAGE_CHAINENTRY_FREE) {
876 sbd[i] = STORAGE_CHAINENTRY_ENDOFCHAIN;
877 ret = STORAGE_put_big_block(str,bigblocknr,block);
878 assert(ret);
879 memset(block,0x42,sizeof(block));
880 ret = STORAGE_put_big_block(str,i+curblock*128,block);
881 assert(ret);
882 return i+curblock*128;
884 lastbigblocknr = bigblocknr;
885 bigblocknr = sth.bbd_list[++curblock];
887 bigblocknr = curblock*128;
888 /* since we have marked all blocks from 0 up to curblock*128-1
889 * the next free one is curblock*128, where we happily put our
890 * next large block depot.
892 memset(block,0xff,sizeof(block));
893 /* mark the block allocated and returned by this function */
894 sbd[1] = STORAGE_CHAINENTRY_ENDOFCHAIN;
895 ret = STORAGE_put_big_block(str,bigblocknr,block);
896 assert(ret);
898 /* if we had a bbd block already (most likely) we need
899 * to link the new one into the chain
901 if (lastbigblocknr!=-1) {
902 ret = STORAGE_set_big_chain(str,lastbigblocknr,bigblocknr);
903 assert(ret);
905 sth.bbd_list[curblock]=bigblocknr;
906 sth.num_of_bbd_blocks++;
907 assert(sth.num_of_bbd_blocks==curblock+1);
908 ret = STORAGE_put_big_block(str,-1,(LPBYTE)&sth);
909 assert(ret);
911 /* Set the end of the chain for the bigblockdepots */
912 ret = STORAGE_set_big_chain(str,bigblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN);
913 assert(ret);
914 /* add 1, for the first entry is used for the additional big block
915 * depot. (means we already used bigblocknr) */
916 memset(block,0x42,sizeof(block));
917 /* allocate this block (filled with 0x42) */
918 ret = STORAGE_put_big_block(str,bigblocknr+1,block);
919 assert(ret);
920 return bigblocknr+1;
924 /******************************************************************************
925 * STORAGE_get_free_small_blocknr [Internal]
927 static int
928 STORAGE_get_free_small_blocknr(stream_access16 *str) {
929 BYTE block[BIGSIZE];
930 LPINT sbd = (LPINT)block;
931 int lastbigblocknr,newblocknr,i,curblock,bigblocknr;
932 struct storage_pps_entry root;
933 struct storage_header sth;
935 READ_HEADER(str);
936 bigblocknr = sth.sbd_startblock;
937 curblock = 0;
938 lastbigblocknr = -1;
939 newblocknr = -1;
940 while (bigblocknr>=0) {
941 if (!STORAGE_get_big_block(str,bigblocknr,block))
942 return -1;
943 for (i=0;i<128;i++)
944 if (sbd[i]==STORAGE_CHAINENTRY_FREE) {
945 sbd[i]=STORAGE_CHAINENTRY_ENDOFCHAIN;
946 newblocknr = i+curblock*128;
947 break;
949 if (i!=128)
950 break;
951 lastbigblocknr = bigblocknr;
952 bigblocknr = STORAGE_get_next_big_blocknr(str,bigblocknr);
953 curblock++;
955 if (newblocknr==-1) {
956 bigblocknr = STORAGE_get_free_big_blocknr(str);
957 if (bigblocknr<0)
958 return -1;
959 READ_HEADER(str);
960 memset(block,0xff,sizeof(block));
961 sbd[0]=STORAGE_CHAINENTRY_ENDOFCHAIN;
962 if (!STORAGE_put_big_block(str,bigblocknr,block))
963 return -1;
964 if (lastbigblocknr==-1) {
965 sth.sbd_startblock = bigblocknr;
966 if (!STORAGE_put_big_block(str,-1,(LPBYTE)&sth)) /* need to write it */
967 return -1;
968 } else {
969 if (!STORAGE_set_big_chain(str,lastbigblocknr,bigblocknr))
970 return -1;
972 if (!STORAGE_set_big_chain(str,bigblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
973 return -1;
974 newblocknr = curblock*128;
976 /* allocate enough big blocks for storing the allocated small block */
977 if (!STORAGE_get_root_pps_entry(str,&root))
978 return -1;
979 if (root.pps_sb==-1)
980 lastbigblocknr = -1;
981 else
982 lastbigblocknr = STORAGE_get_nth_next_big_blocknr(str,root.pps_sb,(root.pps_size-1)/BIGSIZE);
983 while (root.pps_size < (newblocknr*SMALLSIZE+SMALLSIZE-1)) {
984 /* we need to allocate more stuff */
985 bigblocknr = STORAGE_get_free_big_blocknr(str);
986 if (bigblocknr<0)
987 return -1;
988 READ_HEADER(str);
989 if (root.pps_sb==-1) {
990 root.pps_sb = bigblocknr;
991 root.pps_size += BIGSIZE;
992 } else {
993 if (!STORAGE_set_big_chain(str,lastbigblocknr,bigblocknr))
994 return -1;
995 root.pps_size += BIGSIZE;
997 lastbigblocknr = bigblocknr;
999 if (!STORAGE_set_big_chain(str,lastbigblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1000 return -1;
1001 if (!STORAGE_put_pps_entry(str,0,&root))
1002 return -1;
1003 return newblocknr;
1006 /******************************************************************************
1007 * STORAGE_get_free_pps_entry [Internal]
1009 static int
1010 STORAGE_get_free_pps_entry(stream_access16*str) {
1011 int blocknr, i, curblock, lastblocknr=-1;
1012 BYTE block[BIGSIZE];
1013 struct storage_pps_entry *stde = (struct storage_pps_entry*)block;
1014 struct storage_header sth;
1016 READ_HEADER(str);
1017 blocknr = sth.root_startblock;
1018 assert(blocknr>=0);
1019 curblock=0;
1020 while (blocknr>=0) {
1021 if (!STORAGE_get_big_block(str,blocknr,block))
1022 return -1;
1023 for (i=0;i<4;i++)
1024 if (stde[i].pps_sizeofname==0) /* free */
1025 return curblock*4+i;
1026 lastblocknr = blocknr;
1027 blocknr = STORAGE_get_next_big_blocknr(str,blocknr);
1028 curblock++;
1030 assert(blocknr==STORAGE_CHAINENTRY_ENDOFCHAIN);
1031 blocknr = STORAGE_get_free_big_blocknr(str);
1032 /* sth invalidated */
1033 if (blocknr<0)
1034 return -1;
1036 if (!STORAGE_set_big_chain(str,lastblocknr,blocknr))
1037 return -1;
1038 if (!STORAGE_set_big_chain(str,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1039 return -1;
1040 memset(block,0,sizeof(block));
1041 STORAGE_put_big_block(str,blocknr,block);
1042 return curblock*4;
1045 /* --- IStream16 implementation */
1047 typedef struct
1049 IStream16 IStream16_iface;
1050 LONG ref;
1051 /* IStream16 fields */
1052 SEGPTR thisptr; /* pointer to this struct as segmented */
1053 struct storage_pps_entry stde;
1054 int ppsent;
1055 ULARGE_INTEGER offset;
1056 stream_access16 str;
1057 } IStream16Impl;
1059 static inline IStream16Impl *impl_from_IStream16(IStream16 *iface)
1061 return CONTAINING_RECORD(iface, IStream16Impl, IStream16_iface);
1064 /******************************************************************************
1065 * IStream16_QueryInterface [STORAGE.518]
1067 HRESULT CDECL IStream16_fnQueryInterface(IStream16 *iface, REFIID refiid, void **obj)
1069 IStream16Impl *This = impl_from_IStream16(iface);
1070 TRACE_(relay)("(%p)->(%s,%p)\n",This,debugstr_guid(refiid),obj);
1071 if (!memcmp(&IID_IUnknown,refiid,sizeof(IID_IUnknown))) {
1072 *obj = This;
1073 return 0;
1075 return OLE_E_ENUM_NOMORE;
1079 /******************************************************************************
1080 * IStream16_AddRef [STORAGE.519]
1082 ULONG CDECL IStream16_fnAddRef(IStream16 *iface)
1084 IStream16Impl *This = impl_from_IStream16(iface);
1085 return InterlockedIncrement(&This->ref);
1088 static void
1089 _ilockbytes16_addref(SEGPTR lockbytes) {
1090 DWORD args[1];
1091 HRESULT hres;
1093 args[0] = (DWORD)lockbytes; /* iface */
1094 if (!WOWCallback16Ex(
1095 (DWORD)((const ILockBytes16Vtbl*)MapSL(
1096 (SEGPTR)((LPLOCKBYTES16)MapSL(lockbytes))->lpVtbl)
1097 )->AddRef,
1098 WCB16_PASCAL,
1099 1*sizeof(DWORD),
1100 (LPVOID)args,
1101 (LPDWORD)&hres
1103 ERR("CallTo16 ILockBytes16::AddRef() failed, hres %lx\n",hres);
1106 static void
1107 _ilockbytes16_release(SEGPTR lockbytes) {
1108 DWORD args[1];
1109 HRESULT hres;
1111 args[0] = (DWORD)lockbytes; /* iface */
1112 if (!WOWCallback16Ex(
1113 (DWORD)((const ILockBytes16Vtbl*)MapSL(
1114 (SEGPTR)((LPLOCKBYTES16)MapSL(lockbytes))->lpVtbl)
1115 )->Release,
1116 WCB16_PASCAL,
1117 1*sizeof(DWORD),
1118 (LPVOID)args,
1119 (LPDWORD)&hres
1121 ERR("CallTo16 ILockBytes16::Release() failed, hres %lx\n",hres);
1124 static void
1125 _ilockbytes16_flush(SEGPTR lockbytes) {
1126 DWORD args[1];
1127 HRESULT hres;
1129 args[0] = (DWORD)lockbytes; /* iface */
1130 if (!WOWCallback16Ex(
1131 (DWORD)((const ILockBytes16Vtbl*)MapSL(
1132 (SEGPTR)((LPLOCKBYTES16)MapSL(lockbytes))->lpVtbl)
1133 )->Flush,
1134 WCB16_PASCAL,
1135 1*sizeof(DWORD),
1136 (LPVOID)args,
1137 (LPDWORD)&hres
1139 ERR("CallTo16 ILockBytes16::Flush() failed, hres %lx\n",hres);
1142 /******************************************************************************
1143 * IStream16_Release [STORAGE.520]
1145 ULONG CDECL IStream16_fnRelease(IStream16 *iface)
1147 IStream16Impl *This = impl_from_IStream16(iface);
1148 ULONG ref;
1150 if (This->str.hf)
1151 FlushFileBuffers(This->str.hf);
1152 else
1153 _ilockbytes16_flush(This->str.lockbytes);
1154 ref = InterlockedDecrement(&This->ref);
1155 if (ref)
1156 return ref;
1158 if (This->str.hf)
1159 CloseHandle(This->str.hf);
1160 else
1161 _ilockbytes16_release(This->str.lockbytes);
1162 UnMapLS( This->thisptr );
1163 HeapFree( GetProcessHeap(), 0, This );
1164 return 0;
1167 /******************************************************************************
1168 * IStream16_Seek [STORAGE.523]
1170 * FIXME
1171 * Does not handle 64 bits
1173 HRESULT CDECL IStream16_fnSeek(IStream16 *iface, LARGE_INTEGER offset, DWORD whence,
1174 ULARGE_INTEGER *newpos)
1176 IStream16Impl *This = impl_from_IStream16(iface);
1177 TRACE_(relay)("(%p)->([%ld.%ld],%ld,%p)\n",This,offset.u.HighPart,offset.u.LowPart,whence,newpos);
1179 switch (whence) {
1180 case STREAM_SEEK_SET:
1181 This->offset.QuadPart = offset.QuadPart;
1182 break;
1183 case STREAM_SEEK_CUR:
1184 if ((offset.QuadPart < 0 && -offset.QuadPart > This->offset.QuadPart) ||
1185 (offset.QuadPart > 0 && -offset.QuadPart <= This->offset.QuadPart))
1186 return STG_E_INVALIDFUNCTION;
1187 This->offset.QuadPart += offset.QuadPart;
1188 break;
1189 case STREAM_SEEK_END:
1190 if (-offset.QuadPart > This->stde.pps_size)
1191 return STG_E_INVALIDFUNCTION;
1193 This->offset.QuadPart = This->stde.pps_size + offset.QuadPart;
1194 break;
1197 if (This->offset.QuadPart>This->stde.pps_size)
1198 This->offset.QuadPart=This->stde.pps_size;
1199 if (newpos) *newpos = This->offset;
1200 return S_OK;
1203 /******************************************************************************
1204 * IStream16_Read [STORAGE.521]
1206 HRESULT CDECL IStream16_fnRead(IStream16 *iface, void *pv, ULONG cb, ULONG *pcbRead)
1208 IStream16Impl *This = impl_from_IStream16(iface);
1209 BYTE block[BIGSIZE];
1210 ULONG *bytesread=pcbRead,xxread;
1211 int blocknr;
1212 LPBYTE pbv = pv;
1214 TRACE_(relay)("(%p)->(%p,%ld,%p)\n",This,pv,cb,pcbRead);
1215 if (!pcbRead) bytesread=&xxread;
1216 *bytesread = 0;
1218 if (cb>This->stde.pps_size-This->offset.u.LowPart)
1219 cb=This->stde.pps_size-This->offset.u.LowPart;
1220 if (This->stde.pps_size < 0x1000) {
1221 /* use small block reader */
1222 blocknr = STORAGE_get_nth_next_small_blocknr(&This->str,This->stde.pps_sb,This->offset.u.LowPart/SMALLSIZE);
1223 while (cb) {
1224 unsigned int cc;
1226 if (!STORAGE_get_small_block(&This->str,blocknr,block)) {
1227 WARN("small block read failed!!!\n");
1228 return E_FAIL;
1230 cc = cb;
1231 if (cc>SMALLSIZE-(This->offset.u.LowPart&(SMALLSIZE-1)))
1232 cc=SMALLSIZE-(This->offset.u.LowPart&(SMALLSIZE-1));
1233 memcpy(pbv,block+(This->offset.u.LowPart&(SMALLSIZE-1)),cc);
1234 This->offset.u.LowPart+=cc;
1235 pbv+=cc;
1236 *bytesread+=cc;
1237 cb-=cc;
1238 blocknr = STORAGE_get_next_small_blocknr(&This->str,blocknr);
1240 } else {
1241 /* use big block reader */
1242 blocknr = STORAGE_get_nth_next_big_blocknr(&This->str,This->stde.pps_sb,This->offset.u.LowPart/BIGSIZE);
1243 while (cb) {
1244 unsigned int cc;
1246 if (!STORAGE_get_big_block(&This->str,blocknr,block)) {
1247 WARN("big block read failed!!!\n");
1248 return E_FAIL;
1250 cc = cb;
1251 if (cc>BIGSIZE-(This->offset.u.LowPart&(BIGSIZE-1)))
1252 cc=BIGSIZE-(This->offset.u.LowPart&(BIGSIZE-1));
1253 memcpy(pbv,block+(This->offset.u.LowPart&(BIGSIZE-1)),cc);
1254 This->offset.u.LowPart+=cc;
1255 pbv+=cc;
1256 *bytesread+=cc;
1257 cb-=cc;
1258 blocknr=STORAGE_get_next_big_blocknr(&This->str,blocknr);
1261 return S_OK;
1264 /******************************************************************************
1265 * IStream16_Write [STORAGE.522]
1267 HRESULT CDECL IStream16_fnWrite(IStream16 *iface, const void *pv, ULONG cb, ULONG *pcbWrite)
1269 IStream16Impl *This = impl_from_IStream16(iface);
1270 BYTE block[BIGSIZE];
1271 ULONG *byteswritten=pcbWrite,xxwritten;
1272 int oldsize,newsize,i,curoffset=0,lastblocknr,blocknr,cc;
1273 const BYTE* pbv = pv;
1275 if (!pcbWrite) byteswritten=&xxwritten;
1276 *byteswritten = 0;
1278 TRACE_(relay)("(%p)->(%p,%ld,%p)\n",This,pv,cb,pcbWrite);
1279 /* do we need to junk some blocks? */
1280 newsize = This->offset.u.LowPart+cb;
1281 oldsize = This->stde.pps_size;
1282 if (newsize < oldsize) {
1283 if (oldsize < 0x1000) {
1284 /* only small blocks */
1285 blocknr=STORAGE_get_nth_next_small_blocknr(&This->str,This->stde.pps_sb,newsize/SMALLSIZE);
1287 assert(blocknr>=0);
1289 /* will set the rest of the chain to 'free' */
1290 if (!STORAGE_set_small_chain(&This->str,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1291 return E_FAIL;
1292 } else {
1293 if (newsize >= 0x1000) {
1294 blocknr=STORAGE_get_nth_next_big_blocknr(&This->str,This->stde.pps_sb,newsize/BIGSIZE);
1295 assert(blocknr>=0);
1297 /* will set the rest of the chain to 'free' */
1298 if (!STORAGE_set_big_chain(&This->str,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1299 return E_FAIL;
1300 } else {
1301 /* Migrate large blocks to small blocks
1302 * (we just migrate newsize bytes)
1304 LPBYTE curdata,data = HeapAlloc(GetProcessHeap(),0,newsize+BIGSIZE);
1305 HRESULT r = E_FAIL;
1307 cc = newsize;
1308 blocknr = This->stde.pps_sb;
1309 curdata = data;
1310 while (cc>0) {
1311 if (!STORAGE_get_big_block(&This->str,blocknr,curdata)) {
1312 HeapFree(GetProcessHeap(),0,data);
1313 return E_FAIL;
1315 curdata += BIGSIZE;
1316 cc -= BIGSIZE;
1317 blocknr = STORAGE_get_next_big_blocknr(&This->str,blocknr);
1319 /* frees complete chain for this stream */
1320 if (!STORAGE_set_big_chain(&This->str,This->stde.pps_sb,STORAGE_CHAINENTRY_FREE))
1321 goto err;
1322 curdata = data;
1323 blocknr = This->stde.pps_sb = STORAGE_get_free_small_blocknr(&This->str);
1324 if (blocknr<0)
1325 goto err;
1326 cc = newsize;
1327 while (cc>0) {
1328 if (!STORAGE_put_small_block(&This->str,blocknr,curdata))
1329 goto err;
1330 cc -= SMALLSIZE;
1331 if (cc<=0) {
1332 if (!STORAGE_set_small_chain(&This->str,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1333 goto err;
1334 break;
1335 } else {
1336 int newblocknr = STORAGE_get_free_small_blocknr(&This->str);
1337 if (newblocknr<0)
1338 goto err;
1339 if (!STORAGE_set_small_chain(&This->str,blocknr,newblocknr))
1340 goto err;
1341 blocknr = newblocknr;
1343 curdata += SMALLSIZE;
1345 r = S_OK;
1346 err:
1347 HeapFree(GetProcessHeap(),0,data);
1348 if(r != S_OK)
1349 return r;
1352 This->stde.pps_size = newsize;
1355 if (newsize > oldsize) {
1356 if (oldsize >= 0x1000) {
1357 /* should return the block right before the 'endofchain' */
1358 blocknr = STORAGE_get_nth_next_big_blocknr(&This->str,This->stde.pps_sb,This->stde.pps_size/BIGSIZE);
1359 assert(blocknr>=0);
1360 lastblocknr = blocknr;
1361 for (i=oldsize/BIGSIZE;i<newsize/BIGSIZE;i++) {
1362 blocknr = STORAGE_get_free_big_blocknr(&This->str);
1363 if (blocknr<0)
1364 return E_FAIL;
1365 if (!STORAGE_set_big_chain(&This->str,lastblocknr,blocknr))
1366 return E_FAIL;
1367 lastblocknr = blocknr;
1369 if (!STORAGE_set_big_chain(&This->str,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1370 return E_FAIL;
1371 } else {
1372 if (newsize < 0x1000) {
1373 /* find startblock */
1374 if (!oldsize)
1375 This->stde.pps_sb = blocknr = STORAGE_get_free_small_blocknr(&This->str);
1376 else
1377 blocknr = STORAGE_get_nth_next_small_blocknr(&This->str,This->stde.pps_sb,This->stde.pps_size/SMALLSIZE);
1378 if (blocknr<0)
1379 return E_FAIL;
1381 /* allocate required new small blocks */
1382 lastblocknr = blocknr;
1383 for (i=oldsize/SMALLSIZE;i<newsize/SMALLSIZE;i++) {
1384 blocknr = STORAGE_get_free_small_blocknr(&This->str);
1385 if (blocknr<0)
1386 return E_FAIL;
1387 if (!STORAGE_set_small_chain(&This->str,lastblocknr,blocknr))
1388 return E_FAIL;
1389 lastblocknr = blocknr;
1391 /* and terminate the chain */
1392 if (!STORAGE_set_small_chain(&This->str,lastblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1393 return E_FAIL;
1394 } else {
1395 if (!oldsize) {
1396 /* no single block allocated yet */
1397 blocknr=STORAGE_get_free_big_blocknr(&This->str);
1398 if (blocknr<0)
1399 return E_FAIL;
1400 This->stde.pps_sb = blocknr;
1401 } else {
1402 /* Migrate small blocks to big blocks */
1403 LPBYTE curdata,data = HeapAlloc(GetProcessHeap(),0,oldsize+BIGSIZE);
1404 HRESULT r = E_FAIL;
1406 cc = oldsize;
1407 blocknr = This->stde.pps_sb;
1408 curdata = data;
1409 /* slurp in */
1410 while (cc>0) {
1411 if (!STORAGE_get_small_block(&This->str,blocknr,curdata))
1412 goto err2;
1413 curdata += SMALLSIZE;
1414 cc -= SMALLSIZE;
1415 blocknr = STORAGE_get_next_small_blocknr(&This->str,blocknr);
1417 /* free small block chain */
1418 if (!STORAGE_set_small_chain(&This->str,This->stde.pps_sb,STORAGE_CHAINENTRY_FREE))
1419 goto err2;
1420 curdata = data;
1421 blocknr = This->stde.pps_sb = STORAGE_get_free_big_blocknr(&This->str);
1422 if (blocknr<0)
1423 goto err2;
1424 /* put the data into the big blocks */
1425 cc = This->stde.pps_size;
1426 while (cc>0) {
1427 if (!STORAGE_put_big_block(&This->str,blocknr,curdata))
1428 goto err2;
1429 cc -= BIGSIZE;
1430 if (cc<=0) {
1431 if (!STORAGE_set_big_chain(&This->str,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1432 goto err2;
1433 break;
1434 } else {
1435 int newblocknr = STORAGE_get_free_big_blocknr(&This->str);
1436 if (newblocknr<0)
1437 goto err2;
1438 if (!STORAGE_set_big_chain(&This->str,blocknr,newblocknr))
1439 goto err2;
1440 blocknr = newblocknr;
1442 curdata += BIGSIZE;
1444 r = S_OK;
1445 err2:
1446 HeapFree(GetProcessHeap(),0,data);
1447 if(r != S_OK)
1448 return r;
1450 /* generate big blocks to fit the new data */
1451 lastblocknr = blocknr;
1452 for (i=oldsize/BIGSIZE;i<newsize/BIGSIZE;i++) {
1453 blocknr = STORAGE_get_free_big_blocknr(&This->str);
1454 if (blocknr<0)
1455 return E_FAIL;
1456 if (!STORAGE_set_big_chain(&This->str,lastblocknr,blocknr))
1457 return E_FAIL;
1458 lastblocknr = blocknr;
1460 /* terminate chain */
1461 if (!STORAGE_set_big_chain(&This->str,lastblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1462 return E_FAIL;
1465 This->stde.pps_size = newsize;
1468 /* There are just some cases where we didn't modify it, we write it out
1469 * every time
1471 if (!STORAGE_put_pps_entry(&This->str,This->ppsent,&(This->stde)))
1472 return E_FAIL;
1474 /* finally the write pass */
1475 if (This->stde.pps_size < 0x1000) {
1476 blocknr = STORAGE_get_nth_next_small_blocknr(&This->str,This->stde.pps_sb,This->offset.u.LowPart/SMALLSIZE);
1477 assert(blocknr>=0);
1478 while (cb>0) {
1479 /* we ensured that it is allocated above */
1480 assert(blocknr>=0);
1481 /* Read old block every time, since we can have
1482 * overlapping data at START and END of the write
1484 if (!STORAGE_get_small_block(&This->str,blocknr,block))
1485 return E_FAIL;
1487 cc = SMALLSIZE-(This->offset.u.LowPart&(SMALLSIZE-1));
1488 if (cc>cb)
1489 cc=cb;
1490 memcpy( ((LPBYTE)block)+(This->offset.u.LowPart&(SMALLSIZE-1)),
1491 pbv+curoffset,
1494 if (!STORAGE_put_small_block(&This->str,blocknr,block))
1495 return E_FAIL;
1496 cb -= cc;
1497 curoffset += cc;
1498 pbv += cc;
1499 This->offset.u.LowPart += cc;
1500 *byteswritten += cc;
1501 blocknr = STORAGE_get_next_small_blocknr(&This->str,blocknr);
1503 } else {
1504 blocknr = STORAGE_get_nth_next_big_blocknr(&This->str,This->stde.pps_sb,This->offset.u.LowPart/BIGSIZE);
1505 assert(blocknr>=0);
1506 while (cb>0) {
1507 /* we ensured that it is allocated above, so it better is */
1508 assert(blocknr>=0);
1509 /* read old block every time, since we can have
1510 * overlapping data at START and END of the write
1512 if (!STORAGE_get_big_block(&This->str,blocknr,block))
1513 return E_FAIL;
1515 cc = BIGSIZE-(This->offset.u.LowPart&(BIGSIZE-1));
1516 if (cc>cb)
1517 cc=cb;
1518 memcpy( ((LPBYTE)block)+(This->offset.u.LowPart&(BIGSIZE-1)),
1519 pbv+curoffset,
1522 if (!STORAGE_put_big_block(&This->str,blocknr,block))
1523 return E_FAIL;
1524 cb -= cc;
1525 curoffset += cc;
1526 pbv += cc;
1527 This->offset.u.LowPart += cc;
1528 *byteswritten += cc;
1529 blocknr = STORAGE_get_next_big_blocknr(&This->str,blocknr);
1532 return S_OK;
1535 /******************************************************************************
1536 * _create_istream16 [Internal]
1538 static void _create_istream16(LPSTREAM16 *str) {
1539 IStream16Impl* lpst;
1541 if (!strvt16.QueryInterface) {
1542 HMODULE16 wp = GetModuleHandle16("STORAGE");
1543 if (wp>=32) {
1544 /* FIXME: what is This GetProcAddress16. Should the name be IStream16_QueryInterface of IStream16_fnQueryInterface */
1545 #define VTENT(xfn) strvt16.xfn = (void*)GetProcAddress16(wp,"IStream16_"#xfn);assert(strvt16.xfn)
1546 VTENT(QueryInterface);
1547 VTENT(AddRef);
1548 VTENT(Release);
1549 VTENT(Read);
1550 VTENT(Write);
1551 VTENT(Seek);
1552 VTENT(SetSize);
1553 VTENT(CopyTo);
1554 VTENT(Commit);
1555 VTENT(Revert);
1556 VTENT(LockRegion);
1557 VTENT(UnlockRegion);
1558 VTENT(Stat);
1559 VTENT(Clone);
1560 #undef VTENT
1561 segstrvt16 = (const IStream16Vtbl*)MapLS( &strvt16 );
1562 } else {
1563 #define VTENT(xfn) strvt16.xfn = IStream16_fn##xfn;
1564 VTENT(QueryInterface);
1565 VTENT(AddRef);
1566 VTENT(Release);
1567 VTENT(Read);
1568 VTENT(Write);
1569 VTENT(Seek);
1571 VTENT(CopyTo);
1572 VTENT(Commit);
1573 VTENT(SetSize);
1574 VTENT(Revert);
1575 VTENT(LockRegion);
1576 VTENT(UnlockRegion);
1577 VTENT(Stat);
1578 VTENT(Clone);
1580 #undef VTENT
1581 segstrvt16 = &strvt16;
1584 lpst = HeapAlloc( GetProcessHeap(), 0, sizeof(*lpst) );
1585 lpst->IStream16_iface.lpVtbl = segstrvt16;
1586 lpst->ref = 1;
1587 lpst->thisptr = MapLS( lpst );
1588 lpst->str.hf = NULL;
1589 lpst->str.lockbytes = 0;
1590 *str = (void*)lpst->thisptr;
1593 static inline IStorage16Impl *impl_from_IStorage16(IStorage16 *iface)
1595 return CONTAINING_RECORD(iface, IStorage16Impl, IStorage16_iface);
1598 /******************************************************************************
1599 * IStorage16_QueryInterface [STORAGE.500]
1601 HRESULT CDECL IStorage16_fnQueryInterface(IStorage16 *iface, REFIID refiid, void **obj)
1603 IStorage16Impl *This = impl_from_IStorage16(iface);
1605 TRACE_(relay)("(%p)->(%s,%p)\n",This,debugstr_guid(refiid),obj);
1607 if (!memcmp(&IID_IUnknown,refiid,sizeof(IID_IUnknown))) {
1608 *obj = This;
1609 return 0;
1611 return OLE_E_ENUM_NOMORE;
1614 /******************************************************************************
1615 * IStorage16_AddRef [STORAGE.501]
1617 ULONG CDECL IStorage16_fnAddRef(IStorage16 *iface)
1619 IStorage16Impl *This = impl_from_IStorage16(iface);
1620 return InterlockedIncrement(&This->ref);
1623 /******************************************************************************
1624 * IStorage16_Release [STORAGE.502]
1626 ULONG CDECL IStorage16_fnRelease(IStorage16 *iface)
1628 IStorage16Impl *This = impl_from_IStorage16(iface);
1629 ULONG ref;
1630 ref = InterlockedDecrement(&This->ref);
1631 if (!ref)
1633 UnMapLS( This->thisptr );
1634 HeapFree( GetProcessHeap(), 0, This );
1636 return ref;
1639 /******************************************************************************
1640 * IStorage16_Stat [STORAGE.517]
1642 HRESULT CDECL IStorage16_fnStat(IStorage16 *iface, STATSTG16 *pstatstg, DWORD grfStatFlag)
1644 IStorage16Impl *This = impl_from_IStorage16(iface);
1645 DWORD len = WideCharToMultiByte( CP_ACP, 0, This->stde.pps_rawname, -1, NULL, 0, NULL, NULL );
1646 LPSTR nameA = HeapAlloc( GetProcessHeap(), 0, len );
1648 TRACE("(%p)->(%p,0x%08lx)\n",
1649 This,pstatstg,grfStatFlag
1651 WideCharToMultiByte( CP_ACP, 0, This->stde.pps_rawname, -1, nameA, len, NULL, NULL );
1652 pstatstg->pwcsName=(LPOLESTR16)MapLS( nameA );
1653 pstatstg->type = This->stde.pps_type;
1654 pstatstg->cbSize.u.LowPart = This->stde.pps_size;
1655 pstatstg->mtime = This->stde.pps_ft1; /* FIXME */ /* why? */
1656 pstatstg->atime = This->stde.pps_ft2; /* FIXME */
1657 pstatstg->ctime = This->stde.pps_ft2; /* FIXME */
1658 pstatstg->grfMode = 0; /* FIXME */
1659 pstatstg->grfLocksSupported = 0; /* FIXME */
1660 pstatstg->clsid = This->stde.pps_guid;
1661 pstatstg->grfStateBits = 0; /* FIXME */
1662 pstatstg->reserved = 0;
1663 return S_OK;
1666 /******************************************************************************
1667 * IStorage16_Commit [STORAGE.509]
1669 HRESULT CDECL IStorage16_fnCommit(IStorage16 *iface, DWORD commitflags)
1671 IStorage16Impl *This = impl_from_IStorage16(iface);
1672 FIXME("(%p)->(0x%08lx),STUB!\n",
1673 This,commitflags
1675 return S_OK;
1678 /******************************************************************************
1679 * IStorage16_CopyTo [STORAGE.507]
1681 HRESULT CDECL IStorage16_fnCopyTo(IStorage16 *iface, DWORD ciidExclude, const IID *rgiidExclude,
1682 SNB16 SNB16Exclude, IStorage16 *pstgDest)
1684 IStorage16Impl *This = impl_from_IStorage16(iface);
1685 FIXME("IStorage16(%p)->(0x%08lx,%s,%p,%p),stub!\n",
1686 This,ciidExclude,debugstr_guid(rgiidExclude),SNB16Exclude,pstgDest
1688 return S_OK;
1692 /******************************************************************************
1693 * IStorage16_CreateStorage [STORAGE.505]
1695 HRESULT CDECL IStorage16_fnCreateStorage(IStorage16 *iface, LPCOLESTR16 pwcsName, DWORD grfMode,
1696 DWORD dwStgFormat, DWORD reserved2, IStorage16 **ppstg)
1698 IStorage16Impl *This = impl_from_IStorage16(iface);
1699 IStorage16Impl* lpstg;
1700 int ppsent,x;
1701 struct storage_pps_entry stde;
1702 struct storage_header sth;
1703 BOOL ret;
1704 int nPPSEntries;
1706 READ_HEADER(&This->str);
1707 TRACE("(%p)->(%s,0x%08lx,0x%08lx,0x%08lx,%p)\n",
1708 This,pwcsName,grfMode,dwStgFormat,reserved2,ppstg
1710 if (grfMode & STGM_TRANSACTED)
1711 FIXME("We do not support transacted Compound Storage. Using direct mode.\n");
1712 _create_istorage16(ppstg);
1713 lpstg = MapSL((SEGPTR)*ppstg);
1714 if (This->str.hf) {
1715 DuplicateHandle( GetCurrentProcess(), This->str.hf, GetCurrentProcess(),
1716 &lpstg->str.hf, 0, TRUE, DUPLICATE_SAME_ACCESS );
1717 } else {
1718 lpstg->str.lockbytes = This->str.lockbytes;
1719 _ilockbytes16_addref(This->str.lockbytes);
1722 ppsent=STORAGE_get_free_pps_entry(&lpstg->str);
1723 if (ppsent<0)
1724 return E_FAIL;
1725 stde=This->stde;
1726 if (stde.pps_dir==-1) {
1727 stde.pps_dir = ppsent;
1728 x = This->ppsent;
1729 } else {
1730 FIXME(" use prev chain too ?\n");
1731 x=stde.pps_dir;
1732 if (1!=STORAGE_get_pps_entry(&lpstg->str,x,&stde))
1733 return E_FAIL;
1734 while (stde.pps_next!=-1) {
1735 x=stde.pps_next;
1736 if (1!=STORAGE_get_pps_entry(&lpstg->str,x,&stde))
1737 return E_FAIL;
1739 stde.pps_next = ppsent;
1741 ret = STORAGE_put_pps_entry(&lpstg->str,x,&stde);
1742 assert(ret);
1743 nPPSEntries = STORAGE_get_pps_entry(&lpstg->str,ppsent,&(lpstg->stde));
1744 assert(nPPSEntries == 1);
1745 MultiByteToWideChar(CP_ACP, 0, pwcsName, -1, lpstg->stde.pps_rawname,
1746 ARRAY_SIZE(lpstg->stde.pps_rawname));
1747 lpstg->stde.pps_sizeofname = (lstrlenW(lpstg->stde.pps_rawname)+1)*sizeof(WCHAR);
1748 lpstg->stde.pps_next = -1;
1749 lpstg->stde.pps_prev = -1;
1750 lpstg->stde.pps_dir = -1;
1751 lpstg->stde.pps_sb = -1;
1752 lpstg->stde.pps_size = 0;
1753 lpstg->stde.pps_type = 1;
1754 lpstg->ppsent = ppsent;
1755 /* FIXME: timestamps? */
1756 if (!STORAGE_put_pps_entry(&lpstg->str,ppsent,&(lpstg->stde)))
1757 return E_FAIL;
1758 return S_OK;
1761 /******************************************************************************
1762 * IStorage16_CreateStream [STORAGE.503]
1764 HRESULT CDECL IStorage16_fnCreateStream(IStorage16 *iface, LPCOLESTR16 pwcsName, DWORD grfMode,
1765 DWORD reserved1, DWORD reserved2, IStream16 **ppstm)
1767 IStorage16Impl *This = impl_from_IStorage16(iface);
1768 IStream16Impl* lpstr;
1769 int ppsent,x;
1770 struct storage_pps_entry stde;
1771 BOOL ret;
1772 int nPPSEntries;
1774 TRACE("(%p)->(%s,0x%08lx,0x%08lx,0x%08lx,%p)\n",
1775 This,pwcsName,grfMode,reserved1,reserved2,ppstm
1777 if (grfMode & STGM_TRANSACTED)
1778 FIXME("We do not support transacted Compound Storage. Using direct mode.\n");
1779 _create_istream16(ppstm);
1780 lpstr = MapSL((SEGPTR)*ppstm);
1781 if (This->str.hf) {
1782 DuplicateHandle( GetCurrentProcess(), This->str.hf, GetCurrentProcess(),
1783 &lpstr->str.hf, 0, TRUE, DUPLICATE_SAME_ACCESS );
1784 } else {
1785 lpstr->str.lockbytes = This->str.lockbytes;
1786 _ilockbytes16_addref(This->str.lockbytes);
1788 lpstr->offset.u.LowPart = 0;
1789 lpstr->offset.u.HighPart= 0;
1791 ppsent=STORAGE_get_free_pps_entry(&lpstr->str);
1792 if (ppsent<0)
1793 return E_FAIL;
1794 stde=This->stde;
1795 if (stde.pps_next==-1)
1796 x=This->ppsent;
1797 else
1798 while (stde.pps_next!=-1) {
1799 x=stde.pps_next;
1800 if (1!=STORAGE_get_pps_entry(&lpstr->str,x,&stde))
1801 return E_FAIL;
1803 stde.pps_next = ppsent;
1804 ret = STORAGE_put_pps_entry(&lpstr->str,x,&stde);
1805 assert(ret);
1806 nPPSEntries = STORAGE_get_pps_entry(&lpstr->str,ppsent,&(lpstr->stde));
1807 assert(nPPSEntries == 1);
1808 MultiByteToWideChar(CP_ACP, 0, pwcsName, -1, lpstr->stde.pps_rawname,
1809 ARRAY_SIZE(lpstr->stde.pps_rawname));
1810 lpstr->stde.pps_sizeofname = (lstrlenW(lpstr->stde.pps_rawname)+1) * sizeof(WCHAR);
1811 lpstr->stde.pps_next = -1;
1812 lpstr->stde.pps_prev = -1;
1813 lpstr->stde.pps_dir = -1;
1814 lpstr->stde.pps_sb = -1;
1815 lpstr->stde.pps_size = 0;
1816 lpstr->stde.pps_type = 2;
1817 lpstr->ppsent = ppsent;
1819 /* FIXME: timestamps? */
1820 if (!STORAGE_put_pps_entry(&lpstr->str,ppsent,&(lpstr->stde)))
1821 return E_FAIL;
1822 return S_OK;
1825 /******************************************************************************
1826 * IStorage16_OpenStorage [STORAGE.506]
1828 HRESULT CDECL IStorage16_fnOpenStorage(IStorage16 *iface, LPCOLESTR16 pwcsName,
1829 IStorage16 *pstgPrio, DWORD grfMode, SNB16 snbExclude, DWORD reserved, IStorage16 **ppstg)
1831 IStorage16Impl *This = impl_from_IStorage16(iface);
1832 IStorage16Impl *lpstg;
1833 WCHAR name[33];
1834 int newpps;
1836 TRACE("(%p)->(%s,%p,0x%08lx,%p,0x%08lx,%p)\n",
1837 This,pwcsName,pstgPrio,grfMode,snbExclude,reserved,ppstg
1839 if (grfMode & STGM_TRANSACTED)
1840 FIXME("We do not support transacted Compound Storage. Using direct mode.\n");
1841 _create_istorage16(ppstg);
1842 lpstg = MapSL((SEGPTR)*ppstg);
1843 if (This->str.hf) {
1844 DuplicateHandle( GetCurrentProcess(), This->str.hf, GetCurrentProcess(),
1845 &lpstg->str.hf, 0, TRUE, DUPLICATE_SAME_ACCESS );
1846 } else {
1847 lpstg->str.lockbytes = This->str.lockbytes;
1848 _ilockbytes16_addref(This->str.lockbytes);
1850 MultiByteToWideChar(CP_ACP, 0, pwcsName, -1, name, ARRAY_SIZE(name));
1851 newpps = STORAGE_look_for_named_pps(&lpstg->str,This->stde.pps_dir,name);
1852 if (newpps==-1) {
1853 IStorage16_fnRelease(&lpstg->IStorage16_iface);
1854 *ppstg = NULL;
1855 return E_FAIL;
1858 if (1!=STORAGE_get_pps_entry(&lpstg->str,newpps,&(lpstg->stde))) {
1859 IStorage16_fnRelease(&lpstg->IStorage16_iface);
1860 *ppstg = NULL;
1861 return E_FAIL;
1863 lpstg->ppsent = newpps;
1864 return S_OK;
1867 /******************************************************************************
1868 * IStorage16_OpenStream [STORAGE.504]
1870 HRESULT CDECL IStorage16_fnOpenStream(IStorage16 *iface, LPCOLESTR16 pwcsName, void *reserved1,
1871 DWORD grfMode, DWORD reserved2, IStream16 **ppstm)
1873 IStorage16Impl *This = impl_from_IStorage16(iface);
1874 IStream16Impl* lpstr;
1875 WCHAR name[33];
1876 int newpps;
1878 TRACE("(%p)->(%s,%p,0x%08lx,0x%08lx,%p)\n",
1879 This,pwcsName,reserved1,grfMode,reserved2,ppstm
1881 if (grfMode & STGM_TRANSACTED)
1882 FIXME("We do not support transacted Compound Storage. Using direct mode.\n");
1883 _create_istream16(ppstm);
1884 lpstr = MapSL((SEGPTR)*ppstm);
1885 if (This->str.hf) {
1886 DuplicateHandle( GetCurrentProcess(), This->str.hf, GetCurrentProcess(),
1887 &lpstr->str.hf, 0, TRUE, DUPLICATE_SAME_ACCESS );
1888 } else {
1889 lpstr->str.lockbytes = This->str.lockbytes;
1890 _ilockbytes16_addref(This->str.lockbytes);
1892 MultiByteToWideChar(CP_ACP, 0, pwcsName, -1, name, ARRAY_SIZE(name));
1893 newpps = STORAGE_look_for_named_pps(&lpstr->str,This->stde.pps_dir,name);
1894 if (newpps==-1) {
1895 IStream16_fnRelease(&lpstr->IStream16_iface);
1896 *ppstm = NULL;
1897 return E_FAIL;
1900 if (1!=STORAGE_get_pps_entry(&lpstr->str,newpps,&(lpstr->stde))) {
1901 IStream16_fnRelease(&lpstr->IStream16_iface);
1902 *ppstm = NULL;
1903 return E_FAIL;
1905 lpstr->offset.u.LowPart = 0;
1906 lpstr->offset.u.HighPart = 0;
1907 lpstr->ppsent = newpps;
1908 return S_OK;
1911 /******************************************************************************
1912 * _create_istorage16 [INTERNAL]
1914 static void _create_istorage16(LPSTORAGE16 *stg) {
1915 IStorage16Impl* lpst;
1917 if (!stvt16.QueryInterface) {
1918 HMODULE16 wp = GetModuleHandle16("STORAGE");
1919 if (wp>=32) {
1920 #define VTENT(xfn) stvt16.xfn = (void*)GetProcAddress16(wp,"IStorage16_"#xfn);
1921 VTENT(QueryInterface)
1922 VTENT(AddRef)
1923 VTENT(Release)
1924 VTENT(CreateStream)
1925 VTENT(OpenStream)
1926 VTENT(CreateStorage)
1927 VTENT(OpenStorage)
1928 VTENT(CopyTo)
1929 VTENT(MoveElementTo)
1930 VTENT(Commit)
1931 VTENT(Revert)
1932 VTENT(EnumElements)
1933 VTENT(DestroyElement)
1934 VTENT(RenameElement)
1935 VTENT(SetElementTimes)
1936 VTENT(SetClass)
1937 VTENT(SetStateBits)
1938 VTENT(Stat)
1939 #undef VTENT
1940 segstvt16 = (const IStorage16Vtbl*)MapLS( &stvt16 );
1941 } else {
1942 #define VTENT(xfn) stvt16.xfn = IStorage16_fn##xfn;
1943 VTENT(QueryInterface)
1944 VTENT(AddRef)
1945 VTENT(Release)
1946 VTENT(CreateStream)
1947 VTENT(OpenStream)
1948 VTENT(CreateStorage)
1949 VTENT(OpenStorage)
1950 VTENT(CopyTo)
1951 VTENT(Commit)
1952 /* not (yet) implemented ...
1953 VTENT(MoveElementTo)
1954 VTENT(Revert)
1955 VTENT(EnumElements)
1956 VTENT(DestroyElement)
1957 VTENT(RenameElement)
1958 VTENT(SetElementTimes)
1959 VTENT(SetClass)
1960 VTENT(SetStateBits)
1961 VTENT(Stat)
1963 #undef VTENT
1964 segstvt16 = &stvt16;
1967 lpst = HeapAlloc( GetProcessHeap(), 0, sizeof(*lpst) );
1968 lpst->IStorage16_iface.lpVtbl = segstvt16;
1969 lpst->str.hf = NULL;
1970 lpst->str.lockbytes = 0;
1971 lpst->ref = 1;
1972 lpst->thisptr = MapLS(lpst);
1973 *stg = (void*)lpst->thisptr;
1976 /******************************************************************************
1977 * Storage API functions
1980 /******************************************************************************
1981 * StgCreateDocFileA [STORAGE.1]
1983 HRESULT WINAPI StgCreateDocFile16(
1984 LPCOLESTR16 pwcsName,DWORD grfMode,DWORD reserved,IStorage16 **ppstgOpen
1986 HANDLE hf;
1987 int i,ret;
1988 IStorage16Impl* lpstg;
1989 struct storage_pps_entry stde;
1991 TRACE("(%s,0x%08lx,0x%08lx,%p)\n",
1992 pwcsName,grfMode,reserved,ppstgOpen
1994 _create_istorage16(ppstgOpen);
1995 hf = CreateFileA(pwcsName,GENERIC_READ|GENERIC_WRITE,0,NULL,CREATE_NEW,0,0);
1996 if (hf==INVALID_HANDLE_VALUE) {
1997 WARN("couldn't open file for storage:%ld\n",GetLastError());
1998 return E_FAIL;
2000 lpstg = MapSL((SEGPTR)*ppstgOpen);
2001 lpstg->str.hf = hf;
2002 lpstg->str.lockbytes = 0;
2003 /* FIXME: check for existence before overwriting? */
2004 if (!STORAGE_init_storage(&lpstg->str)) {
2005 CloseHandle(hf);
2006 return E_FAIL;
2008 i=0;ret=0;
2009 while (!ret) { /* neither 1 nor <0 */
2010 ret=STORAGE_get_pps_entry(&lpstg->str,i,&stde);
2011 if ((ret==1) && (stde.pps_type==5)) {
2012 lpstg->stde = stde;
2013 lpstg->ppsent = i;
2014 break;
2016 i++;
2018 if (ret!=1) {
2019 IStorage16_fnRelease(&lpstg->IStorage16_iface);
2020 return E_FAIL;
2023 return S_OK;
2026 /******************************************************************************
2027 * StgIsStorageFile [STORAGE.5]
2029 HRESULT WINAPI StgIsStorageFile16(LPCOLESTR16 fn) {
2030 UNICODE_STRING strW;
2031 HRESULT ret;
2033 RtlCreateUnicodeStringFromAsciiz(&strW, fn);
2034 ret = StgIsStorageFile( strW.Buffer );
2035 RtlFreeUnicodeString( &strW );
2037 return ret;
2040 /******************************************************************************
2041 * StgOpenStorage [STORAGE.3]
2043 HRESULT WINAPI StgOpenStorage16(
2044 LPCOLESTR16 pwcsName,IStorage16 *pstgPriority,DWORD grfMode,
2045 SNB16 snbExclude,DWORD reserved, IStorage16 **ppstgOpen
2047 HANDLE hf;
2048 int ret,i;
2049 IStorage16Impl* lpstg;
2050 struct storage_pps_entry stde;
2052 TRACE("(%s,%p,0x%08lx,%p,%ld,%p)\n",
2053 pwcsName,pstgPriority,grfMode,snbExclude,reserved,ppstgOpen
2055 _create_istorage16(ppstgOpen);
2056 hf = CreateFileA(pwcsName,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
2057 if (hf==INVALID_HANDLE_VALUE) {
2058 WARN("Couldn't open file for storage\n");
2059 return E_FAIL;
2061 lpstg = MapSL((SEGPTR)*ppstgOpen);
2062 lpstg->str.hf = hf;
2064 i=0;ret=0;
2065 while (!ret) { /* neither 1 nor <0 */
2066 ret=STORAGE_get_pps_entry(&lpstg->str,i,&stde);
2067 if ((ret==1) && (stde.pps_type==5)) {
2068 lpstg->stde=stde;
2069 break;
2071 i++;
2073 if (ret!=1) {
2074 IStorage16_fnRelease(&lpstg->IStorage16_iface);
2075 return E_FAIL;
2077 return S_OK;
2081 /******************************************************************************
2082 * StgIsStorageILockBytes [STORAGE.6]
2084 * Determines if the ILockBytes contains a storage object.
2086 HRESULT WINAPI StgIsStorageILockBytes16(SEGPTR plkbyt)
2088 DWORD args[6];
2089 HRESULT hres;
2090 HANDLE16 hsig;
2092 args[0] = (DWORD)plkbyt; /* iface */
2093 args[1] = args[2] = 0; /* ULARGE_INTEGER offset */
2094 args[3] = WOWGlobalAllocLock16( 0, 8, &hsig ); /* sig */
2095 args[4] = 8;
2096 args[5] = 0;
2098 if (!WOWCallback16Ex(
2099 (DWORD)((const ILockBytes16Vtbl*)MapSL(
2100 (SEGPTR)((LPLOCKBYTES16)MapSL(plkbyt))->lpVtbl)
2101 )->ReadAt,
2102 WCB16_PASCAL,
2103 6*sizeof(DWORD),
2104 (LPVOID)args,
2105 (LPDWORD)&hres
2106 )) {
2107 ERR("CallTo16 ILockBytes16::ReadAt() failed, hres %lx\n",hres);
2108 return hres;
2110 if (memcmp(MapSL(args[3]), STORAGE_magic, sizeof(STORAGE_magic)) == 0) {
2111 WOWGlobalUnlockFree16(args[3]);
2112 return S_OK;
2114 WOWGlobalUnlockFree16(args[3]);
2115 return S_FALSE;
2118 /******************************************************************************
2119 * StgOpenStorageOnILockBytes [STORAGE.4]
2121 * PARAMS
2122 * plkbyt FIXME: Should probably be an ILockBytes16 *.
2124 HRESULT WINAPI StgOpenStorageOnILockBytes16(
2125 SEGPTR plkbyt,
2126 IStorage16 *pstgPriority,
2127 DWORD grfMode,
2128 SNB16 snbExclude,
2129 DWORD reserved,
2130 IStorage16 **ppstgOpen)
2132 IStorage16Impl* lpstg;
2133 int i,ret;
2134 struct storage_pps_entry stde;
2136 FIXME("(%lx, %p, 0x%08lx, %d, %lx, %p)\n", plkbyt, pstgPriority, grfMode, (int)snbExclude, reserved, ppstgOpen);
2137 if ((plkbyt == 0) || (ppstgOpen == 0))
2138 return STG_E_INVALIDPOINTER;
2140 *ppstgOpen = 0;
2142 _create_istorage16(ppstgOpen);
2143 lpstg = MapSL((SEGPTR)*ppstgOpen);
2144 lpstg->str.hf = NULL;
2145 lpstg->str.lockbytes = plkbyt;
2146 i=0;ret=0;
2147 while (!ret) { /* neither 1 nor <0 */
2148 ret=STORAGE_get_pps_entry(&lpstg->str,i,&stde);
2149 if ((ret==1) && (stde.pps_type==5)) {
2150 lpstg->stde=stde;
2151 break;
2153 i++;
2155 if (ret!=1) {
2156 IStorage16_fnRelease(&lpstg->IStorage16_iface);
2157 return E_FAIL;
2159 return S_OK;