winex11: Use NtUserBuildHwndList for has_owned_popup implementation.
[wine.git] / dlls / storage.dll16 / storage.c
blobd2f91a77b46a279c2adbe894595537af25306cd8
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 #define NONAMELESSUNION
32 #include "windef.h"
33 #include "winbase.h"
34 #include "winternl.h"
35 #include "winerror.h"
36 #include "wine/winbase16.h"
37 #include "wownt32.h"
38 #include "objbase.h"
39 #include "wine/debug.h"
41 #include "ifs.h"
43 WINE_DEFAULT_DEBUG_CHANNEL(ole);
44 WINE_DECLARE_DEBUG_CHANNEL(relay);
46 struct storage_header {
47 BYTE magic[8]; /* 00: magic */
48 BYTE unknown1[36]; /* 08: unknown */
49 DWORD num_of_bbd_blocks;/* 2C: length of big datablocks */
50 DWORD root_startblock;/* 30: root storage first big block */
51 DWORD unknown2[2]; /* 34: unknown */
52 DWORD sbd_startblock; /* 3C: small block depot first big block */
53 DWORD unknown3[3]; /* 40: unknown */
54 DWORD bbd_list[109]; /* 4C: big data block list (up to end of sector)*/
56 struct storage_pps_entry {
57 WCHAR pps_rawname[32];/* 00: \0 terminated widechar name */
58 WORD pps_sizeofname; /* 40: namelength in bytes */
59 BYTE pps_type; /* 42: flags, 1 storage/dir, 2 stream, 5 root */
60 BYTE pps_unknown0; /* 43: unknown */
61 DWORD pps_prev; /* 44: previous pps */
62 DWORD pps_next; /* 48: next pps */
63 DWORD pps_dir; /* 4C: directory pps */
64 GUID pps_guid; /* 50: class ID */
65 DWORD pps_unknown1; /* 60: unknown */
66 FILETIME pps_ft1; /* 64: filetime1 */
67 FILETIME pps_ft2; /* 6C: filetime2 */
68 DWORD pps_sb; /* 74: data startblock */
69 DWORD pps_size; /* 78: datalength. (<0x1000)?small:big blocks*/
70 DWORD pps_unknown2; /* 7C: unknown */
73 #define STORAGE_CHAINENTRY_FAT 0xfffffffd
74 #define STORAGE_CHAINENTRY_ENDOFCHAIN 0xfffffffe
75 #define STORAGE_CHAINENTRY_FREE 0xffffffff
78 static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1};
80 #define BIGSIZE 512
81 #define SMALLSIZE 64
83 #define SMALLBLOCKS_PER_BIGBLOCK (BIGSIZE/SMALLSIZE)
85 #define READ_HEADER(str) STORAGE_get_big_block(str,-1,(LPBYTE)&sth);assert(!memcmp(STORAGE_magic,sth.magic,sizeof(STORAGE_magic)));
86 static IStorage16Vtbl stvt16;
87 static const IStorage16Vtbl *segstvt16 = NULL;
88 static IStream16Vtbl strvt16;
89 static const IStream16Vtbl *segstrvt16 = NULL;
91 /*ULONG WINAPI IStorage16_AddRef(LPSTORAGE16 this);*/
92 static void _create_istorage16(LPSTORAGE16 *stg);
93 static void _create_istream16(LPSTREAM16 *str);
95 #define IMPLEMENTED 1
97 /* The following is taken from the CorVu implementation of docfiles, and
98 * documents things about the file format that are not implemented here, and
99 * not documented by the LAOLA project. The CorVu implementation was posted
100 * to wine-devel in February 2004, and released under the LGPL at the same
101 * time. Because that implementation is in C++, it's not directly usable in
102 * Wine, but does have documentation value.
105 * #define DF_EXT_VTOC -4
106 * #define DF_VTOC_VTOC -3
107 * #define DF_VTOC_EOF -2
108 * #define DF_VTOC_FREE -1
109 * #define DF_NAMELEN 0x20 // Maximum entry name length - 31 characters plus
110 * // a NUL terminator
112 * #define DF_FT_STORAGE 1
113 * #define DF_FT_STREAM 2
114 * #define DF_FT_LOCKBYTES 3 // Not used -- How the bloody hell did I manage
115 * #define DF_FT_PROPERTY 4 // Not Used -- to figure these two out?
116 * #define DF_FT_ROOT 5
118 * #define DF_BLOCK_SIZE 0x200
119 * #define DF_VTOC_SIZE 0x80
120 * #define DF_DE_PER_BLOCK 4
121 * #define DF_STREAM_BLOCK_SIZE 0x40
123 * A DocFile is divided into blocks of 512 bytes.
124 * The first block contains the header.
126 * The file header contains The first 109 entries in the VTOC of VTOCs.
128 * Each block pointed to by a VTOC of VTOCs contains a VTOC, which
129 * includes block chains - just like FAT. This is a somewhat poor
130 * design for the following reasons:
132 * 1. FAT was a poor file system design to begin with, and
133 * has long been known to be horrendously inefficient
134 * for day to day operations.
136 * 2. The problem is compounded here, since the file
137 * level streams are generally *not* read sequentially.
138 * This means that a significant percentage of reads
139 * require seeking from the start of the chain.
141 * Data chains also contain an internal VTOC. The block size for
142 * the standard VTOC is 512. The block size for the internal VTOC
143 * is 64.
145 * Now, the 109 blocks in the VTOC of VTOCs allows for files of
146 * up to around 7MB. So what do you think happens if that's
147 * exceeded? Well, there's an entry in the header block which
148 * points to the first block used as additional storage for
149 * the VTOC of VTOCs.
151 * Now we can get up to around 15MB. Now, guess how the file
152 * format adds in another block to the VTOC of VTOCs. Come on,
153 * it's no big surprise. That's right - the last entry in each
154 * block extending the VTOC of VTOCs is, you guessed it, the
155 * block number of the next block containing an extension to
156 * the VTOC of VTOCs. The VTOC of VTOCs is chained!!!!
158 * So, to review:
160 * 1. If you are using a FAT file system, the location of
161 * your file's blocks is stored in chains.
163 * 2. At the abstract level, the file contains a VTOC of VTOCs,
164 * which is stored in the most inefficient possible format for
165 * random access - a chain (AKA list).
167 * 3. The VTOC of VTOCs contains descriptions of three file level
168 * streams:
170 * a. The Directory stream
171 * b. The Data stream
172 * c. The Data VTOC stream
174 * These are, of course, represented as chains.
176 * 4. The Data VTOC contains data describing the chains of blocks
177 * within the Data stream.
179 * That's right - we have a total of four levels of block chains!
181 * Now, is that complicated enough for you? No? OK, there's another
182 * complication. If an individual stream (ie. an IStream) reaches
183 * 4096 bytes in size, it gets moved from the Data Stream to
184 * a new file level stream. Now, if the stream then gets truncated
185 * back to less than 4096 bytes, it returns to the data stream.
187 * The effect of using this format can be seen very easily. Pick
188 * an arbitrary application with a grid data representation that
189 * can export to both Lotus 123 and Excel 5 or higher. Export
190 * a large file to Lotus 123 and time it. Export the same thing
191 * to Excel 5 and time that. The difference is the inefficiency
192 * of the Microsoft DocFile format.
195 * #define TOTAL_SIMPLE_VTOCS 109
197 * struct DocFile_Header
199 * df_byte iMagic1; // 0xd0
200 * df_byte iMagic2; // 0xcf
201 * df_byte iMagic3; // 0x11
202 * df_byte iMagic4; // 0xe0 - Spells D0CF11E0, or DocFile
203 * df_byte iMagic5; // 161 (igi upside down)
204 * df_byte iMagic6; // 177 (lli upside down - see below
205 * df_byte iMagic7; // 26 (gz upside down)
206 * df_byte iMagic8; // 225 (szz upside down) - see below
207 * df_int4 aiUnknown1[4];
208 * df_int4 iVersion; // DocFile Version - 0x03003E
209 * df_int4 aiUnknown2[4];
210 * df_int4 nVTOCs; // Number of VTOCs
211 * df_int4 iFirstDirBlock; // First Directory Block
212 * df_int4 aiUnknown3[2];
213 * df_int4 iFirstDataVTOC; // First data VTOC block
214 * df_int4 iHasData; // 1 if there is data in the file - yes, this is important
215 * df_int4 iExtendedVTOC; // Extended VTOC location
216 * df_int4 iExtendedVTOCSize; // Size of extended VTOC (+1?)
217 * df_int4 aiVTOCofVTOCs[TOTAL_SIMPLE_VTOCS];
218 * };
220 * struct DocFile_VTOC
222 * df_int4 aiBlocks[DF_VTOC_SIZE];
223 * };
226 * The meaning of the magic numbers
228 * 0xd0cf11e0 is DocFile with a zero on the end (sort of)
230 * If you key 177161 into a calculator, then turn the calculator
231 * upside down, you get igilli, which may be a reference to
232 * somebody's name, or to the Hebrew word for "angel".
234 * If you key 26225 into a calculator, then turn it upside down, you
235 * get szzgz. Microsoft has a tradition of creating nonsense words
236 * using the letters s, g, z and y. We think szzgz may be one of the
237 * Microsoft placeholder variables, along the lines of foo, bar and baz.
238 * Alternatively, it could be 22526, which would be gzszz.
241 * struct DocFile_DirEnt
243 * df_char achEntryName[DF_NAMELEN]; // Entry Name
244 * df_int2 iNameLen; // Name length in bytes, including NUL terminator
245 * df_byte iFileType; // Entry type
246 * df_byte iColour; // 1 = Black, 0 = Red
247 * df_int4 iLeftSibling; // Next Left Sibling Entry - See below
248 * df_int4 iRightSibling; // Next Right Sibling Entry
249 * df_int4 iFirstChild; // First Child Entry
250 * df_byte achClassID[16]; // Class ID
251 * df_int4 iStateBits; // [GS]etStateBits value
252 * df_int4 iCreatedLow; // Low DWORD of creation time
253 * df_int4 iCreatedHigh; // High DWORD of creation time
254 * df_int4 iModifiedLow; // Low DWORD of modification time
255 * df_int4 iModifiedHigh; // High DWORD of modification time
256 * df_int4 iVTOCPosition; // VTOC Position
257 * df_int4 iFileSize; // Size of the stream
258 * df_int4 iZero; // We think this is part of the 64 bit stream size - must be 0
259 * };
261 * Siblings
262 * ========
264 * Siblings are stored in an obscure but incredibly elegant
265 * data structure called a red-black tree. This is generally
266 * defined as a 2-3-4 tree stored in a binary tree.
268 * A red-black tree can always be balanced very easily. The rules
269 * for a red-black tree are as follows:
271 * 1. The root node is always black.
272 * 2. The parent of a red node is always black.
274 * There is a Java demo of red-black trees at:
276 * http://langevin.usc.edu/BST/RedBlackTree-Example.html
278 * This demo is an excellent tool for learning how red-black
279 * trees work, without having to go through the process of
280 * learning how they were derived.
282 * Within the tree, elements are ordered by the length of the
283 * name and within that, ASCII order by name. This causes the
284 * apparently bizarre reordering you see when you use dfview.
286 * This is a somewhat bizarre choice. It suggests that the
287 * designer of the DocFile format was trying to optimise
288 * searching through the directory entries. However searching
289 * through directory entries is a relatively rare operation.
290 * Reading and seeking within a stream are much more common
291 * operations, especially within the file level streams, yet
292 * these use the horrendously inefficient FAT chains.
294 * This suggests that the designer was probably somebody
295 * fresh out of university, who had some basic knowledge of
296 * basic data structures, but little knowledge of anything
297 * more practical. It is bizarre to attempt to optimise
298 * directory searches while not using a more efficient file
299 * block locating system than FAT (seedling/sapling/tree
300 * would result in a massive improvement - in fact we have
301 * an alternative to docfiles that we use internally that
302 * uses seedling/sapling/tree and *is* far more efficient).
304 * It is worth noting that the MS implementation of red-black
305 * trees is incorrect (I can tell you're surprised) and
306 * actually causes more operations to occur than are really
307 * needed. Fortunately the fact that our implementation is
308 * correct will not cause any problems - the MS implementation
309 * still appears to cause the tree to satisfy the rules, albeit
310 * a sequence of the same insertions in the different
311 * implementations may result in a different, and possibly
312 * deeper (but never shallower) tree.
315 typedef struct {
316 HANDLE hf;
317 SEGPTR lockbytes;
318 } stream_access16;
319 /* --- IStorage16 implementation struct */
321 typedef struct
323 IStorage16 IStorage16_iface;
324 LONG ref;
325 /* IStorage16 fields */
326 SEGPTR thisptr; /* pointer to this struct as segmented */
327 struct storage_pps_entry stde;
328 int ppsent;
329 stream_access16 str;
330 } IStorage16Impl;
333 /******************************************************************************
334 * STORAGE_get_big_block [Internal]
336 * Reading OLE compound storage
338 static BOOL
339 STORAGE_get_big_block(stream_access16 *str,int n,BYTE *block)
341 DWORD result;
343 assert(n>=-1);
344 if (str->hf) {
345 if ((SetFilePointer( str->hf, (n+1)*BIGSIZE, NULL,
346 SEEK_SET ) == INVALID_SET_FILE_POINTER) && GetLastError())
348 WARN("(%p,%d,%p), seek failed (%ld)\n",str->hf, n, block, GetLastError());
349 return FALSE;
351 if (!ReadFile( str->hf, block, BIGSIZE, &result, NULL ) || result != BIGSIZE)
353 WARN("(hf=%p, block size %d): read didn't read (%ld)\n",str->hf,n,GetLastError());
354 return FALSE;
356 } else {
357 DWORD args[6];
358 HRESULT hres;
359 HANDLE16 hsig;
361 args[0] = (DWORD)str->lockbytes; /* iface */
362 args[1] = (n+1)*BIGSIZE;
363 args[2] = 0; /* ULARGE_INTEGER offset */
364 args[3] = WOWGlobalAllocLock16( 0, BIGSIZE, &hsig ); /* sig */
365 args[4] = BIGSIZE;
366 args[5] = 0;
368 if (!WOWCallback16Ex(
369 (DWORD)((const ILockBytes16Vtbl*)MapSL(
370 (SEGPTR)((LPLOCKBYTES16)MapSL(str->lockbytes))->lpVtbl)
371 )->ReadAt,
372 WCB16_PASCAL,
373 6*sizeof(DWORD),
374 (LPVOID)args,
375 (LPDWORD)&hres
376 )) {
377 ERR("CallTo16 ILockBytes16::ReadAt() failed, hres %lx\n",hres);
378 return FALSE;
380 memcpy(block, MapSL(args[3]), BIGSIZE);
381 WOWGlobalUnlockFree16(args[3]);
383 return TRUE;
386 static BOOL
387 _ilockbytes16_writeat(SEGPTR lockbytes, DWORD offset, DWORD length, void *buffer) {
388 DWORD args[6];
389 HRESULT hres;
391 args[0] = (DWORD)lockbytes; /* iface */
392 args[1] = offset;
393 args[2] = 0; /* ULARGE_INTEGER offset */
394 args[3] = (DWORD)MapLS( buffer );
395 args[4] = length;
396 args[5] = 0;
398 /* THIS_ ULARGE_INTEGER ulOffset, const void *pv, ULONG cb, ULONG *pcbWritten); */
400 if (!WOWCallback16Ex(
401 (DWORD)((const ILockBytes16Vtbl*)MapSL(
402 (SEGPTR)((LPLOCKBYTES16)MapSL(lockbytes))->lpVtbl)
403 )->WriteAt,
404 WCB16_PASCAL,
405 6*sizeof(DWORD),
406 (LPVOID)args,
407 (LPDWORD)&hres
408 )) {
409 ERR("CallTo16 ILockBytes16::WriteAt() failed, hres %lx\n",hres);
410 return FALSE;
412 UnMapLS(args[3]);
413 return TRUE;
416 /******************************************************************************
417 * STORAGE_put_big_block [INTERNAL]
419 static BOOL
420 STORAGE_put_big_block(stream_access16 *str,int n,BYTE *block)
422 DWORD result;
424 assert(n>=-1);
425 if (str->hf) {
426 if ((SetFilePointer( str->hf, (n+1)*BIGSIZE, NULL,
427 SEEK_SET ) == INVALID_SET_FILE_POINTER) && GetLastError())
429 WARN("seek failed (%ld)\n",GetLastError());
430 return FALSE;
432 if (!WriteFile( str->hf, block, BIGSIZE, &result, NULL ) || result != BIGSIZE)
434 WARN(" write failed (%ld)\n",GetLastError());
435 return FALSE;
437 return TRUE;
438 } else {
439 _ilockbytes16_writeat(str->lockbytes, (n+1)*BIGSIZE, BIGSIZE, block);
440 return TRUE;
444 /******************************************************************************
445 * STORAGE_get_next_big_blocknr [INTERNAL]
447 static int
448 STORAGE_get_next_big_blocknr(stream_access16 *str,int blocknr) {
449 INT bbs[BIGSIZE/sizeof(INT)];
450 struct storage_header sth;
452 READ_HEADER(str);
454 assert(blocknr>>7<sth.num_of_bbd_blocks);
455 if (sth.bbd_list[blocknr>>7]==0xffffffff)
456 return -5;
457 if (!STORAGE_get_big_block(str,sth.bbd_list[blocknr>>7],(LPBYTE)bbs))
458 return -5;
459 assert(bbs[blocknr&0x7f]!=STORAGE_CHAINENTRY_FREE);
460 return bbs[blocknr&0x7f];
463 /******************************************************************************
464 * STORAGE_get_nth_next_big_blocknr [INTERNAL]
466 static int
467 STORAGE_get_nth_next_big_blocknr(stream_access16 *str,int blocknr,int nr) {
468 INT bbs[BIGSIZE/sizeof(INT)];
469 int lastblock = -1;
470 struct storage_header sth;
472 TRACE("(blocknr=%d, nr=%d)\n", blocknr, nr);
473 READ_HEADER(str);
475 assert(blocknr>=0);
476 while (nr--) {
477 assert((blocknr>>7)<sth.num_of_bbd_blocks);
478 assert(sth.bbd_list[blocknr>>7]!=0xffffffff);
480 /* simple caching... */
481 if (lastblock!=sth.bbd_list[blocknr>>7]) {
482 BOOL ret = STORAGE_get_big_block(str,sth.bbd_list[blocknr>>7],(LPBYTE)bbs);
483 assert(ret);
484 lastblock = sth.bbd_list[blocknr>>7];
486 blocknr = bbs[blocknr&0x7f];
488 return blocknr;
491 /******************************************************************************
492 * STORAGE_get_root_pps_entry [Internal]
494 static BOOL
495 STORAGE_get_root_pps_entry(stream_access16* str,struct storage_pps_entry *pstde) {
496 int blocknr,i;
497 BYTE block[BIGSIZE];
498 struct storage_pps_entry *stde=(struct storage_pps_entry*)block;
499 struct storage_header sth;
501 READ_HEADER(str);
502 blocknr = sth.root_startblock;
503 TRACE("startblock is %d\n", blocknr);
504 while (blocknr>=0) {
505 BOOL ret = STORAGE_get_big_block(str,blocknr,block);
506 assert(ret);
507 for (i=0;i<4;i++) {
508 if (!stde[i].pps_sizeofname)
509 continue;
510 if (stde[i].pps_type==5) {
511 *pstde=stde[i];
512 return TRUE;
515 blocknr=STORAGE_get_next_big_blocknr(str,blocknr);
516 TRACE("next block is %d\n", blocknr);
518 return FALSE;
521 /******************************************************************************
522 * STORAGE_get_small_block [INTERNAL]
524 static BOOL
525 STORAGE_get_small_block(stream_access16 *str,int blocknr,BYTE *sblock) {
526 BYTE block[BIGSIZE];
527 int bigblocknr;
528 struct storage_pps_entry root;
529 BOOL ret;
531 TRACE("(blocknr=%d)\n", blocknr);
532 assert(blocknr>=0);
533 ret = STORAGE_get_root_pps_entry(str,&root);
534 assert(ret);
535 bigblocknr = STORAGE_get_nth_next_big_blocknr(str,root.pps_sb,blocknr/SMALLBLOCKS_PER_BIGBLOCK);
536 assert(bigblocknr>=0);
537 ret = STORAGE_get_big_block(str,bigblocknr,block);
538 assert(ret);
540 memcpy(sblock,((LPBYTE)block)+SMALLSIZE*(blocknr&(SMALLBLOCKS_PER_BIGBLOCK-1)),SMALLSIZE);
541 return TRUE;
544 /******************************************************************************
545 * STORAGE_put_small_block [INTERNAL]
547 static BOOL
548 STORAGE_put_small_block(stream_access16 *str,int blocknr,const BYTE *sblock) {
549 BYTE block[BIGSIZE];
550 int bigblocknr;
551 struct storage_pps_entry root;
552 BOOL ret;
554 assert(blocknr>=0);
555 TRACE("(blocknr=%d)\n", blocknr);
557 ret = STORAGE_get_root_pps_entry(str,&root);
558 assert(ret);
559 bigblocknr = STORAGE_get_nth_next_big_blocknr(str,root.pps_sb,blocknr/SMALLBLOCKS_PER_BIGBLOCK);
560 assert(bigblocknr>=0);
561 ret = STORAGE_get_big_block(str,bigblocknr,block);
562 assert(ret);
564 memcpy(((LPBYTE)block)+SMALLSIZE*(blocknr&(SMALLBLOCKS_PER_BIGBLOCK-1)),sblock,SMALLSIZE);
565 ret = STORAGE_put_big_block(str,bigblocknr,block);
566 assert(ret);
567 return TRUE;
570 /******************************************************************************
571 * STORAGE_get_next_small_blocknr [INTERNAL]
573 static int
574 STORAGE_get_next_small_blocknr(stream_access16 *str,int blocknr) {
575 BYTE block[BIGSIZE];
576 LPINT sbd = (LPINT)block;
577 int bigblocknr;
578 struct storage_header sth;
579 BOOL ret;
581 TRACE("(blocknr=%d)\n", blocknr);
582 READ_HEADER(str);
583 assert(blocknr>=0);
584 bigblocknr = STORAGE_get_nth_next_big_blocknr(str,sth.sbd_startblock,blocknr/128);
585 assert(bigblocknr>=0);
586 ret = STORAGE_get_big_block(str,bigblocknr,block);
587 assert(ret);
588 assert(sbd[blocknr & 127]!=STORAGE_CHAINENTRY_FREE);
589 return sbd[blocknr & (128-1)];
592 /******************************************************************************
593 * STORAGE_get_nth_next_small_blocknr [INTERNAL]
595 static int
596 STORAGE_get_nth_next_small_blocknr(stream_access16*str,int blocknr,int nr) {
597 int lastblocknr=-129;
598 BYTE block[BIGSIZE];
599 LPINT sbd = (LPINT)block;
600 struct storage_header sth;
601 BOOL ret;
603 TRACE("(blocknr=%d, nr=%d)\n", blocknr, nr);
604 READ_HEADER(str);
605 assert(blocknr>=0);
606 while ((nr--) && (blocknr>=0)) {
607 if (lastblocknr/128!=blocknr/128) {
608 int bigblocknr;
609 bigblocknr = STORAGE_get_nth_next_big_blocknr(str,sth.sbd_startblock,blocknr/128);
610 assert(bigblocknr>=0);
611 ret = STORAGE_get_big_block(str,bigblocknr,block);
612 assert(ret);
613 lastblocknr = blocknr;
615 assert(lastblocknr>=0);
616 lastblocknr=blocknr;
617 blocknr=sbd[blocknr & (128-1)];
618 assert(blocknr!=STORAGE_CHAINENTRY_FREE);
620 return blocknr;
623 /******************************************************************************
624 * STORAGE_get_pps_entry [INTERNAL]
626 static int
627 STORAGE_get_pps_entry(stream_access16*str,int n,struct storage_pps_entry *pstde) {
628 int blocknr;
629 BYTE block[BIGSIZE];
630 struct storage_pps_entry *stde = (struct storage_pps_entry*)(((LPBYTE)block)+128*(n&3));
631 struct storage_header sth;
632 BOOL ret;
634 TRACE("(n=%d)\n", n);
635 READ_HEADER(str);
636 /* we have 4 pps entries per big block */
637 blocknr = STORAGE_get_nth_next_big_blocknr(str,sth.root_startblock,n/4);
638 assert(blocknr>=0);
639 ret = STORAGE_get_big_block(str,blocknr,block);
640 assert(ret);
642 *pstde=*stde;
643 return 1;
646 /******************************************************************************
647 * STORAGE_put_pps_entry [Internal]
649 static int
650 STORAGE_put_pps_entry(stream_access16*str,int n,const struct storage_pps_entry *pstde) {
651 int blocknr;
652 BYTE block[BIGSIZE];
653 struct storage_pps_entry *stde = (struct storage_pps_entry*)(((LPBYTE)block)+128*(n&3));
654 struct storage_header sth;
655 BOOL ret;
657 TRACE("(n=%d)\n", n);
658 READ_HEADER(str);
659 /* we have 4 pps entries per big block */
660 blocknr = STORAGE_get_nth_next_big_blocknr(str,sth.root_startblock,n/4);
661 assert(blocknr>=0);
662 ret = STORAGE_get_big_block(str,blocknr,block);
663 assert(ret);
664 *stde=*pstde;
665 ret = STORAGE_put_big_block(str,blocknr,block);
666 assert(ret);
667 return 1;
670 /******************************************************************************
671 * STORAGE_look_for_named_pps [Internal]
673 static int
674 STORAGE_look_for_named_pps(stream_access16*str,int n,LPOLESTR name) {
675 struct storage_pps_entry stde;
676 int ret;
678 TRACE("(n=%d,name=%s)\n", n, debugstr_w(name));
679 if (n==-1)
680 return -1;
681 if (1!=STORAGE_get_pps_entry(str,n,&stde))
682 return -1;
684 if (!wcscmp(name,stde.pps_rawname))
685 return n;
686 if (stde.pps_prev != -1) {
687 ret=STORAGE_look_for_named_pps(str,stde.pps_prev,name);
688 if (ret!=-1)
689 return ret;
691 if (stde.pps_next != -1) {
692 ret=STORAGE_look_for_named_pps(str,stde.pps_next,name);
693 if (ret!=-1)
694 return ret;
696 return -1;
699 /******************************************************************************
700 * STORAGE_dump_pps_entry [Internal]
702 * This function is there to simplify debugging. It is otherwise unused.
704 void
705 STORAGE_dump_pps_entry(struct storage_pps_entry *stde) {
706 char name[33];
708 WideCharToMultiByte( CP_ACP, 0, stde->pps_rawname, -1, name, sizeof(name), NULL, NULL);
709 if (!stde->pps_sizeofname)
710 return;
711 TRACE("name: %s\n",name);
712 TRACE("type: %d\n",stde->pps_type);
713 TRACE("prev pps: %ld\n",stde->pps_prev);
714 TRACE("next pps: %ld\n",stde->pps_next);
715 TRACE("dir pps: %ld\n",stde->pps_dir);
716 TRACE("guid: %s\n",debugstr_guid(&(stde->pps_guid)));
717 if (stde->pps_type !=2) {
718 time_t t;
719 DWORD dw;
720 RtlTimeToSecondsSince1970((LARGE_INTEGER *)&(stde->pps_ft1),&dw);
721 t = dw;
722 TRACE("ts1: %s\n",ctime(&t));
723 RtlTimeToSecondsSince1970((LARGE_INTEGER *)&(stde->pps_ft2),&dw);
724 t = dw;
725 TRACE("ts2: %s\n",ctime(&t));
727 TRACE("startblock: %ld\n",stde->pps_sb);
728 TRACE("size: %ld\n",stde->pps_size);
731 /******************************************************************************
732 * STORAGE_init_storage [INTERNAL]
734 static BOOL
735 STORAGE_init_storage(stream_access16 *str) {
736 BYTE block[BIGSIZE];
737 LPDWORD bbs;
738 struct storage_header *sth;
739 struct storage_pps_entry *stde;
740 DWORD result;
742 if (str->hf)
743 SetFilePointer( str->hf, 0, NULL, SEEK_SET );
744 /* block -1 is the storage header */
745 sth = (struct storage_header*)block;
746 memcpy(sth->magic,STORAGE_magic,8);
747 memset(sth->unknown1,0,sizeof(sth->unknown1));
748 memset(sth->unknown2,0,sizeof(sth->unknown2));
749 memset(sth->unknown3,0,sizeof(sth->unknown3));
750 sth->num_of_bbd_blocks = 1;
751 sth->root_startblock = 1;
752 sth->sbd_startblock = 0xffffffff;
753 memset(sth->bbd_list,0xff,sizeof(sth->bbd_list));
754 sth->bbd_list[0] = 0;
755 if (str->hf) {
756 if (!WriteFile( str->hf, block, BIGSIZE, &result, NULL ) || result != BIGSIZE) return FALSE;
757 } else {
758 if (!_ilockbytes16_writeat(str->lockbytes, 0, BIGSIZE, block)) return FALSE;
760 /* block 0 is the big block directory */
761 bbs=(LPDWORD)block;
762 memset(block,0xff,sizeof(block)); /* mark all blocks as free */
763 bbs[0]=STORAGE_CHAINENTRY_ENDOFCHAIN; /* for this block */
764 bbs[1]=STORAGE_CHAINENTRY_ENDOFCHAIN; /* for directory entry */
765 if (str->hf) {
766 if (!WriteFile( str->hf, block, BIGSIZE, &result, NULL ) || result != BIGSIZE) return FALSE;
767 } else {
768 if (!_ilockbytes16_writeat(str->lockbytes, BIGSIZE, BIGSIZE, block)) return FALSE;
770 /* block 1 is the root directory entry */
771 memset(block,0x00,sizeof(block));
772 stde = (struct storage_pps_entry*)block;
773 MultiByteToWideChar(CP_ACP, 0, "RootEntry", -1, stde->pps_rawname,
774 ARRAY_SIZE(stde->pps_rawname));
775 stde->pps_sizeofname = (lstrlenW(stde->pps_rawname)+1) * sizeof(WCHAR);
776 stde->pps_type = 5;
777 stde->pps_dir = -1;
778 stde->pps_next = -1;
779 stde->pps_prev = -1;
780 stde->pps_sb = 0xffffffff;
781 stde->pps_size = 0;
782 if (str->hf) {
783 return (WriteFile( str->hf, block, BIGSIZE, &result, NULL ) && result == BIGSIZE);
784 } else {
785 return _ilockbytes16_writeat(str->lockbytes, BIGSIZE, BIGSIZE, block);
789 /******************************************************************************
790 * STORAGE_set_big_chain [Internal]
792 static BOOL
793 STORAGE_set_big_chain(stream_access16*str,int blocknr,INT type) {
794 BYTE block[BIGSIZE];
795 LPINT bbd = (LPINT)block;
796 int nextblocknr,bigblocknr;
797 struct storage_header sth;
798 BOOL ret;
800 READ_HEADER(str);
801 assert(blocknr!=type);
802 while (blocknr>=0) {
803 bigblocknr = sth.bbd_list[blocknr/128];
804 assert(bigblocknr>=0);
805 ret = STORAGE_get_big_block(str,bigblocknr,block);
806 assert(ret);
808 nextblocknr = bbd[blocknr&(128-1)];
809 bbd[blocknr&(128-1)] = type;
810 if (type>=0)
811 return TRUE;
812 ret = STORAGE_put_big_block(str,bigblocknr,block);
813 assert(ret);
814 type = STORAGE_CHAINENTRY_FREE;
815 blocknr = nextblocknr;
817 return TRUE;
820 /******************************************************************************
821 * STORAGE_set_small_chain [Internal]
823 static BOOL
824 STORAGE_set_small_chain(stream_access16*str,int blocknr,INT type) {
825 BYTE block[BIGSIZE];
826 LPINT sbd = (LPINT)block;
827 int lastblocknr,nextsmallblocknr,bigblocknr;
828 struct storage_header sth;
829 BOOL ret;
831 READ_HEADER(str);
833 assert(blocknr!=type);
834 lastblocknr=-129;bigblocknr=-2;
835 while (blocknr>=0) {
836 /* cache block ... */
837 if (lastblocknr/128!=blocknr/128) {
838 bigblocknr = STORAGE_get_nth_next_big_blocknr(str,sth.sbd_startblock,blocknr/128);
839 assert(bigblocknr>=0);
840 ret = STORAGE_get_big_block(str,bigblocknr,block);
841 assert(ret);
843 lastblocknr = blocknr;
844 nextsmallblocknr = sbd[blocknr&(128-1)];
845 sbd[blocknr&(128-1)] = type;
846 ret = STORAGE_put_big_block(str,bigblocknr,block);
847 assert(ret);
848 if (type>=0)
849 return TRUE;
850 type = STORAGE_CHAINENTRY_FREE;
851 blocknr = nextsmallblocknr;
853 return TRUE;
856 /******************************************************************************
857 * STORAGE_get_free_big_blocknr [Internal]
859 static int
860 STORAGE_get_free_big_blocknr(stream_access16 *str) {
861 BYTE block[BIGSIZE];
862 LPINT sbd = (LPINT)block;
863 int lastbigblocknr,i,bigblocknr;
864 unsigned int curblock;
865 struct storage_header sth;
866 BOOL ret;
868 READ_HEADER(str);
869 curblock = 0;
870 lastbigblocknr = -1;
871 bigblocknr = sth.bbd_list[curblock];
872 while (curblock<sth.num_of_bbd_blocks) {
873 assert(bigblocknr>=0);
874 ret = STORAGE_get_big_block(str,bigblocknr,block);
875 assert(ret);
876 for (i=0;i<128;i++)
877 if (sbd[i]==STORAGE_CHAINENTRY_FREE) {
878 sbd[i] = STORAGE_CHAINENTRY_ENDOFCHAIN;
879 ret = STORAGE_put_big_block(str,bigblocknr,block);
880 assert(ret);
881 memset(block,0x42,sizeof(block));
882 ret = STORAGE_put_big_block(str,i+curblock*128,block);
883 assert(ret);
884 return i+curblock*128;
886 lastbigblocknr = bigblocknr;
887 bigblocknr = sth.bbd_list[++curblock];
889 bigblocknr = curblock*128;
890 /* since we have marked all blocks from 0 up to curblock*128-1
891 * the next free one is curblock*128, where we happily put our
892 * next large block depot.
894 memset(block,0xff,sizeof(block));
895 /* mark the block allocated and returned by this function */
896 sbd[1] = STORAGE_CHAINENTRY_ENDOFCHAIN;
897 ret = STORAGE_put_big_block(str,bigblocknr,block);
898 assert(ret);
900 /* if we had a bbd block already (most likely) we need
901 * to link the new one into the chain
903 if (lastbigblocknr!=-1) {
904 ret = STORAGE_set_big_chain(str,lastbigblocknr,bigblocknr);
905 assert(ret);
907 sth.bbd_list[curblock]=bigblocknr;
908 sth.num_of_bbd_blocks++;
909 assert(sth.num_of_bbd_blocks==curblock+1);
910 ret = STORAGE_put_big_block(str,-1,(LPBYTE)&sth);
911 assert(ret);
913 /* Set the end of the chain for the bigblockdepots */
914 ret = STORAGE_set_big_chain(str,bigblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN);
915 assert(ret);
916 /* add 1, for the first entry is used for the additional big block
917 * depot. (means we already used bigblocknr) */
918 memset(block,0x42,sizeof(block));
919 /* allocate this block (filled with 0x42) */
920 ret = STORAGE_put_big_block(str,bigblocknr+1,block);
921 assert(ret);
922 return bigblocknr+1;
926 /******************************************************************************
927 * STORAGE_get_free_small_blocknr [Internal]
929 static int
930 STORAGE_get_free_small_blocknr(stream_access16 *str) {
931 BYTE block[BIGSIZE];
932 LPINT sbd = (LPINT)block;
933 int lastbigblocknr,newblocknr,i,curblock,bigblocknr;
934 struct storage_pps_entry root;
935 struct storage_header sth;
937 READ_HEADER(str);
938 bigblocknr = sth.sbd_startblock;
939 curblock = 0;
940 lastbigblocknr = -1;
941 newblocknr = -1;
942 while (bigblocknr>=0) {
943 if (!STORAGE_get_big_block(str,bigblocknr,block))
944 return -1;
945 for (i=0;i<128;i++)
946 if (sbd[i]==STORAGE_CHAINENTRY_FREE) {
947 sbd[i]=STORAGE_CHAINENTRY_ENDOFCHAIN;
948 newblocknr = i+curblock*128;
949 break;
951 if (i!=128)
952 break;
953 lastbigblocknr = bigblocknr;
954 bigblocknr = STORAGE_get_next_big_blocknr(str,bigblocknr);
955 curblock++;
957 if (newblocknr==-1) {
958 bigblocknr = STORAGE_get_free_big_blocknr(str);
959 if (bigblocknr<0)
960 return -1;
961 READ_HEADER(str);
962 memset(block,0xff,sizeof(block));
963 sbd[0]=STORAGE_CHAINENTRY_ENDOFCHAIN;
964 if (!STORAGE_put_big_block(str,bigblocknr,block))
965 return -1;
966 if (lastbigblocknr==-1) {
967 sth.sbd_startblock = bigblocknr;
968 if (!STORAGE_put_big_block(str,-1,(LPBYTE)&sth)) /* need to write it */
969 return -1;
970 } else {
971 if (!STORAGE_set_big_chain(str,lastbigblocknr,bigblocknr))
972 return -1;
974 if (!STORAGE_set_big_chain(str,bigblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
975 return -1;
976 newblocknr = curblock*128;
978 /* allocate enough big blocks for storing the allocated small block */
979 if (!STORAGE_get_root_pps_entry(str,&root))
980 return -1;
981 if (root.pps_sb==-1)
982 lastbigblocknr = -1;
983 else
984 lastbigblocknr = STORAGE_get_nth_next_big_blocknr(str,root.pps_sb,(root.pps_size-1)/BIGSIZE);
985 while (root.pps_size < (newblocknr*SMALLSIZE+SMALLSIZE-1)) {
986 /* we need to allocate more stuff */
987 bigblocknr = STORAGE_get_free_big_blocknr(str);
988 if (bigblocknr<0)
989 return -1;
990 READ_HEADER(str);
991 if (root.pps_sb==-1) {
992 root.pps_sb = bigblocknr;
993 root.pps_size += BIGSIZE;
994 } else {
995 if (!STORAGE_set_big_chain(str,lastbigblocknr,bigblocknr))
996 return -1;
997 root.pps_size += BIGSIZE;
999 lastbigblocknr = bigblocknr;
1001 if (!STORAGE_set_big_chain(str,lastbigblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1002 return -1;
1003 if (!STORAGE_put_pps_entry(str,0,&root))
1004 return -1;
1005 return newblocknr;
1008 /******************************************************************************
1009 * STORAGE_get_free_pps_entry [Internal]
1011 static int
1012 STORAGE_get_free_pps_entry(stream_access16*str) {
1013 int blocknr, i, curblock, lastblocknr=-1;
1014 BYTE block[BIGSIZE];
1015 struct storage_pps_entry *stde = (struct storage_pps_entry*)block;
1016 struct storage_header sth;
1018 READ_HEADER(str);
1019 blocknr = sth.root_startblock;
1020 assert(blocknr>=0);
1021 curblock=0;
1022 while (blocknr>=0) {
1023 if (!STORAGE_get_big_block(str,blocknr,block))
1024 return -1;
1025 for (i=0;i<4;i++)
1026 if (stde[i].pps_sizeofname==0) /* free */
1027 return curblock*4+i;
1028 lastblocknr = blocknr;
1029 blocknr = STORAGE_get_next_big_blocknr(str,blocknr);
1030 curblock++;
1032 assert(blocknr==STORAGE_CHAINENTRY_ENDOFCHAIN);
1033 blocknr = STORAGE_get_free_big_blocknr(str);
1034 /* sth invalidated */
1035 if (blocknr<0)
1036 return -1;
1038 if (!STORAGE_set_big_chain(str,lastblocknr,blocknr))
1039 return -1;
1040 if (!STORAGE_set_big_chain(str,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1041 return -1;
1042 memset(block,0,sizeof(block));
1043 STORAGE_put_big_block(str,blocknr,block);
1044 return curblock*4;
1047 /* --- IStream16 implementation */
1049 typedef struct
1051 IStream16 IStream16_iface;
1052 LONG ref;
1053 /* IStream16 fields */
1054 SEGPTR thisptr; /* pointer to this struct as segmented */
1055 struct storage_pps_entry stde;
1056 int ppsent;
1057 ULARGE_INTEGER offset;
1058 stream_access16 str;
1059 } IStream16Impl;
1061 static inline IStream16Impl *impl_from_IStream16(IStream16 *iface)
1063 return CONTAINING_RECORD(iface, IStream16Impl, IStream16_iface);
1066 /******************************************************************************
1067 * IStream16_QueryInterface [STORAGE.518]
1069 HRESULT CDECL IStream16_fnQueryInterface(IStream16 *iface, REFIID refiid, void **obj)
1071 IStream16Impl *This = impl_from_IStream16(iface);
1072 TRACE_(relay)("(%p)->(%s,%p)\n",This,debugstr_guid(refiid),obj);
1073 if (!memcmp(&IID_IUnknown,refiid,sizeof(IID_IUnknown))) {
1074 *obj = This;
1075 return 0;
1077 return OLE_E_ENUM_NOMORE;
1081 /******************************************************************************
1082 * IStream16_AddRef [STORAGE.519]
1084 ULONG CDECL IStream16_fnAddRef(IStream16 *iface)
1086 IStream16Impl *This = impl_from_IStream16(iface);
1087 return InterlockedIncrement(&This->ref);
1090 static void
1091 _ilockbytes16_addref(SEGPTR lockbytes) {
1092 DWORD args[1];
1093 HRESULT hres;
1095 args[0] = (DWORD)lockbytes; /* iface */
1096 if (!WOWCallback16Ex(
1097 (DWORD)((const ILockBytes16Vtbl*)MapSL(
1098 (SEGPTR)((LPLOCKBYTES16)MapSL(lockbytes))->lpVtbl)
1099 )->AddRef,
1100 WCB16_PASCAL,
1101 1*sizeof(DWORD),
1102 (LPVOID)args,
1103 (LPDWORD)&hres
1105 ERR("CallTo16 ILockBytes16::AddRef() failed, hres %lx\n",hres);
1108 static void
1109 _ilockbytes16_release(SEGPTR lockbytes) {
1110 DWORD args[1];
1111 HRESULT hres;
1113 args[0] = (DWORD)lockbytes; /* iface */
1114 if (!WOWCallback16Ex(
1115 (DWORD)((const ILockBytes16Vtbl*)MapSL(
1116 (SEGPTR)((LPLOCKBYTES16)MapSL(lockbytes))->lpVtbl)
1117 )->Release,
1118 WCB16_PASCAL,
1119 1*sizeof(DWORD),
1120 (LPVOID)args,
1121 (LPDWORD)&hres
1123 ERR("CallTo16 ILockBytes16::Release() failed, hres %lx\n",hres);
1126 static void
1127 _ilockbytes16_flush(SEGPTR lockbytes) {
1128 DWORD args[1];
1129 HRESULT hres;
1131 args[0] = (DWORD)lockbytes; /* iface */
1132 if (!WOWCallback16Ex(
1133 (DWORD)((const ILockBytes16Vtbl*)MapSL(
1134 (SEGPTR)((LPLOCKBYTES16)MapSL(lockbytes))->lpVtbl)
1135 )->Flush,
1136 WCB16_PASCAL,
1137 1*sizeof(DWORD),
1138 (LPVOID)args,
1139 (LPDWORD)&hres
1141 ERR("CallTo16 ILockBytes16::Flush() failed, hres %lx\n",hres);
1144 /******************************************************************************
1145 * IStream16_Release [STORAGE.520]
1147 ULONG CDECL IStream16_fnRelease(IStream16 *iface)
1149 IStream16Impl *This = impl_from_IStream16(iface);
1150 ULONG ref;
1152 if (This->str.hf)
1153 FlushFileBuffers(This->str.hf);
1154 else
1155 _ilockbytes16_flush(This->str.lockbytes);
1156 ref = InterlockedDecrement(&This->ref);
1157 if (ref)
1158 return ref;
1160 if (This->str.hf)
1161 CloseHandle(This->str.hf);
1162 else
1163 _ilockbytes16_release(This->str.lockbytes);
1164 UnMapLS( This->thisptr );
1165 HeapFree( GetProcessHeap(), 0, This );
1166 return 0;
1169 /******************************************************************************
1170 * IStream16_Seek [STORAGE.523]
1172 * FIXME
1173 * Does not handle 64 bits
1175 HRESULT CDECL IStream16_fnSeek(IStream16 *iface, LARGE_INTEGER offset, DWORD whence,
1176 ULARGE_INTEGER *newpos)
1178 IStream16Impl *This = impl_from_IStream16(iface);
1179 TRACE_(relay)("(%p)->([%ld.%ld],%ld,%p)\n",This,offset.u.HighPart,offset.u.LowPart,whence,newpos);
1181 switch (whence) {
1182 case STREAM_SEEK_SET:
1183 This->offset.QuadPart = offset.QuadPart;
1184 break;
1185 case STREAM_SEEK_CUR:
1186 if ((offset.QuadPart < 0 && -offset.QuadPart > This->offset.QuadPart) ||
1187 (offset.QuadPart > 0 && -offset.QuadPart <= This->offset.QuadPart))
1188 return STG_E_INVALIDFUNCTION;
1189 This->offset.QuadPart += offset.QuadPart;
1190 break;
1191 case STREAM_SEEK_END:
1192 if (-offset.QuadPart > This->stde.pps_size)
1193 return STG_E_INVALIDFUNCTION;
1195 This->offset.QuadPart = This->stde.pps_size + offset.QuadPart;
1196 break;
1199 if (This->offset.QuadPart>This->stde.pps_size)
1200 This->offset.QuadPart=This->stde.pps_size;
1201 if (newpos) *newpos = This->offset;
1202 return S_OK;
1205 /******************************************************************************
1206 * IStream16_Read [STORAGE.521]
1208 HRESULT CDECL IStream16_fnRead(IStream16 *iface, void *pv, ULONG cb, ULONG *pcbRead)
1210 IStream16Impl *This = impl_from_IStream16(iface);
1211 BYTE block[BIGSIZE];
1212 ULONG *bytesread=pcbRead,xxread;
1213 int blocknr;
1214 LPBYTE pbv = pv;
1216 TRACE_(relay)("(%p)->(%p,%ld,%p)\n",This,pv,cb,pcbRead);
1217 if (!pcbRead) bytesread=&xxread;
1218 *bytesread = 0;
1220 if (cb>This->stde.pps_size-This->offset.u.LowPart)
1221 cb=This->stde.pps_size-This->offset.u.LowPart;
1222 if (This->stde.pps_size < 0x1000) {
1223 /* use small block reader */
1224 blocknr = STORAGE_get_nth_next_small_blocknr(&This->str,This->stde.pps_sb,This->offset.u.LowPart/SMALLSIZE);
1225 while (cb) {
1226 unsigned int cc;
1228 if (!STORAGE_get_small_block(&This->str,blocknr,block)) {
1229 WARN("small block read failed!!!\n");
1230 return E_FAIL;
1232 cc = cb;
1233 if (cc>SMALLSIZE-(This->offset.u.LowPart&(SMALLSIZE-1)))
1234 cc=SMALLSIZE-(This->offset.u.LowPart&(SMALLSIZE-1));
1235 memcpy(pbv,block+(This->offset.u.LowPart&(SMALLSIZE-1)),cc);
1236 This->offset.u.LowPart+=cc;
1237 pbv+=cc;
1238 *bytesread+=cc;
1239 cb-=cc;
1240 blocknr = STORAGE_get_next_small_blocknr(&This->str,blocknr);
1242 } else {
1243 /* use big block reader */
1244 blocknr = STORAGE_get_nth_next_big_blocknr(&This->str,This->stde.pps_sb,This->offset.u.LowPart/BIGSIZE);
1245 while (cb) {
1246 unsigned int cc;
1248 if (!STORAGE_get_big_block(&This->str,blocknr,block)) {
1249 WARN("big block read failed!!!\n");
1250 return E_FAIL;
1252 cc = cb;
1253 if (cc>BIGSIZE-(This->offset.u.LowPart&(BIGSIZE-1)))
1254 cc=BIGSIZE-(This->offset.u.LowPart&(BIGSIZE-1));
1255 memcpy(pbv,block+(This->offset.u.LowPart&(BIGSIZE-1)),cc);
1256 This->offset.u.LowPart+=cc;
1257 pbv+=cc;
1258 *bytesread+=cc;
1259 cb-=cc;
1260 blocknr=STORAGE_get_next_big_blocknr(&This->str,blocknr);
1263 return S_OK;
1266 /******************************************************************************
1267 * IStream16_Write [STORAGE.522]
1269 HRESULT CDECL IStream16_fnWrite(IStream16 *iface, const void *pv, ULONG cb, ULONG *pcbWrite)
1271 IStream16Impl *This = impl_from_IStream16(iface);
1272 BYTE block[BIGSIZE];
1273 ULONG *byteswritten=pcbWrite,xxwritten;
1274 int oldsize,newsize,i,curoffset=0,lastblocknr,blocknr,cc;
1275 const BYTE* pbv = pv;
1277 if (!pcbWrite) byteswritten=&xxwritten;
1278 *byteswritten = 0;
1280 TRACE_(relay)("(%p)->(%p,%ld,%p)\n",This,pv,cb,pcbWrite);
1281 /* do we need to junk some blocks? */
1282 newsize = This->offset.u.LowPart+cb;
1283 oldsize = This->stde.pps_size;
1284 if (newsize < oldsize) {
1285 if (oldsize < 0x1000) {
1286 /* only small blocks */
1287 blocknr=STORAGE_get_nth_next_small_blocknr(&This->str,This->stde.pps_sb,newsize/SMALLSIZE);
1289 assert(blocknr>=0);
1291 /* will set the rest of the chain to 'free' */
1292 if (!STORAGE_set_small_chain(&This->str,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1293 return E_FAIL;
1294 } else {
1295 if (newsize >= 0x1000) {
1296 blocknr=STORAGE_get_nth_next_big_blocknr(&This->str,This->stde.pps_sb,newsize/BIGSIZE);
1297 assert(blocknr>=0);
1299 /* will set the rest of the chain to 'free' */
1300 if (!STORAGE_set_big_chain(&This->str,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1301 return E_FAIL;
1302 } else {
1303 /* Migrate large blocks to small blocks
1304 * (we just migrate newsize bytes)
1306 LPBYTE curdata,data = HeapAlloc(GetProcessHeap(),0,newsize+BIGSIZE);
1307 HRESULT r = E_FAIL;
1309 cc = newsize;
1310 blocknr = This->stde.pps_sb;
1311 curdata = data;
1312 while (cc>0) {
1313 if (!STORAGE_get_big_block(&This->str,blocknr,curdata)) {
1314 HeapFree(GetProcessHeap(),0,data);
1315 return E_FAIL;
1317 curdata += BIGSIZE;
1318 cc -= BIGSIZE;
1319 blocknr = STORAGE_get_next_big_blocknr(&This->str,blocknr);
1321 /* frees complete chain for this stream */
1322 if (!STORAGE_set_big_chain(&This->str,This->stde.pps_sb,STORAGE_CHAINENTRY_FREE))
1323 goto err;
1324 curdata = data;
1325 blocknr = This->stde.pps_sb = STORAGE_get_free_small_blocknr(&This->str);
1326 if (blocknr<0)
1327 goto err;
1328 cc = newsize;
1329 while (cc>0) {
1330 if (!STORAGE_put_small_block(&This->str,blocknr,curdata))
1331 goto err;
1332 cc -= SMALLSIZE;
1333 if (cc<=0) {
1334 if (!STORAGE_set_small_chain(&This->str,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1335 goto err;
1336 break;
1337 } else {
1338 int newblocknr = STORAGE_get_free_small_blocknr(&This->str);
1339 if (newblocknr<0)
1340 goto err;
1341 if (!STORAGE_set_small_chain(&This->str,blocknr,newblocknr))
1342 goto err;
1343 blocknr = newblocknr;
1345 curdata += SMALLSIZE;
1347 r = S_OK;
1348 err:
1349 HeapFree(GetProcessHeap(),0,data);
1350 if(r != S_OK)
1351 return r;
1354 This->stde.pps_size = newsize;
1357 if (newsize > oldsize) {
1358 if (oldsize >= 0x1000) {
1359 /* should return the block right before the 'endofchain' */
1360 blocknr = STORAGE_get_nth_next_big_blocknr(&This->str,This->stde.pps_sb,This->stde.pps_size/BIGSIZE);
1361 assert(blocknr>=0);
1362 lastblocknr = blocknr;
1363 for (i=oldsize/BIGSIZE;i<newsize/BIGSIZE;i++) {
1364 blocknr = STORAGE_get_free_big_blocknr(&This->str);
1365 if (blocknr<0)
1366 return E_FAIL;
1367 if (!STORAGE_set_big_chain(&This->str,lastblocknr,blocknr))
1368 return E_FAIL;
1369 lastblocknr = blocknr;
1371 if (!STORAGE_set_big_chain(&This->str,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1372 return E_FAIL;
1373 } else {
1374 if (newsize < 0x1000) {
1375 /* find startblock */
1376 if (!oldsize)
1377 This->stde.pps_sb = blocknr = STORAGE_get_free_small_blocknr(&This->str);
1378 else
1379 blocknr = STORAGE_get_nth_next_small_blocknr(&This->str,This->stde.pps_sb,This->stde.pps_size/SMALLSIZE);
1380 if (blocknr<0)
1381 return E_FAIL;
1383 /* allocate required new small blocks */
1384 lastblocknr = blocknr;
1385 for (i=oldsize/SMALLSIZE;i<newsize/SMALLSIZE;i++) {
1386 blocknr = STORAGE_get_free_small_blocknr(&This->str);
1387 if (blocknr<0)
1388 return E_FAIL;
1389 if (!STORAGE_set_small_chain(&This->str,lastblocknr,blocknr))
1390 return E_FAIL;
1391 lastblocknr = blocknr;
1393 /* and terminate the chain */
1394 if (!STORAGE_set_small_chain(&This->str,lastblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1395 return E_FAIL;
1396 } else {
1397 if (!oldsize) {
1398 /* no single block allocated yet */
1399 blocknr=STORAGE_get_free_big_blocknr(&This->str);
1400 if (blocknr<0)
1401 return E_FAIL;
1402 This->stde.pps_sb = blocknr;
1403 } else {
1404 /* Migrate small blocks to big blocks */
1405 LPBYTE curdata,data = HeapAlloc(GetProcessHeap(),0,oldsize+BIGSIZE);
1406 HRESULT r = E_FAIL;
1408 cc = oldsize;
1409 blocknr = This->stde.pps_sb;
1410 curdata = data;
1411 /* slurp in */
1412 while (cc>0) {
1413 if (!STORAGE_get_small_block(&This->str,blocknr,curdata))
1414 goto err2;
1415 curdata += SMALLSIZE;
1416 cc -= SMALLSIZE;
1417 blocknr = STORAGE_get_next_small_blocknr(&This->str,blocknr);
1419 /* free small block chain */
1420 if (!STORAGE_set_small_chain(&This->str,This->stde.pps_sb,STORAGE_CHAINENTRY_FREE))
1421 goto err2;
1422 curdata = data;
1423 blocknr = This->stde.pps_sb = STORAGE_get_free_big_blocknr(&This->str);
1424 if (blocknr<0)
1425 goto err2;
1426 /* put the data into the big blocks */
1427 cc = This->stde.pps_size;
1428 while (cc>0) {
1429 if (!STORAGE_put_big_block(&This->str,blocknr,curdata))
1430 goto err2;
1431 cc -= BIGSIZE;
1432 if (cc<=0) {
1433 if (!STORAGE_set_big_chain(&This->str,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1434 goto err2;
1435 break;
1436 } else {
1437 int newblocknr = STORAGE_get_free_big_blocknr(&This->str);
1438 if (newblocknr<0)
1439 goto err2;
1440 if (!STORAGE_set_big_chain(&This->str,blocknr,newblocknr))
1441 goto err2;
1442 blocknr = newblocknr;
1444 curdata += BIGSIZE;
1446 r = S_OK;
1447 err2:
1448 HeapFree(GetProcessHeap(),0,data);
1449 if(r != S_OK)
1450 return r;
1452 /* generate big blocks to fit the new data */
1453 lastblocknr = blocknr;
1454 for (i=oldsize/BIGSIZE;i<newsize/BIGSIZE;i++) {
1455 blocknr = STORAGE_get_free_big_blocknr(&This->str);
1456 if (blocknr<0)
1457 return E_FAIL;
1458 if (!STORAGE_set_big_chain(&This->str,lastblocknr,blocknr))
1459 return E_FAIL;
1460 lastblocknr = blocknr;
1462 /* terminate chain */
1463 if (!STORAGE_set_big_chain(&This->str,lastblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN))
1464 return E_FAIL;
1467 This->stde.pps_size = newsize;
1470 /* There are just some cases where we didn't modify it, we write it out
1471 * every time
1473 if (!STORAGE_put_pps_entry(&This->str,This->ppsent,&(This->stde)))
1474 return E_FAIL;
1476 /* finally the write pass */
1477 if (This->stde.pps_size < 0x1000) {
1478 blocknr = STORAGE_get_nth_next_small_blocknr(&This->str,This->stde.pps_sb,This->offset.u.LowPart/SMALLSIZE);
1479 assert(blocknr>=0);
1480 while (cb>0) {
1481 /* we ensured that it is allocated above */
1482 assert(blocknr>=0);
1483 /* Read old block every time, since we can have
1484 * overlapping data at START and END of the write
1486 if (!STORAGE_get_small_block(&This->str,blocknr,block))
1487 return E_FAIL;
1489 cc = SMALLSIZE-(This->offset.u.LowPart&(SMALLSIZE-1));
1490 if (cc>cb)
1491 cc=cb;
1492 memcpy( ((LPBYTE)block)+(This->offset.u.LowPart&(SMALLSIZE-1)),
1493 pbv+curoffset,
1496 if (!STORAGE_put_small_block(&This->str,blocknr,block))
1497 return E_FAIL;
1498 cb -= cc;
1499 curoffset += cc;
1500 pbv += cc;
1501 This->offset.u.LowPart += cc;
1502 *byteswritten += cc;
1503 blocknr = STORAGE_get_next_small_blocknr(&This->str,blocknr);
1505 } else {
1506 blocknr = STORAGE_get_nth_next_big_blocknr(&This->str,This->stde.pps_sb,This->offset.u.LowPart/BIGSIZE);
1507 assert(blocknr>=0);
1508 while (cb>0) {
1509 /* we ensured that it is allocated above, so it better is */
1510 assert(blocknr>=0);
1511 /* read old block every time, since we can have
1512 * overlapping data at START and END of the write
1514 if (!STORAGE_get_big_block(&This->str,blocknr,block))
1515 return E_FAIL;
1517 cc = BIGSIZE-(This->offset.u.LowPart&(BIGSIZE-1));
1518 if (cc>cb)
1519 cc=cb;
1520 memcpy( ((LPBYTE)block)+(This->offset.u.LowPart&(BIGSIZE-1)),
1521 pbv+curoffset,
1524 if (!STORAGE_put_big_block(&This->str,blocknr,block))
1525 return E_FAIL;
1526 cb -= cc;
1527 curoffset += cc;
1528 pbv += cc;
1529 This->offset.u.LowPart += cc;
1530 *byteswritten += cc;
1531 blocknr = STORAGE_get_next_big_blocknr(&This->str,blocknr);
1534 return S_OK;
1537 /******************************************************************************
1538 * _create_istream16 [Internal]
1540 static void _create_istream16(LPSTREAM16 *str) {
1541 IStream16Impl* lpst;
1543 if (!strvt16.QueryInterface) {
1544 HMODULE16 wp = GetModuleHandle16("STORAGE");
1545 if (wp>=32) {
1546 /* FIXME: what is This GetProcAddress16. Should the name be IStream16_QueryInterface of IStream16_fnQueryInterface */
1547 #define VTENT(xfn) strvt16.xfn = (void*)GetProcAddress16(wp,"IStream16_"#xfn);assert(strvt16.xfn)
1548 VTENT(QueryInterface);
1549 VTENT(AddRef);
1550 VTENT(Release);
1551 VTENT(Read);
1552 VTENT(Write);
1553 VTENT(Seek);
1554 VTENT(SetSize);
1555 VTENT(CopyTo);
1556 VTENT(Commit);
1557 VTENT(Revert);
1558 VTENT(LockRegion);
1559 VTENT(UnlockRegion);
1560 VTENT(Stat);
1561 VTENT(Clone);
1562 #undef VTENT
1563 segstrvt16 = (const IStream16Vtbl*)MapLS( &strvt16 );
1564 } else {
1565 #define VTENT(xfn) strvt16.xfn = IStream16_fn##xfn;
1566 VTENT(QueryInterface);
1567 VTENT(AddRef);
1568 VTENT(Release);
1569 VTENT(Read);
1570 VTENT(Write);
1571 VTENT(Seek);
1573 VTENT(CopyTo);
1574 VTENT(Commit);
1575 VTENT(SetSize);
1576 VTENT(Revert);
1577 VTENT(LockRegion);
1578 VTENT(UnlockRegion);
1579 VTENT(Stat);
1580 VTENT(Clone);
1582 #undef VTENT
1583 segstrvt16 = &strvt16;
1586 lpst = HeapAlloc( GetProcessHeap(), 0, sizeof(*lpst) );
1587 lpst->IStream16_iface.lpVtbl = segstrvt16;
1588 lpst->ref = 1;
1589 lpst->thisptr = MapLS( lpst );
1590 lpst->str.hf = NULL;
1591 lpst->str.lockbytes = 0;
1592 *str = (void*)lpst->thisptr;
1595 static inline IStorage16Impl *impl_from_IStorage16(IStorage16 *iface)
1597 return CONTAINING_RECORD(iface, IStorage16Impl, IStorage16_iface);
1600 /******************************************************************************
1601 * IStorage16_QueryInterface [STORAGE.500]
1603 HRESULT CDECL IStorage16_fnQueryInterface(IStorage16 *iface, REFIID refiid, void **obj)
1605 IStorage16Impl *This = impl_from_IStorage16(iface);
1607 TRACE_(relay)("(%p)->(%s,%p)\n",This,debugstr_guid(refiid),obj);
1609 if (!memcmp(&IID_IUnknown,refiid,sizeof(IID_IUnknown))) {
1610 *obj = This;
1611 return 0;
1613 return OLE_E_ENUM_NOMORE;
1616 /******************************************************************************
1617 * IStorage16_AddRef [STORAGE.501]
1619 ULONG CDECL IStorage16_fnAddRef(IStorage16 *iface)
1621 IStorage16Impl *This = impl_from_IStorage16(iface);
1622 return InterlockedIncrement(&This->ref);
1625 /******************************************************************************
1626 * IStorage16_Release [STORAGE.502]
1628 ULONG CDECL IStorage16_fnRelease(IStorage16 *iface)
1630 IStorage16Impl *This = impl_from_IStorage16(iface);
1631 ULONG ref;
1632 ref = InterlockedDecrement(&This->ref);
1633 if (!ref)
1635 UnMapLS( This->thisptr );
1636 HeapFree( GetProcessHeap(), 0, This );
1638 return ref;
1641 /******************************************************************************
1642 * IStorage16_Stat [STORAGE.517]
1644 HRESULT CDECL IStorage16_fnStat(IStorage16 *iface, STATSTG16 *pstatstg, DWORD grfStatFlag)
1646 IStorage16Impl *This = impl_from_IStorage16(iface);
1647 DWORD len = WideCharToMultiByte( CP_ACP, 0, This->stde.pps_rawname, -1, NULL, 0, NULL, NULL );
1648 LPSTR nameA = HeapAlloc( GetProcessHeap(), 0, len );
1650 TRACE("(%p)->(%p,0x%08lx)\n",
1651 This,pstatstg,grfStatFlag
1653 WideCharToMultiByte( CP_ACP, 0, This->stde.pps_rawname, -1, nameA, len, NULL, NULL );
1654 pstatstg->pwcsName=(LPOLESTR16)MapLS( nameA );
1655 pstatstg->type = This->stde.pps_type;
1656 pstatstg->cbSize.u.LowPart = This->stde.pps_size;
1657 pstatstg->mtime = This->stde.pps_ft1; /* FIXME */ /* why? */
1658 pstatstg->atime = This->stde.pps_ft2; /* FIXME */
1659 pstatstg->ctime = This->stde.pps_ft2; /* FIXME */
1660 pstatstg->grfMode = 0; /* FIXME */
1661 pstatstg->grfLocksSupported = 0; /* FIXME */
1662 pstatstg->clsid = This->stde.pps_guid;
1663 pstatstg->grfStateBits = 0; /* FIXME */
1664 pstatstg->reserved = 0;
1665 return S_OK;
1668 /******************************************************************************
1669 * IStorage16_Commit [STORAGE.509]
1671 HRESULT CDECL IStorage16_fnCommit(IStorage16 *iface, DWORD commitflags)
1673 IStorage16Impl *This = impl_from_IStorage16(iface);
1674 FIXME("(%p)->(0x%08lx),STUB!\n",
1675 This,commitflags
1677 return S_OK;
1680 /******************************************************************************
1681 * IStorage16_CopyTo [STORAGE.507]
1683 HRESULT CDECL IStorage16_fnCopyTo(IStorage16 *iface, DWORD ciidExclude, const IID *rgiidExclude,
1684 SNB16 SNB16Exclude, IStorage16 *pstgDest)
1686 IStorage16Impl *This = impl_from_IStorage16(iface);
1687 FIXME("IStorage16(%p)->(0x%08lx,%s,%p,%p),stub!\n",
1688 This,ciidExclude,debugstr_guid(rgiidExclude),SNB16Exclude,pstgDest
1690 return S_OK;
1694 /******************************************************************************
1695 * IStorage16_CreateStorage [STORAGE.505]
1697 HRESULT CDECL IStorage16_fnCreateStorage(IStorage16 *iface, LPCOLESTR16 pwcsName, DWORD grfMode,
1698 DWORD dwStgFormat, DWORD reserved2, IStorage16 **ppstg)
1700 IStorage16Impl *This = impl_from_IStorage16(iface);
1701 IStorage16Impl* lpstg;
1702 int ppsent,x;
1703 struct storage_pps_entry stde;
1704 struct storage_header sth;
1705 BOOL ret;
1706 int nPPSEntries;
1708 READ_HEADER(&This->str);
1709 TRACE("(%p)->(%s,0x%08lx,0x%08lx,0x%08lx,%p)\n",
1710 This,pwcsName,grfMode,dwStgFormat,reserved2,ppstg
1712 if (grfMode & STGM_TRANSACTED)
1713 FIXME("We do not support transacted Compound Storage. Using direct mode.\n");
1714 _create_istorage16(ppstg);
1715 lpstg = MapSL((SEGPTR)*ppstg);
1716 if (This->str.hf) {
1717 DuplicateHandle( GetCurrentProcess(), This->str.hf, GetCurrentProcess(),
1718 &lpstg->str.hf, 0, TRUE, DUPLICATE_SAME_ACCESS );
1719 } else {
1720 lpstg->str.lockbytes = This->str.lockbytes;
1721 _ilockbytes16_addref(This->str.lockbytes);
1724 ppsent=STORAGE_get_free_pps_entry(&lpstg->str);
1725 if (ppsent<0)
1726 return E_FAIL;
1727 stde=This->stde;
1728 if (stde.pps_dir==-1) {
1729 stde.pps_dir = ppsent;
1730 x = This->ppsent;
1731 } else {
1732 FIXME(" use prev chain too ?\n");
1733 x=stde.pps_dir;
1734 if (1!=STORAGE_get_pps_entry(&lpstg->str,x,&stde))
1735 return E_FAIL;
1736 while (stde.pps_next!=-1) {
1737 x=stde.pps_next;
1738 if (1!=STORAGE_get_pps_entry(&lpstg->str,x,&stde))
1739 return E_FAIL;
1741 stde.pps_next = ppsent;
1743 ret = STORAGE_put_pps_entry(&lpstg->str,x,&stde);
1744 assert(ret);
1745 nPPSEntries = STORAGE_get_pps_entry(&lpstg->str,ppsent,&(lpstg->stde));
1746 assert(nPPSEntries == 1);
1747 MultiByteToWideChar(CP_ACP, 0, pwcsName, -1, lpstg->stde.pps_rawname,
1748 ARRAY_SIZE(lpstg->stde.pps_rawname));
1749 lpstg->stde.pps_sizeofname = (lstrlenW(lpstg->stde.pps_rawname)+1)*sizeof(WCHAR);
1750 lpstg->stde.pps_next = -1;
1751 lpstg->stde.pps_prev = -1;
1752 lpstg->stde.pps_dir = -1;
1753 lpstg->stde.pps_sb = -1;
1754 lpstg->stde.pps_size = 0;
1755 lpstg->stde.pps_type = 1;
1756 lpstg->ppsent = ppsent;
1757 /* FIXME: timestamps? */
1758 if (!STORAGE_put_pps_entry(&lpstg->str,ppsent,&(lpstg->stde)))
1759 return E_FAIL;
1760 return S_OK;
1763 /******************************************************************************
1764 * IStorage16_CreateStream [STORAGE.503]
1766 HRESULT CDECL IStorage16_fnCreateStream(IStorage16 *iface, LPCOLESTR16 pwcsName, DWORD grfMode,
1767 DWORD reserved1, DWORD reserved2, IStream16 **ppstm)
1769 IStorage16Impl *This = impl_from_IStorage16(iface);
1770 IStream16Impl* lpstr;
1771 int ppsent,x;
1772 struct storage_pps_entry stde;
1773 BOOL ret;
1774 int nPPSEntries;
1776 TRACE("(%p)->(%s,0x%08lx,0x%08lx,0x%08lx,%p)\n",
1777 This,pwcsName,grfMode,reserved1,reserved2,ppstm
1779 if (grfMode & STGM_TRANSACTED)
1780 FIXME("We do not support transacted Compound Storage. Using direct mode.\n");
1781 _create_istream16(ppstm);
1782 lpstr = MapSL((SEGPTR)*ppstm);
1783 if (This->str.hf) {
1784 DuplicateHandle( GetCurrentProcess(), This->str.hf, GetCurrentProcess(),
1785 &lpstr->str.hf, 0, TRUE, DUPLICATE_SAME_ACCESS );
1786 } else {
1787 lpstr->str.lockbytes = This->str.lockbytes;
1788 _ilockbytes16_addref(This->str.lockbytes);
1790 lpstr->offset.u.LowPart = 0;
1791 lpstr->offset.u.HighPart= 0;
1793 ppsent=STORAGE_get_free_pps_entry(&lpstr->str);
1794 if (ppsent<0)
1795 return E_FAIL;
1796 stde=This->stde;
1797 if (stde.pps_next==-1)
1798 x=This->ppsent;
1799 else
1800 while (stde.pps_next!=-1) {
1801 x=stde.pps_next;
1802 if (1!=STORAGE_get_pps_entry(&lpstr->str,x,&stde))
1803 return E_FAIL;
1805 stde.pps_next = ppsent;
1806 ret = STORAGE_put_pps_entry(&lpstr->str,x,&stde);
1807 assert(ret);
1808 nPPSEntries = STORAGE_get_pps_entry(&lpstr->str,ppsent,&(lpstr->stde));
1809 assert(nPPSEntries == 1);
1810 MultiByteToWideChar(CP_ACP, 0, pwcsName, -1, lpstr->stde.pps_rawname,
1811 ARRAY_SIZE(lpstr->stde.pps_rawname));
1812 lpstr->stde.pps_sizeofname = (lstrlenW(lpstr->stde.pps_rawname)+1) * sizeof(WCHAR);
1813 lpstr->stde.pps_next = -1;
1814 lpstr->stde.pps_prev = -1;
1815 lpstr->stde.pps_dir = -1;
1816 lpstr->stde.pps_sb = -1;
1817 lpstr->stde.pps_size = 0;
1818 lpstr->stde.pps_type = 2;
1819 lpstr->ppsent = ppsent;
1821 /* FIXME: timestamps? */
1822 if (!STORAGE_put_pps_entry(&lpstr->str,ppsent,&(lpstr->stde)))
1823 return E_FAIL;
1824 return S_OK;
1827 /******************************************************************************
1828 * IStorage16_OpenStorage [STORAGE.506]
1830 HRESULT CDECL IStorage16_fnOpenStorage(IStorage16 *iface, LPCOLESTR16 pwcsName,
1831 IStorage16 *pstgPrio, DWORD grfMode, SNB16 snbExclude, DWORD reserved, IStorage16 **ppstg)
1833 IStorage16Impl *This = impl_from_IStorage16(iface);
1834 IStorage16Impl *lpstg;
1835 WCHAR name[33];
1836 int newpps;
1838 TRACE("(%p)->(%s,%p,0x%08lx,%p,0x%08lx,%p)\n",
1839 This,pwcsName,pstgPrio,grfMode,snbExclude,reserved,ppstg
1841 if (grfMode & STGM_TRANSACTED)
1842 FIXME("We do not support transacted Compound Storage. Using direct mode.\n");
1843 _create_istorage16(ppstg);
1844 lpstg = MapSL((SEGPTR)*ppstg);
1845 if (This->str.hf) {
1846 DuplicateHandle( GetCurrentProcess(), This->str.hf, GetCurrentProcess(),
1847 &lpstg->str.hf, 0, TRUE, DUPLICATE_SAME_ACCESS );
1848 } else {
1849 lpstg->str.lockbytes = This->str.lockbytes;
1850 _ilockbytes16_addref(This->str.lockbytes);
1852 MultiByteToWideChar(CP_ACP, 0, pwcsName, -1, name, ARRAY_SIZE(name));
1853 newpps = STORAGE_look_for_named_pps(&lpstg->str,This->stde.pps_dir,name);
1854 if (newpps==-1) {
1855 IStorage16_fnRelease(&lpstg->IStorage16_iface);
1856 *ppstg = NULL;
1857 return E_FAIL;
1860 if (1!=STORAGE_get_pps_entry(&lpstg->str,newpps,&(lpstg->stde))) {
1861 IStorage16_fnRelease(&lpstg->IStorage16_iface);
1862 *ppstg = NULL;
1863 return E_FAIL;
1865 lpstg->ppsent = newpps;
1866 return S_OK;
1869 /******************************************************************************
1870 * IStorage16_OpenStream [STORAGE.504]
1872 HRESULT CDECL IStorage16_fnOpenStream(IStorage16 *iface, LPCOLESTR16 pwcsName, void *reserved1,
1873 DWORD grfMode, DWORD reserved2, IStream16 **ppstm)
1875 IStorage16Impl *This = impl_from_IStorage16(iface);
1876 IStream16Impl* lpstr;
1877 WCHAR name[33];
1878 int newpps;
1880 TRACE("(%p)->(%s,%p,0x%08lx,0x%08lx,%p)\n",
1881 This,pwcsName,reserved1,grfMode,reserved2,ppstm
1883 if (grfMode & STGM_TRANSACTED)
1884 FIXME("We do not support transacted Compound Storage. Using direct mode.\n");
1885 _create_istream16(ppstm);
1886 lpstr = MapSL((SEGPTR)*ppstm);
1887 if (This->str.hf) {
1888 DuplicateHandle( GetCurrentProcess(), This->str.hf, GetCurrentProcess(),
1889 &lpstr->str.hf, 0, TRUE, DUPLICATE_SAME_ACCESS );
1890 } else {
1891 lpstr->str.lockbytes = This->str.lockbytes;
1892 _ilockbytes16_addref(This->str.lockbytes);
1894 MultiByteToWideChar(CP_ACP, 0, pwcsName, -1, name, ARRAY_SIZE(name));
1895 newpps = STORAGE_look_for_named_pps(&lpstr->str,This->stde.pps_dir,name);
1896 if (newpps==-1) {
1897 IStream16_fnRelease(&lpstr->IStream16_iface);
1898 *ppstm = NULL;
1899 return E_FAIL;
1902 if (1!=STORAGE_get_pps_entry(&lpstr->str,newpps,&(lpstr->stde))) {
1903 IStream16_fnRelease(&lpstr->IStream16_iface);
1904 *ppstm = NULL;
1905 return E_FAIL;
1907 lpstr->offset.u.LowPart = 0;
1908 lpstr->offset.u.HighPart = 0;
1909 lpstr->ppsent = newpps;
1910 return S_OK;
1913 /******************************************************************************
1914 * _create_istorage16 [INTERNAL]
1916 static void _create_istorage16(LPSTORAGE16 *stg) {
1917 IStorage16Impl* lpst;
1919 if (!stvt16.QueryInterface) {
1920 HMODULE16 wp = GetModuleHandle16("STORAGE");
1921 if (wp>=32) {
1922 #define VTENT(xfn) stvt16.xfn = (void*)GetProcAddress16(wp,"IStorage16_"#xfn);
1923 VTENT(QueryInterface)
1924 VTENT(AddRef)
1925 VTENT(Release)
1926 VTENT(CreateStream)
1927 VTENT(OpenStream)
1928 VTENT(CreateStorage)
1929 VTENT(OpenStorage)
1930 VTENT(CopyTo)
1931 VTENT(MoveElementTo)
1932 VTENT(Commit)
1933 VTENT(Revert)
1934 VTENT(EnumElements)
1935 VTENT(DestroyElement)
1936 VTENT(RenameElement)
1937 VTENT(SetElementTimes)
1938 VTENT(SetClass)
1939 VTENT(SetStateBits)
1940 VTENT(Stat)
1941 #undef VTENT
1942 segstvt16 = (const IStorage16Vtbl*)MapLS( &stvt16 );
1943 } else {
1944 #define VTENT(xfn) stvt16.xfn = IStorage16_fn##xfn;
1945 VTENT(QueryInterface)
1946 VTENT(AddRef)
1947 VTENT(Release)
1948 VTENT(CreateStream)
1949 VTENT(OpenStream)
1950 VTENT(CreateStorage)
1951 VTENT(OpenStorage)
1952 VTENT(CopyTo)
1953 VTENT(Commit)
1954 /* not (yet) implemented ...
1955 VTENT(MoveElementTo)
1956 VTENT(Revert)
1957 VTENT(EnumElements)
1958 VTENT(DestroyElement)
1959 VTENT(RenameElement)
1960 VTENT(SetElementTimes)
1961 VTENT(SetClass)
1962 VTENT(SetStateBits)
1963 VTENT(Stat)
1965 #undef VTENT
1966 segstvt16 = &stvt16;
1969 lpst = HeapAlloc( GetProcessHeap(), 0, sizeof(*lpst) );
1970 lpst->IStorage16_iface.lpVtbl = segstvt16;
1971 lpst->str.hf = NULL;
1972 lpst->str.lockbytes = 0;
1973 lpst->ref = 1;
1974 lpst->thisptr = MapLS(lpst);
1975 *stg = (void*)lpst->thisptr;
1978 /******************************************************************************
1979 * Storage API functions
1982 /******************************************************************************
1983 * StgCreateDocFileA [STORAGE.1]
1985 HRESULT WINAPI StgCreateDocFile16(
1986 LPCOLESTR16 pwcsName,DWORD grfMode,DWORD reserved,IStorage16 **ppstgOpen
1988 HANDLE hf;
1989 int i,ret;
1990 IStorage16Impl* lpstg;
1991 struct storage_pps_entry stde;
1993 TRACE("(%s,0x%08lx,0x%08lx,%p)\n",
1994 pwcsName,grfMode,reserved,ppstgOpen
1996 _create_istorage16(ppstgOpen);
1997 hf = CreateFileA(pwcsName,GENERIC_READ|GENERIC_WRITE,0,NULL,CREATE_NEW,0,0);
1998 if (hf==INVALID_HANDLE_VALUE) {
1999 WARN("couldn't open file for storage:%ld\n",GetLastError());
2000 return E_FAIL;
2002 lpstg = MapSL((SEGPTR)*ppstgOpen);
2003 lpstg->str.hf = hf;
2004 lpstg->str.lockbytes = 0;
2005 /* FIXME: check for existence before overwriting? */
2006 if (!STORAGE_init_storage(&lpstg->str)) {
2007 CloseHandle(hf);
2008 return E_FAIL;
2010 i=0;ret=0;
2011 while (!ret) { /* neither 1 nor <0 */
2012 ret=STORAGE_get_pps_entry(&lpstg->str,i,&stde);
2013 if ((ret==1) && (stde.pps_type==5)) {
2014 lpstg->stde = stde;
2015 lpstg->ppsent = i;
2016 break;
2018 i++;
2020 if (ret!=1) {
2021 IStorage16_fnRelease(&lpstg->IStorage16_iface);
2022 return E_FAIL;
2025 return S_OK;
2028 /******************************************************************************
2029 * StgIsStorageFile [STORAGE.5]
2031 HRESULT WINAPI StgIsStorageFile16(LPCOLESTR16 fn) {
2032 UNICODE_STRING strW;
2033 HRESULT ret;
2035 RtlCreateUnicodeStringFromAsciiz(&strW, fn);
2036 ret = StgIsStorageFile( strW.Buffer );
2037 RtlFreeUnicodeString( &strW );
2039 return ret;
2042 /******************************************************************************
2043 * StgOpenStorage [STORAGE.3]
2045 HRESULT WINAPI StgOpenStorage16(
2046 LPCOLESTR16 pwcsName,IStorage16 *pstgPriority,DWORD grfMode,
2047 SNB16 snbExclude,DWORD reserved, IStorage16 **ppstgOpen
2049 HANDLE hf;
2050 int ret,i;
2051 IStorage16Impl* lpstg;
2052 struct storage_pps_entry stde;
2054 TRACE("(%s,%p,0x%08lx,%p,%ld,%p)\n",
2055 pwcsName,pstgPriority,grfMode,snbExclude,reserved,ppstgOpen
2057 _create_istorage16(ppstgOpen);
2058 hf = CreateFileA(pwcsName,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0);
2059 if (hf==INVALID_HANDLE_VALUE) {
2060 WARN("Couldn't open file for storage\n");
2061 return E_FAIL;
2063 lpstg = MapSL((SEGPTR)*ppstgOpen);
2064 lpstg->str.hf = hf;
2066 i=0;ret=0;
2067 while (!ret) { /* neither 1 nor <0 */
2068 ret=STORAGE_get_pps_entry(&lpstg->str,i,&stde);
2069 if ((ret==1) && (stde.pps_type==5)) {
2070 lpstg->stde=stde;
2071 break;
2073 i++;
2075 if (ret!=1) {
2076 IStorage16_fnRelease(&lpstg->IStorage16_iface);
2077 return E_FAIL;
2079 return S_OK;
2083 /******************************************************************************
2084 * StgIsStorageILockBytes [STORAGE.6]
2086 * Determines if the ILockBytes contains a storage object.
2088 HRESULT WINAPI StgIsStorageILockBytes16(SEGPTR plkbyt)
2090 DWORD args[6];
2091 HRESULT hres;
2092 HANDLE16 hsig;
2094 args[0] = (DWORD)plkbyt; /* iface */
2095 args[1] = args[2] = 0; /* ULARGE_INTEGER offset */
2096 args[3] = WOWGlobalAllocLock16( 0, 8, &hsig ); /* sig */
2097 args[4] = 8;
2098 args[5] = 0;
2100 if (!WOWCallback16Ex(
2101 (DWORD)((const ILockBytes16Vtbl*)MapSL(
2102 (SEGPTR)((LPLOCKBYTES16)MapSL(plkbyt))->lpVtbl)
2103 )->ReadAt,
2104 WCB16_PASCAL,
2105 6*sizeof(DWORD),
2106 (LPVOID)args,
2107 (LPDWORD)&hres
2108 )) {
2109 ERR("CallTo16 ILockBytes16::ReadAt() failed, hres %lx\n",hres);
2110 return hres;
2112 if (memcmp(MapSL(args[3]), STORAGE_magic, sizeof(STORAGE_magic)) == 0) {
2113 WOWGlobalUnlockFree16(args[3]);
2114 return S_OK;
2116 WOWGlobalUnlockFree16(args[3]);
2117 return S_FALSE;
2120 /******************************************************************************
2121 * StgOpenStorageOnILockBytes [STORAGE.4]
2123 * PARAMS
2124 * plkbyt FIXME: Should probably be an ILockBytes16 *.
2126 HRESULT WINAPI StgOpenStorageOnILockBytes16(
2127 SEGPTR plkbyt,
2128 IStorage16 *pstgPriority,
2129 DWORD grfMode,
2130 SNB16 snbExclude,
2131 DWORD reserved,
2132 IStorage16 **ppstgOpen)
2134 IStorage16Impl* lpstg;
2135 int i,ret;
2136 struct storage_pps_entry stde;
2138 FIXME("(%lx, %p, 0x%08lx, %d, %lx, %p)\n", plkbyt, pstgPriority, grfMode, (int)snbExclude, reserved, ppstgOpen);
2139 if ((plkbyt == 0) || (ppstgOpen == 0))
2140 return STG_E_INVALIDPOINTER;
2142 *ppstgOpen = 0;
2144 _create_istorage16(ppstgOpen);
2145 lpstg = MapSL((SEGPTR)*ppstgOpen);
2146 lpstg->str.hf = NULL;
2147 lpstg->str.lockbytes = plkbyt;
2148 i=0;ret=0;
2149 while (!ret) { /* neither 1 nor <0 */
2150 ret=STORAGE_get_pps_entry(&lpstg->str,i,&stde);
2151 if ((ret==1) && (stde.pps_type==5)) {
2152 lpstg->stde=stde;
2153 break;
2155 i++;
2157 if (ret!=1) {
2158 IStorage16_fnRelease(&lpstg->IStorage16_iface);
2159 return E_FAIL;
2161 return S_OK;