| /* Compound Storage | |
| * | |
| * Implemented using the documentation of the LAOLA project at | |
| * <URL:http://wwwwbs.cs.tu-berlin.de/~schwartz/pmh/index.html> | |
| * (Thanks to Martin Schwartz <schwartz@cs.tu-berlin.de>) | |
| * | |
| * Copyright 1998 Marcus Meissner | |
| * | |
| * This library is free software; you can redistribute it and/or | |
| * modify it under the terms of the GNU Lesser General Public | |
| * License as published by the Free Software Foundation; either | |
| * version 2.1 of the License, or (at your option) any later version. | |
| * | |
| * This library is distributed in the hope that it will be useful, | |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
| * Lesser General Public License for more details. | |
| * | |
| * You should have received a copy of the GNU Lesser General Public | |
| * License along with this library; if not, write to the Free Software | |
| * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
| */ | |
| #include "config.h" | |
| #include <assert.h> | |
| #include <time.h> | |
| #include <string.h> | |
| #include <sys/types.h> | |
| #ifdef HAVE_UNISTD_H | |
| # include <unistd.h> | |
| #endif | |
| #include "windef.h" | |
| #include "winternl.h" | |
| #include "winerror.h" | |
| #include "wine/winbase16.h" | |
| #include "wine/unicode.h" | |
| #include "wtypes.h" | |
| #include "wine/obj_base.h" | |
| #include "wine/obj_storage.h" | |
| #include "wine/debug.h" | |
| WINE_DEFAULT_DEBUG_CHANNEL(ole); | |
| WINE_DECLARE_DEBUG_CHANNEL(relay); | |
| struct storage_header { | |
| BYTE magic[8]; /* 00: magic */ | |
| BYTE unknown1[36]; /* 08: unknown */ | |
| DWORD num_of_bbd_blocks;/* 2C: length of big datablocks */ | |
| DWORD root_startblock;/* 30: root storage first big block */ | |
| DWORD unknown2[2]; /* 34: unknown */ | |
| DWORD sbd_startblock; /* 3C: small block depot first big block */ | |
| DWORD unknown3[3]; /* 40: unknown */ | |
| DWORD bbd_list[109]; /* 4C: big data block list (up to end of sector)*/ | |
| }; | |
| struct storage_pps_entry { | |
| WCHAR pps_rawname[32];/* 00: \0 terminated widechar name */ | |
| WORD pps_sizeofname; /* 40: namelength in bytes */ | |
| BYTE pps_type; /* 42: flags, 1 storage/dir, 2 stream, 5 root */ | |
| BYTE pps_unknown0; /* 43: unknown */ | |
| DWORD pps_prev; /* 44: previous pps */ | |
| DWORD pps_next; /* 48: next pps */ | |
| DWORD pps_dir; /* 4C: directory pps */ | |
| GUID pps_guid; /* 50: class ID */ | |
| DWORD pps_unknown1; /* 60: unknown */ | |
| FILETIME pps_ft1; /* 64: filetime1 */ | |
| FILETIME pps_ft2; /* 70: filetime2 */ | |
| DWORD pps_sb; /* 74: data startblock */ | |
| DWORD pps_size; /* 78: datalength. (<0x1000)?small:big blocks*/ | |
| DWORD pps_unknown2; /* 7C: unknown */ | |
| }; | |
| #define STORAGE_CHAINENTRY_FAT 0xfffffffd | |
| #define STORAGE_CHAINENTRY_ENDOFCHAIN 0xfffffffe | |
| #define STORAGE_CHAINENTRY_FREE 0xffffffff | |
| static const BYTE STORAGE_magic[8] ={0xd0,0xcf,0x11,0xe0,0xa1,0xb1,0x1a,0xe1}; | |
| static const BYTE STORAGE_notmagic[8]={0x0e,0x11,0xfc,0x0d,0xd0,0xcf,0x11,0xe0}; | |
| static const BYTE STORAGE_oldmagic[8]={0xd0,0xcf,0x11,0xe0,0x0e,0x11,0xfc,0x0d}; | |
| #define BIGSIZE 512 | |
| #define SMALLSIZE 64 | |
| #define SMALLBLOCKS_PER_BIGBLOCK (BIGSIZE/SMALLSIZE) | |
| #define READ_HEADER assert(STORAGE_get_big_block(hf,-1,(LPBYTE)&sth));assert(!memcmp(STORAGE_magic,sth.magic,sizeof(STORAGE_magic))); | |
| static ICOM_VTABLE(IStorage16) stvt16; | |
| static ICOM_VTABLE(IStorage16) *segstvt16 = NULL; | |
| static ICOM_VTABLE(IStream16) strvt16; | |
| static ICOM_VTABLE(IStream16) *segstrvt16 = NULL; | |
| /*ULONG WINAPI IStorage16_AddRef(LPSTORAGE16 this);*/ | |
| static void _create_istorage16(LPSTORAGE16 *stg); | |
| static void _create_istream16(LPSTREAM16 *str); | |
| #define IMPLEMENTED 1 | |
| /****************************************************************************** | |
| * STORAGE_get_big_block [Internal] | |
| * | |
| * Reading OLE compound storage | |
| */ | |
| static BOOL | |
| STORAGE_get_big_block(HFILE hf,int n,BYTE *block) { | |
| assert(n>=-1); | |
| if (-1==_llseek(hf,(n+1)*BIGSIZE,SEEK_SET)) { | |
| WARN(" seek failed (%ld)\n",GetLastError()); | |
| return FALSE; | |
| } | |
| assert((n+1)*BIGSIZE==_llseek(hf,0,SEEK_CUR)); | |
| if (BIGSIZE!=_lread(hf,block,BIGSIZE)) { | |
| WARN("(block size %d): read didn't read (%ld)\n",n,GetLastError()); | |
| assert(0); | |
| return FALSE; | |
| } | |
| return TRUE; | |
| } | |
| /****************************************************************************** | |
| * STORAGE_put_big_block [INTERNAL] | |
| */ | |
| static BOOL | |
| STORAGE_put_big_block(HFILE hf,int n,BYTE *block) { | |
| assert(n>=-1); | |
| if (-1==_llseek(hf,(n+1)*BIGSIZE,SEEK_SET)) { | |
| WARN(" seek failed (%ld)\n",GetLastError()); | |
| return FALSE; | |
| } | |
| assert((n+1)*BIGSIZE==_llseek(hf,0,SEEK_CUR)); | |
| if (BIGSIZE!=_lwrite(hf,block,BIGSIZE)) { | |
| WARN(" write failed (%ld)\n",GetLastError()); | |
| return FALSE; | |
| } | |
| return TRUE; | |
| } | |
| /****************************************************************************** | |
| * STORAGE_get_next_big_blocknr [INTERNAL] | |
| */ | |
| static int | |
| STORAGE_get_next_big_blocknr(HFILE hf,int blocknr) { | |
| INT bbs[BIGSIZE/sizeof(INT)]; | |
| struct storage_header sth; | |
| READ_HEADER; | |
| assert(blocknr>>7<sth.num_of_bbd_blocks); | |
| if (sth.bbd_list[blocknr>>7]==0xffffffff) | |
| return -5; | |
| if (!STORAGE_get_big_block(hf,sth.bbd_list[blocknr>>7],(LPBYTE)bbs)) | |
| return -5; | |
| assert(bbs[blocknr&0x7f]!=STORAGE_CHAINENTRY_FREE); | |
| return bbs[blocknr&0x7f]; | |
| } | |
| /****************************************************************************** | |
| * STORAGE_get_nth_next_big_blocknr [INTERNAL] | |
| */ | |
| static int | |
| STORAGE_get_nth_next_big_blocknr(HFILE hf,int blocknr,int nr) { | |
| INT bbs[BIGSIZE/sizeof(INT)]; | |
| int lastblock = -1; | |
| struct storage_header sth; | |
| READ_HEADER; | |
| assert(blocknr>=0); | |
| while (nr--) { | |
| assert((blocknr>>7)<sth.num_of_bbd_blocks); | |
| assert(sth.bbd_list[blocknr>>7]!=0xffffffff); | |
| /* simple caching... */ | |
| if (lastblock!=sth.bbd_list[blocknr>>7]) { | |
| assert(STORAGE_get_big_block(hf,sth.bbd_list[blocknr>>7],(LPBYTE)bbs)); | |
| lastblock = sth.bbd_list[blocknr>>7]; | |
| } | |
| blocknr = bbs[blocknr&0x7f]; | |
| } | |
| return blocknr; | |
| } | |
| /****************************************************************************** | |
| * STORAGE_get_root_pps_entry [Internal] | |
| */ | |
| static BOOL | |
| STORAGE_get_root_pps_entry(HFILE hf,struct storage_pps_entry *pstde) { | |
| int blocknr,i; | |
| BYTE block[BIGSIZE]; | |
| struct storage_pps_entry *stde=(struct storage_pps_entry*)block; | |
| struct storage_header sth; | |
| READ_HEADER; | |
| blocknr = sth.root_startblock; | |
| while (blocknr>=0) { | |
| assert(STORAGE_get_big_block(hf,blocknr,block)); | |
| for (i=0;i<4;i++) { | |
| if (!stde[i].pps_sizeofname) | |
| continue; | |
| if (stde[i].pps_type==5) { | |
| *pstde=stde[i]; | |
| return TRUE; | |
| } | |
| } | |
| blocknr=STORAGE_get_next_big_blocknr(hf,blocknr); | |
| } | |
| return FALSE; | |
| } | |
| /****************************************************************************** | |
| * STORAGE_get_small_block [INTERNAL] | |
| */ | |
| static BOOL | |
| STORAGE_get_small_block(HFILE hf,int blocknr,BYTE *sblock) { | |
| BYTE block[BIGSIZE]; | |
| int bigblocknr; | |
| struct storage_pps_entry root; | |
| assert(blocknr>=0); | |
| assert(STORAGE_get_root_pps_entry(hf,&root)); | |
| bigblocknr = STORAGE_get_nth_next_big_blocknr(hf,root.pps_sb,blocknr/SMALLBLOCKS_PER_BIGBLOCK); | |
| assert(bigblocknr>=0); | |
| assert(STORAGE_get_big_block(hf,bigblocknr,block)); | |
| memcpy(sblock,((LPBYTE)block)+SMALLSIZE*(blocknr&(SMALLBLOCKS_PER_BIGBLOCK-1)),SMALLSIZE); | |
| return TRUE; | |
| } | |
| /****************************************************************************** | |
| * STORAGE_put_small_block [INTERNAL] | |
| */ | |
| static BOOL | |
| STORAGE_put_small_block(HFILE hf,int blocknr,BYTE *sblock) { | |
| BYTE block[BIGSIZE]; | |
| int bigblocknr; | |
| struct storage_pps_entry root; | |
| assert(blocknr>=0); | |
| assert(STORAGE_get_root_pps_entry(hf,&root)); | |
| bigblocknr = STORAGE_get_nth_next_big_blocknr(hf,root.pps_sb,blocknr/SMALLBLOCKS_PER_BIGBLOCK); | |
| assert(bigblocknr>=0); | |
| assert(STORAGE_get_big_block(hf,bigblocknr,block)); | |
| memcpy(((LPBYTE)block)+SMALLSIZE*(blocknr&(SMALLBLOCKS_PER_BIGBLOCK-1)),sblock,SMALLSIZE); | |
| assert(STORAGE_put_big_block(hf,bigblocknr,block)); | |
| return TRUE; | |
| } | |
| /****************************************************************************** | |
| * STORAGE_get_next_small_blocknr [INTERNAL] | |
| */ | |
| static int | |
| STORAGE_get_next_small_blocknr(HFILE hf,int blocknr) { | |
| BYTE block[BIGSIZE]; | |
| LPINT sbd = (LPINT)block; | |
| int bigblocknr; | |
| struct storage_header sth; | |
| READ_HEADER; | |
| assert(blocknr>=0); | |
| bigblocknr = STORAGE_get_nth_next_big_blocknr(hf,sth.sbd_startblock,blocknr/128); | |
| assert(bigblocknr>=0); | |
| assert(STORAGE_get_big_block(hf,bigblocknr,block)); | |
| assert(sbd[blocknr & 127]!=STORAGE_CHAINENTRY_FREE); | |
| return sbd[blocknr & (128-1)]; | |
| } | |
| /****************************************************************************** | |
| * STORAGE_get_nth_next_small_blocknr [INTERNAL] | |
| */ | |
| static int | |
| STORAGE_get_nth_next_small_blocknr(HFILE hf,int blocknr,int nr) { | |
| int lastblocknr; | |
| BYTE block[BIGSIZE]; | |
| LPINT sbd = (LPINT)block; | |
| struct storage_header sth; | |
| READ_HEADER; | |
| lastblocknr=-1; | |
| assert(blocknr>=0); | |
| while ((nr--) && (blocknr>=0)) { | |
| if (lastblocknr/128!=blocknr/128) { | |
| int bigblocknr; | |
| bigblocknr = STORAGE_get_nth_next_big_blocknr(hf,sth.sbd_startblock,blocknr/128); | |
| assert(bigblocknr>=0); | |
| assert(STORAGE_get_big_block(hf,bigblocknr,block)); | |
| lastblocknr = blocknr; | |
| } | |
| assert(lastblocknr>=0); | |
| lastblocknr=blocknr; | |
| blocknr=sbd[blocknr & (128-1)]; | |
| assert(blocknr!=STORAGE_CHAINENTRY_FREE); | |
| } | |
| return blocknr; | |
| } | |
| /****************************************************************************** | |
| * STORAGE_get_pps_entry [INTERNAL] | |
| */ | |
| static int | |
| STORAGE_get_pps_entry(HFILE hf,int n,struct storage_pps_entry *pstde) { | |
| int blocknr; | |
| BYTE block[BIGSIZE]; | |
| struct storage_pps_entry *stde = (struct storage_pps_entry*)(((LPBYTE)block)+128*(n&3)); | |
| struct storage_header sth; | |
| READ_HEADER; | |
| /* we have 4 pps entries per big block */ | |
| blocknr = STORAGE_get_nth_next_big_blocknr(hf,sth.root_startblock,n/4); | |
| assert(blocknr>=0); | |
| assert(STORAGE_get_big_block(hf,blocknr,block)); | |
| *pstde=*stde; | |
| return 1; | |
| } | |
| /****************************************************************************** | |
| * STORAGE_put_pps_entry [Internal] | |
| */ | |
| static int | |
| STORAGE_put_pps_entry(HFILE hf,int n,struct storage_pps_entry *pstde) { | |
| int blocknr; | |
| BYTE block[BIGSIZE]; | |
| struct storage_pps_entry *stde = (struct storage_pps_entry*)(((LPBYTE)block)+128*(n&3)); | |
| struct storage_header sth; | |
| READ_HEADER; | |
| /* we have 4 pps entries per big block */ | |
| blocknr = STORAGE_get_nth_next_big_blocknr(hf,sth.root_startblock,n/4); | |
| assert(blocknr>=0); | |
| assert(STORAGE_get_big_block(hf,blocknr,block)); | |
| *stde=*pstde; | |
| assert(STORAGE_put_big_block(hf,blocknr,block)); | |
| return 1; | |
| } | |
| /****************************************************************************** | |
| * STORAGE_look_for_named_pps [Internal] | |
| */ | |
| static int | |
| STORAGE_look_for_named_pps(HFILE hf,int n,LPOLESTR name) { | |
| struct storage_pps_entry stde; | |
| int ret; | |
| if (n==-1) | |
| return -1; | |
| if (1!=STORAGE_get_pps_entry(hf,n,&stde)) | |
| return -1; | |
| if (!lstrcmpW(name,stde.pps_rawname)) | |
| return n; | |
| if (stde.pps_prev != -1) { | |
| ret=STORAGE_look_for_named_pps(hf,stde.pps_prev,name); | |
| if (ret!=-1) | |
| return ret; | |
| } | |
| if (stde.pps_next != -1) { | |
| ret=STORAGE_look_for_named_pps(hf,stde.pps_next,name); | |
| if (ret!=-1) | |
| return ret; | |
| } | |
| return -1; | |
| } | |
| /****************************************************************************** | |
| * STORAGE_dump_pps_entry [Internal] | |
| * | |
| * FIXME | |
| * Function is unused | |
| */ | |
| void | |
| STORAGE_dump_pps_entry(struct storage_pps_entry *stde) { | |
| char name[33]; | |
| WideCharToMultiByte( CP_ACP, 0, stde->pps_rawname, -1, name, sizeof(name), NULL, NULL); | |
| if (!stde->pps_sizeofname) | |
| return; | |
| DPRINTF("name: %s\n",name); | |
| DPRINTF("type: %d\n",stde->pps_type); | |
| DPRINTF("prev pps: %ld\n",stde->pps_prev); | |
| DPRINTF("next pps: %ld\n",stde->pps_next); | |
| DPRINTF("dir pps: %ld\n",stde->pps_dir); | |
| DPRINTF("guid: %s\n",debugstr_guid(&(stde->pps_guid))); | |
| if (stde->pps_type !=2) { | |
| time_t t; | |
| DWORD dw; | |
| RtlTimeToSecondsSince1970((LARGE_INTEGER *)&(stde->pps_ft1),&dw); | |
| t = dw; | |
| DPRINTF("ts1: %s\n",ctime(&t)); | |
| RtlTimeToSecondsSince1970((LARGE_INTEGER *)&(stde->pps_ft2),&dw); | |
| t = dw; | |
| DPRINTF("ts2: %s\n",ctime(&t)); | |
| } | |
| DPRINTF("startblock: %ld\n",stde->pps_sb); | |
| DPRINTF("size: %ld\n",stde->pps_size); | |
| } | |
| /****************************************************************************** | |
| * STORAGE_init_storage [INTERNAL] | |
| */ | |
| static BOOL | |
| STORAGE_init_storage(HFILE hf) { | |
| BYTE block[BIGSIZE]; | |
| LPDWORD bbs; | |
| struct storage_header *sth; | |
| struct storage_pps_entry *stde; | |
| assert(-1!=_llseek(hf,0,SEEK_SET)); | |
| /* block -1 is the storage header */ | |
| sth = (struct storage_header*)block; | |
| memcpy(sth->magic,STORAGE_magic,8); | |
| memset(sth->unknown1,0,sizeof(sth->unknown1)); | |
| memset(sth->unknown2,0,sizeof(sth->unknown2)); | |
| memset(sth->unknown3,0,sizeof(sth->unknown3)); | |
| sth->num_of_bbd_blocks = 1; | |
| sth->root_startblock = 1; | |
| sth->sbd_startblock = 0xffffffff; | |
| memset(sth->bbd_list,0xff,sizeof(sth->bbd_list)); | |
| sth->bbd_list[0] = 0; | |
| assert(BIGSIZE==_lwrite(hf,block,BIGSIZE)); | |
| /* block 0 is the big block directory */ | |
| bbs=(LPDWORD)block; | |
| memset(block,0xff,sizeof(block)); /* mark all blocks as free */ | |
| bbs[0]=STORAGE_CHAINENTRY_ENDOFCHAIN; /* for this block */ | |
| bbs[1]=STORAGE_CHAINENTRY_ENDOFCHAIN; /* for directory entry */ | |
| assert(BIGSIZE==_lwrite(hf,block,BIGSIZE)); | |
| /* block 1 is the root directory entry */ | |
| memset(block,0x00,sizeof(block)); | |
| stde = (struct storage_pps_entry*)block; | |
| MultiByteToWideChar( CP_ACP, 0, "RootEntry", -1, stde->pps_rawname, | |
| sizeof(stde->pps_rawname)/sizeof(WCHAR)); | |
| stde->pps_sizeofname = (strlenW(stde->pps_rawname)+1) * sizeof(WCHAR); | |
| stde->pps_type = 5; | |
| stde->pps_dir = -1; | |
| stde->pps_next = -1; | |
| stde->pps_prev = -1; | |
| stde->pps_sb = 0xffffffff; | |
| stde->pps_size = 0; | |
| assert(BIGSIZE==_lwrite(hf,block,BIGSIZE)); | |
| return TRUE; | |
| } | |
| /****************************************************************************** | |
| * STORAGE_set_big_chain [Internal] | |
| */ | |
| static BOOL | |
| STORAGE_set_big_chain(HFILE hf,int blocknr,INT type) { | |
| BYTE block[BIGSIZE]; | |
| LPINT bbd = (LPINT)block; | |
| int nextblocknr,bigblocknr; | |
| struct storage_header sth; | |
| READ_HEADER; | |
| assert(blocknr!=type); | |
| while (blocknr>=0) { | |
| bigblocknr = sth.bbd_list[blocknr/128]; | |
| assert(bigblocknr>=0); | |
| assert(STORAGE_get_big_block(hf,bigblocknr,block)); | |
| nextblocknr = bbd[blocknr&(128-1)]; | |
| bbd[blocknr&(128-1)] = type; | |
| if (type>=0) | |
| return TRUE; | |
| assert(STORAGE_put_big_block(hf,bigblocknr,block)); | |
| type = STORAGE_CHAINENTRY_FREE; | |
| blocknr = nextblocknr; | |
| } | |
| return TRUE; | |
| } | |
| /****************************************************************************** | |
| * STORAGE_set_small_chain [Internal] | |
| */ | |
| static BOOL | |
| STORAGE_set_small_chain(HFILE hf,int blocknr,INT type) { | |
| BYTE block[BIGSIZE]; | |
| LPINT sbd = (LPINT)block; | |
| int lastblocknr,nextsmallblocknr,bigblocknr; | |
| struct storage_header sth; | |
| READ_HEADER; | |
| assert(blocknr!=type); | |
| lastblocknr=-129;bigblocknr=-2; | |
| while (blocknr>=0) { | |
| /* cache block ... */ | |
| if (lastblocknr/128!=blocknr/128) { | |
| bigblocknr = STORAGE_get_nth_next_big_blocknr(hf,sth.sbd_startblock,blocknr/128); | |
| assert(bigblocknr>=0); | |
| assert(STORAGE_get_big_block(hf,bigblocknr,block)); | |
| } | |
| lastblocknr = blocknr; | |
| nextsmallblocknr = sbd[blocknr&(128-1)]; | |
| sbd[blocknr&(128-1)] = type; | |
| assert(STORAGE_put_big_block(hf,bigblocknr,block)); | |
| if (type>=0) | |
| return TRUE; | |
| type = STORAGE_CHAINENTRY_FREE; | |
| blocknr = nextsmallblocknr; | |
| } | |
| return TRUE; | |
| } | |
| /****************************************************************************** | |
| * STORAGE_get_free_big_blocknr [Internal] | |
| */ | |
| static int | |
| STORAGE_get_free_big_blocknr(HFILE hf) { | |
| BYTE block[BIGSIZE]; | |
| LPINT sbd = (LPINT)block; | |
| int lastbigblocknr,i,curblock,bigblocknr; | |
| struct storage_header sth; | |
| READ_HEADER; | |
| curblock = 0; | |
| lastbigblocknr = -1; | |
| bigblocknr = sth.bbd_list[curblock]; | |
| while (curblock<sth.num_of_bbd_blocks) { | |
| assert(bigblocknr>=0); | |
| assert(STORAGE_get_big_block(hf,bigblocknr,block)); | |
| for (i=0;i<128;i++) | |
| if (sbd[i]==STORAGE_CHAINENTRY_FREE) { | |
| sbd[i] = STORAGE_CHAINENTRY_ENDOFCHAIN; | |
| assert(STORAGE_put_big_block(hf,bigblocknr,block)); | |
| memset(block,0x42,sizeof(block)); | |
| assert(STORAGE_put_big_block(hf,i+curblock*128,block)); | |
| return i+curblock*128; | |
| } | |
| lastbigblocknr = bigblocknr; | |
| bigblocknr = sth.bbd_list[++curblock]; | |
| } | |
| bigblocknr = curblock*128; | |
| /* since we have marked all blocks from 0 up to curblock*128-1 | |
| * the next free one is curblock*128, where we happily put our | |
| * next large block depot. | |
| */ | |
| memset(block,0xff,sizeof(block)); | |
| /* mark the block allocated and returned by this function */ | |
| sbd[1] = STORAGE_CHAINENTRY_ENDOFCHAIN; | |
| assert(STORAGE_put_big_block(hf,bigblocknr,block)); | |
| /* if we had a bbd block already (mostlikely) we need | |
| * to link the new one into the chain | |
| */ | |
| if (lastbigblocknr!=-1) | |
| assert(STORAGE_set_big_chain(hf,lastbigblocknr,bigblocknr)); | |
| sth.bbd_list[curblock]=bigblocknr; | |
| sth.num_of_bbd_blocks++; | |
| assert(sth.num_of_bbd_blocks==curblock+1); | |
| assert(STORAGE_put_big_block(hf,-1,(LPBYTE)&sth)); | |
| /* Set the end of the chain for the bigblockdepots */ | |
| assert(STORAGE_set_big_chain(hf,bigblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN)); | |
| /* add 1, for the first entry is used for the additional big block | |
| * depot. (means we already used bigblocknr) */ | |
| memset(block,0x42,sizeof(block)); | |
| /* allocate this block (filled with 0x42) */ | |
| assert(STORAGE_put_big_block(hf,bigblocknr+1,block)); | |
| return bigblocknr+1; | |
| } | |
| /****************************************************************************** | |
| * STORAGE_get_free_small_blocknr [Internal] | |
| */ | |
| static int | |
| STORAGE_get_free_small_blocknr(HFILE hf) { | |
| BYTE block[BIGSIZE]; | |
| LPINT sbd = (LPINT)block; | |
| int lastbigblocknr,newblocknr,i,curblock,bigblocknr; | |
| struct storage_pps_entry root; | |
| struct storage_header sth; | |
| READ_HEADER; | |
| bigblocknr = sth.sbd_startblock; | |
| curblock = 0; | |
| lastbigblocknr = -1; | |
| newblocknr = -1; | |
| while (bigblocknr>=0) { | |
| if (!STORAGE_get_big_block(hf,bigblocknr,block)) | |
| return -1; | |
| for (i=0;i<128;i++) | |
| if (sbd[i]==STORAGE_CHAINENTRY_FREE) { | |
| sbd[i]=STORAGE_CHAINENTRY_ENDOFCHAIN; | |
| newblocknr = i+curblock*128; | |
| break; | |
| } | |
| if (i!=128) | |
| break; | |
| lastbigblocknr = bigblocknr; | |
| bigblocknr = STORAGE_get_next_big_blocknr(hf,bigblocknr); | |
| curblock++; | |
| } | |
| if (newblocknr==-1) { | |
| bigblocknr = STORAGE_get_free_big_blocknr(hf); | |
| if (bigblocknr<0) | |
| return -1; | |
| READ_HEADER; | |
| memset(block,0xff,sizeof(block)); | |
| sbd[0]=STORAGE_CHAINENTRY_ENDOFCHAIN; | |
| if (!STORAGE_put_big_block(hf,bigblocknr,block)) | |
| return -1; | |
| if (lastbigblocknr==-1) { | |
| sth.sbd_startblock = bigblocknr; | |
| if (!STORAGE_put_big_block(hf,-1,(LPBYTE)&sth)) /* need to write it */ | |
| return -1; | |
| } else { | |
| if (!STORAGE_set_big_chain(hf,lastbigblocknr,bigblocknr)) | |
| return -1; | |
| } | |
| if (!STORAGE_set_big_chain(hf,bigblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN)) | |
| return -1; | |
| newblocknr = curblock*128; | |
| } | |
| /* allocate enough big blocks for storing the allocated small block */ | |
| if (!STORAGE_get_root_pps_entry(hf,&root)) | |
| return -1; | |
| if (root.pps_sb==-1) | |
| lastbigblocknr = -1; | |
| else | |
| lastbigblocknr = STORAGE_get_nth_next_big_blocknr(hf,root.pps_sb,(root.pps_size-1)/BIGSIZE); | |
| while (root.pps_size < (newblocknr*SMALLSIZE+SMALLSIZE-1)) { | |
| /* we need to allocate more stuff */ | |
| bigblocknr = STORAGE_get_free_big_blocknr(hf); | |
| if (bigblocknr<0) | |
| return -1; | |
| READ_HEADER; | |
| if (root.pps_sb==-1) { | |
| root.pps_sb = bigblocknr; | |
| root.pps_size += BIGSIZE; | |
| } else { | |
| if (!STORAGE_set_big_chain(hf,lastbigblocknr,bigblocknr)) | |
| return -1; | |
| root.pps_size += BIGSIZE; | |
| } | |
| lastbigblocknr = bigblocknr; | |
| } | |
| if (!STORAGE_set_big_chain(hf,lastbigblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN)) | |
| return -1; | |
| if (!STORAGE_put_pps_entry(hf,0,&root)) | |
| return -1; | |
| return newblocknr; | |
| } | |
| /****************************************************************************** | |
| * STORAGE_get_free_pps_entry [Internal] | |
| */ | |
| static int | |
| STORAGE_get_free_pps_entry(HFILE hf) { | |
| int blocknr,i,curblock,lastblocknr; | |
| BYTE block[BIGSIZE]; | |
| struct storage_pps_entry *stde = (struct storage_pps_entry*)block; | |
| struct storage_header sth; | |
| READ_HEADER; | |
| blocknr = sth.root_startblock; | |
| assert(blocknr>=0); | |
| curblock=0; | |
| while (blocknr>=0) { | |
| if (!STORAGE_get_big_block(hf,blocknr,block)) | |
| return -1; | |
| for (i=0;i<4;i++) | |
| if (stde[i].pps_sizeofname==0) /* free */ | |
| return curblock*4+i; | |
| lastblocknr = blocknr; | |
| blocknr = STORAGE_get_next_big_blocknr(hf,blocknr); | |
| curblock++; | |
| } | |
| assert(blocknr==STORAGE_CHAINENTRY_ENDOFCHAIN); | |
| blocknr = STORAGE_get_free_big_blocknr(hf); | |
| /* sth invalidated */ | |
| if (blocknr<0) | |
| return -1; | |
| if (!STORAGE_set_big_chain(hf,lastblocknr,blocknr)) | |
| return -1; | |
| if (!STORAGE_set_big_chain(hf,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN)) | |
| return -1; | |
| memset(block,0,sizeof(block)); | |
| STORAGE_put_big_block(hf,blocknr,block); | |
| return curblock*4; | |
| } | |
| /* --- IStream16 implementation */ | |
| typedef struct | |
| { | |
| /* IUnknown fields */ | |
| ICOM_VFIELD(IStream16); | |
| DWORD ref; | |
| /* IStream16 fields */ | |
| SEGPTR thisptr; /* pointer to this struct as segmented */ | |
| struct storage_pps_entry stde; | |
| int ppsent; | |
| HFILE hf; | |
| ULARGE_INTEGER offset; | |
| } IStream16Impl; | |
| /****************************************************************************** | |
| * IStream16_QueryInterface [STORAGE.518] | |
| */ | |
| HRESULT WINAPI IStream16_fnQueryInterface( | |
| IStream16* iface,REFIID refiid,LPVOID *obj | |
| ) { | |
| ICOM_THIS(IStream16Impl,iface); | |
| TRACE_(relay)("(%p)->(%s,%p)\n",This,debugstr_guid(refiid),obj); | |
| if (!memcmp(&IID_IUnknown,refiid,sizeof(IID_IUnknown))) { | |
| *obj = This; | |
| return 0; | |
| } | |
| return OLE_E_ENUM_NOMORE; | |
| } | |
| /****************************************************************************** | |
| * IStream16_AddRef [STORAGE.519] | |
| */ | |
| ULONG WINAPI IStream16_fnAddRef(IStream16* iface) { | |
| ICOM_THIS(IStream16Impl,iface); | |
| return ++(This->ref); | |
| } | |
| /****************************************************************************** | |
| * IStream16_Release [STORAGE.520] | |
| */ | |
| ULONG WINAPI IStream16_fnRelease(IStream16* iface) { | |
| ICOM_THIS(IStream16Impl,iface); | |
| FlushFileBuffers(This->hf); | |
| This->ref--; | |
| if (!This->ref) { | |
| CloseHandle(This->hf); | |
| UnMapLS( This->thisptr ); | |
| HeapFree( GetProcessHeap(), 0, This ); | |
| return 0; | |
| } | |
| return This->ref; | |
| } | |
| /****************************************************************************** | |
| * IStream16_Seek [STORAGE.523] | |
| * | |
| * FIXME | |
| * Does not handle 64 bits | |
| */ | |
| HRESULT WINAPI IStream16_fnSeek( | |
| IStream16* iface,LARGE_INTEGER offset,DWORD whence,ULARGE_INTEGER *newpos | |
| ) { | |
| ICOM_THIS(IStream16Impl,iface); | |
| TRACE_(relay)("(%p)->([%ld.%ld],%ld,%p)\n",This,offset.s.HighPart,offset.s.LowPart,whence,newpos); | |
| switch (whence) { | |
| /* unix SEEK_xx should be the same as win95 ones */ | |
| case SEEK_SET: | |
| /* offset must be ==0 (<0 is invalid, and >0 cannot be handled | |
| * right now. | |
| */ | |
| assert(offset.s.HighPart==0); | |
| This->offset.s.HighPart = offset.s.HighPart; | |
| This->offset.s.LowPart = offset.s.LowPart; | |
| break; | |
| case SEEK_CUR: | |
| if (offset.s.HighPart < 0) { | |
| /* FIXME: is this negation correct ? */ | |
| offset.s.HighPart = -offset.s.HighPart; | |
| offset.s.LowPart = (0xffffffff ^ offset.s.LowPart)+1; | |
| assert(offset.s.HighPart==0); | |
| assert(This->offset.s.LowPart >= offset.s.LowPart); | |
| This->offset.s.LowPart -= offset.s.LowPart; | |
| } else { | |
| assert(offset.s.HighPart==0); | |
| This->offset.s.LowPart+= offset.s.LowPart; | |
| } | |
| break; | |
| case SEEK_END: | |
| assert(offset.s.HighPart==0); | |
| This->offset.s.LowPart = This->stde.pps_size-offset.s.LowPart; | |
| break; | |
| } | |
| if (This->offset.s.LowPart>This->stde.pps_size) | |
| This->offset.s.LowPart=This->stde.pps_size; | |
| if (newpos) *newpos = This->offset; | |
| return S_OK; | |
| } | |
| /****************************************************************************** | |
| * IStream16_Read [STORAGE.521] | |
| */ | |
| HRESULT WINAPI IStream16_fnRead( | |
| IStream16* iface,void *pv,ULONG cb,ULONG *pcbRead | |
| ) { | |
| ICOM_THIS(IStream16Impl,iface); | |
| BYTE block[BIGSIZE]; | |
| ULONG *bytesread=pcbRead,xxread; | |
| int blocknr; | |
| TRACE_(relay)("(%p)->(%p,%ld,%p)\n",This,pv,cb,pcbRead); | |
| if (!pcbRead) bytesread=&xxread; | |
| *bytesread = 0; | |
| if (cb>This->stde.pps_size-This->offset.s.LowPart) | |
| cb=This->stde.pps_size-This->offset.s.LowPart; | |
| if (This->stde.pps_size < 0x1000) { | |
| /* use small block reader */ | |
| blocknr = STORAGE_get_nth_next_small_blocknr(This->hf,This->stde.pps_sb,This->offset.s.LowPart/SMALLSIZE); | |
| while (cb) { | |
| int cc; | |
| if (!STORAGE_get_small_block(This->hf,blocknr,block)) { | |
| WARN("small block read failed!!!\n"); | |
| return E_FAIL; | |
| } | |
| cc = cb; | |
| if (cc>SMALLSIZE-(This->offset.s.LowPart&(SMALLSIZE-1))) | |
| cc=SMALLSIZE-(This->offset.s.LowPart&(SMALLSIZE-1)); | |
| memcpy((LPBYTE)pv,block+(This->offset.s.LowPart&(SMALLSIZE-1)),cc); | |
| This->offset.s.LowPart+=cc; | |
| (LPBYTE)pv+=cc; | |
| *bytesread+=cc; | |
| cb-=cc; | |
| blocknr = STORAGE_get_next_small_blocknr(This->hf,blocknr); | |
| } | |
| } else { | |
| /* use big block reader */ | |
| blocknr = STORAGE_get_nth_next_big_blocknr(This->hf,This->stde.pps_sb,This->offset.s.LowPart/BIGSIZE); | |
| while (cb) { | |
| int cc; | |
| if (!STORAGE_get_big_block(This->hf,blocknr,block)) { | |
| WARN("big block read failed!!!\n"); | |
| return E_FAIL; | |
| } | |
| cc = cb; | |
| if (cc>BIGSIZE-(This->offset.s.LowPart&(BIGSIZE-1))) | |
| cc=BIGSIZE-(This->offset.s.LowPart&(BIGSIZE-1)); | |
| memcpy((LPBYTE)pv,block+(This->offset.s.LowPart&(BIGSIZE-1)),cc); | |
| This->offset.s.LowPart+=cc; | |
| (LPBYTE)pv+=cc; | |
| *bytesread+=cc; | |
| cb-=cc; | |
| blocknr=STORAGE_get_next_big_blocknr(This->hf,blocknr); | |
| } | |
| } | |
| return S_OK; | |
| } | |
| /****************************************************************************** | |
| * IStream16_Write [STORAGE.522] | |
| */ | |
| HRESULT WINAPI IStream16_fnWrite( | |
| IStream16* iface,const void *pv,ULONG cb,ULONG *pcbWrite | |
| ) { | |
| ICOM_THIS(IStream16Impl,iface); | |
| BYTE block[BIGSIZE]; | |
| ULONG *byteswritten=pcbWrite,xxwritten; | |
| int oldsize,newsize,i,curoffset=0,lastblocknr,blocknr,cc; | |
| HFILE hf = This->hf; | |
| if (!pcbWrite) byteswritten=&xxwritten; | |
| *byteswritten = 0; | |
| TRACE_(relay)("(%p)->(%p,%ld,%p)\n",This,pv,cb,pcbWrite); | |
| /* do we need to junk some blocks? */ | |
| newsize = This->offset.s.LowPart+cb; | |
| oldsize = This->stde.pps_size; | |
| if (newsize < oldsize) { | |
| if (oldsize < 0x1000) { | |
| /* only small blocks */ | |
| blocknr=STORAGE_get_nth_next_small_blocknr(hf,This->stde.pps_sb,newsize/SMALLSIZE); | |
| assert(blocknr>=0); | |
| /* will set the rest of the chain to 'free' */ | |
| if (!STORAGE_set_small_chain(hf,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN)) | |
| return E_FAIL; | |
| } else { | |
| if (newsize >= 0x1000) { | |
| blocknr=STORAGE_get_nth_next_big_blocknr(hf,This->stde.pps_sb,newsize/BIGSIZE); | |
| assert(blocknr>=0); | |
| /* will set the rest of the chain to 'free' */ | |
| if (!STORAGE_set_big_chain(hf,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN)) | |
| return E_FAIL; | |
| } else { | |
| /* Migrate large blocks to small blocks | |
| * (we just migrate newsize bytes) | |
| */ | |
| LPBYTE curdata,data = HeapAlloc(GetProcessHeap(),0,newsize+BIGSIZE); | |
| HRESULT r = E_FAIL; | |
| cc = newsize; | |
| blocknr = This->stde.pps_sb; | |
| curdata = data; | |
| while (cc>0) { | |
| if (!STORAGE_get_big_block(hf,blocknr,curdata)) { | |
| HeapFree(GetProcessHeap(),0,data); | |
| return E_FAIL; | |
| } | |
| curdata += BIGSIZE; | |
| cc -= BIGSIZE; | |
| blocknr = STORAGE_get_next_big_blocknr(hf,blocknr); | |
| } | |
| /* frees complete chain for this stream */ | |
| if (!STORAGE_set_big_chain(hf,This->stde.pps_sb,STORAGE_CHAINENTRY_FREE)) | |
| goto err; | |
| curdata = data; | |
| blocknr = This->stde.pps_sb = STORAGE_get_free_small_blocknr(hf); | |
| if (blocknr<0) | |
| goto err; | |
| cc = newsize; | |
| while (cc>0) { | |
| if (!STORAGE_put_small_block(hf,blocknr,curdata)) | |
| goto err; | |
| cc -= SMALLSIZE; | |
| if (cc<=0) { | |
| if (!STORAGE_set_small_chain(hf,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN)) | |
| goto err; | |
| break; | |
| } else { | |
| int newblocknr = STORAGE_get_free_small_blocknr(hf); | |
| if (newblocknr<0) | |
| goto err; | |
| if (!STORAGE_set_small_chain(hf,blocknr,newblocknr)) | |
| goto err; | |
| blocknr = newblocknr; | |
| } | |
| curdata += SMALLSIZE; | |
| } | |
| r = S_OK; | |
| err: | |
| HeapFree(GetProcessHeap(),0,data); | |
| if(r != S_OK) | |
| return r; | |
| } | |
| } | |
| This->stde.pps_size = newsize; | |
| } | |
| if (newsize > oldsize) { | |
| if (oldsize >= 0x1000) { | |
| /* should return the block right before the 'endofchain' */ | |
| blocknr = STORAGE_get_nth_next_big_blocknr(hf,This->stde.pps_sb,This->stde.pps_size/BIGSIZE); | |
| assert(blocknr>=0); | |
| lastblocknr = blocknr; | |
| for (i=oldsize/BIGSIZE;i<newsize/BIGSIZE;i++) { | |
| blocknr = STORAGE_get_free_big_blocknr(hf); | |
| if (blocknr<0) | |
| return E_FAIL; | |
| if (!STORAGE_set_big_chain(hf,lastblocknr,blocknr)) | |
| return E_FAIL; | |
| lastblocknr = blocknr; | |
| } | |
| if (!STORAGE_set_big_chain(hf,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN)) | |
| return E_FAIL; | |
| } else { | |
| if (newsize < 0x1000) { | |
| /* find startblock */ | |
| if (!oldsize) | |
| This->stde.pps_sb = blocknr = STORAGE_get_free_small_blocknr(hf); | |
| else | |
| blocknr = STORAGE_get_nth_next_small_blocknr(hf,This->stde.pps_sb,This->stde.pps_size/SMALLSIZE); | |
| if (blocknr<0) | |
| return E_FAIL; | |
| /* allocate required new small blocks */ | |
| lastblocknr = blocknr; | |
| for (i=oldsize/SMALLSIZE;i<newsize/SMALLSIZE;i++) { | |
| blocknr = STORAGE_get_free_small_blocknr(hf); | |
| if (blocknr<0) | |
| return E_FAIL; | |
| if (!STORAGE_set_small_chain(hf,lastblocknr,blocknr)) | |
| return E_FAIL; | |
| lastblocknr = blocknr; | |
| } | |
| /* and terminate the chain */ | |
| if (!STORAGE_set_small_chain(hf,lastblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN)) | |
| return E_FAIL; | |
| } else { | |
| if (!oldsize) { | |
| /* no single block allocated yet */ | |
| blocknr=STORAGE_get_free_big_blocknr(hf); | |
| if (blocknr<0) | |
| return E_FAIL; | |
| This->stde.pps_sb = blocknr; | |
| } else { | |
| /* Migrate small blocks to big blocks */ | |
| LPBYTE curdata,data = HeapAlloc(GetProcessHeap(),0,oldsize+BIGSIZE); | |
| HRESULT r = E_FAIL; | |
| cc = oldsize; | |
| blocknr = This->stde.pps_sb; | |
| curdata = data; | |
| /* slurp in */ | |
| while (cc>0) { | |
| if (!STORAGE_get_small_block(hf,blocknr,curdata)) | |
| goto err2; | |
| curdata += SMALLSIZE; | |
| cc -= SMALLSIZE; | |
| blocknr = STORAGE_get_next_small_blocknr(hf,blocknr); | |
| } | |
| /* free small block chain */ | |
| if (!STORAGE_set_small_chain(hf,This->stde.pps_sb,STORAGE_CHAINENTRY_FREE)) | |
| goto err2; | |
| curdata = data; | |
| blocknr = This->stde.pps_sb = STORAGE_get_free_big_blocknr(hf); | |
| if (blocknr<0) | |
| goto err2; | |
| /* put the data into the big blocks */ | |
| cc = This->stde.pps_size; | |
| while (cc>0) { | |
| if (!STORAGE_put_big_block(hf,blocknr,curdata)) | |
| goto err2; | |
| cc -= BIGSIZE; | |
| if (cc<=0) { | |
| if (!STORAGE_set_big_chain(hf,blocknr,STORAGE_CHAINENTRY_ENDOFCHAIN)) | |
| goto err2; | |
| break; | |
| } else { | |
| int newblocknr = STORAGE_get_free_big_blocknr(hf); | |
| if (newblocknr<0) | |
| goto err2; | |
| if (!STORAGE_set_big_chain(hf,blocknr,newblocknr)) | |
| goto err2; | |
| blocknr = newblocknr; | |
| } | |
| curdata += BIGSIZE; | |
| } | |
| r = S_OK; | |
| err2: | |
| HeapFree(GetProcessHeap(),0,data); | |
| if(r != S_OK) | |
| return r; | |
| } | |
| /* generate big blocks to fit the new data */ | |
| lastblocknr = blocknr; | |
| for (i=oldsize/BIGSIZE;i<newsize/BIGSIZE;i++) { | |
| blocknr = STORAGE_get_free_big_blocknr(hf); | |
| if (blocknr<0) | |
| return E_FAIL; | |
| if (!STORAGE_set_big_chain(hf,lastblocknr,blocknr)) | |
| return E_FAIL; | |
| lastblocknr = blocknr; | |
| } | |
| /* terminate chain */ | |
| if (!STORAGE_set_big_chain(hf,lastblocknr,STORAGE_CHAINENTRY_ENDOFCHAIN)) | |
| return E_FAIL; | |
| } | |
| } | |
| This->stde.pps_size = newsize; | |
| } | |
| /* There are just some cases where we didn't modify it, we write it out | |
| * everytime | |
| */ | |
| if (!STORAGE_put_pps_entry(hf,This->ppsent,&(This->stde))) | |
| return E_FAIL; | |
| /* finally the write pass */ | |
| if (This->stde.pps_size < 0x1000) { | |
| blocknr = STORAGE_get_nth_next_small_blocknr(hf,This->stde.pps_sb,This->offset.s.LowPart/SMALLSIZE); | |
| assert(blocknr>=0); | |
| while (cb>0) { | |
| /* we ensured that it is allocated above */ | |
| assert(blocknr>=0); | |
| /* Read old block everytime, since we can have | |
| * overlapping data at START and END of the write | |
| */ | |
| if (!STORAGE_get_small_block(hf,blocknr,block)) | |
| return E_FAIL; | |
| cc = SMALLSIZE-(This->offset.s.LowPart&(SMALLSIZE-1)); | |
| if (cc>cb) | |
| cc=cb; | |
| memcpy( ((LPBYTE)block)+(This->offset.s.LowPart&(SMALLSIZE-1)), | |
| (LPBYTE)((char *) pv+curoffset), | |
| cc | |
| ); | |
| if (!STORAGE_put_small_block(hf,blocknr,block)) | |
| return E_FAIL; | |
| cb -= cc; | |
| curoffset += cc; | |
| (LPBYTE)pv += cc; | |
| This->offset.s.LowPart += cc; | |
| *byteswritten += cc; | |
| blocknr = STORAGE_get_next_small_blocknr(hf,blocknr); | |
| } | |
| } else { | |
| blocknr = STORAGE_get_nth_next_big_blocknr(hf,This->stde.pps_sb,This->offset.s.LowPart/BIGSIZE); | |
| assert(blocknr>=0); | |
| while (cb>0) { | |
| /* we ensured that it is allocated above, so it better is */ | |
| assert(blocknr>=0); | |
| /* read old block everytime, since we can have | |
| * overlapping data at START and END of the write | |
| */ | |
| if (!STORAGE_get_big_block(hf,blocknr,block)) | |
| return E_FAIL; | |
| cc = BIGSIZE-(This->offset.s.LowPart&(BIGSIZE-1)); | |
| if (cc>cb) | |
| cc=cb; | |
| memcpy( ((LPBYTE)block)+(This->offset.s.LowPart&(BIGSIZE-1)), | |
| (LPBYTE)((char *) pv+curoffset), | |
| cc | |
| ); | |
| if (!STORAGE_put_big_block(hf,blocknr,block)) | |
| return E_FAIL; | |
| cb -= cc; | |
| curoffset += cc; | |
| (LPBYTE)pv += cc; | |
| This->offset.s.LowPart += cc; | |
| *byteswritten += cc; | |
| blocknr = STORAGE_get_next_big_blocknr(hf,blocknr); | |
| } | |
| } | |
| return S_OK; | |
| } | |
| /****************************************************************************** | |
| * _create_istream16 [Internal] | |
| */ | |
| static void _create_istream16(LPSTREAM16 *str) { | |
| IStream16Impl* lpst; | |
| if (!strvt16.QueryInterface) { | |
| HMODULE16 wp = GetModuleHandle16("STORAGE"); | |
| if (wp>=32) { | |
| /* FIXME: what is This GetProcAddress16. Should the name be IStream16_QueryInterface of IStream16_fnQueryInterface */ | |
| #define VTENT(xfn) strvt16.xfn = (void*)GetProcAddress16(wp,"IStream16_"#xfn);assert(strvt16.xfn) | |
| VTENT(QueryInterface); | |
| VTENT(AddRef); | |
| VTENT(Release); | |
| VTENT(Read); | |
| VTENT(Write); | |
| VTENT(Seek); | |
| VTENT(SetSize); | |
| VTENT(CopyTo); | |
| VTENT(Commit); | |
| VTENT(Revert); | |
| VTENT(LockRegion); | |
| VTENT(UnlockRegion); | |
| VTENT(Stat); | |
| VTENT(Clone); | |
| #undef VTENT | |
| segstrvt16 = (ICOM_VTABLE(IStream16)*)MapLS( &strvt16 ); | |
| } else { | |
| #define VTENT(xfn) strvt16.xfn = IStream16_fn##xfn; | |
| VTENT(QueryInterface); | |
| VTENT(AddRef); | |
| VTENT(Release); | |
| VTENT(Read); | |
| VTENT(Write); | |
| VTENT(Seek); | |
| /* | |
| VTENT(CopyTo); | |
| VTENT(Commit); | |
| VTENT(SetSize); | |
| VTENT(Revert); | |
| VTENT(LockRegion); | |
| VTENT(UnlockRegion); | |
| VTENT(Stat); | |
| VTENT(Clone); | |
| */ | |
| #undef VTENT | |
| segstrvt16 = &strvt16; | |
| } | |
| } | |
| lpst = HeapAlloc( GetProcessHeap(), 0, sizeof(*lpst) ); | |
| ICOM_VTBL(lpst) = segstrvt16; | |
| lpst->ref = 1; | |
| lpst->thisptr = MapLS( lpst ); | |
| *str = (void*)lpst->thisptr; | |
| } | |
| /* --- IStream32 implementation */ | |
| typedef struct | |
| { | |
| /* IUnknown fields */ | |
| ICOM_VFIELD(IStream); | |
| DWORD ref; | |
| /* IStream32 fields */ | |
| struct storage_pps_entry stde; | |
| int ppsent; | |
| HFILE hf; | |
| ULARGE_INTEGER offset; | |
| } IStream32Impl; | |
| /***************************************************************************** | |
| * IStream32_QueryInterface [VTABLE] | |
| */ | |
| HRESULT WINAPI IStream_fnQueryInterface( | |
| IStream* iface,REFIID refiid,LPVOID *obj | |
| ) { | |
| ICOM_THIS(IStream32Impl,iface); | |
| TRACE_(relay)("(%p)->(%s,%p)\n",This,debugstr_guid(refiid),obj); | |
| if (!memcmp(&IID_IUnknown,refiid,sizeof(IID_IUnknown))) { | |
| *obj = This; | |
| return 0; | |
| } | |
| return OLE_E_ENUM_NOMORE; | |
| } | |
| /****************************************************************************** | |
| * IStream32_AddRef [VTABLE] | |
| */ | |
| ULONG WINAPI IStream_fnAddRef(IStream* iface) { | |
| ICOM_THIS(IStream32Impl,iface); | |
| return ++(This->ref); | |
| } | |
| /****************************************************************************** | |
| * IStream32_Release [VTABLE] | |
| */ | |
| ULONG WINAPI IStream_fnRelease(IStream* iface) { | |
| ICOM_THIS(IStream32Impl,iface); | |
| FlushFileBuffers(This->hf); | |
| This->ref--; | |
| if (!This->ref) { | |
| CloseHandle(This->hf); | |
| HeapFree( GetProcessHeap(), 0, This ); | |
| return 0; | |
| } | |
| return This->ref; | |
| } | |
| /* --- IStorage16 implementation */ | |
| typedef struct | |
| { | |
| /* IUnknown fields */ | |
| ICOM_VFIELD(IStorage16); | |
| DWORD ref; | |
| /* IStorage16 fields */ | |
| SEGPTR thisptr; /* pointer to this struct as segmented */ | |
| struct storage_pps_entry stde; | |
| int ppsent; | |
| HFILE hf; | |
| } IStorage16Impl; | |
| /****************************************************************************** | |
| * IStorage16_QueryInterface [STORAGE.500] | |
| */ | |
| HRESULT WINAPI IStorage16_fnQueryInterface( | |
| IStorage16* iface,REFIID refiid,LPVOID *obj | |
| ) { | |
| ICOM_THIS(IStorage16Impl,iface); | |
| TRACE_(relay)("(%p)->(%s,%p)\n",This,debugstr_guid(refiid),obj); | |
| if (!memcmp(&IID_IUnknown,refiid,sizeof(IID_IUnknown))) { | |
| *obj = This; | |
| return 0; | |
| } | |
| return OLE_E_ENUM_NOMORE; | |
| } | |
| /****************************************************************************** | |
| * IStorage16_AddRef [STORAGE.501] | |
| */ | |
| ULONG WINAPI IStorage16_fnAddRef(IStorage16* iface) { | |
| ICOM_THIS(IStorage16Impl,iface); | |
| return ++(This->ref); | |
| } | |
| /****************************************************************************** | |
| * IStorage16_Release [STORAGE.502] | |
| */ | |
| ULONG WINAPI IStorage16_fnRelease(IStorage16* iface) { | |
| ICOM_THIS(IStorage16Impl,iface); | |
| This->ref--; | |
| if (This->ref) | |
| return This->ref; | |
| UnMapLS( This->thisptr ); | |
| HeapFree( GetProcessHeap(), 0, This ); | |
| return 0; | |
| } | |
| /****************************************************************************** | |
| * IStorage16_Stat [STORAGE.517] | |
| */ | |
| HRESULT WINAPI IStorage16_fnStat( | |
| LPSTORAGE16 iface,STATSTG16 *pstatstg, DWORD grfStatFlag | |
| ) { | |
| ICOM_THIS(IStorage16Impl,iface); | |
| DWORD len = WideCharToMultiByte( CP_ACP, 0, This->stde.pps_rawname, -1, NULL, 0, NULL, NULL ); | |
| LPSTR nameA = HeapAlloc( GetProcessHeap(), 0, len ); | |
| TRACE("(%p)->(%p,0x%08lx)\n", | |
| This,pstatstg,grfStatFlag | |
| ); | |
| WideCharToMultiByte( CP_ACP, 0, This->stde.pps_rawname, -1, nameA, len, NULL, NULL ); | |
| pstatstg->pwcsName=(LPOLESTR16)MapLS( nameA ); | |
| pstatstg->type = This->stde.pps_type; | |
| pstatstg->cbSize.s.LowPart = This->stde.pps_size; | |
| pstatstg->mtime = This->stde.pps_ft1; /* FIXME */ /* why? */ | |
| pstatstg->atime = This->stde.pps_ft2; /* FIXME */ | |
| pstatstg->ctime = This->stde.pps_ft2; /* FIXME */ | |
| pstatstg->grfMode = 0; /* FIXME */ | |
| pstatstg->grfLocksSupported = 0; /* FIXME */ | |
| pstatstg->clsid = This->stde.pps_guid; | |
| pstatstg->grfStateBits = 0; /* FIXME */ | |
| pstatstg->reserved = 0; | |
| return S_OK; | |
| } | |
| /****************************************************************************** | |
| * IStorage16_Commit [STORAGE.509] | |
| */ | |
| HRESULT WINAPI IStorage16_fnCommit( | |
| LPSTORAGE16 iface,DWORD commitflags | |
| ) { | |
| ICOM_THIS(IStorage16Impl,iface); | |
| FIXME("(%p)->(0x%08lx),STUB!\n", | |
| This,commitflags | |
| ); | |
| return S_OK; | |
| } | |
| /****************************************************************************** | |
| * IStorage16_CopyTo [STORAGE.507] | |
| */ | |
| HRESULT WINAPI IStorage16_fnCopyTo(LPSTORAGE16 iface,DWORD ciidExclude,const IID *rgiidExclude,SNB16 SNB16Exclude,IStorage16 *pstgDest) { | |
| ICOM_THIS(IStorage16Impl,iface); | |
| FIXME("IStorage16(%p)->(0x%08lx,%s,%p,%p),stub!\n", | |
| This,ciidExclude,debugstr_guid(rgiidExclude),SNB16Exclude,pstgDest | |
| ); | |
| return S_OK; | |
| } | |
| /****************************************************************************** | |
| * IStorage16_CreateStorage [STORAGE.505] | |
| */ | |
| HRESULT WINAPI IStorage16_fnCreateStorage( | |
| LPSTORAGE16 iface,LPCOLESTR16 pwcsName,DWORD grfMode,DWORD dwStgFormat,DWORD reserved2, IStorage16 **ppstg | |
| ) { | |
| ICOM_THIS(IStorage16Impl,iface); | |
| IStorage16Impl* lpstg; | |
| int ppsent,x; | |
| struct storage_pps_entry stde; | |
| struct storage_header sth; | |
| HFILE hf=This->hf; | |
| READ_HEADER; | |
| TRACE("(%p)->(%s,0x%08lx,0x%08lx,0x%08lx,%p)\n", | |
| This,pwcsName,grfMode,dwStgFormat,reserved2,ppstg | |
| ); | |
| if (grfMode & STGM_TRANSACTED) | |
| FIXME("We do not support transacted Compound Storage. Using direct mode.\n"); | |
| _create_istorage16(ppstg); | |
| lpstg = MapSL((SEGPTR)*ppstg); | |
| lpstg->hf = This->hf; | |
| ppsent=STORAGE_get_free_pps_entry(lpstg->hf); | |
| if (ppsent<0) | |
| return E_FAIL; | |
| stde=This->stde; | |
| if (stde.pps_dir==-1) { | |
| stde.pps_dir = ppsent; | |
| x = This->ppsent; | |
| } else { | |
| FIXME(" use prev chain too ?\n"); | |
| x=stde.pps_dir; | |
| if (1!=STORAGE_get_pps_entry(lpstg->hf,x,&stde)) | |
| return E_FAIL; | |
| while (stde.pps_next!=-1) { | |
| x=stde.pps_next; | |
| if (1!=STORAGE_get_pps_entry(lpstg->hf,x,&stde)) | |
| return E_FAIL; | |
| } | |
| stde.pps_next = ppsent; | |
| } | |
| assert(STORAGE_put_pps_entry(lpstg->hf,x,&stde)); | |
| assert(1==STORAGE_get_pps_entry(lpstg->hf,ppsent,&(lpstg->stde))); | |
| MultiByteToWideChar( CP_ACP, 0, pwcsName, -1, lpstg->stde.pps_rawname, | |
| sizeof(lpstg->stde.pps_rawname)/sizeof(WCHAR)); | |
| lpstg->stde.pps_sizeofname = (strlenW(lpstg->stde.pps_rawname)+1)*sizeof(WCHAR); | |
| lpstg->stde.pps_next = -1; | |
| lpstg->stde.pps_prev = -1; | |
| lpstg->stde.pps_dir = -1; | |
| lpstg->stde.pps_sb = -1; | |
| lpstg->stde.pps_size = 0; | |
| lpstg->stde.pps_type = 1; | |
| lpstg->ppsent = ppsent; | |
| /* FIXME: timestamps? */ | |
| if (!STORAGE_put_pps_entry(lpstg->hf,ppsent,&(lpstg->stde))) | |
| return E_FAIL; | |
| return S_OK; | |
| } | |
| /****************************************************************************** | |
| * IStorage16_CreateStream [STORAGE.503] | |
| */ | |
| HRESULT WINAPI IStorage16_fnCreateStream( | |
| LPSTORAGE16 iface,LPCOLESTR16 pwcsName,DWORD grfMode,DWORD reserved1,DWORD reserved2, IStream16 **ppstm | |
| ) { | |
| ICOM_THIS(IStorage16Impl,iface); | |
| IStream16Impl* lpstr; | |
| int ppsent,x; | |
| struct storage_pps_entry stde; | |
| TRACE("(%p)->(%s,0x%08lx,0x%08lx,0x%08lx,%p)\n", | |
| This,pwcsName,grfMode,reserved1,reserved2,ppstm | |
| ); | |
| if (grfMode & STGM_TRANSACTED) | |
| FIXME("We do not support transacted Compound Storage. Using direct mode.\n"); | |
| _create_istream16(ppstm); | |
| lpstr = MapSL((SEGPTR)*ppstm); | |
| DuplicateHandle( GetCurrentProcess(), This->hf, GetCurrentProcess(), | |
| &lpstr->hf, 0, TRUE, DUPLICATE_SAME_ACCESS ); | |
| lpstr->offset.s.LowPart = 0; | |
| lpstr->offset.s.HighPart = 0; | |
| ppsent=STORAGE_get_free_pps_entry(lpstr->hf); | |
| if (ppsent<0) | |
| return E_FAIL; | |
| stde=This->stde; | |
| if (stde.pps_next==-1) | |
| x=This->ppsent; | |
| else | |
| while (stde.pps_next!=-1) { | |
| x=stde.pps_next; | |
| if (1!=STORAGE_get_pps_entry(lpstr->hf,x,&stde)) | |
| return E_FAIL; | |
| } | |
| stde.pps_next = ppsent; | |
| assert(STORAGE_put_pps_entry(lpstr->hf,x,&stde)); | |
| assert(1==STORAGE_get_pps_entry(lpstr->hf,ppsent,&(lpstr->stde))); | |
| MultiByteToWideChar( CP_ACP, 0, pwcsName, -1, lpstr->stde.pps_rawname, | |
| sizeof(lpstr->stde.pps_rawname)/sizeof(WCHAR)); | |
| lpstr->stde.pps_sizeofname = (strlenW(lpstr->stde.pps_rawname)+1) * sizeof(WCHAR); | |
| lpstr->stde.pps_next = -1; | |
| lpstr->stde.pps_prev = -1; | |
| lpstr->stde.pps_dir = -1; | |
| lpstr->stde.pps_sb = -1; | |
| lpstr->stde.pps_size = 0; | |
| lpstr->stde.pps_type = 2; | |
| lpstr->ppsent = ppsent; | |
| /* FIXME: timestamps? */ | |
| if (!STORAGE_put_pps_entry(lpstr->hf,ppsent,&(lpstr->stde))) | |
| return E_FAIL; | |
| return S_OK; | |
| } | |
| /****************************************************************************** | |
| * IStorage16_OpenStorage [STORAGE.506] | |
| */ | |
| HRESULT WINAPI IStorage16_fnOpenStorage( | |
| LPSTORAGE16 iface,LPCOLESTR16 pwcsName, IStorage16 *pstgPrio, DWORD grfMode, SNB16 snbExclude, DWORD reserved, IStorage16 **ppstg | |
| ) { | |
| ICOM_THIS(IStorage16Impl,iface); | |
| IStream16Impl* lpstg; | |
| WCHAR name[33]; | |
| int newpps; | |
| TRACE_(relay)("(%p)->(%s,%p,0x%08lx,%p,0x%08lx,%p)\n", | |
| This,pwcsName,pstgPrio,grfMode,snbExclude,reserved,ppstg | |
| ); | |
| if (grfMode & STGM_TRANSACTED) | |
| FIXME("We do not support transacted Compound Storage. Using direct mode.\n"); | |
| _create_istorage16(ppstg); | |
| lpstg = MapSL((SEGPTR)*ppstg); | |
| DuplicateHandle( GetCurrentProcess(), This->hf, GetCurrentProcess(), | |
| &lpstg->hf, 0, TRUE, DUPLICATE_SAME_ACCESS ); | |
| MultiByteToWideChar( CP_ACP, 0, pwcsName, -1, name, sizeof(name)/sizeof(WCHAR)); | |
| newpps = STORAGE_look_for_named_pps(lpstg->hf,This->stde.pps_dir,name); | |
| if (newpps==-1) { | |
| IStream16_fnRelease((IStream16*)lpstg); | |
| return E_FAIL; | |
| } | |
| if (1!=STORAGE_get_pps_entry(lpstg->hf,newpps,&(lpstg->stde))) { | |
| IStream16_fnRelease((IStream16*)lpstg); | |
| return E_FAIL; | |
| } | |
| lpstg->ppsent = newpps; | |
| return S_OK; | |
| } | |
| /****************************************************************************** | |
| * IStorage16_OpenStream [STORAGE.504] | |
| */ | |
| HRESULT WINAPI IStorage16_fnOpenStream( | |
| LPSTORAGE16 iface,LPCOLESTR16 pwcsName, void *reserved1, DWORD grfMode, DWORD reserved2, IStream16 **ppstm | |
| ) { | |
| ICOM_THIS(IStorage16Impl,iface); | |
| IStream16Impl* lpstr; | |
| WCHAR name[33]; | |
| int newpps; | |
| TRACE_(relay)("(%p)->(%s,%p,0x%08lx,0x%08lx,%p)\n", | |
| This,pwcsName,reserved1,grfMode,reserved2,ppstm | |
| ); | |
| if (grfMode & STGM_TRANSACTED) | |
| FIXME("We do not support transacted Compound Storage. Using direct mode.\n"); | |
| _create_istream16(ppstm); | |
| lpstr = MapSL((SEGPTR)*ppstm); | |
| DuplicateHandle( GetCurrentProcess(), This->hf, GetCurrentProcess(), | |
| &lpstr->hf, 0, TRUE, DUPLICATE_SAME_ACCESS ); | |
| MultiByteToWideChar( CP_ACP, 0, pwcsName, -1, name, sizeof(name)/sizeof(WCHAR)); | |
| newpps = STORAGE_look_for_named_pps(lpstr->hf,This->stde.pps_dir,name); | |
| if (newpps==-1) { | |
| IStream16_fnRelease((IStream16*)lpstr); | |
| return E_FAIL; | |
| } | |
| if (1!=STORAGE_get_pps_entry(lpstr->hf,newpps,&(lpstr->stde))) { | |
| IStream16_fnRelease((IStream16*)lpstr); | |
| return E_FAIL; | |
| } | |
| lpstr->offset.s.LowPart = 0; | |
| lpstr->offset.s.HighPart = 0; | |
| lpstr->ppsent = newpps; | |
| return S_OK; | |
| } | |
| /****************************************************************************** | |
| * _create_istorage16 [INTERNAL] | |
| */ | |
| static void _create_istorage16(LPSTORAGE16 *stg) { | |
| IStorage16Impl* lpst; | |
| if (!stvt16.QueryInterface) { | |
| HMODULE16 wp = GetModuleHandle16("STORAGE"); | |
| if (wp>=32) { | |
| #define VTENT(xfn) stvt16.xfn = (void*)GetProcAddress16(wp,"IStorage16_"#xfn); | |
| VTENT(QueryInterface) | |
| VTENT(AddRef) | |
| VTENT(Release) | |
| VTENT(CreateStream) | |
| VTENT(OpenStream) | |
| VTENT(CreateStorage) | |
| VTENT(OpenStorage) | |
| VTENT(CopyTo) | |
| VTENT(MoveElementTo) | |
| VTENT(Commit) | |
| VTENT(Revert) | |
| VTENT(EnumElements) | |
| VTENT(DestroyElement) | |
| VTENT(RenameElement) | |
| VTENT(SetElementTimes) | |
| VTENT(SetClass) | |
| VTENT(SetStateBits) | |
| VTENT(Stat) | |
| #undef VTENT | |
| segstvt16 = (ICOM_VTABLE(IStorage16)*)MapLS( &stvt16 ); | |
| } else { | |
| #define VTENT(xfn) stvt16.xfn = IStorage16_fn##xfn; | |
| VTENT(QueryInterface) | |
| VTENT(AddRef) | |
| VTENT(Release) | |
| VTENT(CreateStream) | |
| VTENT(OpenStream) | |
| VTENT(CreateStorage) | |
| VTENT(OpenStorage) | |
| VTENT(CopyTo) | |
| VTENT(Commit) | |
| /* not (yet) implemented ... | |
| VTENT(MoveElementTo) | |
| VTENT(Revert) | |
| VTENT(EnumElements) | |
| VTENT(DestroyElement) | |
| VTENT(RenameElement) | |
| VTENT(SetElementTimes) | |
| VTENT(SetClass) | |
| VTENT(SetStateBits) | |
| VTENT(Stat) | |
| */ | |
| #undef VTENT | |
| segstvt16 = &stvt16; | |
| } | |
| } | |
| lpst = HeapAlloc( GetProcessHeap(), 0, sizeof(*lpst) ); | |
| ICOM_VTBL(lpst) = segstvt16; | |
| lpst->ref = 1; | |
| lpst->thisptr = MapLS(lpst); | |
| *stg = (void*)lpst->thisptr; | |
| } | |
| /****************************************************************************** | |
| * Storage API functions | |
| */ | |
| /****************************************************************************** | |
| * StgCreateDocFileA [STORAGE.1] | |
| */ | |
| HRESULT WINAPI StgCreateDocFile16( | |
| LPCOLESTR16 pwcsName,DWORD grfMode,DWORD reserved,IStorage16 **ppstgOpen | |
| ) { | |
| HFILE hf; | |
| int i,ret; | |
| IStorage16Impl* lpstg; | |
| struct storage_pps_entry stde; | |
| TRACE("(%s,0x%08lx,0x%08lx,%p)\n", | |
| pwcsName,grfMode,reserved,ppstgOpen | |
| ); | |
| _create_istorage16(ppstgOpen); | |
| hf = CreateFileA(pwcsName,GENERIC_READ|GENERIC_WRITE,0,NULL,CREATE_NEW,0,0); | |
| if (hf==INVALID_HANDLE_VALUE) { | |
| WARN("couldn't open file for storage:%ld\n",GetLastError()); | |
| return E_FAIL; | |
| } | |
| lpstg = MapSL((SEGPTR)*ppstgOpen); | |
| lpstg->hf = hf; | |
| /* FIXME: check for existence before overwriting? */ | |
| if (!STORAGE_init_storage(hf)) { | |
| CloseHandle(hf); | |
| return E_FAIL; | |
| } | |
| i=0;ret=0; | |
| while (!ret) { /* neither 1 nor <0 */ | |
| ret=STORAGE_get_pps_entry(hf,i,&stde); | |
| if ((ret==1) && (stde.pps_type==5)) { | |
| lpstg->stde = stde; | |
| lpstg->ppsent = i; | |
| break; | |
| } | |
| i++; | |
| } | |
| if (ret!=1) { | |
| IStorage16_fnRelease((IStorage16*)lpstg); /* will remove it */ | |
| return E_FAIL; | |
| } | |
| return S_OK; | |
| } | |
| /****************************************************************************** | |
| * StgIsStorageFile [STORAGE.5] | |
| */ | |
| HRESULT WINAPI StgIsStorageFile16(LPCOLESTR16 fn) { | |
| HFILE hf; | |
| OFSTRUCT ofs; | |
| BYTE magic[24]; | |
| TRACE("(\'%s\')\n",fn); | |
| hf = OpenFile(fn,&ofs,OF_SHARE_DENY_NONE); | |
| if (hf==HFILE_ERROR) | |
| return STG_E_FILENOTFOUND; | |
| if (24!=_lread(hf,magic,24)) { | |
| WARN(" too short\n"); | |
| _lclose(hf); | |
| return S_FALSE; | |
| } | |
| if (!memcmp(magic,STORAGE_magic,8)) { | |
| WARN(" -> YES\n"); | |
| _lclose(hf); | |
| return S_OK; | |
| } | |
| if (!memcmp(magic,STORAGE_notmagic,8)) { | |
| WARN(" -> NO\n"); | |
| _lclose(hf); | |
| return S_FALSE; | |
| } | |
| if (!memcmp(magic,STORAGE_oldmagic,8)) { | |
| WARN(" -> old format\n"); | |
| _lclose(hf); | |
| return STG_E_OLDFORMAT; | |
| } | |
| WARN(" -> Invalid header.\n"); | |
| _lclose(hf); | |
| return STG_E_INVALIDHEADER; | |
| } | |
| /****************************************************************************** | |
| * StgIsStorageFile [OLE32.146] | |
| */ | |
| HRESULT WINAPI | |
| StgIsStorageFile(LPCOLESTR fn) | |
| { | |
| HRESULT ret; | |
| DWORD len = WideCharToMultiByte( CP_ACP, 0, fn, -1, NULL, 0, NULL, NULL ); | |
| LPSTR strA = HeapAlloc( GetProcessHeap(), 0, len ); | |
| WideCharToMultiByte( CP_ACP, 0, fn, -1, strA, len, NULL, NULL ); | |
| ret = StgIsStorageFile16(strA); | |
| HeapFree( GetProcessHeap(), 0, strA ); | |
| return ret; | |
| } | |
| /****************************************************************************** | |
| * StgOpenStorage [STORAGE.3] | |
| */ | |
| HRESULT WINAPI StgOpenStorage16( | |
| LPCOLESTR16 pwcsName,IStorage16 *pstgPriority,DWORD grfMode, | |
| SNB16 snbExclude,DWORD reserved, IStorage16 **ppstgOpen | |
| ) { | |
| HFILE hf; | |
| int ret,i; | |
| IStorage16Impl* lpstg; | |
| struct storage_pps_entry stde; | |
| TRACE("(%s,%p,0x%08lx,%p,%ld,%p)\n", | |
| pwcsName,pstgPriority,grfMode,snbExclude,reserved,ppstgOpen | |
| ); | |
| _create_istorage16(ppstgOpen); | |
| hf = CreateFileA(pwcsName,GENERIC_READ,FILE_SHARE_READ,NULL,OPEN_EXISTING,FILE_ATTRIBUTE_NORMAL,0); | |
| if (hf==INVALID_HANDLE_VALUE) { | |
| WARN("Couldn't open file for storage\n"); | |
| return E_FAIL; | |
| } | |
| lpstg = MapSL((SEGPTR)*ppstgOpen); | |
| lpstg->hf = hf; | |
| i=0;ret=0; | |
| while (!ret) { /* neither 1 nor <0 */ | |
| ret=STORAGE_get_pps_entry(hf,i,&stde); | |
| if ((ret==1) && (stde.pps_type==5)) { | |
| lpstg->stde=stde; | |
| break; | |
| } | |
| i++; | |
| } | |
| if (ret!=1) { | |
| IStorage16_fnRelease((IStorage16*)lpstg); /* will remove it */ | |
| return E_FAIL; | |
| } | |
| return S_OK; | |
| } |