//得到包中某个文件的大小 unsigned int PackFile::getFileSize(const char* szFileName) const { assert(szFileName); if(!szFileName || szFileName[0]==0) { setLastError(AXP_ERR_PARAM); return 0; } //在hash表中搜索 int nHashPos = getStringPosInHashTable(normaliseName(szFileName, true, true).c_str()); if(nHashPos < 0) { setLastError(AXP_ERR_FILE_NOTFOUND, "%s", szFileName); return 0; } const FILE_HASHNODE& hashNode = m_hashTable[nHashPos]; //得到Block数据 unsigned int nBlockIndex = getHashNodeBlockIndex(hashNode); assert(nBlockIndex < (unsigned int)m_blockTable.size()); const FILE_BLOCKNODE& blockNode = m_blockTable[nBlockIndex]; return blockNode.nBlockSize; }
bool PackFile::removeFile(const char* szFileInPak, bool bSaveAtOnce) { assert(szFileInPak); if(!szFileInPak || szFileInPak[0]==0 || !m_hPakFile) { setLastError(AXP_ERR_PARAM); return false; } //只读模式不支持 if(m_bConst) { setLastError(AXP_ERR_FILE_ACCESS); return false; } //在hash表中搜索 int nHashPos = getStringPosInHashTable(normaliseName(szFileInPak, true, true).c_str()); if(nHashPos < 0) { setLastError(AXP_ERR_FILE_NOTFOUND, "%s", szFileInPak); return false; } FILE_HASHNODE& hashNode = m_hashTable[nHashPos]; //Block Node unsigned int nBlockPos = getHashNodeBlockIndex(hashNode); FILE_BLOCKNODE& blockNode = m_blockTable[nBlockPos]; //设定值 setBlockNodeUsed(blockNode, false); setHashNodeExists(hashNode, false); //保存空闲快 m_mapFreeBlock.insert(std::make_pair(upBoundBlockSize(blockNode.nBlockSize), nBlockPos)); m_fileHead.nData_HoleSize += upBoundBlockSize(blockNode.nBlockSize); //################################### //!! 磁盘操作开始 保存文件 //################################### if(!writeBlockNode((unsigned int)nBlockPos)) return false; if(!writeHashNode((unsigned int)nHashPos)) return false; if(!writeFileHead()) return false; if(bSaveAtOnce) { FlushFileBuffers(m_hPakFile); } //################################### //!!磁盘操作结束 //################################### return true; }
//某文件在包中是否存在 bool PackFile::isFileExists(const char* szFileName) const { assert(szFileName); if(!szFileName || szFileName[0]==0) { setLastError(AXP_ERR_PARAM); return false; } //在hash表中搜索 int nHashPos = getStringPosInHashTable(normaliseName(szFileName, true, true).c_str()); return nHashPos >= 0; }
/********************************************** 在Hash表中创建一个字符串的位置, 如果已经存在,则会被替换,如果找不到位置,返回-1 ***********************************************/ int PackFile::allocStringPosInHashTable(const char* szString) { assert(szString); if(!szString || szString[0]==0 ) return -1; //查看是否已经存在 int nOldPos = getStringPosInHashTable(szString); if(nOldPos >= 0) return nOldPos; unsigned int nHash = _hashString(HT_OFFSET, szString); // for pos unsigned int nHashA = _hashString(HT_A, szString); // for check unsigned int nHashB = _hashString(HT_B, szString); // for check again unsigned int nHashStart = nHash%HASH_TABLE_SIZE; register unsigned int nHashPos = nHashStart; do { FILE_HASHNODE& hashNode = m_hashTable[nHashPos]; //该位置为空 if(!getHashNodeExists(hashNode)) { hashNode.nHashA = nHashA; hashNode.nHashB = nHashB; return (int)nHashPos; } //查找下一个 nHashPos = (nHashPos+1)%HASH_TABLE_SIZE; if(nHashPos == nHashStart)break; //回到了起点,退出,防止死循环 }while(true); //Hash表已经满了 return -1; }
//以文件流方式打开包中的一个文件 IStream* PackFile::openFile(const char* szFileName) { //进入关键段,考虑多线程访问 AUTO_LOCK autoLock(&m_secFile); assert(szFileName && m_hPakFile); if(!szFileName || szFileName[0]==0 || m_hPakFile==0) { setLastError(AXP_ERR_PARAM); return false; } //在hash表中搜索 int nHashPos = getStringPosInHashTable(normaliseName(szFileName, true, true).c_str()); if(nHashPos < 0) { setLastError(AXP_ERR_FILE_NOTFOUND, szFileName); return 0; } const FILE_HASHNODE& hashNode = m_hashTable[nHashPos]; //得到Block数据 unsigned int nBlockIndex = getHashNodeBlockIndex(hashNode); assert(nBlockIndex < (unsigned int)m_blockTable.size()); const FILE_BLOCKNODE& blockNode = m_blockTable[nBlockIndex]; //得到当前线程id unsigned int uCurrentThread = (unsigned int)::GetCurrentThreadId(); FileHandleMap::iterator itHandle = m_mapFileHandle.find(uCurrentThread); //是否已经有句柄 if(itHandle == m_mapFileHandle.end()) { //分配一个句柄 HANDLE hFile = ::CreateFile(m_strFileName.c_str(), GENERIC_READ, FILE_SHARE_READ|FILE_SHARE_WRITE, 0, OPEN_EXISTING, FILE_ATTRIBUTE_READONLY | FILE_ATTRIBUTE_SYSTEM | FILE_FLAG_SEQUENTIAL_SCAN, 0); if(hFile == INVALID_HANDLE_VALUE) { setLastError(AXP_ERR_FILE_ACCESS, "File=%s, WinErr=%d", m_strFileName.c_str(), ::GetLastError()); return 0; } //加入map m_mapFileHandle.insert(std::make_pair(uCurrentThread, hFile)); itHandle = m_mapFileHandle.find(uCurrentThread); assert(itHandle != m_mapFileHandle.end()); } //Alloc a datastream DataStream* pNewStream = new DataStream(this, itHandle->second, blockNode.nDataOffset, blockNode.nBlockSize, blockNode.nBlockSize<=STREAM_PREREAD_SIZE); m_listStream.push_back(pNewStream); return (IStream*)pNewStream; }
//文件自检功能 bool PackFile::selfCheck(SELFCHECK_CALLBACK callBack) { if(0==m_hPakFile) { setLastError(AXP_ERR_PARAM); return false; } //编辑模式错误 if(m_fileHead.nEditFlag != 0) { setLastError(AXP_ERR_FILE_EDITMODE); return false; } //打开List文件 IStream* pListStream = openFile(LIST_FILENAME); if(!pListStream) return false; //skip first line pListStream->skipLine(); //read second line char szTempLine[MAX_PATH*4] = {0}; int nLineSize = pListStream->readLine(szTempLine, MAX_PATH*4); int nFileCount = atoi(szTempLine); //记录已经检查过的HashNode索引 std::set< unsigned int > setHashNode; //记录已经检查过的BlockNode索引 std::set< unsigned int > setBlockNode; //逐行读文件 int nFileListSize = 0; while (!pListStream->eof()) { int nLineLength = pListStream->readLine(szTempLine, MAX_PATH*4); if(0 == nLineLength) { pListStream->close(); setLastError(AXP_ERR_FILE_FORMAT, "list file second line=0"); return false; } //分析文件描述 std::vector< std::string > vStringVec; convertStringToVector(szTempLine, vStringVec, "|", true, false); if(vStringVec.size() != 3) { pListStream->close(); setLastError(AXP_ERR_FILE_FORMAT, "list file=%s", szTempLine); return false; } //获得文件信息 std::string& strFileName = vStringVec[0]; unsigned int nFileSize, nFileCRC; sscanf(vStringVec[1].c_str(), "%08X", &(nFileSize)); sscanf(vStringVec[2].c_str(), "%08X", &(nFileCRC)); //读入文件 IStream* pFileStream = openFile(strFileName.c_str()); //打开文件 if(!pFileStream) { pListStream->close(); return false; } unsigned int nStreamSize = pFileStream->size(); if(nStreamSize != nFileSize) { pFileStream->close(); pListStream->close(); setLastError(AXP_ERR_FILE_FORMAT, "file=%s, sizedif(%d!=%d)", strFileName.c_str(), nStreamSize, nFileSize); return false; } char* pTempBuf = new char[nStreamSize]; if(nStreamSize != pFileStream->read(pTempBuf, nStreamSize)) { pFileStream->close(); pListStream->close(); setLastError(AXP_ERR_FILE_READ, "file=%s", strFileName.c_str()); } pFileStream->close(); pFileStream=0; //计算文件实际crc unsigned int nStreamCRC; crcMemory32(pTempBuf, nStreamSize, nStreamCRC); delete[] pTempBuf; pTempBuf=0; if(nStreamCRC != nFileCRC) { // pFileStream->close(); pListStream->close(); setLastError(AXP_ERR_FILE_FORMAT, "file=%s, crcdif(%d!=%d)", strFileName.c_str(), nStreamCRC, nFileCRC); return false; } //保存分析过的文件Hash和Block数据 //得到Hash数据 int nHashPos = getStringPosInHashTable(normaliseName(strFileName).c_str()); setHashNode.insert(nHashPos); //得到Block数据 unsigned int nBlockIndex = getHashNodeBlockIndex(m_hashTable[nHashPos]); setBlockNode.insert(nBlockIndex); nFileListSize++; }; pListStream->close(); pListStream=0; //文件个数检查 if(nFileListSize != nFileCount) { setLastError(AXP_ERR_FILE_FORMAT, "file countdif(%d!=%d)", nFileListSize, nFileCount); return false; } //得到(list)的Hash数据 int nListFileHashPos = getStringPosInHashTable(LIST_FILENAME); //得到(list)的Block数据 unsigned int nListFileBlockIndex = getHashNodeBlockIndex(m_hashTable[nListFileHashPos]); //检查是否有未引用的Hash数据 for(int i=0; i<HASH_TABLE_SIZE; i++) { const FILE_HASHNODE& hashNode = m_hashTable[i]; if(!getHashNodeExists(hashNode)) continue; if(setHashNode.find(i) != setHashNode.end()) continue; if(i==nListFileHashPos) continue; //未引用的Hash数据 setLastError(AXP_ERR_FILE_FORMAT, "unref hashnode(%d)", i); return false; } //检查是否有未引用的BlockNode数据 for(int i=0; i<(int)m_blockTable.size(); i++) { const FILE_BLOCKNODE& blockNode = m_blockTable[i]; if(!getBlockNodeUsed(blockNode)) continue; if(setBlockNode.find(i) != setBlockNode.end()) continue; if(i==nListFileBlockIndex) continue; //未引用的Hash数据 setLastError(AXP_ERR_FILE_FORMAT, "unref blocknode(%d)", i); return false; } return true; }