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