PageRenderTime 94ms CodeModel.GetById 21ms RepoModel.GetById 7ms app.codeStats 1ms

/src/network/C4Network2Res.cpp

https://bitbucket.org/randrian/openclonk2
C++ | 1762 lines | 1277 code | 140 blank | 345 comment | 322 complexity | beb1ed90a472155ffb565bb4131b443a MD5 | raw file
Possible License(s): WTFPL, 0BSD, LGPL-2.1, CC-BY-3.0
  1. /*
  2. * OpenClonk, http://www.openclonk.org
  3. *
  4. * Copyright (c) 2004-2007 Peter Wortmann
  5. * Copyright (c) 2005-2007 Sven Eberhardt
  6. * Copyright (c) 2005-2006, 2008 G?nther Brammer
  7. * Copyright (c) 2007 Julian Raschke
  8. * Copyright (c) 2008 Matthes Bender
  9. * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de
  10. *
  11. * Portions might be copyrighted by other authors who have contributed
  12. * to OpenClonk.
  13. *
  14. * Permission to use, copy, modify, and/or distribute this software for any
  15. * purpose with or without fee is hereby granted, provided that the above
  16. * copyright notice and this permission notice appear in all copies.
  17. * See isc_license.txt for full license and disclaimer.
  18. *
  19. * "Clonk" is a registered trademark of Matthes Bender.
  20. * See clonk_trademark_license.txt for full license.
  21. */
  22. #include <C4Include.h>
  23. #include <C4Network2Res.h>
  24. #ifndef BIG_C4INCLUDE
  25. #include <C4Random.h>
  26. #include <C4Config.h>
  27. #include <C4Log.h>
  28. #include <C4Group.h>
  29. #include <C4Components.h>
  30. #include <C4Game.h>
  31. #include <C4GameControl.h>
  32. #endif
  33. #include <fcntl.h>
  34. #include <sys/types.h>
  35. #include <sys/stat.h>
  36. #ifdef _WIN32
  37. #include <direct.h>
  38. #endif
  39. #include <errno.h>
  40. #ifdef _MSC_VER
  41. #define snprintf _snprintf
  42. #pragma warning (disable : 4355)
  43. #endif
  44. // compile debug options
  45. // #define C4NET2RES_LOAD_ALL
  46. #define C4NET2RES_DEBUG_LOG
  47. // helper
  48. class DirSizeHelper {
  49. static uint32_t iSize, iMaxSize;
  50. static bool Helper(const char *szPath)
  51. {
  52. if(szPath[SLen(szPath)-1] == '.')
  53. return false;
  54. if(iSize > iMaxSize)
  55. return false;
  56. if(DirectoryExists(szPath))
  57. ForEachFile(szPath, &Helper);
  58. else if(FileExists(szPath))
  59. iSize += FileSize(szPath);
  60. return true;
  61. }
  62. public:
  63. static bool GetDirSize(const char *szPath, uint32_t *pSize, uint32_t inMaxSize = ~0)
  64. {
  65. // Security
  66. if(!pSize) return false;
  67. // Fold it
  68. iSize = 0; iMaxSize = inMaxSize;
  69. ForEachFile(szPath, &Helper);
  70. // Return
  71. *pSize = iSize;
  72. return true;
  73. }
  74. };
  75. uint32_t DirSizeHelper::iSize, DirSizeHelper::iMaxSize;
  76. // *** C4Network2ResCore
  77. C4Network2ResCore::C4Network2ResCore()
  78. : eType(NRT_Null),
  79. iID(-1), iDerID(-1),
  80. fLoadable(false),
  81. iFileSize(~0u), iFileCRC(~0u), iContentsCRC(~0u),
  82. iChunkSize(C4NetResChunkSize),
  83. fHasFileSHA(false)
  84. {
  85. }
  86. void C4Network2ResCore::Set(C4Network2ResType enType, int32_t iResID, const char *strFileName, uint32_t inContentsCRC, const char *strAuthor)
  87. {
  88. // Initialize base data
  89. eType = enType; iID = iResID; iDerID = -1;
  90. fLoadable = false;
  91. iFileSize = iFileCRC = ~0; iContentsCRC = inContentsCRC;
  92. iChunkSize = C4NetResChunkSize;
  93. FileName.Copy(strFileName);
  94. Author.Copy(strAuthor);
  95. }
  96. void C4Network2ResCore::SetLoadable(uint32_t iSize, uint32_t iCRC)
  97. {
  98. fLoadable = true;
  99. iFileSize = iSize;
  100. iFileCRC = iCRC;
  101. }
  102. void C4Network2ResCore::Clear()
  103. {
  104. eType = NRT_Null;
  105. iID = iDerID = -1;
  106. fLoadable = false;
  107. FileName.Clear();
  108. Author.Clear();
  109. iFileSize = iFileCRC = iContentsCRC = ~0;
  110. fHasFileSHA = false;
  111. }
  112. // C4PacketBase virtuals
  113. void C4Network2ResCore::CompileFunc(StdCompiler *pComp)
  114. {
  115. pComp->Value(mkNamingAdapt(mkEnumAdaptT<uint8_t>(eType, C4Network2ResType_EnumMap), "Type", NRT_Null));
  116. pComp->Value(mkNamingAdapt(iID, "ID", -1));
  117. pComp->Value(mkNamingAdapt(iDerID, "DerID", -1));
  118. pComp->Value(mkNamingAdapt(fLoadable, "Loadable", true));
  119. if(fLoadable)
  120. {
  121. pComp->Value(mkNamingAdapt(iFileSize, "FileSize", 0U));
  122. pComp->Value(mkNamingAdapt(iFileCRC, "FileCRC", 0U));
  123. pComp->Value(mkNamingAdapt(iChunkSize, "ChunkSize", C4NetResChunkSize));
  124. if(!iChunkSize) pComp->excCorrupt("zero chunk size");
  125. }
  126. pComp->Value(mkNamingAdapt(iContentsCRC, "ContentsCRC", 0U));
  127. pComp->Value(mkNamingCountAdapt(fHasFileSHA, "FileSHA"));
  128. if(fHasFileSHA)
  129. pComp->Value(mkNamingAdapt(mkHexAdapt(FileSHA), "FileSHA"));
  130. pComp->Value(mkNamingAdapt(mkNetFilenameAdapt(FileName), "Filename", ""));
  131. pComp->Value(mkNamingAdapt(mkNetFilenameAdapt(Author), "Author", ""));
  132. }
  133. // *** C4Network2ResLoad
  134. C4Network2ResLoad::C4Network2ResLoad(int32_t inChunk, int32_t inByClient)
  135. : iChunk(inChunk), iByClient(inByClient), Timestamp(time(NULL)), pNext(NULL)
  136. {
  137. }
  138. C4Network2ResLoad::~C4Network2ResLoad()
  139. {
  140. }
  141. bool C4Network2ResLoad::CheckTimeout()
  142. {
  143. return difftime(time(NULL), Timestamp) >= C4NetResLoadTimeout;
  144. }
  145. // *** C4Network2ResChunkData
  146. C4Network2ResChunkData::C4Network2ResChunkData()
  147. : iChunkCnt(0), iPresentChunkCnt(0),
  148. pChunkRanges(NULL), iChunkRangeCnt(0)
  149. {
  150. }
  151. C4Network2ResChunkData::C4Network2ResChunkData(const C4Network2ResChunkData &Data2)
  152. : C4PacketBase(Data2),
  153. iChunkCnt(Data2.getChunkCnt()), iPresentChunkCnt(0),
  154. pChunkRanges(NULL), iChunkRangeCnt(0)
  155. {
  156. // add ranges
  157. Merge(Data2);
  158. }
  159. C4Network2ResChunkData::~C4Network2ResChunkData()
  160. {
  161. Clear();
  162. }
  163. C4Network2ResChunkData &C4Network2ResChunkData::operator =(const C4Network2ResChunkData &Data2)
  164. {
  165. // clear, merge
  166. SetIncomplete(Data2.getChunkCnt());
  167. Merge(Data2);
  168. return *this;
  169. }
  170. void C4Network2ResChunkData::SetIncomplete(int32_t inChunkCnt)
  171. {
  172. Clear();
  173. // just set total chunk count
  174. iChunkCnt = inChunkCnt;
  175. }
  176. void C4Network2ResChunkData::SetComplete(int32_t inChunkCnt)
  177. {
  178. Clear();
  179. // set total chunk count
  180. iPresentChunkCnt = iChunkCnt = inChunkCnt;
  181. // create one range
  182. ChunkRange *pRange = new ChunkRange;
  183. pRange->Start = 0; pRange->Length = iChunkCnt;
  184. pRange->Next = NULL;
  185. pChunkRanges = pRange;
  186. }
  187. void C4Network2ResChunkData::AddChunk(int32_t iChunk)
  188. {
  189. AddChunkRange(iChunk, 1);
  190. }
  191. void C4Network2ResChunkData::AddChunkRange(int32_t iStart, int32_t iLength)
  192. {
  193. // security
  194. if(iStart < 0 || iStart + iLength > iChunkCnt || iLength <= 0) return;
  195. // find position
  196. ChunkRange *pRange, *pPrev;
  197. for(pRange = pChunkRanges, pPrev = NULL; pRange; pPrev = pRange, pRange = pRange->Next)
  198. if(pRange->Start >= iStart)
  199. break;
  200. // create new
  201. ChunkRange *pNew = new ChunkRange;
  202. pNew->Start = iStart; pNew->Length = iLength;
  203. // add to list
  204. pNew->Next = pRange;
  205. (pPrev ? pPrev->Next : pChunkRanges) = pNew;
  206. // counts
  207. iPresentChunkCnt += iLength; iChunkRangeCnt++;
  208. // check merges
  209. if(pPrev && MergeRanges(pPrev))
  210. while(MergeRanges(pPrev)) {}
  211. else
  212. while(MergeRanges(pNew)) {}
  213. }
  214. void C4Network2ResChunkData::Merge(const C4Network2ResChunkData &Data2)
  215. {
  216. // must have same basis chunk count
  217. assert(iChunkCnt == Data2.getChunkCnt());
  218. // add ranges
  219. for(ChunkRange *pRange = Data2.pChunkRanges; pRange; pRange = pRange->Next)
  220. AddChunkRange(pRange->Start, pRange->Length);
  221. }
  222. void C4Network2ResChunkData::Clear()
  223. {
  224. iChunkCnt = iPresentChunkCnt = iChunkRangeCnt = 0;
  225. // remove all ranges
  226. while(pChunkRanges)
  227. {
  228. ChunkRange *pDelete = pChunkRanges;
  229. pChunkRanges = pDelete->Next;
  230. delete pDelete;
  231. }
  232. }
  233. int32_t C4Network2ResChunkData::GetChunkToRetrieve(const C4Network2ResChunkData &Available, int32_t iLoadingCnt, int32_t *pLoading) const
  234. {
  235. // (this version is highly calculation-intensitive, yet the most satisfactory
  236. // solution I could find)
  237. // find everything that should not be retrieved
  238. C4Network2ResChunkData ChData; Available.GetNegative(ChData);
  239. ChData.Merge(*this);
  240. for(int32_t i = 0; i < iLoadingCnt; i++)
  241. ChData.AddChunk(pLoading[i]);
  242. // nothing to retrieve?
  243. if(ChData.isComplete()) return -1;
  244. // invert to get everything that should be retrieved
  245. C4Network2ResChunkData ChData2; ChData.GetNegative(ChData2);
  246. // select chunk (random)
  247. int32_t iRetrieveChunk = SafeRandom(ChData2.getPresentChunkCnt());
  248. // return
  249. return ChData2.getPresentChunk(iRetrieveChunk);
  250. }
  251. bool C4Network2ResChunkData::MergeRanges(ChunkRange *pRange)
  252. {
  253. // no next entry?
  254. if(!pRange || !pRange->Next) return false;
  255. // do merge?
  256. ChunkRange *pNext = pRange->Next;
  257. if(pRange->Start + pRange->Length < pNext->Start) return false;
  258. // get overlap
  259. int32_t iOverlap = Min((pRange->Start + pRange->Length) - pNext->Start, pNext->Length);
  260. // set new chunk range
  261. pRange->Length += pNext->Length - iOverlap;
  262. // remove range
  263. pRange->Next = pNext->Next;
  264. delete pNext;
  265. // counts
  266. iChunkRangeCnt--; iPresentChunkCnt -= iOverlap;
  267. // ok
  268. return true;
  269. }
  270. void C4Network2ResChunkData::GetNegative(C4Network2ResChunkData &Target) const
  271. {
  272. // clear target
  273. Target.SetIncomplete(iChunkCnt);
  274. // add all ranges that are missing
  275. int32_t iFreeStart = 0;
  276. for(ChunkRange *pRange = pChunkRanges; pRange; pRange = pRange->Next)
  277. {
  278. // add range
  279. Target.AddChunkRange(iFreeStart, pRange->Start - iFreeStart);
  280. // safe new start
  281. iFreeStart = pRange->Start + pRange->Length;
  282. }
  283. // add last range
  284. Target.AddChunkRange(iFreeStart, iChunkCnt - iFreeStart);
  285. }
  286. int32_t C4Network2ResChunkData::getPresentChunk(int32_t iNr) const
  287. {
  288. for(ChunkRange *pRange = pChunkRanges; pRange; pRange = pRange->Next)
  289. if(iNr < pRange->Length)
  290. return iNr + pRange->Start;
  291. else
  292. iNr -= pRange->Length;
  293. return -1;
  294. }
  295. void C4Network2ResChunkData::CompileFunc(StdCompiler *pComp)
  296. {
  297. bool fCompiler = pComp->isCompiler();
  298. if(fCompiler) Clear();
  299. // Data
  300. pComp->Value(mkNamingAdapt(mkIntPackAdapt(iChunkCnt), "ChunkCnt", 0));
  301. pComp->Value(mkNamingAdapt(mkIntPackAdapt(iChunkRangeCnt), "ChunkRangeCnt", 0));
  302. // Ranges
  303. if(!pComp->Name("Ranges"))
  304. pComp->excCorrupt("ResChunk ranges expected!");
  305. ChunkRange *pRange = NULL;
  306. for(int32_t i = 0; i < iChunkRangeCnt; i++)
  307. {
  308. // Create new range / go to next range
  309. if(fCompiler)
  310. pRange = (pRange ? pRange->Next : pChunkRanges) = new ChunkRange;
  311. else
  312. pRange = pRange ? pRange->Next : pChunkRanges;
  313. // Seperate
  314. if(i) pComp->Seperator();
  315. // Compile range
  316. pComp->Value(mkIntPackAdapt(pRange->Start));
  317. pComp->Seperator(StdCompiler::SEP_PART2);
  318. pComp->Value(mkIntPackAdapt(pRange->Length));
  319. }
  320. // Terminate list
  321. if(fCompiler)
  322. (pRange ? pRange->Next : pChunkRanges) = NULL;
  323. pComp->NameEnd();
  324. }
  325. // *** C4Network2Res
  326. C4Network2Res::C4Network2Res(C4Network2ResList *pnParent)
  327. : fDirty(false),
  328. fTempFile(false), fStandaloneFailed(false),
  329. iRefCnt(0), fRemoved(false),
  330. iLastReqTime(0),
  331. fLoading(false),
  332. pCChunks(NULL), iDiscoverStartTime(0), pLoads(NULL), iLoadCnt(0),
  333. pNext(NULL),
  334. pParent(pnParent)
  335. {
  336. szFile[0] = szStandalone[0] = '\0';
  337. }
  338. C4Network2Res::~C4Network2Res()
  339. {
  340. assert(!pNext);
  341. Clear();
  342. }
  343. bool C4Network2Res::SetByFile(const char *strFilePath, bool fTemp, C4Network2ResType eType, int32_t iResID, const char *szResName, bool fSilent)
  344. {
  345. CStdLock FileLock(&FileCSec);
  346. // default ressource name: relative path
  347. if(!szResName) szResName = Config.AtRelativePath(strFilePath);
  348. SCopy(strFilePath, szFile, sizeof(szFile)-1);
  349. // group?
  350. C4Group Grp;
  351. if(Grp.Open(strFilePath))
  352. return SetByGroup(&Grp, fTemp, eType, iResID, szResName, fSilent);
  353. // so it needs to be a file
  354. if(!FileExists(szFile))
  355. { if(!fSilent) LogF("SetByFile: file %s not found!", strFilePath); return false; }
  356. // calc checksum
  357. uint32_t iCRC32;
  358. if(!C4Group_GetFileCRC(szFile, &iCRC32)) return false;
  359. #ifdef C4NET2RES_DEBUG_LOG
  360. // log
  361. LogSilentF("Network: Resource: complete %d:%s is file %s (%s)", iResID, szResName, szFile, fTemp ? "temp" : "static");
  362. #endif
  363. // set core
  364. Core.Set(eType, iResID, szResName, iCRC32, "");
  365. // set own data
  366. fDirty = true;
  367. fTempFile = fTemp;
  368. fStandaloneFailed = false;
  369. fRemoved = false;
  370. iLastReqTime = time(NULL);
  371. fLoading = false;
  372. // ok
  373. return true;
  374. }
  375. bool C4Network2Res::SetByGroup(C4Group *pGrp, bool fTemp, C4Network2ResType eType, int32_t iResID, const char *szResName, bool fSilent) // by main thread
  376. {
  377. Clear();
  378. CStdLock FileLock(&FileCSec);
  379. // default ressource name: relative path
  380. StdStrBuf sResName;
  381. if (szResName)
  382. sResName = szResName;
  383. else
  384. {
  385. StdStrBuf sFullName = pGrp->GetFullName();
  386. sResName.Copy(Config.AtRelativePath(sFullName.getData()));
  387. }
  388. SCopy(pGrp->GetFullName().getData(), szFile, sizeof(szFile)-1);
  389. // set core
  390. Core.Set(eType, iResID, sResName.getData(), pGrp->EntryCRC32(), pGrp->GetMaker());
  391. #ifdef C4NET2RES_DEBUG_LOG
  392. // log
  393. LogSilentF("Network: Resource: complete %d:%s is file %s (%s)", iResID, sResName.getData(), szFile, fTemp ? "temp" : "static");
  394. #endif
  395. // set data
  396. fDirty = true;
  397. fTempFile = fTemp;
  398. fStandaloneFailed = false;
  399. fRemoved = false;
  400. iLastReqTime = time(NULL);
  401. fLoading = false;
  402. // ok
  403. return true;
  404. }
  405. bool C4Network2Res::SetByCore(const C4Network2ResCore &nCore, bool fSilent, const char *szAsFilename, int32_t iRecursion) // by main thread
  406. {
  407. StdStrBuf sFilename;
  408. // try open local file
  409. const char *szFilename = szAsFilename ? szAsFilename : GetC4Filename(nCore.getFileName());
  410. if(SetByFile(szFilename, false, nCore.getType(), nCore.getID(), nCore.getFileName(), fSilent))
  411. {
  412. // check contents checksum
  413. if(Core.getContentsCRC() == nCore.getContentsCRC())
  414. {
  415. // set core
  416. fDirty = true;
  417. Core = nCore;
  418. // ok then
  419. return true;
  420. }
  421. }
  422. // get and search for filename without specified folder (e.g., Castle.c4s when the opened game is Easy.c4f\Castle.c4s)
  423. const char *szFilenameOnly = GetFilename(szFilename);
  424. const char *szFilenameC4 = GetC4Filename(szFilename);
  425. if (szFilenameOnly != szFilenameC4)
  426. {
  427. sFilename.Copy(szFilename, SLen(szFilename) - SLen(szFilenameC4));
  428. sFilename.Append(szFilenameOnly);
  429. if (SetByCore(nCore, fSilent, szFilenameOnly, Config.Network.MaxResSearchRecursion)) return true;
  430. }
  431. // if it could still not be set, try within all folders of root (ignoring special folders), and try as file outside the folder
  432. // but do not recurse any deeper than set by config (default: One folder)
  433. if (iRecursion >= Config.Network.MaxResSearchRecursion) return false;
  434. StdStrBuf sSearchPath; const char *szSearchPath;
  435. if (!iRecursion)
  436. szSearchPath = Config.General.ExePath;
  437. else
  438. {
  439. sSearchPath.Copy(szFilename, SLen(szFilename) - SLen(szFilenameC4));
  440. szSearchPath = sSearchPath.getData();
  441. }
  442. StdStrBuf sNetPath; sNetPath.Copy(Config.Network.WorkPath);
  443. char *szNetPath = sNetPath.GrabPointer();
  444. TruncateBackslash(szNetPath);
  445. sNetPath.Take(szNetPath);
  446. for (DirectoryIterator i(szSearchPath); *i; ++i)
  447. if (DirectoryExists(*i))
  448. if (!*GetExtension(*i)) // directories without extension only
  449. if (!szNetPath || !*szNetPath || !ItemIdentical(*i, szNetPath)) // ignore network path
  450. {
  451. // search for complete name at subpath (e.g. MyFolder\Easy.c4f\Castle.c4s)
  452. sFilename.Format("%s%c%s", *i, DirectorySeparator, szFilenameC4);
  453. if (SetByCore(nCore, fSilent, sFilename.getData(), iRecursion + 1))
  454. return true;
  455. }
  456. // file could not be found locally
  457. return false;
  458. }
  459. bool C4Network2Res::SetLoad(const C4Network2ResCore &nCore) // by main thread
  460. {
  461. Clear();
  462. CStdLock FileLock(&FileCSec);
  463. // must be loadable
  464. if(!nCore.isLoadable()) return false;
  465. // save core, set chunks
  466. Core = nCore;
  467. Chunks.SetIncomplete(Core.getChunkCnt());
  468. // create temporary file
  469. if(!pParent->FindTempResFileName(Core.getFileName(), szFile))
  470. return false;
  471. #ifdef C4NET2RES_DEBUG_LOG
  472. // log
  473. Application.InteractiveThread.ThreadLogS("Network: Resource: loading %d:%s to file %s", Core.getID(), Core.getFileName(), szFile);
  474. #endif
  475. // set standalone (result is going to be binary-compatible)
  476. SCopy(szFile, szStandalone, sizeof(szStandalone) - 1);
  477. // set flags
  478. fDirty = false;
  479. fTempFile = true;
  480. fStandaloneFailed = false;
  481. fRemoved = false;
  482. iLastReqTime = time(NULL);
  483. fLoading = true;
  484. // No discovery yet
  485. iDiscoverStartTime = 0;
  486. return true;
  487. }
  488. bool C4Network2Res::SetDerived(const char *strName, const char *strFilePath, bool fTemp, C4Network2ResType eType, int32_t iDResID)
  489. {
  490. Clear();
  491. CStdLock FileLock(&FileCSec);
  492. // set core
  493. Core.Set(eType, C4NetResIDAnonymous, strName, ~0, "");
  494. Core.SetDerived(iDResID);
  495. // save file path
  496. SCopy(strFilePath, szFile, _MAX_PATH);
  497. *szStandalone = '\0';
  498. // set flags
  499. fDirty = false;
  500. fTempFile = fTemp;
  501. fStandaloneFailed = false;
  502. fRemoved = false;
  503. iLastReqTime = time(NULL);
  504. fLoading = false;
  505. // Do not set any chunk data - anonymous ressources are very likely to change.
  506. // Wait for FinishDerived()-call.
  507. return true;
  508. }
  509. void C4Network2Res::ChangeID(int32_t inID)
  510. {
  511. Core.SetID(inID);
  512. }
  513. bool C4Network2Res::IsBinaryCompatible()
  514. {
  515. // returns wether the standalone of this ressource is binary compatible
  516. // to the official version (means: matches the file checksum)
  517. CStdLock FileLock(&FileCSec);
  518. // standalone set? ok then (see GetStandalone)
  519. if(szStandalone[0]) return true;
  520. // is a directory?
  521. if(DirectoryExists(szFile))
  522. // forget it - if the file is packed now, the creation time and author
  523. // won't match.
  524. return false;
  525. // try to create the standalone
  526. return GetStandalone(NULL, 0, false, false, true);
  527. }
  528. bool C4Network2Res::GetStandalone(char *pTo, int32_t iMaxL, bool fSetOfficial, bool fAllowUnloadable, bool fSilent)
  529. {
  530. // already set?
  531. if(szStandalone[0])
  532. {
  533. if(pTo) SCopy(szStandalone, pTo, iMaxL);
  534. return true;
  535. }
  536. // already tried and failed? No point in retrying
  537. if(fStandaloneFailed) return false;
  538. // not loadable? Wo won't be able to check the standalone as the core will lack the needed information.
  539. // the standalone won't be interesting in this case, anyway.
  540. if(!fSetOfficial && !Core.isLoadable()) return false;
  541. // set flag, so failure below will let future calls fail
  542. fStandaloneFailed = true;
  543. // lock file
  544. CStdLock FileLock(&FileCSec);
  545. // directory?
  546. SCopy(szFile, szStandalone, sizeof(szStandalone)-1);
  547. if(DirectoryExists(szFile))
  548. {
  549. // size check for the directory, if allowed
  550. if(fAllowUnloadable)
  551. {
  552. uint32_t iDirSize;
  553. if(!DirSizeHelper::GetDirSize(szFile, &iDirSize, Config.Network.MaxLoadFileSize))
  554. { if(!fSilent) LogF("Network: could not get directory size of %s!", szFile); szStandalone[0] = '\0'; return false; }
  555. if(iDirSize > uint32_t(Config.Network.MaxLoadFileSize))
  556. { if(!fSilent) LogSilentF("Network: %s over size limit, will be marked unloadable!", szFile); szStandalone[0] = '\0'; return false; }
  557. }
  558. // log - this may take a few seconds
  559. if(!fSilent) LogF(LoadResStr("IDS_PRC_NETPACKING"), GetFilename(szFile));
  560. // pack inplace?
  561. if(!fTempFile)
  562. {
  563. if(!pParent->FindTempResFileName(szFile, szStandalone))
  564. { if(!fSilent) Log("GetStandalone: could not find free name for temporary file!"); szStandalone[0] = '\0'; return false; }
  565. if(!C4Group_PackDirectoryTo(szFile, szStandalone))
  566. { if(!fSilent) Log("GetStandalone: could not pack directory!"); szStandalone[0] = '\0'; return false; }
  567. }
  568. else if(!C4Group_PackDirectory(szStandalone))
  569. { if(!fSilent) Log("GetStandalone: could not pack directory!"); if(!SEqual(szFile, szStandalone)) EraseDirectory(szStandalone); szStandalone[0] = '\0'; return false; }
  570. // make sure directory is packed
  571. if(DirectoryExists(szStandalone))
  572. { if(!fSilent) Log("GetStandalone: directory hasn't been packed!"); if(!SEqual(szFile, szStandalone)) EraseDirectory(szStandalone); szStandalone[0] = '\0'; return false; }
  573. // fallthru
  574. }
  575. // doesn't exist physically?
  576. if(!FileExists(szStandalone))
  577. {
  578. // try C4Group (might be packed)
  579. if(!pParent->FindTempResFileName(szFile, szStandalone))
  580. { if(!fSilent) Log("GetStandalone: could not find free name for temporary file!"); szStandalone[0] = '\0'; return false; }
  581. if(!C4Group_CopyItem(szFile, szStandalone))
  582. { if(!fSilent) Log("GetStandalone: could not copy to temporary file!"); szStandalone[0] = '\0'; return false; }
  583. }
  584. // remains missing? give up.
  585. if(!FileExists(szStandalone))
  586. { if(!fSilent) Log("GetStandalone: file not found!"); szStandalone[0] = '\0'; return false; }
  587. // do optimizations (delete unneeded entries)
  588. if(!OptimizeStandalone(fSilent))
  589. { if(!SEqual(szFile, szStandalone)) remove(szStandalone); szStandalone[0] = '\0'; return false; }
  590. // get file size
  591. size_t iSize = FileSize(szStandalone);
  592. // size limit
  593. if(fAllowUnloadable)
  594. if(iSize > uint32_t(Config.Network.MaxLoadFileSize))
  595. { if(!fSilent) LogSilentF("Network: %s over size limit, will be marked unloadable!", szFile); szStandalone[0] = '\0'; return false; }
  596. // check
  597. if(!fSetOfficial && iSize != Core.getFileSize())
  598. {
  599. // remove file
  600. if(!SEqual(szFile, szStandalone)) remove(szStandalone); szStandalone[0] = '\0';
  601. // sorry, this version isn't good enough :(
  602. return false;
  603. }
  604. // calc checksum
  605. uint32_t iCRC32;
  606. if(!C4Group_GetFileCRC(szStandalone, &iCRC32))
  607. { if(!fSilent) Log("GetStandalone: could not calculate checksum!"); return false; }
  608. // set / check
  609. if(!fSetOfficial && iCRC32 != Core.getFileCRC())
  610. {
  611. // remove file, return
  612. if(!SEqual(szFile, szStandalone)) remove(szStandalone); szStandalone[0] = '\0';
  613. return false;
  614. }
  615. // we didn't fail
  616. fStandaloneFailed = false;
  617. // mark resource as loadable and safe file information
  618. Core.SetLoadable(iSize, iCRC32);
  619. // set up chunk data
  620. Chunks.SetComplete(Core.getChunkCnt());
  621. // ok
  622. return true;
  623. }
  624. bool C4Network2Res::CalculateSHA()
  625. {
  626. // already present?
  627. if(Core.hasFileSHA()) return true;
  628. // get the file
  629. char szStandalone[_MAX_PATH + 1];
  630. if(!GetStandalone(szStandalone, _MAX_PATH, false))
  631. SCopy(szFile, szStandalone, _MAX_PATH);
  632. // get the hash
  633. BYTE hash[SHA_DIGEST_LENGTH];
  634. if(!C4Group_GetFileSHA1(szStandalone, hash))
  635. return false;
  636. // save it back
  637. Core.SetFileSHA(hash);
  638. // okay
  639. return true;
  640. }
  641. C4Network2Res::Ref C4Network2Res::Derive()
  642. {
  643. // Called before the file is changed. Rescues all files and creates a
  644. // new ressource for the file. This ressource is flagged as "anonymous", as it
  645. // has no official core (no res ID, to be exact).
  646. // The resource gets its final core when FinishDerive() is called.
  647. // For security: This doesn't make much sense if the resource is currently being
  648. // loaded. So better assume the caller doesn't know what he's doing and check.
  649. if(isLoading()) return NULL;
  650. CStdLock FileLock(&FileCSec);
  651. // Save back original file name
  652. char szOrgFile[_MAX_PATH+1];
  653. SCopy(szFile, szOrgFile, _MAX_PATH);
  654. bool fOrgTempFile = fTempFile;
  655. // Create a copy of the file, if neccessary
  656. if(!*szStandalone || SEqual(szStandalone, szFile))
  657. {
  658. if(!pParent->FindTempResFileName(szOrgFile, szFile))
  659. { Log("Derive: could not find free name for temporary file!"); return NULL; }
  660. if(!C4Group_CopyItem(szOrgFile, szFile))
  661. { Log("Derive: could not copy to temporary file!"); return NULL; }
  662. // set standalone
  663. if(*szStandalone)
  664. SCopy(szFile, szStandalone, _MAX_PATH);
  665. fTempFile = true;
  666. }
  667. else
  668. {
  669. // Standlone exists: Just set szFile to point on the standlone. It's
  670. // assumed that the original file isn't of intrest after this point anyway.
  671. SCopy(szStandalone, szFile, _MAX_PATH);
  672. fTempFile = true;
  673. }
  674. Application.InteractiveThread.ThreadLogS("Network: Ressource: deriving from %d:%s, original at %s", getResID(), Core.getFileName(), szFile);
  675. // (note: should remove temp file if something fails after this point)
  676. // create new ressource
  677. C4Network2Res::Ref pDRes = new C4Network2Res(pParent);
  678. if(!pDRes) return NULL;
  679. // initialize
  680. if(!pDRes->SetDerived(Core.getFileName(), szOrgFile, fOrgTempFile, getType(), getResID()))
  681. return NULL;
  682. // add to list
  683. pParent->Add(pDRes);
  684. // return new ressource
  685. return pDRes;
  686. }
  687. bool C4Network2Res::FinishDerive() // by main thread
  688. {
  689. // All changes have been made. Register this ressource with a new ID.
  690. // security
  691. if(!isAnonymous()) return false;
  692. CStdLock FileLock(&FileCSec);
  693. // Save back data
  694. int32_t iDerID = Core.getDerID();
  695. char szName[_MAX_PATH+1]; SCopy(Core.getFileName(), szName, _MAX_PATH);
  696. char szFileC[_MAX_PATH+1]; SCopy(szFile, szFileC, _MAX_PATH);
  697. // Set by file
  698. if(!SetByFile(szFileC, fTempFile, getType(), pParent->nextResID(), szName))
  699. return false;
  700. // create standalone
  701. if(!GetStandalone(NULL, 0, true))
  702. return false;
  703. // Set ID
  704. Core.SetDerived(iDerID);
  705. // announce derive
  706. pParent->getIOClass()->BroadcastMsg(MkC4NetIOPacket(PID_NetResDerive, Core));
  707. // derivation is dirty bussines
  708. fDirty = true;
  709. // ok
  710. return true;
  711. }
  712. bool C4Network2Res::FinishDerive(const C4Network2ResCore &nCore)
  713. {
  714. // security
  715. if(!isAnonymous()) return false;
  716. // Set core
  717. Core = nCore;
  718. // Set chunks (assume the ressource is complete)
  719. Chunks.SetComplete(Core.getChunkCnt());
  720. // Note that the Contents-CRC is /not/ checked. Derivation needs to be
  721. // synchronized outside of C4Network2Res.
  722. // But note that the ressource /might/ be binary compatible (though very
  723. // unlikely), so do not set fNotBinaryCompatible.
  724. // ok
  725. return true;
  726. }
  727. C4Group *C4Network2Res::OpenAsGrp() const
  728. {
  729. C4Group *pnGrp = new C4Group();
  730. if(!pnGrp->Open(szFile))
  731. {
  732. delete pnGrp;
  733. return NULL;
  734. }
  735. return pnGrp;
  736. }
  737. void C4Network2Res::Remove()
  738. {
  739. // schedule for removal
  740. fRemoved = true;
  741. }
  742. bool C4Network2Res::SendStatus(C4Network2IOConnection *pTo)
  743. {
  744. // pack status
  745. C4NetIOPacket Pkt = MkC4NetIOPacket(PID_NetResStat, C4PacketResStatus(Core.getID(), Chunks));
  746. // to one client?
  747. if(pTo)
  748. return pTo->Send(Pkt);
  749. else
  750. {
  751. // reset dirty flag
  752. fDirty = false;
  753. // broadcast status
  754. assert(pParent && pParent->getIOClass());
  755. return pParent->getIOClass()->BroadcastMsg(Pkt);
  756. }
  757. }
  758. bool C4Network2Res::SendChunk(uint32_t iChunk, int32_t iToClient)
  759. {
  760. assert(pParent && pParent->getIOClass());
  761. if(!szStandalone[0] || iChunk >= Core.getChunkCnt()) return false;
  762. // find connection for given client (one of the rare uses of the data connection)
  763. C4Network2IOConnection *pConn = pParent->getIOClass()->GetDataConnection(iToClient);
  764. if(!pConn) return false;
  765. // save last request time
  766. iLastReqTime = time(NULL);
  767. // create packet
  768. CStdLock FileLock(&FileCSec);
  769. C4Network2ResChunk ResChunk;
  770. ResChunk.Set(this, iChunk);
  771. // send
  772. bool fSuccess = pConn->Send(MkC4NetIOPacket(PID_NetResData, ResChunk));
  773. pConn->DelRef();
  774. return fSuccess;
  775. }
  776. void C4Network2Res::AddRef()
  777. {
  778. InterlockedIncrement(&iRefCnt);
  779. }
  780. void C4Network2Res::DelRef()
  781. {
  782. if(!InterlockedDecrement(&iRefCnt))
  783. delete this;
  784. }
  785. void C4Network2Res::OnDiscover(C4Network2IOConnection *pBy)
  786. {
  787. if(!IsBinaryCompatible()) return;
  788. // discovered
  789. iLastReqTime = time(NULL);
  790. // send status back
  791. SendStatus(pBy);
  792. }
  793. void C4Network2Res::OnStatus(const C4Network2ResChunkData &rChunkData, C4Network2IOConnection *pBy)
  794. {
  795. if(!fLoading) return;
  796. // discovered a source: reset timeout
  797. iDiscoverStartTime = 0;
  798. // check if the chunk data is valid
  799. if(rChunkData.getChunkCnt() != Chunks.getChunkCnt())
  800. return;
  801. // add chunk data
  802. ClientChunks *pChunks;
  803. for(pChunks = pCChunks; pChunks; pChunks = pChunks->Next)
  804. if(pChunks->ClientID == pBy->getClientID())
  805. break;
  806. // not found? add
  807. if(!pChunks)
  808. {
  809. pChunks = new ClientChunks();
  810. pChunks->Next = pCChunks;
  811. pCChunks = pChunks;
  812. }
  813. pChunks->ClientID = pBy->getClientID();
  814. pChunks->Chunks = rChunkData;
  815. // check load
  816. if(!StartLoad(pChunks->ClientID, pChunks->Chunks))
  817. RemoveCChunks(pCChunks);
  818. }
  819. void C4Network2Res::OnChunk(const C4Network2ResChunk &rChunk)
  820. {
  821. if(!fLoading) return;
  822. // correct ressource?
  823. if(rChunk.getResID() != getResID()) return;
  824. // add ressource data
  825. CStdLock FileLock(&FileCSec);
  826. bool fSuccess = rChunk.AddTo(this, pParent->getIOClass());
  827. #ifdef C4NET2RES_DEBUG_LOG
  828. // log
  829. Application.InteractiveThread.ThreadLogS("Network: Res: %s chunk %d to ressource %s (%s)%s", fSuccess ? "added" : "could not add", rChunk.getChunkNr(), Core.getFileName(), szFile, fSuccess ? "" : "!");
  830. #endif
  831. if(fSuccess)
  832. {
  833. // status changed
  834. fDirty = true;
  835. // remove load waits
  836. for(C4Network2ResLoad *pLoad = pLoads, *pNext, *pPrev = NULL; pLoad; pPrev = pLoad, pLoad = pNext)
  837. {
  838. pNext = pLoad->Next();
  839. if(pLoad->getChunk() == rChunk.getChunkNr())
  840. RemoveLoad(pLoad);
  841. }
  842. }
  843. // complete?
  844. if(Chunks.isComplete())
  845. EndLoad();
  846. // check: start new loads?
  847. else
  848. StartNewLoads();
  849. }
  850. bool C4Network2Res::DoLoad()
  851. {
  852. if(!fLoading) return true;
  853. // any loads currently active?
  854. if(iLoadCnt)
  855. {
  856. // check for load timeouts
  857. int32_t iLoadsRemoved = 0;
  858. for(C4Network2ResLoad *pLoad = pLoads, *pNext; pLoad; pLoad = pNext)
  859. {
  860. pNext = pLoad->Next();
  861. if(pLoad->CheckTimeout())
  862. {
  863. RemoveLoad(pLoad);
  864. iLoadsRemoved++;
  865. }
  866. }
  867. // start new loads
  868. if(iLoadsRemoved) StartNewLoads();
  869. }
  870. else
  871. {
  872. // discover timeout?
  873. if(iDiscoverStartTime)
  874. if(difftime(time(NULL), iDiscoverStartTime) > C4NetResDiscoverTimeout)
  875. return false;
  876. }
  877. // ok
  878. return true;
  879. }
  880. bool C4Network2Res::NeedsDiscover()
  881. {
  882. // loading, but no active load sources?
  883. if(fLoading && !iLoadCnt)
  884. {
  885. // set timeout, if this is the first discover
  886. if(!iDiscoverStartTime)
  887. iDiscoverStartTime = time(NULL);
  888. // do discover
  889. return true;
  890. }
  891. return false;
  892. }
  893. void C4Network2Res::Clear()
  894. {
  895. CStdLock FileLock(&FileCSec);
  896. // delete files
  897. if(fTempFile)
  898. if(FileExists(szFile))
  899. if(remove(szFile))
  900. //Log(_strerror("Network: Could not delete temporary ressource file"));
  901. LogSilentF("Network: Could not delete temporary resource file (%s)", strerror(errno));
  902. if(szStandalone[0] && !SEqual(szFile, szStandalone))
  903. if(FileExists(szStandalone))
  904. if(remove(szStandalone))
  905. //Log(_strerror("Network: Could not delete temporary ressource file"));
  906. LogSilentF("Network: Could not delete temporary resource file (%s)", strerror(errno));
  907. szFile[0] = szStandalone[0] = '\0';
  908. fDirty = false;
  909. fTempFile = false;
  910. Core.Clear();
  911. Chunks.Clear();
  912. fRemoved = false;
  913. ClearLoad();
  914. }
  915. int32_t C4Network2Res::OpenFileRead()
  916. {
  917. CStdLock FileLock(&FileCSec);
  918. if(!GetStandalone(NULL, 0, false, false, true)) return -1;
  919. return open(szStandalone, _O_BINARY | O_RDONLY);
  920. }
  921. int32_t C4Network2Res::OpenFileWrite()
  922. {
  923. CStdLock FileLock(&FileCSec);
  924. return open(szStandalone, _O_BINARY | O_CREAT | O_WRONLY, S_IREAD | S_IWRITE);
  925. }
  926. void C4Network2Res::StartNewLoads()
  927. {
  928. if(!pCChunks) return;
  929. // count clients
  930. int32_t iCChunkCnt = 0; ClientChunks *pChunks;
  931. for(pChunks = pCChunks; pChunks; pChunks = pChunks->Next)
  932. iCChunkCnt++;
  933. // create array
  934. ClientChunks **pC = new ClientChunks *[iCChunkCnt];
  935. // initialize
  936. int32_t i;
  937. for(i = 0; i < iCChunkCnt; i++) pC[i] = NULL;
  938. // create shuffled order
  939. for(pChunks = pCChunks, i = 0; i < iCChunkCnt; i++, pChunks = pChunks->Next)
  940. {
  941. // determine position
  942. int32_t iPos = SafeRandom(iCChunkCnt - i);
  943. // find & set
  944. for(int32_t j = 0; ;j++)
  945. if(!pC[j] && !iPos--)
  946. {
  947. pC[j] = pChunks;
  948. break;
  949. }
  950. }
  951. // start new load until maximum count reached
  952. while(iLoadCnt + 1 <= C4NetResMaxLoad)
  953. {
  954. int32_t ioLoadCnt = iLoadCnt;
  955. // search someone
  956. for(i = 0; i < iCChunkCnt; i++)
  957. if(pC[i])
  958. {
  959. // try to start load
  960. if(!StartLoad(pC[i]->ClientID, pC[i]->Chunks))
  961. { RemoveCChunks(pC[i]); pC[i] = NULL; continue; }
  962. // success?
  963. if(iLoadCnt > ioLoadCnt) break;
  964. }
  965. // not found?
  966. if(i >= iCChunkCnt)
  967. break;
  968. }
  969. // clear up
  970. delete [] pC;
  971. }
  972. bool C4Network2Res::StartLoad(int32_t iFromClient, const C4Network2ResChunkData &Available)
  973. {
  974. assert(pParent && pParent->getIOClass());
  975. // all slots used? ignore
  976. if(iLoadCnt + 1 >= C4NetResMaxLoad) return true;
  977. // is there already a load by this client? ignore
  978. for(C4Network2ResLoad *pPos = pLoads; pPos; pPos = pPos->Next())
  979. if(pPos->getByClient() == iFromClient)
  980. return true;
  981. // find chunk to retrieve
  982. int32_t iLoads[C4NetResMaxLoad]; int32_t i = 0;
  983. for(C4Network2ResLoad *pLoad = pLoads; pLoad; pLoad = pLoad->Next())
  984. iLoads[i++] = pLoad->getChunk();
  985. int32_t iRetrieveChunk = Chunks.GetChunkToRetrieve(Available, i, iLoads);
  986. // nothing? ignore
  987. if(iRetrieveChunk < 0 || (uint32_t)iRetrieveChunk >= Core.getChunkCnt())
  988. return true;
  989. // search message connection for client
  990. C4Network2IOConnection *pConn = pParent->getIOClass()->GetMsgConnection(iFromClient);
  991. if(!pConn) return false;
  992. // send request
  993. if(!pConn->Send(MkC4NetIOPacket(PID_NetResReq, C4PacketResRequest(Core.getID(), iRetrieveChunk))))
  994. { pConn->DelRef(); return false; }
  995. pConn->DelRef();
  996. #ifdef C4NET2RES_DEBUG_LOG
  997. // log
  998. Application.InteractiveThread.ThreadLogS("Network: Res: requesting chunk %d of %d:%s (%s) from client %d",
  999. iRetrieveChunk, Core.getID(), Core.getFileName(), szFile, iFromClient);
  1000. #endif
  1001. // create load class
  1002. C4Network2ResLoad *pnLoad = new C4Network2ResLoad(iRetrieveChunk, iFromClient);
  1003. // add to list
  1004. pnLoad->pNext = pLoads;
  1005. pLoads = pnLoad;
  1006. iLoadCnt++;
  1007. // ok
  1008. return true;
  1009. }
  1010. void C4Network2Res::EndLoad()
  1011. {
  1012. // clear loading data
  1013. ClearLoad();
  1014. // set complete
  1015. fLoading = false;
  1016. // call handler
  1017. assert(pParent);
  1018. pParent->OnResComplete(this);
  1019. }
  1020. void C4Network2Res::ClearLoad()
  1021. {
  1022. // remove client chunks and loads
  1023. fLoading = false;
  1024. while(pCChunks) RemoveCChunks(pCChunks);
  1025. while(pLoads) RemoveLoad(pLoads);
  1026. iDiscoverStartTime = iLoadCnt = 0;
  1027. }
  1028. void C4Network2Res::RemoveLoad(C4Network2ResLoad *pLoad)
  1029. {
  1030. if(pLoad == pLoads)
  1031. pLoads = pLoad->Next();
  1032. else
  1033. {
  1034. // find previous entry
  1035. C4Network2ResLoad *pPrev;
  1036. for(pPrev = pLoads; pPrev && pPrev->Next() != pLoad; pPrev = pPrev->Next()) {}
  1037. // remove
  1038. if(pPrev)
  1039. pPrev->pNext = pLoad->Next();
  1040. }
  1041. // delete
  1042. delete pLoad;
  1043. iLoadCnt--;
  1044. }
  1045. void C4Network2Res::RemoveCChunks(ClientChunks *pChunks)
  1046. {
  1047. if(pChunks == pCChunks)
  1048. pCChunks = pChunks->Next;
  1049. else
  1050. {
  1051. // find previous entry
  1052. ClientChunks *pPrev;
  1053. for(pPrev = pCChunks; pPrev && pPrev->Next != pChunks; pPrev = pPrev->Next) {}
  1054. // remove
  1055. if(pPrev)
  1056. pPrev->Next = pChunks->Next;
  1057. }
  1058. // delete
  1059. delete pChunks;
  1060. }
  1061. bool C4Network2Res::OptimizeStandalone(bool fSilent)
  1062. {
  1063. CStdLock FileLock(&FileCSec);
  1064. // for now: player files only
  1065. if(Core.getType() == NRT_Player)
  1066. {
  1067. // log - this may take a few seconds
  1068. if(!fSilent) LogF(LoadResStr("IDS_PRC_NETPREPARING"), GetFilename(szFile));
  1069. // copy to temp file, if needed
  1070. if(!fTempFile && SEqual(szFile, szStandalone))
  1071. {
  1072. char szNewStandalone[_MAX_PATH + 1];
  1073. if(!pParent->FindTempResFileName(szStandalone, szNewStandalone))
  1074. { if(!fSilent) Log("OptimizeStandalone: could not find free name for temporary file!"); return false; }
  1075. if(!C4Group_CopyItem(szStandalone, szNewStandalone))
  1076. { if(!fSilent) Log("OptimizeStandalone: could not copy to temporary file!"); return false; } /* TODO: Test failure */
  1077. SCopy(szNewStandalone, szStandalone, sizeof(szStandalone) - 1);
  1078. }
  1079. // open as group
  1080. C4Group Grp;
  1081. if(!Grp.Open(szStandalone))
  1082. { if(!fSilent) Log("OptimizeStandalone: could not open player file!"); return false; }
  1083. // remove portrais
  1084. Grp.Delete(C4CFN_Portraits, true);
  1085. // remove bigicon, if the file size is too large
  1086. size_t iBigIconSize=0;
  1087. if (Grp.FindEntry(C4CFN_BigIcon, NULL, &iBigIconSize))
  1088. if (iBigIconSize > C4NetResMaxBigicon*1024)
  1089. Grp.Delete(C4CFN_BigIcon);
  1090. Grp.Close();
  1091. }
  1092. return true;
  1093. }
  1094. // *** C4Network2ResChunk
  1095. C4Network2ResChunk::C4Network2ResChunk()
  1096. {
  1097. }
  1098. C4Network2ResChunk::~C4Network2ResChunk()
  1099. {
  1100. }
  1101. bool C4Network2ResChunk::Set(C4Network2Res *pRes, uint32_t inChunk)
  1102. {
  1103. const C4Network2ResCore &Core = pRes->getCore();
  1104. iResID = pRes->getResID();
  1105. iChunk = inChunk;
  1106. // calculate offset and size
  1107. int32_t iOffset = iChunk * Core.getChunkSize(),
  1108. iSize = Min<int32_t>(Core.getFileSize() - iOffset, C4NetResChunkSize);
  1109. if (iSize < 0) { LogF("Network: could not get chunk from offset %d from resource file %s: File size is only %d!", iOffset, pRes->getFile(), Core.getFileSize()); return false; }
  1110. // open file
  1111. int32_t f = pRes->OpenFileRead();
  1112. if(f == -1) { LogF("Network: could not open resource file %s!", pRes->getFile()); return false; }
  1113. // seek
  1114. if(iOffset)
  1115. if(lseek(f, iOffset, SEEK_SET) != iOffset)
  1116. { close(f); LogF("Network: could not read resource file %s!", pRes->getFile()); return false; }
  1117. // read chunk of data
  1118. char *pBuf = new char[iSize];
  1119. if(read(f, pBuf, iSize) != iSize)
  1120. { delete [] pBuf; close(f); LogF("Network: could not read resource file %s!", pRes->getFile()); return false; }
  1121. // set
  1122. Data.Take(pBuf, iSize);
  1123. // close
  1124. close(f);
  1125. // ok
  1126. return true;
  1127. }
  1128. bool C4Network2ResChunk::AddTo(C4Network2Res *pRes, C4Network2IO *pIO) const
  1129. {
  1130. assert(pRes); assert(pIO);
  1131. const C4Network2ResCore &Core = pRes->getCore();
  1132. // check
  1133. if(iResID != pRes->getResID())
  1134. {
  1135. #ifdef C4NET2RES_DEBUG_LOG
  1136. Application.InteractiveThread.ThreadLogS(FormatString("C4Network2ResChunk(%d)::AddTo(%s [%d]): Ressource ID mismatch!", (int) iResID, (const char *) Core.getFileName(), (int) pRes->getResID()).getData());
  1137. #endif
  1138. return false;
  1139. }
  1140. // calculate offset and size
  1141. int32_t iOffset = iChunk * Core.getChunkSize();
  1142. if(iOffset + Data.getSize() > Core.getFileSize())
  1143. {
  1144. #ifdef C4NET2RES_DEBUG_LOG
  1145. Application.InteractiveThread.ThreadLogS(FormatString("C4Network2ResChunk(%d)::AddTo(%s [%d]): Adding %d bytes at offset %d exceeds expected file size of %d!", (int) iResID, (const char *) Core.getFileName(), (int) pRes->getResID(), (int) Data.getSize(), (int) iOffset, (int) Core.getFileSize()).getData());
  1146. #endif
  1147. return false;
  1148. }
  1149. // open file
  1150. int32_t f = pRes->OpenFileWrite();
  1151. if(f == -1)
  1152. {
  1153. #ifdef C4NET2RES_DEBUG_LOG
  1154. Application.InteractiveThread.ThreadLogS(FormatString("C4Network2ResChunk(%d)::AddTo(%s [%d]): Open write file error: %s!", (int) iResID, (const char *) Core.getFileName(), (int) pRes->getResID(), strerror(errno)).getData());
  1155. #endif
  1156. return false;
  1157. }
  1158. // seek
  1159. if(iOffset)
  1160. if(lseek(f, iOffset, SEEK_SET) != iOffset)
  1161. {
  1162. #ifdef C4NET2RES_DEBUG_LOG
  1163. Application.InteractiveThread.ThreadLogS(FormatString("C4Network2ResChunk(%d)::AddTo(%s [%d]): lseek file error: %s!", (int) iResID, (const char *) Core.getFileName(), (int) pRes->getResID(), strerror(errno)).getData());
  1164. #endif
  1165. close(f);
  1166. return false;
  1167. }
  1168. // write
  1169. if(write(f, Data.getData(), Data.getSize()) != int32_t(Data.getSize()))
  1170. {
  1171. #ifdef C4NET2RES_DEBUG_LOG
  1172. Application.InteractiveThread.ThreadLogS(FormatString("C4Network2ResChunk(%d)::AddTo(%s [%d]): write error: %s!", (int) iResID, (const char *) Core.getFileName(), (int) pRes->getResID(), strerror(errno)).getData());
  1173. #endif
  1174. close(f);
  1175. return false;
  1176. }
  1177. // ok, add chunks
  1178. close(f);
  1179. pRes->Chunks.AddChunk(iChunk);
  1180. return true;
  1181. }
  1182. void C4Network2ResChunk::CompileFunc(StdCompiler *pComp)
  1183. {
  1184. // pack header
  1185. pComp->Value(mkNamingAdapt(iResID, "ResID", -1));
  1186. pComp->Value(mkNamingAdapt(iChunk, "Chunk", ~0U));
  1187. // Data
  1188. pComp->Value(mkNamingAdapt(Data, "Data"));
  1189. }
  1190. // *** C4Network2ResList
  1191. C4Network2ResList::C4Network2ResList()
  1192. : iClientID(-1),
  1193. iNextResID((-1) << 16),
  1194. pFirst(NULL),
  1195. ResListCSec(this),
  1196. iLastDiscover(0), iLastStatus(0),
  1197. pIO(NULL)
  1198. {
  1199. }
  1200. C4Network2ResList::~C4Network2ResList()
  1201. {
  1202. Clear();
  1203. }
  1204. bool C4Network2ResList::Init(int32_t inClientID, C4Network2IO *pIOClass) // by main thread
  1205. {
  1206. // clear old list
  1207. Clear();
  1208. // safe IO class
  1209. pIO = pIOClass;
  1210. // set client id
  1211. iNextResID = iClientID = 0;
  1212. SetLocalID(inClientID);
  1213. // create network path
  1214. if(!CreateNetworkFolder()) return false;
  1215. // ok
  1216. return true;
  1217. }
  1218. void C4Network2ResList::SetLocalID(int32_t inClientID)
  1219. {
  1220. CStdLock ResIDLock(&ResIDCSec);
  1221. int32_t iOldClientID = iClientID;
  1222. int32_t iIDDiff = (inClientID - iClientID) << 16;
  1223. // set new counter
  1224. iClientID = inClientID;
  1225. iNextResID += iIDDiff;
  1226. // change ressource ids
  1227. CStdLock ResListLock(&ResListCSec);
  1228. for(C4Network2Res *pRes = pFirst; pRes; pRes = pRes->pNext)
  1229. if(pRes->getResClient() == iOldClientID)
  1230. pRes->ChangeID(pRes->getResID() + iIDDiff);
  1231. }
  1232. int32_t C4Network2ResList::nextResID() // by main thread
  1233. {
  1234. CStdLock ResIDLock(&ResIDCSec);
  1235. assert(iNextResID >= (iClientID << 16));
  1236. if(iNextResID >= ((iClientID+1) << 16) - 1)
  1237. iNextResID = Max<int32_t>(0, iClientID) << 16;
  1238. // find free
  1239. while(getRes(iNextResID))
  1240. iNextResID++;
  1241. return iNextResID++;
  1242. }
  1243. C4Network2Res *C4Network2ResList::getRes(int32_t iResID)
  1244. {
  1245. CStdShareLock ResListLock(&ResListCSec);
  1246. for(C4Network2Res *pCur = pFirst; pCur; pCur = pCur->pNext)
  1247. if(pCur->getResID() == iResID)
  1248. return pCur;
  1249. return NULL;
  1250. }
  1251. C4Network2Res *C4Network2ResList::getRes(const char *szFile, bool fLocalOnly)
  1252. {
  1253. CStdShareLock ResListLock(&ResListCSec);
  1254. for(C4Network2Res *pCur = pFirst; pCur; pCur = pCur->pNext)
  1255. if(!pCur->isAnonymous())
  1256. if(SEqual(pCur->getFile(), szFile))
  1257. if (!fLocalOnly || pCur->getResClient()==iClientID)
  1258. return pCur;
  1259. return NULL;
  1260. }
  1261. C4Network2Res::Ref C4Network2ResList::getRefRes(int32_t iResID)
  1262. {
  1263. CStdShareLock ResListLock(&ResListCSec);
  1264. return getRes(iResID);
  1265. }
  1266. C4Network2Res::Ref C4Network2ResList::getRefRes(const char *szFile, bool fLocalOnly)
  1267. {
  1268. CStdShareLock ResListLock(&ResListCSec);
  1269. return getRes(szFile, fLocalOnly);
  1270. }
  1271. C4Network2Res::Ref C4Network2ResList::getRefNextRes(int32_t iResID)
  1272. {
  1273. CStdShareLock ResListLock(&ResListCSec);
  1274. C4Network2Res *pRes = NULL;
  1275. for(C4Network2Res *pCur = pFirst; pCur; pCur = pCur->pNext)
  1276. if(!pCur->isRemoved() && pCur->getResID() >= iResID)
  1277. if(!pRes || pRes->getResID() > pCur->getResID())
  1278. pRes = pCur;
  1279. return pRes;
  1280. }
  1281. void C4Network2ResList::Add(C4Network2Res *pRes)
  1282. {
  1283. // get locks
  1284. CStdShareLock ResListLock(&ResListCSec);
  1285. CStdLock ResListAddLock(&ResListAddCSec);
  1286. // reference
  1287. pRes->AddRef();
  1288. // add
  1289. pRes->pNext = pFirst;
  1290. pFirst = pRes;
  1291. }
  1292. C4Network2Res::Ref C4Network2ResList::AddByFile(const char *strFilePath, bool fTemp, C4Network2ResType eType, int32_t iResID, const char *szResName, bool fAllowUnloadable)
  1293. {
  1294. // already in list?
  1295. C4Network2Res::Ref pRes = getRefRes(strFilePath);
  1296. if(pRes) return pRes;
  1297. // get ressource ID
  1298. if(iResID < 0) iResID = nextResID();
  1299. if(iResID < 0) { Log("AddByFile: no more ressource IDs available!"); return NULL; }
  1300. // create new
  1301. pRes = new C4Network2Res(this);
  1302. // initialize
  1303. if(!pRes->SetByFile(strFilePath, fTemp, eType, iResID, szResName)) { return NULL; }
  1304. // create standalone for non-system files
  1305. // system files shouldn't create a standalone; they should never be marked loadable!
  1306. if (eType != NRT_System)
  1307. if(!pRes->GetStandalone(NULL, 0, true, fAllowUnloadable))
  1308. if(!fAllowUnloadable)
  1309. {
  1310. delete pRes;
  1311. return NULL;
  1312. }
  1313. // add to list
  1314. Add(pRes);
  1315. return pRes;
  1316. }
  1317. C4Network2Res::Ref C4Network2ResList::AddByGroup(C4Group *pGrp, bool fTemp, C4Network2ResType eType, int32_t iResID, const char *szResName, bool fAllowUnloadable)
  1318. {
  1319. // get ressource ID
  1320. if(iResID < 0) iResID = nextResID();
  1321. if(iResID < 0) { Log("AddByGroup: no more ressource IDs available!"); return NULL; }
  1322. // create new
  1323. C4Network2Res::Ref pRes = new C4Network2Res(this);
  1324. // initialize
  1325. if(!pRes->SetByGroup(pGrp, fTemp, eType, iResID, szResName))
  1326. {
  1327. delete pRes;
  1328. return NULL;
  1329. }
  1330. // create standalone
  1331. if(!pRes->GetStandalone(NULL, 0, true, fAllowUnloadable))
  1332. if(!fAllowUnloadable)
  1333. {
  1334. delete pRes;
  1335. return NULL;
  1336. }
  1337. // add to list
  1338. Add(pRes);
  1339. return pRes;
  1340. }
  1341. C4Network2Res::Ref C4Network2ResList::AddByCore(const C4Network2ResCore &Core, bool fLoad) // by main thread
  1342. {
  1343. // already in list?
  1344. C4Network2Res::Ref pRes = getRefRes(Core.getID());
  1345. if(pRes) return pRes;
  1346. #ifdef C4NET2RES_LOAD_ALL
  1347. // load without check (if possible)
  1348. if(Core.isLoadable()) return AddLoad(Core);
  1349. #endif
  1350. // create new
  1351. pRes = new C4Network2Res(this);
  1352. // try set by core
  1353. if(!pRes->SetByCore(Core, true))
  1354. {
  1355. pRes.Clear();
  1356. // try load (if specified)
  1357. return fLoad ? AddLoad(Core) : NULL;
  1358. }
  1359. // log
  1360. Application.InteractiveThread.ThreadLogS("Network: Found identical %s. Not loading.", pRes->getCore().getFileName());
  1361. // add to list
  1362. Add(pRes);
  1363. // ok
  1364. return pRes;
  1365. }
  1366. C4Network2Res::Ref C4Network2ResList::AddLoad(const C4Network2ResCore &Core) // by main thread
  1367. {
  1368. // marked unloadable by creator?
  1369. if(!Core.isLoadable())
  1370. {
  1371. // show error msg
  1372. Application.InteractiveThread.ThreadLog("Network: Cannot load %s (marked unloadable)", Core.getFileName());
  1373. return NULL;
  1374. }
  1375. // create new
  1376. C4Network2Res::Ref pRes = new C4Network2Res(this);
  1377. // initialize
  1378. pRes->SetLoad(Core);
  1379. // log
  1380. Application.InteractiveThread.ThreadLogS("Network: loading %s...", Core.getFileName());
  1381. // add to list
  1382. Add(pRes);
  1383. return pRes;
  1384. }
  1385. void C4Network2ResList::RemoveAtClient(int32_t iClientID) // by main thread
  1386. {
  1387. CStdShareLock ResListLock(&ResListCSec);
  1388. for(C4Network2Res *pRes = pFirst; pRes; pRes = pRes->pNext)
  1389. if(pRes->getResClient() == iClientID)
  1390. pRes->Remove();
  1391. }
  1392. void C4Network2ResList::Clear()
  1393. {
  1394. CStdShareLock ResListLock(&ResListCSec);
  1395. for(C4Network2Res *pRes = pFirst; pRes; pRes = pRes->pNext)
  1396. {
  1397. pRes->Remove();
  1398. pRes->iLastReqTime = 0;
  1399. }
  1400. iClientID = C4ClientIDUnknown;
  1401. iLastDiscover = iLastStatus = 0;
  1402. }
  1403. void C4Network2ResList::OnClientConnect(C4Network2IOConnection *pConn) // by main thread
  1404. {
  1405. // discover ressources
  1406. SendDiscover(pConn);
  1407. }
  1408. void C4Network2ResList::HandlePacket(char cStatus, const C4PacketBase *pPacket, C4Network2IOConnection *pConn)
  1409. {
  1410. // security
  1411. if(!pConn) return;
  1412. #define GETPKT(type, name) \
  1413. assert(pPacket); const type &name = \
  1414. /*dynamic_cast*/ static_cast<const type &>(*pPacket);
  1415. switch(cStatus)
  1416. {
  1417. case PID_NetResDis: // ressource discover
  1418. {
  1419. if(!pConn->isOpen()) break;
  1420. GETPKT(C4PacketResDiscover, Pkt);
  1421. // search matching ressources
  1422. CStdShareLock ResListLock(&ResListCSec);
  1423. for(C4Network2Res *pRes = pFirst; pRes; pRes = pRes->pNext)
  1424. if(Pkt.isIDPresent(pRes->getResID()))
  1425. // must be binary compatible
  1426. if(pRes->IsBinaryCompatible())
  1427. pRes->OnDiscover(pConn);
  1428. }
  1429. break;
  1430. case PID_NetResStat: // ressource status
  1431. {
  1432. if(!pConn->isOpen()) break;
  1433. GETPKT(C4PacketResStatus, Pkt);
  1434. // matching ressource?
  1435. CStdShareLock ResListLock(&ResListCSec);
  1436. C4Network2Res *pRes = getRes(Pkt.getResID());
  1437. // present / being loaded? call handler
  1438. if(pRes)
  1439. pRes->OnStatus(Pkt.getChunks(), pConn);
  1440. }
  1441. break;
  1442. case PID_NetResDerive: // ressource derive
  1443. {
  1444. GETPKT(C4Network2ResCore, Core);
  1445. if(Core.getDerID() < 0) break;
  1446. // Check if there is a anonymous derived ressource with matching parent.
  1447. CStdShareLock ResListLock(&ResListCSec);
  1448. for(C4Network2Res *pRes = pFirst; pRes; pRes = pRes->pNext)
  1449. if(pRes->isAnonymous() && pRes->getCore().getDerID() == Core.getDerID())
  1450. pRes->FinishDerive(Core);
  1451. }
  1452. break;
  1453. case PID_NetResReq: // ressource request
  1454. {
  1455. GETPKT(C4PacketResRequest, Pkt);
  1456. // find ressource
  1457. CStdShareLock ResListLock(&ResListCSec);
  1458. C4Network2Res *pRes = getRes(Pkt.getReqID());
  1459. // send requested chunk
  1460. if(pRes && pRes->IsBinaryCompatible()) pRes->SendChunk(Pkt.getReqChunk(), pConn->getClientID());
  1461. }
  1462. break;
  1463. case PID_NetResData: // a chunk of data is coming in
  1464. {
  1465. GETPKT(C4Network2ResChunk, Chunk);
  1466. // find ressource
  1467. CStdShareLock ResListLock(&ResListCSec);
  1468. C4Network2Res *pRes = getRes(Chunk.getResID());
  1469. // send requested chunk
  1470. if(pRes) pRes->OnChunk(Chunk);
  1471. }
  1472. break;
  1473. }
  1474. #undef GETPKT
  1475. }
  1476. void C4Network2ResList::OnTimer()
  1477. {
  1478. CStdShareLock ResListLock(&ResListCSec);
  1479. C4Network2Res *pRes;
  1480. // do loads, check timeouts
  1481. for(pRes = pFirst; pRes; pRes = pRes->pNext)
  1482. if(pRes->isLoading() && !pRes->isRemoved())
  1483. if(!pRes->DoLoad())
  1484. pRes->Remove();
  1485. // discovery time?
  1486. if(!iLastDiscover || difftime(time(NULL), iLastDiscover) >= C4NetResDiscoverInterval)
  1487. {
  1488. // needed?
  1489. bool fSendDiscover = false;
  1490. for(C4Network2Res *pRes = pFirst; pRes; pRes = pRes->pNext)
  1491. if(pRes->isLoading() && !pRes->isRemoved())
  1492. fSendDiscover |= pRes->NeedsDiscover();
  1493. // send
  1494. if(fSendDiscover)
  1495. SendDiscover();
  1496. }
  1497. // status update?
  1498. if(!iLastStatus || difftime(time(NULL), iLastStatus) >= C4NetResStatusInterval)
  1499. {
  1500. // any?
  1501. bool fStatusUpdates = false;
  1502. for(pRes = pFirst; pRes; pRes = pRes->pNext)
  1503. if(pRes->isDirty() && !pRes->isRemoved())
  1504. fStatusUpdates |= pRes->SendStatus();
  1505. // set time accordingly
  1506. iLastStatus = fStatusUpdates ? time(NULL) : 0;
  1507. }
  1508. }
  1509. void C4Network2ResList::OnShareFree(CStdCSecEx *pCSec)
  1510. {
  1511. if(pCSec == &ResListCSec)
  1512. {
  1513. // remove entries
  1514. for(C4Network2Res *pRes = pFirst, *pNext, *pPrev = NULL; pRes; pRes = pNext)
  1515. {
  1516. pNext = pRes->pNext;
  1517. if(pRes->isRemoved() && (!pRes->getLastReqTime() || difftime(time(NULL), pRes->getLastReqTime()) > C4NetResDeleteTime))
  1518. {
  1519. // unlink
  1520. (pPrev ? pPrev->pNext : pFirst) = pNext;
  1521. // remove
  1522. pRes->pNext = NULL;
  1523. pRes->DelRef();
  1524. }
  1525. else
  1526. pPrev = pRes;
  1527. }
  1528. }
  1529. }
  1530. bool C4Network2ResList::SendDiscover(C4Network2IOConnection *pTo) // by both
  1531. {
  1532. // make packet
  1533. C4PacketResDiscover Pkt;
  1534. // add special retrieves
  1535. CStdShareLock ResListLock(&ResListCSec);
  1536. for(C4Network2Res *pRes = pFirst; pRes; pRes = pRes->pNext)
  1537. if(!pRes->isRemoved())
  1538. if(pRes->isLoading())
  1539. Pkt.AddDisID(pRes->getResID());
  1540. ResListLock.Clear();
  1541. // empty?
  1542. if(!Pkt.getDisIDCnt()) return false;
  1543. // broadcast?
  1544. if(!pTo)
  1545. {
  1546. // save time
  1547. iLastDiscover = time(NULL);
  1548. // send
  1549. return pIO->BroadcastMsg(MkC4NetIOPacket(PID_NetResDis, Pkt));
  1550. }
  1551. else
  1552. return pTo->Send(MkC4NetIOPacket(PID_NetResDis, Pkt));
  1553. }
  1554. void C4Network2ResList::OnResComplete(C4Network2Res *pRes)
  1555. {
  1556. // log (network thread -> ThreadLog)
  1557. Application.InteractiveThread.ThreadLogS("Network: %s received.", pRes->getCore().getFileName());
  1558. // call handler (ctrl might wait for this ressource)
  1559. ::Control.Network.OnResComplete(pRes);
  1560. }
  1561. bool C4Network2ResList::CreateNetworkFolder()
  1562. {
  1563. // get network path without trailing backslash
  1564. char szNetworkPath[_MAX_PATH+1];
  1565. SCopy(Config.AtNetworkPath(""), szNetworkPath, _MAX_PATH);
  1566. TruncateBackslash(szNetworkPath);
  1567. // but make sure that the configured path has one
  1568. AppendBackslash(Config.Network.WorkPath);
  1569. // does not exist?
  1570. if(access(szNetworkPath, 00))
  1571. {
  1572. if(!CreatePath(szNetworkPath))
  1573. { LogFatal("Network: could not create network path!"); return false; }
  1574. return true;
  1575. }
  1576. // stat
  1577. struct stat s;
  1578. if(stat(szNetworkPath, &s))
  1579. { LogFatal("Network: could not stat network path!"); return false; }
  1580. // not a subdir?
  1581. if(!(s.st_mode & S_IFDIR))
  1582. { LogFatal("Network: could not create network path: blocked by a file!"); return false; }
  1583. // ok
  1584. return true;
  1585. }
  1586. bool C4Network2ResList::FindTempResFileName(const char *szFilename, char *pTarget)
  1587. {
  1588. char safeFilename[_MAX_PATH];
  1589. char* safePos = safeFilename;
  1590. while (*szFilename)
  1591. {
  1592. if ((*szFilename >= 'a' && *szFilename <= 'z') ||
  1593. (*szFilename >= 'A' && *szFilename <= 'Z') ||
  1594. (*szFilename >= '0' && *szFilename <= '9') ||
  1595. (*szFilename == '.') || (*szFilename == '/'))
  1596. *safePos = *szFilename;
  1597. else
  1598. *safePos = '_';
  1599. ++safePos;
  1600. ++szFilename;
  1601. }
  1602. *safePos = 0;
  1603. szFilename = safeFilename;
  1604. // create temporary file
  1605. SCopy(Config.AtNetworkPath(GetFilename(szFilename)), pTarget, _MAX_PATH);
  1606. // file name is free?
  1607. if(access(pTarget, F_OK)) return true;
  1608. // find another file name
  1609. char szFileMask[_MAX_PATH+1];
  1610. SCopy(pTarget, szFileMask, GetExtension(pTarget)-1-pTarget);
  1611. SAppend("_%d", szFileMask, _MAX_PATH);
  1612. SAppend(GetExtension(pTarget)-1, szFileMask, _MAX_PATH);
  1613. for(int32_t i = 2; i < 1000; i++)
  1614. {
  1615. snprintf(pTarget, _MAX_PATH, szFileMask, i);
  1616. // doesn't exist?
  1617. if(access(pTarget, F_OK))
  1618. return true;
  1619. }
  1620. // not found
  1621. return false;
  1622. }