#include "PartFile.h" #include "PatchClient.h" #include "Configuration.h" #include "GSEGuard.h" CPartFile::CPartFile(CPatchVersion patchVersion, CPatchClient& patchClient) : m_DownloadVersion(patchVersion), m_PatchClient(patchClient) { string strPatchName = m_DownloadVersion.VersionToFileName(); m_PartFileName = strPatchName + DOWNLOAD_POST; m_PatchFileName = strPatchName + PATCHFILE_POST; memset(m_FileHash, 0, MAX_HASHVALUE_LEN); m_TranferSize = 0; m_LastTime = GetTickCount(); m_DownloadSpeed = 0; m_UploadSpeed = 0; m_IsSaved = false; memset(m_PartData, 0, PARTSIZE); m_Thread = NULL; } CPartFile::~CPartFile(void) { if (m_Thread) { GSEDestroyThread(m_Thread); m_Thread = 0; } } int CPartFile::InitDownload() { //判断是否是未下载完成的文件 if (m_PatchClient.IsPatchExist(m_PartFileName.c_str())) { //加载Part文件 LoadFromFile(); } else { m_PatchClient.ReqPatchFileInfo(m_DownloadVersion); } return 0; } int CPartFile::SavePartFile(uint32 nStartPos, uint32 nDataLen, const char* pData) { //因为获取到的数据有可能跨越一个Part边界,因此就存在可能更新多个Part的可能, nDataLen不会大于一个PartSize uint16 nPartSIndex = nStartPos / PARTSIZE; uint16 nPartEIndex = (nStartPos + nDataLen) / PARTSIZE; assert(nPartEIndex - nPartSIndex <= 1 ); assert(nPartEIndex < m_DownloadGaps.size()); //跨块 int nDataPos = 0; nDataPos = nStartPos - nPartSIndex * PARTSIZE; if (nStartPos + nDataLen - 1 > m_DownloadGaps[nPartSIndex].EndPos) { memcpy(m_PartData + nDataPos, pData, PARTSIZE - nDataPos); unsigned char szMd5[MAX_HASHVALUE_LEN]; CreateMD5((unsigned char*)m_PartData, PARTSIZE, szMd5, MAX_HASHVALUE_LEN); if (memcmp(szMd5, m_PartHashs[nPartSIndex].c_str(), MAX_HASHVALUE_LEN) != 0) { //收到的块已经损坏,放进坏块列表 GAP gap; gap.StartPos = nPartSIndex * PARTSIZE; gap.EndPos = gap.StartPos + PARTSIZE - 1; m_BadPartList.push_back(gap); } else { WriteBufToDisk(nPartSIndex); } memcpy(m_PartData, pData + PARTSIZE - nDataPos, nDataLen - (PARTSIZE - nDataPos)); } else if (nStartPos + nDataLen - 1 == m_DownloadGaps[nPartSIndex].EndPos) { memcpy(m_PartData + nDataPos, pData, nDataLen); unsigned char szMd5[MAX_HASHVALUE_LEN]; CreateMD5((unsigned char*)m_PartData, m_DownloadGaps[nPartSIndex].EndPos == m_FileSize - 1 ? m_FileSize - nPartSIndex * PARTSIZE : PARTSIZE, szMd5, MAX_HASHVALUE_LEN); if (memcmp(szMd5, m_PartHashs[nPartSIndex].c_str(), MAX_HASHVALUE_LEN) != 0) { //收到的块已经损坏,放进坏块列表 GAP gap; gap.StartPos = nPartSIndex * PARTSIZE; gap.EndPos = gap.StartPos + m_DownloadGaps[nPartSIndex].EndPos == m_FileSize - 1 ? m_FileSize - nPartSIndex * PARTSIZE - 1 : PARTSIZE; m_BadPartList.push_back(gap); } else { WriteBufToDisk(nPartSIndex); } } else { memcpy(m_PartData + nDataPos, pData, nDataLen); } return 0; } int CPartFile::LoadFromFile() { //读取part文件 string strFullName = g_Configuration.GetPatchDir() + PATH_DELIMITER + m_PartFileName; FILE* pFile = _fsopen(strFullName.c_str(), "r+b", _SH_DENYWR); if (!pFile) { if (errno == ENOENT) { pFile = _fsopen(strFullName.c_str(), "w+b", _SH_DENYWR); } else { return -1; } } fseek(pFile, 0, SEEK_SET); //读文件头 FILEHEADER fileHeader; memset(&fileHeader, 0, sizeof(FILEHEADER)); size_t nRead = fread(&fileHeader, sizeof(FILEHEADER), 1, pFile); if (nRead < 1) { fclose(pFile); return -1; } m_DownloadVersion.SetVersion(fileHeader.MajorVersion, fileHeader.MinVersion, fileHeader.AddVersion); m_FileSize = fileHeader.nFileSize; memcpy(m_FileHash, fileHeader.FileHash, MAX_HASHVALUE_LEN); char szBuf[MAX_HASHVALUE_LEN]; //读hash表 for (uint32 nIndex = 0; nIndex < fileHeader.PartCount; nIndex++) { fread(szBuf, 1, MAX_HASHVALUE_LEN, pFile); string strHash; strHash.append(szBuf, MAX_HASHVALUE_LEN); m_PartHashs.push_back(strHash); } //读下载信息 for (uint32 nIndex = 0; nIndex < fileHeader.PartCount; nIndex++) { GAP gap; fread(&gap, sizeof(GAP), 1, pFile); if (gap.StartPos != gap.EndPos) { m_TranferSize += gap.EndPos - gap.StartPos + 1; } m_DownloadGaps.push_back(gap); } fclose(pFile); //验证文件 CheckFile(); //计算已下载的数据量 m_TranferSize = m_FileSize - m_TranferSize - 1; if (m_TranferSize == m_FileSize - 1) { m_IsSaved = true; } else { m_Thread = GSECreateThread(this, false); } //开始进行下载 m_PatchClient.Download(); return 0; } int CPartFile::SetPatchInfo(CPatchVersion patchVersion, uint32 nFileSize, uint8 *FileHash) { if (patchVersion != m_DownloadVersion) { return -1; } m_FileSize = nFileSize; memcpy(m_FileHash, FileHash, MAX_HASHVALUE_LEN); return 0; } int CPartFile::AddPartHash(uint8* PartHash) { string strHash; strHash.append((char*)PartHash, MAX_HASHVALUE_LEN); m_PartHashs.push_back(strHash); return 0; } int CPartFile::OnPartComplete() { //判断磁盘空间 //创建Patch文件 string strFullName = g_Configuration.GetPatchDir() + PATH_DELIMITER + m_PatchFileName; int64 FreeBytesToCaller, TotalBytes, FreeBytes; if (GetDiskFreeSpaceEx(NULL, (PULARGE_INTEGER)&FreeBytesToCaller, (PULARGE_INTEGER)&TotalBytes, (PULARGE_INTEGER)&FreeBytes)) { if (FreeBytes < m_FileSize) { m_PatchClient.GetClientEvent()->OnError(MUXPATCH_DISK_NOTENOUGH_ERROR); return 0; } } HANDLE hFile = CreateFile(strFullName.c_str(), GENERIC_READ | GENERIC_WRITE, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, 0, NULL); if (hFile != INVALID_HANDLE_VALUE) { DWORD dwRet = 0; if (!DeviceIoControl(hFile, FSCTL_SET_SPARSE, NULL, 0, NULL, 0, &dwRet, NULL)) { } } SetFilePointer(hFile, m_FileSize - 1, NULL, FILE_BEGIN); DWORD nWrite; char ch = 0; WriteFile(hFile, &ch, 1, &nWrite, NULL); SetEndOfFile(hFile); CloseHandle(hFile); //创建part文件 strFullName = g_Configuration.GetPatchDir() + PATH_DELIMITER + m_PartFileName; FILE* pFile = _fsopen(strFullName.c_str(), "r+b", _SH_DENYWR); if (!pFile) { if (errno == ENOENT) { pFile = _fsopen(strFullName.c_str(), "w+b", _SH_DENYWR); } } fseek(pFile, 0, SEEK_SET); //写文件头 FILEHEADER fileHeader; memset(&fileHeader, 0, sizeof(FILEHEADER)); m_DownloadVersion.GetVersionInfo(fileHeader.MajorVersion, fileHeader.MinVersion, fileHeader.AddVersion); memcpy(fileHeader.FileHash, m_FileHash, MAX_HASHVALUE_LEN); fileHeader.nFileSize = m_FileSize; fileHeader.PartCount = m_PartHashs.size(); fwrite(&fileHeader, sizeof(FILEHEADER), 1, pFile); //写Hash表 for (size_t nIndex = 0; nIndex < m_PartHashs.size(); nIndex++) { fwrite(m_PartHashs[nIndex].c_str(), 1, MAX_HASHVALUE_LEN, pFile); } //写块下载信息 for (size_t nIndex = 0; nIndex < m_PartHashs.size(); nIndex++) { GAP gap; gap.StartPos = nIndex * PARTSIZE; if (gap.StartPos + PARTSIZE > m_FileSize) { gap.EndPos = m_FileSize - 1; } else { gap.EndPos = gap.StartPos + PARTSIZE - 1; } fwrite(&gap, sizeof(gap), 1, pFile); m_DownloadGaps.push_back(gap); } fclose(pFile); m_TranferSize = 0; memset(m_PartData, 0, PARTSIZE); //发起下载请求 m_Thread = GSECreateThread(this, false); m_PatchClient.Download(); return 0; } void CPartFile::GetDownloadPos(uint32& nStartPos, uint32& nEndPos) { nStartPos = 0; nEndPos = 0; for (size_t nIndex = 0; nIndex < m_DownloadGaps.size(); nIndex++) { GAP gap = m_DownloadGaps[nIndex]; if (gap.StartPos < gap.EndPos) { nStartPos = gap.StartPos; nEndPos = gap.EndPos; break; } } } int CPartFile::UpdateDownload(uint32 nStartPos, uint32 nDataLen, const char *pData) { //计算下载速度 if (GetTickCount() == m_LastTime) { m_DownloadSpeed = 0; } else { m_DownloadSpeed = nDataLen / (GetTickCount() - m_LastTime); } //m_LastTime = GetTickCount(); //把数据放入队列中,让IO线程定时写磁盘 { IODATA ioData; memset(&ioData, 0, sizeof(IODATA)); ioData.nStartPos = nStartPos; ioData.nDataLen = nDataLen; ioData.pData = GSEMemAlloc(char, nDataLen); memcpy(ioData.pData, pData, nDataLen); GSEGuard gseGuard(&m_DataLock); m_DowndloadData.push_back(ioData); } m_TranferSize += nDataLen; //因为获取到的数据有可能跨越一个Part边界,因此就存在可能更新多个Part的可能, nDataLen不会大于一个PartSize uint16 nPartSIndex = nStartPos / PARTSIZE; uint16 nPartEIndex = (nStartPos + nDataLen) / PARTSIZE; assert(nPartEIndex - nPartSIndex <= 1 ); assert(nPartEIndex < m_DownloadGaps.size()); assert(m_DownloadGaps[nPartSIndex].StartPos == nStartPos); if (m_DownloadGaps[nPartSIndex].StartPos + nDataLen > m_DownloadGaps[nPartSIndex].EndPos) { m_DownloadGaps[nPartEIndex].StartPos += nDataLen - (m_DownloadGaps[nPartSIndex].EndPos - m_DownloadGaps[nPartSIndex].StartPos + 1); m_DownloadGaps[nPartSIndex].StartPos = m_DownloadGaps[nPartSIndex].EndPos; } else { m_DownloadGaps[nPartSIndex].StartPos += nDataLen; } return 0; } int CPartFile::OnComplete() { if (m_BadPartList.size() != 0) { return -1; } string strFullName = g_Configuration.GetPatchDir() + PATH_DELIMITER + m_PartFileName; DeleteFile(strFullName.c_str()); return 0; } void CPartFile::ThreadProc() { while (!m_IsSaved) { vector vect; { GSEGuard gseGuard(&m_DataLock); vect.swap(m_DowndloadData); } for (size_t nIndex = 0; nIndex < vect.size(); nIndex++) { //保存进文件 IODATA ioData = vect[nIndex]; SavePartFile(ioData.nStartPos, ioData.nDataLen, ioData.pData); GSEMemFree(ioData.pData); } Sleep(1); } } void CPartFile::Terminate() { } int CPartFile::CheckFile() { //从已下载的文件中读取数据,验证 string strFullName = g_Configuration.GetPatchDir() + PATH_DELIMITER + m_PatchFileName; FILE* pFile = _fsopen(strFullName.c_str(), "rb", _SH_DENYWR); if (!pFile) { int nErrCode = errno; if (nErrCode == ENOENT) { pFile = _fsopen(strFullName.c_str(), "w+b", _SH_DENYWR); } } for (int nPartIndex = 0; nPartIndex < m_DownloadGaps.size(); nPartIndex++) { //该部分已经下载完整 if (m_DownloadGaps[nPartIndex].StartPos == m_DownloadGaps[nPartIndex].EndPos) { int nStartPos = nPartIndex * PARTSIZE; int nReadLen = 0; if (nPartIndex == m_DownloadGaps.size() - 1) { nReadLen = m_DownloadGaps[nPartIndex].EndPos - nStartPos + 1; } else { nReadLen = PARTSIZE; } fseek(pFile, 0, SEEK_SET); fseek(pFile, nStartPos, SEEK_SET); if (nReadLen == fread(m_PartData, 1, nReadLen, pFile)) { char szBuf[MAX_HASHVALUE_LEN]; CreateMD5((unsigned char*)m_PartData, nReadLen, (unsigned char*)szBuf, MAX_HASHVALUE_LEN); if (memcmp(szBuf, m_PartHashs[nPartIndex].c_str(), MAX_HASHVALUE_LEN)) { m_DownloadGaps[nPartIndex].StartPos = nStartPos; m_TranferSize -= nReadLen; } } else { //报告错误 按照目前的设定,不应该出现这个错误 } } } fclose(pFile); return 0; } int CPartFile::WriteBufToDisk(uint32 nPartIndex) { //把下载的数据保存到文件中 string strFullName = g_Configuration.GetPatchDir() + PATH_DELIMITER + m_PatchFileName; FILE* pFile = _fsopen(strFullName.c_str(), "r+b", _SH_DENYWR); if (!pFile) { int nErrCode = errno; if (nErrCode == ENOENT) { pFile = _fsopen(strFullName.c_str(), "w+b", _SH_DENYWR); } } int nPartSPos = nPartIndex * PARTSIZE; fseek(pFile, nPartSPos, SEEK_SET); fwrite(m_PartData, 1, nPartIndex == m_PartHashs.size() - 1 ? m_FileSize - nPartIndex * PARTSIZE : PARTSIZE, pFile); fclose(pFile); pFile = NULL; //更新已经下载的数据 strFullName = g_Configuration.GetPatchDir() + PATH_DELIMITER + m_PartFileName; pFile = _fsopen(strFullName.c_str(), "r+b", _SH_DENYWR); if (!pFile) { if (errno == ENOENT) { pFile = _fsopen(strFullName.c_str(), "w+b", _SH_DENYWR); } } uint32 nSpanLen = sizeof(FILEHEADER) + m_PartHashs.size() * MAX_HASHVALUE_LEN + nPartIndex * sizeof(GAP); fseek(pFile, nSpanLen, SEEK_SET); GAP gap; fread(&gap, sizeof(GAP), 1, pFile); gap.StartPos = gap.EndPos; long nPos = sizeof(GAP); fseek(pFile, -nPos, SEEK_CUR); fwrite(&gap, sizeof(GAP), 1, pFile); fclose(pFile); if (nPartIndex == m_PartHashs.size() - 1) { m_IsSaved = true; } return 0; }