/*++ * File name: * bmpdb.c * Contents: * Bitmap database manager * Almost all functions ARE NOT thread safe * * Copyright (C) 1998-1999 Microsoft Corp. --*/ #include #include #include #include #include #include #include #include #include "bmpdb.h" #define DB_NAME "bmpcache.db" // Database name #define TEMPDB_NAME "bmpcache.tmp" // Temp file, used to recopy the database // Global data int g_hDB = 0; // Handle to the opened database int g_hTempDB; // Temp handle BOOL g_bNeedToPack; // True if some entrys are deleted /* * Internal functions definition --*/ void _PackDB(void); /*++ * Function: * OpenDB * Description: * Opens and initializes the database * Arguments: * bWrite - TRUE if the caller wants to write in the database * Return value: * TRUE on success * Called by: * InitCache --*/ BOOL OpenDB(BOOL bWrite) { int hFile, rv = TRUE; int oflag; if (g_hDB) // Already initialized goto exitpt; oflag = (bWrite)?_O_RDWR|_O_CREAT:_O_RDONLY; hFile = _open(DB_NAME, oflag|_O_BINARY, _S_IREAD|_S_IWRITE); if (hFile == -1) rv = FALSE; else g_hDB = hFile; g_bNeedToPack = FALSE; exitpt: return rv; } /*++ * Function: * CloseDB * Description: * Closes the database deletes entry if necessary * Called by: * DeleteCache --*/ VOID CloseDB(VOID) { if (!g_hDB) goto exitpt; if (g_bNeedToPack) _PackDB(); else _close(g_hDB); g_hDB = 0; exitpt: ; } /*++ * Function: * ReadGroup (Thread dependent) * Description: * Read the structure which represents * a bitmap group with the same IDs * Arguments: * nOffset - offset in the DB file * pGroup - pointer to the result * Return value: * TRUE on success * Called by: * internaly --*/ BOOL ReadGroup(FOFFSET nOffset, PGROUPENTRY pGroup) { int rv = FALSE; if (!g_hDB) goto exitpt; if (_lseek(g_hDB, nOffset, SEEK_SET) != nOffset) goto exitpt; if (_read(g_hDB, pGroup, sizeof(*pGroup)) != sizeof(*pGroup)) goto exitpt; rv = TRUE; exitpt: return rv; } /*++ * Function: * WriteGroup (Thread dep) * Description: * Writes GROUPENTRY in the DB file * Arguments: * nOffset - where to store * pGroup - what to store * Return value: * TRUE on success * Called by: * internaly --*/ BOOL WriteGroup(FOFFSET nOffset, PGROUPENTRY pGroup) { BOOL rv = FALSE; if (!g_hDB || !pGroup) goto exitpt; if (_lseek(g_hDB, nOffset, SEEK_SET) != nOffset) goto exitpt; if (_write(g_hDB, pGroup, sizeof(*pGroup)) != sizeof(*pGroup)) goto exitpt; rv = TRUE; exitpt: return rv; } /*++ * Function: * EnumerateGroups (thread dep) * Description: * Enumerates all groups from the DB * Arguments: * pfnEnumGrpProc - Callback function * pParam - Parameter passed to the callback * Called by: * internaly --*/ VOID EnumerateGroups(PFNENUMGROUPS pfnEnumGrpProc, PVOID pParam) { GROUPENTRY Group; BOOL bRun; FOFFSET nOffs = 0; bRun = ReadGroup(nOffs, &Group); while(bRun) { if (!Group.bDeleted) bRun = pfnEnumGrpProc(nOffs, &Group, pParam) && (Group.FOffsNext != 0); if (bRun) { nOffs = Group.FOffsNext; if (nOffs) bRun = ReadGroup(nOffs, &Group); else bRun = FALSE; } } } /*++ * Function: * ReadBitmapHeader (Thread dep) * Description: * Read only the header of the bitmap * Arguments: * nOffset - where in the file * pBitmap - returned structure * Return value: * TRUE on success * Called by: * Internaly --*/ BOOL ReadBitmapHeader(FOFFSET nOffset, PBMPENTRY pBitmap) { BOOL rv = FALSE; if (!g_hDB || !pBitmap) goto exitpt; if (_lseek(g_hDB, nOffset, SEEK_SET) != nOffset) goto exitpt; if (_read(g_hDB, pBitmap, sizeof(*pBitmap)) != sizeof(*pBitmap)) goto exitpt; rv = TRUE; exitpt: return rv; } /*++ * Function: * WriteBitmapHeader (Thread dep) * Description: * Writes only the bitmap header * Arguments: * nOffset - where to store * pBitmap - what to store * Return value: * TRUE on success * Called by: * internaly --*/ BOOL WriteBitmapHeader(FOFFSET nOffset, PBMPENTRY pBitmap) { BOOL rv = FALSE; if (!g_hDB || !pBitmap) goto exitpt; if (_lseek(g_hDB, nOffset, SEEK_SET) != nOffset) goto exitpt; if (_write(g_hDB, pBitmap, sizeof(*pBitmap)) != sizeof(*pBitmap)) goto exitpt; rv = TRUE; exitpt: return rv; } /*++ * Function: * ReadBitmap (Thread dependent) * Description: * Read the whole bitmap and allocates memory for it * Arguments: * nOffset - from where * Return value: * Pointer to the result, NULL on error * Called by: * internaly --*/ PBMPENTRY ReadBitmap(FOFFSET nOffset) { PBMPENTRY rv = NULL; if (!g_hDB) goto exitpt; rv = malloc(sizeof(*rv)); if (rv) { rv->pData = NULL; if (!ReadBitmapHeader(nOffset, rv)) goto exitpt1; rv->pData = malloc(rv->nDataSize); if (rv->pData && _read(g_hDB, rv->pData, rv->nDataSize) != (long)rv->nDataSize) { goto exitpt1; } } exitpt: return rv; exitpt1: if (rv) { if (rv->pData) free(rv->pData); free(rv); } return NULL; } /*++ * Function: * FreeBitmap * Description: * Frees the resources allocated in ReadBitmap * Arguments: * pBmp - The bitmap * Called by: * internaly --*/ VOID FreeBitmap(PBMPENTRY pBmp) { if (pBmp) { if (pBmp->pData) free(pBmp->pData); free(pBmp); } } /*++ * Function: * EnumerateBitmaps * Description: * Enumaretes all bitmaps within a group * Arguments: * nOffset - Location * pfnEnumProc - Callback * pParam - callback parameter * Called by: * internaly --*/ VOID EnumerateBitmaps(FOFFSET nOffset, PFNENUMBITMAPS pfnEnumProc, PVOID pParam) { PBMPENTRY pBmp; BOOL bRun = TRUE; while(bRun && nOffset && (pBmp = ReadBitmap(nOffset))) { if (!pBmp->bDeleted) bRun = pfnEnumProc(nOffset, pBmp, pParam); nOffset = pBmp->FOffsNext; FreeBitmap(pBmp); } } /*++ * Function: * FindGroup * Description: * Retrieves a group by ID * Arguments: * szWText - the ID * Return value: * Group location, -1 on error --*/ FOFFSET FindGroup(LPWSTR szWText) { GROUPENTRY Group; BOOL bRun; FOFFSET rv = 0; bRun = ReadGroup(0, &Group); while(bRun) { if (!Group.bDeleted && !wcscmp(Group.WText, szWText)) break; if (!Group.FOffsNext) bRun = FALSE; else { rv = Group.FOffsNext; bRun = ReadGroup(Group.FOffsNext, &Group); } } if (!bRun) rv = -1; return rv; } /*++ * Function: * FindBitmap * Description: * Finds a bitmap by ID and comment * Arguments: * szWText - ID * szComment - the comment * Return value: * The location of the bitmap, -1 on error --*/ FOFFSET FindBitmap(LPWSTR szWText, char *szComment) { FOFFSET nGrpOffs, nBmpOffs; GROUPENTRY group; BMPENTRY Bitmap; FOFFSET rv = -1; BOOL bRun; if ((nGrpOffs = FindGroup(szWText)) == -1) goto exitpt; if (!ReadGroup(nGrpOffs, &group)) goto exitpt; nBmpOffs = group.FOffsBmp; bRun = TRUE; while(bRun) { bRun = ReadBitmapHeader(nBmpOffs, &Bitmap); if (bRun) { if (!Bitmap.bDeleted && !strcmp(Bitmap.szComment, szComment)) break; nBmpOffs = Bitmap.FOffsNext; } } if (bRun) rv = nBmpOffs; exitpt: return rv; } /*++ * Function: * CheckSum * Description: * Calculates a check sum for block of memory * Helps for bitmaps comapring * Arguments: * pData - pointer to the block * nLen - block size * Return value: * the checksum * Called by: * AddBitMap, Glyph2String --*/ UINT CheckSum(PVOID pData, UINT nLen) { UINT nChkSum = 0; BYTE *pbBlock = (BYTE *)pData; for(;nLen; nLen--, pbBlock++) nChkSum += (*pbBlock); return nChkSum; } /*++ * Function: * AddBitmap (Thread dependent) * Description: * Adds a BitMap to the DB * Arguments: * pBitmap - the bitmap * szWText - ID * Return value: * TRUE on success * Called by: * glyphspy.c --*/ BOOL AddBitMap(PBMPENTRY pBitmap, LPCWSTR szWText) { BMPENTRY bmp; GROUPENTRY group; INT strl; BOOL rv = FALSE; FOFFSET lGroupOffs, lBmpOffs; GROUPENTRY grpTemp; BMPENTRY bmpTemp; FOFFSET nOffs; PVOID pData; if (!g_hDB || !pBitmap || !pBitmap->pData || !wcslen(szWText)) goto exitpt; memset(&group, 0, sizeof(group)); memset(&bmp, 0, sizeof(bmp)); bmp.nDataSize = pBitmap->nDataSize; bmp.bmiSize = pBitmap->bmiSize; bmp.bmpSize = pBitmap->bmpSize; bmp.xSize = pBitmap->xSize; bmp.ySize = pBitmap->ySize; bmp.nChkSum = CheckSum(pBitmap->pData, pBitmap->nDataSize); strcpy(bmp.szComment, pBitmap->szComment); strl = wcslen(szWText); if (strl > (sizeof(group.WText) - 1)/sizeof(WCHAR)) strl = (sizeof(group.WText) - 1)/sizeof(WCHAR); wcsncpy(group.WText, szWText, strl); group.WText[strl] = 0; // Create group if ((lGroupOffs = FindGroup(group.WText)) == -1) { // A new group will be created lGroupOffs = _lseek(g_hDB, 0, SEEK_END); group.FOffsMe = lGroupOffs; if (_write(g_hDB, &group, sizeof(group)) != sizeof(group)) { goto exitpt; } // Add this group to the list if (lGroupOffs) { nOffs = 0; while(ReadGroup(nOffs, &grpTemp) && grpTemp.FOffsNext) nOffs = grpTemp.FOffsNext; grpTemp.FOffsNext = lGroupOffs; if (!WriteGroup(nOffs, &grpTemp)) goto exitpt; } } else { if (ReadGroup(lGroupOffs, &group) == -1) goto exitpt; } // Write the bitmap itself lBmpOffs = _lseek(g_hDB, 0, SEEK_END); bmp.FOffsMe = lBmpOffs; if (_write(g_hDB, &bmp, sizeof(bmp)) != sizeof(bmp)) { goto exitpt; } if (_write(g_hDB, pBitmap->pData, pBitmap->nDataSize) != (long)pBitmap->nDataSize) { goto exitpt; } // Add the bitmap to the list if (group.FOffsBmp) { nOffs = group.FOffsBmp; // Find end of the list and add while(ReadBitmapHeader(nOffs, &bmpTemp) && bmpTemp.FOffsNext) nOffs = bmpTemp.FOffsNext; bmpTemp.FOffsNext = lBmpOffs; if (!WriteBitmapHeader(nOffs, &bmpTemp)) goto exitpt; } else { // No list add to group pointer group.FOffsBmp = lBmpOffs; if (!WriteGroup(lGroupOffs, &group)) goto exitpt; } rv = TRUE; exitpt: return rv; } /*++ * Ascii version of AddBitMap --*/ BOOL AddBitMapA(PBMPENTRY pBitmap, LPCSTR szAText) { WCHAR szWText[MAX_STRING_LENGTH]; BOOL rv = FALSE; INT ccAText = strlen(szAText); if (!strlen(szAText) || !MultiByteToWideChar( CP_ACP, MB_ERR_INVALID_CHARS, szAText, -1, szWText, MAX_STRING_LENGTH - 1)) goto exitpt; rv = AddBitMap(pBitmap, szWText); exitpt: return rv; } /*++ * Function: * DeleteBitmapByPointer (Thread dep) * Description: * Deletes a bitmap identified by pointer * Arguments: * nBmpOffset - the pointer * Return value: * TRUE on success * Called by: * glyphspy.c --*/ BOOL DeleteBitmapByPointer(FOFFSET nBmpOffs) { BMPENTRY Bitmap; BOOL rv = FALSE; if (!g_hDB || !nBmpOffs) goto exitpt; if (!ReadBitmapHeader(nBmpOffs, &Bitmap)) goto exitpt; if (Bitmap.bDeleted) goto exitpt; Bitmap.bDeleted = TRUE; if (!WriteBitmapHeader(nBmpOffs, &Bitmap)) goto exitpt; g_bNeedToPack = TRUE; rv = TRUE; exitpt: return rv; } /*++ * Function: * DeleteGroupByPointer (Thread dep) * Description: * Deletes group with the same ID by pointer * Arguments: * nGrpOffs - the pointer * Return value: * TRUE on success * Called by: * glyphspy.c --*/ BOOL DeleteGroupByPointer(FOFFSET nGrpOffs) { GROUPENTRY Group; BOOL rv = FALSE; if (!g_hDB) goto exitpt; if (!ReadGroup(nGrpOffs, &Group)) goto exitpt; if (Group.bDeleted) goto exitpt; Group.bDeleted = TRUE; if (!WriteGroup(nGrpOffs, &Group)) goto exitpt; g_bNeedToPack = TRUE; rv = TRUE; exitpt: return rv; } /*++ * Function: * DeleteBitmap (Thread dep) * Description: * Deletes a bitmap identified by ID and comment * Arguments: * szWText - the ID * szComment - the comment * Return value: * TRUE on success --*/ BOOL DeleteBitmap(LPWSTR szWText, char *szComment) { FOFFSET nBmpOffs; BOOL rv = FALSE; BMPENTRY Bitmap; if (!g_hDB) goto exitpt; nBmpOffs = FindBitmap(szWText, szComment); if (nBmpOffs == -1) goto exitpt; if (!ReadBitmapHeader(nBmpOffs, &Bitmap)) goto exitpt; if (Bitmap.bDeleted) goto exitpt; Bitmap.bDeleted = TRUE; if (!WriteBitmapHeader(nBmpOffs, &Bitmap)) goto exitpt; g_bNeedToPack = TRUE; rv = TRUE; exitpt: return rv; } /*++ * Function: * _PackDB (Thread dep) * Description: * Copies the all database in new file removing * the deleted entrys * If it fails leaves the old file * Called by: * CloseDB --*/ void _PackDB(void) { GROUPENTRY group; FOFFSET lGrpOffs = 0; FOFFSET lBmpOffs; if (!g_bNeedToPack) goto exitpt; g_hTempDB = _open(TEMPDB_NAME, _O_RDWR|_O_TRUNC|_O_CREAT|_O_BINARY, _S_IREAD|_S_IWRITE); if (g_hTempDB == -1) goto exitpt; do { if (!ReadGroup(lGrpOffs, &group)) goto exitpt; if (!group.bDeleted) { lBmpOffs = group.FOffsBmp; while(lBmpOffs) { BMPENTRY Bitmap; if (!ReadBitmapHeader(lBmpOffs, &Bitmap)) goto exitpt; if (!Bitmap.bDeleted) { PBMPENTRY pBmp = ReadBitmap(lBmpOffs); if (pBmp) { int hSwap; hSwap = g_hDB; g_hDB = g_hTempDB; g_hTempDB = hSwap; AddBitMap(pBmp, group.WText); hSwap = g_hDB; g_hDB = g_hTempDB; g_hTempDB = hSwap; FreeBitmap(pBmp); } } lBmpOffs = Bitmap.FOffsNext; } } lGrpOffs = group.FOffsNext; } while (lGrpOffs); _close(g_hTempDB); _close(g_hDB); remove(DB_NAME); rename(TEMPDB_NAME, DB_NAME); exitpt: ; } /*++ * Function: * _CollectGroups (Thread dep) * Description: * Callback function wich collects all groups * from the database in linked list * Arguments: * nOffs - pointer to group record in the database * pGroup - ghe group * ppList - the list * Return value: * TRUE on success * Called by: * GetGroupList thru EnumerateGroups --*/ BOOL _cdecl _CollectGroups(FOFFSET nOffs, PGROUPENTRY pGroup, PGROUPENTRY *ppList) { BOOL rv = FALSE; PGROUPENTRY pNewGrp, pIter, pPrev; if (!pGroup) goto exitpt; pNewGrp = malloc(sizeof(*pNewGrp)); if (!pNewGrp) goto exitpt; memcpy(pNewGrp, pGroup, sizeof(*pNewGrp)); // Add to the end of the queue pNewGrp->pNext = NULL; pPrev = NULL; pIter = *ppList; while(pIter) { pPrev = pIter; pIter = pIter->pNext; } if (pPrev) pPrev->pNext = pNewGrp; else (*ppList) = pNewGrp; rv = TRUE; exitpt: return rv; } /*++ * Function: * GetGroupList * Description: * Gets all groups from the database * Return value: * linked list * Called by: * InitCache, glyphspy.c --*/ PGROUPENTRY GetGroupList(VOID) { PGROUPENTRY pList = NULL; EnumerateGroups(_CollectGroups, &pList); return pList; } /*++ * Function: * FreeGroupList * Description: * Frees the list allocated in GetGroupList * Arguments: * pList - the list * Called by: * DeleteCache, glyphspy.c --*/ VOID FreeGroupList(PGROUPENTRY pList) { PGROUPENTRY pTmp, pIter = pList; while(pIter) { pTmp = pIter; pIter = pIter->pNext; free(pTmp); } } /*++ * Function: * _CollectBitmaps (thread dep) * Description: * collects bitmaps in linked list * Arguments: * nOffs - pointer in the file * pBitmap - the bitmap * ppList - the list * Return value: * TRUE on success * Called by: * GetBitmapList thru EnumerateBitmaps --*/ BOOL _cdecl _CollectBitmaps(FOFFSET nOffs,PBMPENTRY pBitmap, PBMPENTRY *ppList) { BOOL rv = FALSE; PBMPENTRY pNewBmp, pIter, pPrev; if (!pBitmap) goto exitpt; pNewBmp = malloc(sizeof(*pNewBmp)); if (!pNewBmp) goto exitpt; memcpy(pNewBmp, pBitmap, sizeof(*pNewBmp)); if (pNewBmp->nDataSize) { pNewBmp->pData = malloc(pNewBmp->nDataSize); if (!pNewBmp->pData) goto exitpt1; memcpy(pNewBmp->pData, pBitmap->pData, pNewBmp->nDataSize); } else pNewBmp->pData = NULL; // Add to the end of the queue pNewBmp->pNext = NULL; pPrev = NULL; pIter = *ppList; while(pIter) { pPrev = pIter; pIter = pIter->pNext; } if (pPrev) pPrev->pNext = pNewBmp; else (*ppList) = pNewBmp; rv = TRUE; exitpt: return rv; exitpt1: free(pNewBmp); return FALSE; } /*++ * Function: * GetBitmapList (thread dep) * Description: * Gets all bitmaps within a group * Return value: * linked list * Called by: * Glyph2String, BitmapCacheLookup, glyphspy.c --*/ PBMPENTRY GetBitmapList(HDC hDC, FOFFSET nOffs) { PBMPENTRY pList = NULL; PBMPENTRY pIter; EnumerateBitmaps(nOffs, _CollectBitmaps, &pList); pIter = pList; while(pIter) { // Create bitmaps if needed if (hDC) { if (!pIter->bmiSize) pIter->hBitmap = CreateBitmap(pIter->xSize, pIter->ySize, 1, 1, pIter->pData); else { pIter->hBitmap = CreateDIBitmap(hDC, (BITMAPINFOHEADER *) pIter->pData, CBM_INIT, ((BYTE *)(pIter->pData)) + pIter->bmiSize, (BITMAPINFO *) pIter->pData, DIB_PAL_COLORS); DeleteDC(hDC); } } else pIter->hBitmap = NULL; pIter = pIter->pNext; } return pList; } /*++ * Function: * FreeBitmapList * Description: * Deletes resources allocated by GetBitmapList * Arguments: * pList - the list * Called by: * DeleteCache, glyphspy.c --*/ VOID FreeBitmapList(PBMPENTRY pList) { PBMPENTRY pTmp, pIter = pList; while(pIter) { pTmp = pIter; pIter = pIter->pNext; if (pTmp->hBitmap) DeleteObject(pTmp->hBitmap); if ( pTmp->pData ) free( pTmp->pData ); free(pTmp); } }