/* * Help Access Library * A Library to access the contents of Windows Help files. * * Copyright (C) 1995-2000 Bernd Herd, http://www.herdsoft.com * * This program is free software; you can redistribute it and/or * modify it under the terms of the GNU General Public License as * published by the Free Software Foundation; either version 2 of * the License. * * This program 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 General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program; if not, write to the Free Software * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */ /* IFS.C ---------------------------------------------- * * @doc * @module IFS.C | * Code for internal File System of Windows Help * * Help Access Library Project. * *-----------------------------------------------------*/ #include #include #include #include #include #include #include "hlpacces.h" #include "top.h" // --------- Local function prototypes ----------------- DWORD LOCAL nFindInternalFile(NPIFS ifs, LPCSTR FileName); /* @func HIFS | IFSOpen | * * Opens an existing .HLP or .MVB and reads in Header-Level Data structures * * @rdesc A Handle for the opened File-System to be used in Subsequent calls to * . The Handle must be freed by calling * @parm LPCSTR | HelpFileName | Pointer to a string containing the Filename of the Help-File * @comm This function may also be used to open other internal File Systems, * for example Annotation-Files etc. */ NPIFS WINAPI DLLEXP IFSOpen(LPCSTR HelpFileName) { NPIFS rtn = NULL, ifs = (NPIFS) LocalAlloc(LPTR, sizeof(IFS)); if (ifs) { if (HFILE_ERROR!=(ifs->hf=OpenFile(HelpFileName, &ifs->of, OF_READ | OF_SHARE_DENY_WRITE))) { if (sizeof(ifs->HelpHeader) == _lread(ifs->hf, &ifs->HelpHeader, sizeof(ifs->HelpHeader)) && ledword(ifs->HelpHeader.MagicNumber) == 0x000000035F3F && HFILE_ERROR != _llseek(ifs->hf, ledword(ifs->HelpHeader.DirectoryFileOffset)+sizeof(FILEHEADER), SEEK_SET) && sizeof(ifs->WHIFSHeader) == _lread(ifs->hf, &ifs->WHIFSHeader, sizeof(ifs->WHIFSHeader)) ) { rtn = ifs; ifs->HelpFileName = Hlpstrdup(HelpFileName); } if (!rtn) _lclose(ifs->hf); } if (!rtn) LocalFree((HLOCAL) ifs); } return rtn; } /* @func void | IFSClose | * * Closes Down .HLP-File and frees assigned Structures. Frees memory-Structures * created by * * @parm HIFS | ifs | Handle of the File-System returned by */ void WINAPI DLLEXP IFSClose(NPIFS ifs) { if (ifs) { Hlpfree(ifs->HelpFileName); _lclose(ifs->hf); LocalFree((HLOCAL) ifs); } } /* @func HIFSFILE | IFSOpenFile | * * Opens an internal File. * * Opens a file contained in a File-System for reading. The file system has to be * opened by a previous call to . More than one internal file can be * ópened with calls to IFSOpenFile at the same time using the same HIFS-Handle. * * @rdesc A Handle for the opened file to be used in subsequent calls to * and * @comm All HIFSFILE File handled must be closed with calls to * before the assigned HIFS can be closed by */ NPIFSFILE WINAPI DLLEXP IFSOpenFile( HIFS ifs, // @parm Handle of the File-System opened by LPCSTR InternalFileName) /* @parm Internal filename of the Baggage-File NOTE: filenames are case-different, so you have to transfer the correct case for the Filename */ { NPIFSFILE ifsfile = NULL; DWORD location = nFindInternalFile(ifs, InternalFileName); FILEHEADER FileHeader; if (location) { ifsfile = (NPIFSFILE) LocalAlloc(LPTR, sizeof(IFSFILE)); if (ifsfile) { _llseek(ifs->hf, location, SEEK_SET); _lread(ifs->hf, &FileHeader, sizeof(FileHeader)); ifsfile->dwOffset= location + sizeof(FileHeader); ifsfile->dwSize = ledword(FileHeader.FileSize); ifsfile->ifs = ifs; } } return ifsfile; } /* @func void | IFSCloseFile | * * Closes an internal File opened by . * * @parm HIFSFILE | ifsfile | The file-handle as returned by * @comm All HIFSFILE File handled must be closed with calls to * before the assigned HIFS can be closed by */ void WINAPI DLLEXP IFSCloseFile(NPIFSFILE ifsfile) { LocalFree((HLOCAL) ifsfile); } /* @func LONG | IFSSeekFile | * * Positions an Internal File for subsequent calls to . * @comm This Function is also useful to retrieve the size of a internal File. * by Specifying 2 (SEEK_END) for whence and 0 for pos * @comm This function will not detect if the new position is before the start or * after the end of the file. * @rdesc The new file-position in the internal file relative to the start of * the internal file. */ LONG WINAPI DLLEXP IFSSeekFile( HIFSFILE ifsfile, // @parm Handle of the internal file returned by DWORD pos, // @parm new file-Postion int whence) // @parm 0-Postion relative to start of file, 1-Postion relative to current position, 2-Position relative to end of file { switch (whence) { case SEEK_SET: ifsfile->dwPos = pos; break; case SEEK_END: ifsfile->dwPos = pos + ifsfile->dwSize; break; case SEEK_CUR: ifsfile->dwPos = pos + ifsfile->dwPos; break; } return ifsfile->dwPos; } /* @func LONG | IFSReadFile | * * Reads data from an internal File. * @rdesc Number of bytes actually read from the internal file. * if the Number of bytes that need to be read would exceed the * size of the internal file, the number is reduced to the actual file size. * * The number returned may be -1 to indicate an error. * @comm This function can read more than 65535 bytes at a time on 16-Bit OS. */ LONG WINAPI DLLEXP IFSReadFile( HIFSFILE ifsfile, // @parm Handle to the opened internal file returned by LPVOID dest, // @parm Destination buffer address DWORD count) // @parm Number of Bytes that need to be read { HFILE hf = ifsfile->ifs->hf; LONG rtn, len; len = ifsfile->dwSize < count + ifsfile->dwPos ? ifsfile->dwSize - ifsfile->dwPos : count; _llseek(hf, ifsfile->dwPos + ifsfile->dwOffset, SEEK_SET); rtn = _hread (hf, dest, len); ifsfile->dwPos += rtn; return rtn; } /* nFindInternalFile * * Internal routine does a binary Search for the given * Filename in the IFS * * Returns position of FIle in physical if it's found and * 0 if not. */ #define HlpGotoWHIFSPage(ifs, DestPage) \ _llseek(ifs->hf, FirstPageLoc+(DestPage * \ leword(ifs->WHIFSHeader.PageSize)), SEEK_SET) DWORD LOCAL nFindInternalFile(NPIFS ifs, LPCSTR FileName) { NPSTR buf = (NPSTR) LocalAlloc(LPTR, leword(ifs->WHIFSHeader.PageSize)), // Buffer for one Page nxt; BTREENODEHEADER NEAR *CurrNode = (BTREENODEHEADER NEAR *)buf; /* Current Node in B-Tree */ DWORD offset = 0, FirstPageLoc= ledword(ifs->HelpHeader.DirectoryFileOffset) + sizeof(ifs->WHIFSHeader) + sizeof(FILEHEADER); int CurrPage = leword(ifs->WHIFSHeader.RootPage), i, NLevels = leword(ifs->WHIFSHeader.NLevels); if (buf) { while (NLevels-->=0 && CurrPage>=0 ) { //-------- Read in a Page ---------------------------------- HlpGotoWHIFSPage(ifs, CurrPage); _lread(ifs->hf, buf, leword(ifs->WHIFSHeader.PageSize)); //-------- In node or leaf ? ------------------------------- if (NLevels>0) { //-- We're in a node, search for next sub-node or leaf -- for (i=0, nxt = buf+sizeof(BTREEINDEXHEADER); iNEntries) && _fstrcmp(nxt+2, FileName)<=0; ++i, nxt+=lstrlen(nxt+2)+1+sizeof(WORD)) ; CurrPage=nxt[0]+256*nxt[1]; // CurrPage=((WORD NEAR *) (nxt+lstrlen(nxt+2)+1+sizeof(WORD)))[0]; } else { //-- We're in the right leaf now, scan for Data --------- for (i=0, nxt = buf+sizeof(BTREENODEHEADER); iNEntries)&&_fstrcmp(nxt, FileName); ++i ,nxt+=lstrlen(nxt)+1+sizeof(LONG)) ; if (!_fstrcmp(nxt, FileName)) offset = ledword(((LEDWORD NEAR *) (nxt+lstrlen(nxt)+1))[0]); } } LocalFree((HLOCAL) buf); } return offset; } #ifdef OBSOLETE #define HlpGotoWHIFSPage(ifs, DestPage) \ _llseek(ifs->hf, FirstPageLoc+(DestPage * \ ifs->WHIFSHeader.PageSize), SEEK_SET) DWORD LOCAL nFindInternalFile(NPIFS ifs, LPCSTR FileName) { NPSTR buf = (NPSTR) LocalAlloc(LPTR, ifs->WHIFSHeader.PageSize), // Buffer for one Page nxt; BTREENODEHEADER NEAR *CurrNode = (BTREENODEHEADER NEAR *)buf; /* Current Node in B-Tree */ DWORD offset = 0, FirstPageLoc= ifs->HelpHeader.DirectoryFileOffset + sizeof(ifs->WHIFSHeader) + sizeof(FILEHEADER); int CurrPage = ifs->WHIFSHeader.RootPage, Direction = 1, i; if (buf) { //---------- First we have to scan for the right Page -------- while (!offset && Direction && CurrPage>=0 ) { //-------- Read in a Page ---------------------------------- HlpGotoWHIFSPage(ifs, CurrPage); _lread(ifs->hf, buf, ifs->WHIFSHeader.PageSize); //-------- Search for our Data in the Page ----------------- nxt = buf+sizeof(BTREENODEHEADER); Direction = _fstrcmp(nxt, FileName); if (Direction>0) { CurrPage=CurrNode->PreviousPage; } else { Direction = -1; for (i=0; iNEntries && Direction<0; ++i) { Direction = _fstrcmp(nxt, FileName); nxt+=lstrlen(nxt)+1+sizeof(LONG); } if (!Direction) offset = *((DWORD NEAR *) (nxt-4)); else if (Direction>0) CurrPage=-1; else CurrPage=CurrNode->NextPage; } } LocalFree((HLOCAL) buf); } return offset; } #endif