bool C4Network2Res::SetLoad(const C4Network2ResCore &nCore) // by main thread { Clear(); CStdLock FileLock(&FileCSec); // must be loadable if (!nCore.isLoadable()) return false; // save core, set chunks Core = nCore; Chunks.SetIncomplete(Core.getChunkCnt()); // create temporary file if (!pParent->FindTempResFileName(Core.getFileName(), szFile)) return false; #ifdef C4NET2RES_DEBUG_LOG // log Application.InteractiveThread.ThreadLogS("Network: Resource: loading %d:%s to file %s", Core.getID(), Core.getFileName(), szFile); #endif // set standalone (result is going to be binary-compatible) SCopy(szFile, szStandalone, sizeof(szStandalone) - 1); // set flags fDirty = false; fTempFile = true; fStandaloneFailed = false; fRemoved = false; iLastReqTime = time(NULL); fLoading = true; // No discovery yet iDiscoverStartTime = 0; return true; }
bool C4Network2Res::FinishDerive() // by main thread { // All changes have been made. Register this resource with a new ID. // security if (!isAnonymous()) return false; CStdLock FileLock(&FileCSec); // Save back data int32_t iDerID = Core.getDerID(); char szName[_MAX_PATH+1]; SCopy(Core.getFileName(), szName, _MAX_PATH); char szFileC[_MAX_PATH+1]; SCopy(szFile, szFileC, _MAX_PATH); // Set by file if (!SetByFile(szFileC, fTempFile, getType(), pParent->nextResID(), szName)) return false; // create standalone if (!GetStandalone(NULL, 0, true)) return false; // Set ID Core.SetDerived(iDerID); // announce derive pParent->getIOClass()->BroadcastMsg(MkC4NetIOPacket(PID_NetResDerive, Core)); // derivation is dirty bussines fDirty = true; // ok return true; }
int mtFile::runRead( mtThreadWork::DataUser* pkDataUser ) { char* pcFile = getFileName(pkDataUser->pcAccount); if (NULL == pcFile) { return mtProtocol::E_RESULT_FALT_FILE_INVALIDE_PATH; } if (0 == FileOpen(&pkDataUser->hFile, pcFile)) { return mtProtocol::E_RESULT_FALT_FILE_NOT_EXIST; } pkDataUser->iFileOffset = getFileOffset(pkDataUser->pcAccount); pkDataUser->ulTransferBytes = getDataFileBytes(); pkDataUser->kOverlapped.Offset = pkDataUser->iFileOffset; pkDataUser->eOverlappedType = mtThread::E_OLT_LOCKFILEEx; pkDataUser->iFileRunType = E_FILE_RUN_READ_FILE; if (0 == FileLock(pkDataUser->hFile, pkDataUser->ulTransferBytes, &pkDataUser->kOverlapped)) { FileClose(&pkDataUser->hFile); return mtProtocol::E_RESULT_FALT_FILE_LOCK; } return mtProtocol::E_RESULT_SUCCESS; }
bool C4Network2Res::SetByFile(const char *strFilePath, bool fTemp, C4Network2ResType eType, int32_t iResID, const char *szResName, bool fSilent) { CStdLock FileLock(&FileCSec); // default resource name: relative path if (!szResName) szResName = Config.AtRelativePath(strFilePath); SCopy(strFilePath, szFile, sizeof(szFile)-1); // group? C4Group Grp; if (Reloc.Open(Grp, strFilePath)) return SetByGroup(&Grp, fTemp, eType, iResID, szResName, fSilent); // so it needs to be a file StdStrBuf szFullFile; if (!Reloc.LocateItem(szFile, szFullFile)) { if (!fSilent) LogF("SetByFile: file %s not found!", strFilePath); return false; } // calc checksum uint32_t iCRC32; if (!GetFileCRC(szFullFile.getData(), &iCRC32)) return false; #ifdef C4NET2RES_DEBUG_LOG // log LogSilentF("Network: Resource: complete %d:%s is file %s (%s)", iResID, szResName, szFile, fTemp ? "temp" : "static"); #endif // set core Core.Set(eType, iResID, Config.AtRelativePath(szFullFile.getData()), iCRC32); // set own data fDirty = true; fTempFile = fTemp; fStandaloneFailed = false; fRemoved = false; iLastReqTime = time(NULL); fLoading = false; // ok return true; }
bool C4Network2Res::SetByGroup(C4Group *pGrp, bool fTemp, C4Network2ResType eType, int32_t iResID, const char *szResName, bool fSilent) // by main thread { Clear(); CStdLock FileLock(&FileCSec); // default resource name: relative path StdStrBuf sResName; if (szResName) sResName = szResName; else { StdStrBuf sFullName = pGrp->GetFullName(); sResName.Copy(Config.AtRelativePath(sFullName.getData())); } SCopy(pGrp->GetFullName().getData(), szFile, sizeof(szFile)-1); // set core Core.Set(eType, iResID, sResName.getData(), pGrp->EntryCRC32()); #ifdef C4NET2RES_DEBUG_LOG // log LogSilentF("Network: Resource: complete %d:%s is file %s (%s)", iResID, sResName.getData(), szFile, fTemp ? "temp" : "static"); #endif // set data fDirty = true; fTempFile = fTemp; fStandaloneFailed = false; fRemoved = false; iLastReqTime = time(NULL); fLoading = false; // ok return true; }
bool C4Network2Res::OptimizeStandalone(bool fSilent) { CStdLock FileLock(&FileCSec); // for now: player files only if (Core.getType() == NRT_Player) { // log - this may take a few seconds if (!fSilent) LogF(LoadResStr("IDS_PRC_NETPREPARING"), GetFilename(szFile)); // copy to temp file, if needed if (!fTempFile && SEqual(szFile, szStandalone)) { char szNewStandalone[_MAX_PATH + 1]; if (!pParent->FindTempResFileName(szStandalone, szNewStandalone)) { if (!fSilent) Log("OptimizeStandalone: could not find free name for temporary file!"); return false; } if (!C4Group_CopyItem(szStandalone, szNewStandalone)) { if (!fSilent) Log("OptimizeStandalone: could not copy to temporary file!"); return false; } /* TODO: Test failure */ SCopy(szNewStandalone, szStandalone, sizeof(szStandalone) - 1); } // open as group C4Group Grp; if (!Grp.Open(szStandalone)) { if (!fSilent) Log("OptimizeStandalone: could not open player file!"); return false; } // remove bigicon, if the file size is too large size_t iBigIconSize=0; if (Grp.FindEntry(C4CFN_BigIcon, NULL, &iBigIconSize)) if (iBigIconSize > C4NetResMaxBigicon*1024) Grp.Delete(C4CFN_BigIcon); Grp.Close(); } return true; }
void C4Network2Res::OnChunk(const C4Network2ResChunk &rChunk) { if (!fLoading) return; // correct resource? if (rChunk.getResID() != getResID()) return; // add resource data CStdLock FileLock(&FileCSec); bool fSuccess = rChunk.AddTo(this, pParent->getIOClass()); #ifdef C4NET2RES_DEBUG_LOG // log Application.InteractiveThread.ThreadLogS("Network: Res: %s chunk %d to resource %s (%s)%s", fSuccess ? "added" : "could not add", rChunk.getChunkNr(), Core.getFileName(), szFile, fSuccess ? "" : "!"); #endif if (fSuccess) { // status changed fDirty = true; // remove load waits for (C4Network2ResLoad *pLoad = pLoads, *pNext; pLoad; pLoad = pNext) { pNext = pLoad->Next(); if (static_cast<uint32_t>(pLoad->getChunk()) == rChunk.getChunkNr()) RemoveLoad(pLoad); } } // complete? if (Chunks.isComplete()) EndLoad(); // check: start new loads? else StartNewLoads(); }
int32_t C4Network2Res::OpenFileWrite() { CStdLock FileLock(&FileCSec); // FIXME: Use standard OC file access api here #ifdef _WIN32 return _wopen(GetWideChar(szStandalone), _O_BINARY | O_CREAT | O_WRONLY, S_IREAD | S_IWRITE); #else return open(szStandalone, _O_BINARY | O_CLOEXEC | O_CREAT | O_WRONLY, S_IREAD | S_IWRITE); #endif }
C4Network2Res::Ref C4Network2Res::Derive() { // Called before the file is changed. Rescues all files and creates a // new resource for the file. This resource is flagged as "anonymous", as it // has no official core (no res ID, to be exact). // The resource gets its final core when FinishDerive() is called. // For security: This doesn't make much sense if the resource is currently being // loaded. So better assume the caller doesn't know what he's doing and check. if (isLoading()) return NULL; CStdLock FileLock(&FileCSec); // Save back original file name char szOrgFile[_MAX_PATH+1]; SCopy(szFile, szOrgFile, _MAX_PATH); bool fOrgTempFile = fTempFile; // Create a copy of the file, if neccessary if (!*szStandalone || SEqual(szStandalone, szFile)) { if (!pParent->FindTempResFileName(szOrgFile, szFile)) { Log("Derive: could not find free name for temporary file!"); return NULL; } if (!C4Group_CopyItem(szOrgFile, szFile)) { Log("Derive: could not copy to temporary file!"); return NULL; } // set standalone if (*szStandalone) SCopy(szFile, szStandalone, _MAX_PATH); fTempFile = true; } else { // Standlone exists: Just set szFile to point on the standlone. It's // assumed that the original file isn't of intrest after this point anyway. SCopy(szStandalone, szFile, _MAX_PATH); fTempFile = true; } Application.InteractiveThread.ThreadLogS("Network: Resource: deriving from %d:%s, original at %s", getResID(), Core.getFileName(), szFile); // (note: should remove temp file if something fails after this point) // create new resource C4Network2Res::Ref pDRes = new C4Network2Res(pParent); if (!pDRes) return NULL; // initialize if (!pDRes->SetDerived(Core.getFileName(), szOrgFile, fOrgTempFile, getType(), getResID())) return NULL; // add to list pParent->Add(pDRes); // return new resource return pDRes; }
int32_t C4Network2Res::OpenFileRead() { CStdLock FileLock(&FileCSec); if (!GetStandalone(NULL, 0, false, false, true)) return -1; // FIXME: Use standard OC file access api here #ifdef _WIN32 return _wopen(GetWideChar(szStandalone), _O_BINARY | O_RDONLY); #else return open(szStandalone, _O_BINARY | O_CLOEXEC | O_RDONLY); #endif }
bool C4Network2Res::IsBinaryCompatible() { // returns wether the standalone of this resource is binary compatible // to the official version (means: matches the file checksum) CStdLock FileLock(&FileCSec); // standalone set? ok then (see GetStandalone) if (szStandalone[0]) return true; // is a directory? if (DirectoryExists(szFile)) // forget it - if the file is packed now, the creation time and author // won't match. return false; // try to create the standalone return GetStandalone(NULL, 0, false, false, true); }
int mtFile::completeWriteCreate( mtThreadWork::DataUser* pkDataUser ) { pkDataUser->iFileOffset = getFileOffset(pkDataUser->pcAccount); pkDataUser->ulTransferBytes = getDataFileBytes(); pkDataUser->iFileRunType = E_FILE_RUN_WRITE_FILE; pkDataUser->kOverlapped.Offset = pkDataUser->iFileOffset; pkDataUser->eOverlappedType = mtThread::E_OLT_LOCKFILEEx; if (0 == FileLock(pkDataUser->hFile, pkDataUser->ulTransferBytes, &pkDataUser->kOverlapped, LOCKFILE_EXCLUSIVE_LOCK)) { FileClose(&pkDataUser->hFile); return mtProtocol::E_RESULT_FALT_FILE_LOCK; } return mtProtocol::E_RESULT_SUCCESS; }
bool C4Network2Res::SendChunk(uint32_t iChunk, int32_t iToClient) { assert(pParent && pParent->getIOClass()); if (!szStandalone[0] || iChunk >= Core.getChunkCnt()) return false; // find connection for given client (one of the rare uses of the data connection) C4Network2IOConnection *pConn = pParent->getIOClass()->GetDataConnection(iToClient); if (!pConn) return false; // save last request time iLastReqTime = time(NULL); // create packet CStdLock FileLock(&FileCSec); C4Network2ResChunk ResChunk; ResChunk.Set(this, iChunk); // send bool fSuccess = pConn->Send(MkC4NetIOPacket(PID_NetResData, ResChunk)); pConn->DelRef(); return fSuccess; }
void C4Network2Res::Clear() { CStdLock FileLock(&FileCSec); // delete files if (fTempFile) if (FileExists(szFile)) if (!EraseFile(szFile)) LogSilentF("Network: Could not delete temporary resource file (%s)", strerror(errno)); if (szStandalone[0] && !SEqual(szFile, szStandalone)) if (FileExists(szStandalone)) if (!EraseFile(szStandalone)) LogSilentF("Network: Could not delete temporary resource file (%s)", strerror(errno)); szFile[0] = szStandalone[0] = '\0'; fDirty = false; fTempFile = false; Core.Clear(); Chunks.Clear(); fRemoved = false; ClearLoad(); }
bool C4Network2Res::SetDerived(const char *strName, const char *strFilePath, bool fTemp, C4Network2ResType eType, int32_t iDResID) { Clear(); CStdLock FileLock(&FileCSec); // set core Core.Set(eType, C4NetResIDAnonymous, strName, ~0); Core.SetDerived(iDResID); // save file path SCopy(strFilePath, szFile, _MAX_PATH); *szStandalone = '\0'; // set flags fDirty = false; fTempFile = fTemp; fStandaloneFailed = false; fRemoved = false; iLastReqTime = time(NULL); fLoading = false; // Do not set any chunk data - anonymous resources are very likely to change. // Wait for FinishDerived()-call. return true; }
int mtFile::runWrite( mtThreadWork::DataUser* pkDataUser ) { char* pcFile = getFileName(pkDataUser->pcAccount); if (NULL == pcFile) { return mtProtocol::E_RESULT_FALT_FILE_INVALIDE_PATH; } if (FileOpen(&pkDataUser->hFile, pcFile)) { pkDataUser->iFileOffset = getFileOffset(pkDataUser->pcAccount); pkDataUser->ulTransferBytes = getDataFileBytes(); pkDataUser->iFileRunType = E_FILE_RUN_WRITE_FILE; } else { /// ╢╢╫╗нд╪Ч if (FileCreate(&pkDataUser->hFile, pcFile, getFolderBytes())) { pkDataUser->iFileOffset = 0; pkDataUser->ulTransferBytes = getCreateFileBytes(); pkDataUser->iFileRunType = E_FILE_RUN_WRITE_FILE_CREATE; } else { return mtProtocol::E_RESULT_FALT_FILE_CREATE_FUNCTION; } } pkDataUser->kOverlapped.Offset = pkDataUser->iFileOffset; pkDataUser->eOverlappedType = mtThread::E_OLT_LOCKFILEEx; if (0 == FileLock(pkDataUser->hFile, pkDataUser->ulTransferBytes, &pkDataUser->kOverlapped, LOCKFILE_EXCLUSIVE_LOCK)) { FileClose(&pkDataUser->hFile); return mtProtocol::E_RESULT_FALT_FILE_LOCK; } return mtProtocol::E_RESULT_SUCCESS; }
bool C4Network2Res::GetStandalone(char *pTo, int32_t iMaxL, bool fSetOfficial, bool fAllowUnloadable, bool fSilent) { // already set? if (szStandalone[0]) { if (pTo) SCopy(szStandalone, pTo, iMaxL); return true; } // already tried and failed? No point in retrying if (fStandaloneFailed) return false; // not loadable? Wo won't be able to check the standalone as the core will lack the needed information. // the standalone won't be interesting in this case, anyway. if (!fSetOfficial && !Core.isLoadable()) return false; // set flag, so failure below will let future calls fail fStandaloneFailed = true; // lock file CStdLock FileLock(&FileCSec); // directory? SCopy(szFile, szStandalone, sizeof(szStandalone)-1); if (DirectoryExists(szFile)) { // size check for the directory, if allowed if (fAllowUnloadable) { uint32_t iDirSize; if (!DirSizeHelper::GetDirSize(szFile, &iDirSize, Config.Network.MaxLoadFileSize)) { if (!fSilent) LogF("Network: could not get directory size of %s!", szFile); szStandalone[0] = '\0'; return false; } if (iDirSize > uint32_t(Config.Network.MaxLoadFileSize)) { if (!fSilent) LogSilentF("Network: %s over size limit, will be marked unloadable!", szFile); szStandalone[0] = '\0'; return false; } } // log - this may take a few seconds if (!fSilent) LogF(LoadResStr("IDS_PRC_NETPACKING"), GetFilename(szFile)); // pack inplace? if (!fTempFile) { if (!pParent->FindTempResFileName(szFile, szStandalone)) { if (!fSilent) Log("GetStandalone: could not find free name for temporary file!"); szStandalone[0] = '\0'; return false; } if (!C4Group_PackDirectoryTo(szFile, szStandalone)) { if (!fSilent) Log("GetStandalone: could not pack directory!"); szStandalone[0] = '\0'; return false; } } else if (!C4Group_PackDirectory(szStandalone)) { if (!fSilent) Log("GetStandalone: could not pack directory!"); if (!SEqual(szFile, szStandalone)) EraseDirectory(szStandalone); szStandalone[0] = '\0'; return false; } // make sure directory is packed if (DirectoryExists(szStandalone)) { if (!fSilent) Log("GetStandalone: directory hasn't been packed!"); if (!SEqual(szFile, szStandalone)) EraseDirectory(szStandalone); szStandalone[0] = '\0'; return false; } // fallthru } // doesn't exist physically? if (!FileExists(szStandalone)) { // try C4Group (might be packed) if (!pParent->FindTempResFileName(szFile, szStandalone)) { if (!fSilent) Log("GetStandalone: could not find free name for temporary file!"); szStandalone[0] = '\0'; return false; } if (!C4Group_CopyItem(szFile, szStandalone)) { if (!fSilent) Log("GetStandalone: could not copy to temporary file!"); szStandalone[0] = '\0'; return false; } } // remains missing? give up. if (!FileExists(szStandalone)) { if (!fSilent) Log("GetStandalone: file not found!"); szStandalone[0] = '\0'; return false; } // do optimizations (delete unneeded entries) if (!OptimizeStandalone(fSilent)) { if (!SEqual(szFile, szStandalone)) EraseItem(szStandalone); szStandalone[0] = '\0'; return false; } // get file size size_t iSize = FileSize(szStandalone); // size limit if (fAllowUnloadable) if (iSize > uint32_t(Config.Network.MaxLoadFileSize)) { if (!fSilent) LogSilentF("Network: %s over size limit, will be marked unloadable!", szFile); szStandalone[0] = '\0'; return false; } // check if (!fSetOfficial && iSize != Core.getFileSize()) { // remove file if (!SEqual(szFile, szStandalone)) EraseItem(szStandalone); szStandalone[0] = '\0'; // sorry, this version isn't good enough :( return false; } // calc checksum uint32_t iCRC32; if (!GetFileCRC(szStandalone, &iCRC32)) { if (!fSilent) Log("GetStandalone: could not calculate checksum!"); return false; } // set / check if (!fSetOfficial && iCRC32 != Core.getFileCRC()) { // remove file, return if (!SEqual(szFile, szStandalone)) EraseItem(szStandalone); szStandalone[0] = '\0'; return false; } // we didn't fail fStandaloneFailed = false; // mark resource as loadable and safe file information Core.SetLoadable(iSize, iCRC32); // set up chunk data Chunks.SetComplete(Core.getChunkCnt()); // ok return true; }