OpenClonk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
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();
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  int32_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  // sorry, this version isn't good enough :(
630  return false;
631  }
632 
633  // calc checksum
634  uint32_t iCRC32;
635  if (!GetFileCRC(szStandalone, &iCRC32))
636  { if (!fSilent) Log("GetStandalone: could not calculate checksum!"); return false; }
637  // set / check
638  if (!fSetOfficial && iCRC32 != Core.getFileCRC())
639  {
640  // remove file, return
642  return false;
643  }
644 
645  // we didn't fail
646  fStandaloneFailed = false;
647  // mark resource as loadable and safe file information
648  Core.SetLoadable(iSize, iCRC32);
649  // set up chunk data
651  // ok
652  return true;
653 }
654 
656 {
657  // already present?
658  if (Core.hasFileSHA()) return true;
659  // get the file
660  char szStandalone[_MAX_PATH + 1];
661  if (!GetStandalone(szStandalone, _MAX_PATH, false))
662  SCopy(szFile, szStandalone, _MAX_PATH);
663  // get the hash
664  BYTE hash[SHA_DIGEST_LENGTH];
665  if (!GetFileSHA1(szStandalone, hash))
666  return false;
667  // save it back
668  Core.SetFileSHA(hash);
669  // okay
670  return true;
671 }
672 
673 
675 {
676  // Called before the file is changed. Rescues all files and creates a
677  // new resource for the file. This resource is flagged as "anonymous", as it
678  // has no official core (no res ID, to be exact).
679  // The resource gets its final core when FinishDerive() is called.
680 
681  // For security: This doesn't make much sense if the resource is currently being
682  // loaded. So better assume the caller doesn't know what he's doing and check.
683  if (isLoading()) return nullptr;
684 
685  CStdLock FileLock(&FileCSec);
686  // Save back original file name
687  char szOrgFile[_MAX_PATH+1];
688  SCopy(szFile, szOrgFile, _MAX_PATH);
689  bool fOrgTempFile = fTempFile;
690 
691  // Create a copy of the file, if neccessary
693  {
694  if (!pParent->FindTempResFileName(szOrgFile, szFile))
695  { Log("Derive: could not find free name for temporary file!"); return nullptr; }
696  if (!C4Group_CopyItem(szOrgFile, szFile))
697  { Log("Derive: could not copy to temporary file!"); return nullptr; }
698  // set standalone
699  if (*szStandalone)
701  fTempFile = true;
702  }
703  else
704  {
705  // Standlone exists: Just set szFile to point on the standlone. It's
706  // assumed that the original file isn't of intrest after this point anyway.
708  fTempFile = true;
709  }
710 
711  Application.InteractiveThread.ThreadLogS("Network: Resource: deriving from %d:%s, original at %s", getResID(), Core.getFileName(), szFile);
712 
713  // (note: should remove temp file if something fails after this point)
714 
715  // create new resource
717  if (!pDRes) return nullptr;
718 
719  // initialize
720  if (!pDRes->SetDerived(Core.getFileName(), szOrgFile, fOrgTempFile, getType(), getResID()))
721  return nullptr;
722 
723  // add to list
724  pParent->Add(pDRes);
725 
726  // return new resource
727  return pDRes;
728 }
729 
730 bool C4Network2Res::FinishDerive() // by main thread
731 {
732  // All changes have been made. Register this resource with a new ID.
733 
734  // security
735  if (!isAnonymous()) return false;
736 
737  CStdLock FileLock(&FileCSec);
738  // Save back data
739  int32_t iDerID = Core.getDerID();
740  char szName[_MAX_PATH+1]; SCopy(Core.getFileName(), szName, _MAX_PATH);
741  char szFileC[_MAX_PATH+1]; SCopy(szFile, szFileC, _MAX_PATH);
742  // Set by file
743  if (!SetByFile(szFileC, fTempFile, getType(), pParent->nextResID(), szName))
744  return false;
745  // create standalone
746  if (!GetStandalone(nullptr, 0, true))
747  return false;
748  // Set ID
749  Core.SetDerived(iDerID);
750  // announce derive
752  // derivation is dirty bussines
753  fDirty = true;
754  // ok
755  return true;
756 }
757 
759 {
760  // security
761  if (!isAnonymous()) return false;
762  // Set core
763  Core = nCore;
764  // Set chunks (assume the resource is complete)
766 
767  // Note that the Contents-CRC is /not/ checked. Derivation needs to be
768  // synchronized outside of C4Network2Res.
769 
770  // But note that the resource /might/ be binary compatible (though very
771  // unlikely), so do not set fNotBinaryCompatible.
772 
773  // ok
774  return true;
775 }
776 
778 {
779  C4Group *pnGrp = new C4Group();
780  if (!pnGrp->Open(szFile))
781  {
782  delete pnGrp;
783  return nullptr;
784  }
785  return pnGrp;
786 }
787 
789 {
790  // schedule for removal
791  fRemoved = true;
792 }
793 
795 {
796  // pack status
798  // to one client?
799  if (pTo)
800  return pTo->Send(Pkt);
801  else
802  {
803  // reset dirty flag
804  fDirty = false;
805  // broadcast status
806  assert(pParent && pParent->getIOClass());
807  return pParent->getIOClass()->BroadcastMsg(Pkt);
808  }
809 }
810 
811 bool C4Network2Res::SendChunk(uint32_t iChunk, int32_t iToClient)
812 {
813  assert(pParent && pParent->getIOClass());
814  if (!szStandalone[0] || iChunk >= Core.getChunkCnt()) return false;
815  // find connection for given client (one of the rare uses of the data connection)
817  if (!pConn) return false;
818  // save last request time
819  iLastReqTime = time(nullptr);
820  // create packet
821  CStdLock FileLock(&FileCSec);
822  C4Network2ResChunk ResChunk;
823  ResChunk.Set(this, iChunk);
824  // send
825  bool fSuccess = pConn->Send(MkC4NetIOPacket(PID_NetResData, ResChunk));
826  pConn->DelRef();
827  return fSuccess;
828 }
829 
831 {
832  ++iRefCnt;
833 }
834 
836 {
837  if (--iRefCnt == 0)
838  delete this;
839 }
840 
842 {
843  if (!IsBinaryCompatible()) return;
844  // discovered
845  iLastReqTime = time(nullptr);
846  // send status back
847  SendStatus(pBy);
848 }
849 
851 {
852  if (!fLoading) return;
853  // discovered a source: reset timeout
854  iDiscoverStartTime = 0;
855  // check if the chunk data is valid
856  if (rChunkData.getChunkCnt() != Chunks.getChunkCnt())
857  return;
858  // add chunk data
859  ClientChunks *pChunks;
860  for (pChunks = pCChunks; pChunks; pChunks = pChunks->Next)
861  if (pChunks->ClientID == pBy->getClientID())
862  break;
863  // not found? add
864  if (!pChunks)
865  {
866  pChunks = new ClientChunks();
867  pChunks->Next = pCChunks;
868  pCChunks = pChunks;
869  }
870  pChunks->ClientID = pBy->getClientID();
871  pChunks->Chunks = rChunkData;
872  // check load
873  if (!StartLoad(pChunks->ClientID, pChunks->Chunks))
875 }
876 
878 {
879  if (!fLoading) return;
880  // correct resource?
881  if (rChunk.getResID() != getResID()) return;
882  // add resource data
883  CStdLock FileLock(&FileCSec);
884  bool fSuccess = rChunk.AddTo(this, pParent->getIOClass());
885 #ifdef C4NET2RES_DEBUG_LOG
886  // log
887  Application.InteractiveThread.ThreadLogS("Network: Res: %s chunk %d to resource %s (%s)%s", fSuccess ? "added" : "could not add", rChunk.getChunkNr(), Core.getFileName(), szFile, fSuccess ? "" : "!");
888 #endif
889  if (fSuccess)
890  {
891  // status changed
892  fDirty = true;
893  // remove load waits
894  for (C4Network2ResLoad *pLoad = pLoads, *pNext; pLoad; pLoad = pNext)
895  {
896  pNext = pLoad->Next();
897  if (static_cast<uint32_t>(pLoad->getChunk()) == rChunk.getChunkNr())
898  RemoveLoad(pLoad);
899  }
900  }
901  // complete?
902  if (Chunks.isComplete())
903  EndLoad();
904  // check: start new loads?
905  else
906  StartNewLoads();
907 }
908 
910 {
911  if (!fLoading) return true;
912  // any loads currently active?
913  if (iLoadCnt)
914  {
915  // check for load timeouts
916  int32_t iLoadsRemoved = 0;
917  for (C4Network2ResLoad *pLoad = pLoads, *pNext; pLoad; pLoad = pNext)
918  {
919  pNext = pLoad->Next();
920  if (pLoad->CheckTimeout())
921  {
922  RemoveLoad(pLoad);
923  iLoadsRemoved++;
924  }
925  }
926  // start new loads
927  if (iLoadsRemoved) StartNewLoads();
928  }
929  else
930  {
931  // discover timeout?
932  if (iDiscoverStartTime)
933  if (difftime(time(nullptr), iDiscoverStartTime) > C4NetResDiscoverTimeout)
934  return false;
935  }
936  // ok
937  return true;
938 }
939 
941 {
942  // loading, but no active load sources?
943  if (fLoading && !iLoadCnt)
944  {
945  // set timeout, if this is the first discover
946  if (!iDiscoverStartTime)
947  iDiscoverStartTime = time(nullptr);
948  // do discover
949  return true;
950  }
951  return false;
952 }
953 
955 {
956  CStdLock FileLock(&FileCSec);
957  // delete files
958  if (fTempFile)
959  if (FileExists(szFile))
960  if (!EraseFile(szFile))
961  LogSilentF("Network: Could not delete temporary resource file (%s)", strerror(errno));
962  if (szStandalone[0] && !SEqual(szFile, szStandalone))
964  if (!EraseFile(szStandalone))
965  LogSilentF("Network: Could not delete temporary resource file (%s)", strerror(errno));
966  szFile[0] = szStandalone[0] = '\0';
967  fDirty = false;
968  fTempFile = false;
969  Core.Clear();
970  Chunks.Clear();
971  fRemoved = false;
972  ClearLoad();
973 }
974 
976 {
977  CStdLock FileLock(&FileCSec);
978  if (!GetStandalone(nullptr, 0, false, false, true)) return -1;
979  // FIXME: Use standard OC file access api here
980 #ifdef _WIN32
981  return _wopen(GetWideChar(szStandalone), _O_BINARY | O_RDONLY);
982 #else
983  return open(szStandalone, _O_BINARY | O_CLOEXEC | O_RDONLY);
984 #endif
985 }
986 
988 {
989  CStdLock FileLock(&FileCSec);
990  // FIXME: Use standard OC file access api here
991 #ifdef _WIN32
992  return _wopen(GetWideChar(szStandalone), _O_BINARY | O_CREAT | O_WRONLY, S_IREAD | S_IWRITE);
993 #else
994  return open(szStandalone, _O_BINARY | O_CLOEXEC | O_CREAT | O_WRONLY, S_IREAD | S_IWRITE);
995 #endif
996 }
997 
999 {
1000  if (!pCChunks) return;
1001  // count clients
1002  int32_t iCChunkCnt = 0; ClientChunks *pChunks;
1003  for (pChunks = pCChunks; pChunks; pChunks = pChunks->Next)
1004  iCChunkCnt++;
1005  // create array
1006  ClientChunks **pC = new ClientChunks *[iCChunkCnt];
1007  // initialize
1008  int32_t i;
1009  for (i = 0; i < iCChunkCnt; i++) pC[i] = nullptr;
1010  // create shuffled order
1011  for (pChunks = pCChunks, i = 0; i < iCChunkCnt; i++, pChunks = pChunks->Next)
1012  {
1013  // determine position
1014  int32_t iPos = UnsyncedRandom(iCChunkCnt - i);
1015  // find & set
1016  for (int32_t j = 0; ; j++)
1017  if (!pC[j] && !iPos--)
1018  {
1019  pC[j] = pChunks;
1020  break;
1021  }
1022  }
1023  // start new load until maximum count reached
1024  while (iLoadCnt + 1 <= C4NetResMaxLoad)
1025  {
1026  int32_t ioLoadCnt = iLoadCnt;
1027  // search someone
1028  for (i = 0; i < iCChunkCnt; i++)
1029  if (pC[i])
1030  {
1031  // try to start load
1032  if (!StartLoad(pC[i]->ClientID, pC[i]->Chunks))
1033  { RemoveCChunks(pC[i]); pC[i] = nullptr; continue; }
1034  // success?
1035  if (iLoadCnt > ioLoadCnt) break;
1036  }
1037  // not found?
1038  if (i >= iCChunkCnt)
1039  break;
1040  }
1041  // clear up
1042  delete [] pC;
1043 }
1044 
1045 bool C4Network2Res::StartLoad(int32_t iFromClient, const C4Network2ResChunkData &Available)
1046 {
1047  assert(pParent && pParent->getIOClass());
1048  // all slots used? ignore
1049  if (iLoadCnt + 1 >= C4NetResMaxLoad) return true;
1050  // is there already a load by this client? ignore
1051  for (C4Network2ResLoad *pPos = pLoads; pPos; pPos = pPos->Next())
1052  if (pPos->getByClient() == iFromClient)
1053  return true;
1054  // find chunk to retrieve
1055  int32_t iLoads[C4NetResMaxLoad]; int32_t i = 0;
1056  for (C4Network2ResLoad *pLoad = pLoads; pLoad; pLoad = pLoad->Next())
1057  iLoads[i++] = pLoad->getChunk();
1058  int32_t iRetrieveChunk = Chunks.GetChunkToRetrieve(Available, i, iLoads);
1059  // nothing? ignore
1060  if (iRetrieveChunk < 0 || (uint32_t)iRetrieveChunk >= Core.getChunkCnt())
1061  return true;
1062  // search message connection for client
1063  C4Network2IOConnection *pConn = pParent->getIOClass()->GetMsgConnection(iFromClient);
1064  if (!pConn) return false;
1065  // send request
1066  if (!pConn->Send(MkC4NetIOPacket(PID_NetResReq, C4PacketResRequest(Core.getID(), iRetrieveChunk))))
1067  { pConn->DelRef(); return false; }
1068  pConn->DelRef();
1069 #ifdef C4NET2RES_DEBUG_LOG
1070  // log
1071  Application.InteractiveThread.ThreadLogS("Network: Res: requesting chunk %d of %d:%s (%s) from client %d",
1072  iRetrieveChunk, Core.getID(), Core.getFileName(), szFile, iFromClient);
1073 #endif
1074  // create load class
1075  C4Network2ResLoad *pnLoad = new C4Network2ResLoad(iRetrieveChunk, iFromClient);
1076  // add to list
1077  pnLoad->pNext = pLoads;
1078  pLoads = pnLoad;
1079  iLoadCnt++;
1080  // ok
1081  return true;
1082 }
1083 
1085 {
1086  // clear loading data
1087  ClearLoad();
1088  // set complete
1089  fLoading = false;
1090  // call handler
1091  assert(pParent);
1092  pParent->OnResComplete(this);
1093 }
1094 
1096 {
1097  // remove client chunks and loads
1098  fLoading = false;
1099  while (pCChunks) RemoveCChunks(pCChunks);
1100  while (pLoads) RemoveLoad(pLoads);
1102 }
1103 
1105 {
1106  if (pLoad == pLoads)
1107  pLoads = pLoad->Next();
1108  else
1109  {
1110  // find previous entry
1111  C4Network2ResLoad *pPrev;
1112  for (pPrev = pLoads; pPrev && pPrev->Next() != pLoad; pPrev = pPrev->Next()) {}
1113  // remove
1114  if (pPrev)
1115  pPrev->pNext = pLoad->Next();
1116  }
1117  // delete
1118  delete pLoad;
1119  iLoadCnt--;
1120 }
1121 
1123 {
1124  if (pChunks == pCChunks)
1125  pCChunks = pChunks->Next;
1126  else
1127  {
1128  // find previous entry
1129  ClientChunks *pPrev;
1130  for (pPrev = pCChunks; pPrev && pPrev->Next != pChunks; pPrev = pPrev->Next) {}
1131  // remove
1132  if (pPrev)
1133  pPrev->Next = pChunks->Next;
1134  }
1135  // delete
1136  delete pChunks;
1137 }
1138 
1140 {
1141  CStdLock FileLock(&FileCSec);
1142  // for now: player files only
1143  if (Core.getType() == NRT_Player)
1144  {
1145  // log - this may take a few seconds
1146  if (!fSilent) LogF(LoadResStr("IDS_PRC_NETPREPARING"), GetFilename(szFile));
1147  // copy to temp file, if needed
1148  if (!fTempFile && SEqual(szFile, szStandalone))
1149  {
1150  char szNewStandalone[_MAX_PATH + 1];
1151  if (!pParent->FindTempResFileName(szStandalone, szNewStandalone))
1152  { if (!fSilent) Log("OptimizeStandalone: could not find free name for temporary file!"); return false; }
1153  if (!C4Group_CopyItem(szStandalone, szNewStandalone))
1154  { if (!fSilent) Log("OptimizeStandalone: could not copy to temporary file!"); return false; } /* TODO: Test failure */
1155  SCopy(szNewStandalone, szStandalone, sizeof(szStandalone) - 1);
1156  }
1157  // open as group
1158  C4Group Grp;
1159  if (!Grp.Open(szStandalone))
1160  { if (!fSilent) Log("OptimizeStandalone: could not open player file!"); return false; }
1161  // remove bigicon, if the file size is too large
1162  size_t iBigIconSize=0;
1163  if (Grp.FindEntry(C4CFN_BigIcon, nullptr, &iBigIconSize))
1164  if (iBigIconSize > C4NetResMaxBigicon*1024)
1165  Grp.Delete(C4CFN_BigIcon);
1166  Grp.Close();
1167  }
1168  return true;
1169 }
1170 
1171 // *** C4Network2ResChunk
1172 
1174 
1176 
1177 bool C4Network2ResChunk::Set(C4Network2Res *pRes, uint32_t inChunk)
1178 {
1179  const C4Network2ResCore &Core = pRes->getCore();
1180  iResID = pRes->getResID();
1181  iChunk = inChunk;
1182  // calculate offset and size
1183  int32_t iOffset = iChunk * Core.getChunkSize(),
1184  iSize = std::min<int32_t>(Core.getFileSize() - iOffset, C4NetResChunkSize);
1185  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; }
1186  // open file
1187  int32_t f = pRes->OpenFileRead();
1188  if (f == -1) { LogF("Network: could not open resource file %s!", pRes->getFile()); return false; }
1189  // seek
1190  if (iOffset)
1191  if (lseek(f, iOffset, SEEK_SET) != iOffset)
1192  { close(f); LogF("Network: could not read resource file %s!", pRes->getFile()); return false; }
1193  // read chunk of data
1194  char *pBuf = (char *) malloc(iSize);
1195  if (read(f, pBuf, iSize) != iSize)
1196  { free(pBuf); close(f); LogF("Network: could not read resource file %s!", pRes->getFile()); return false; }
1197  // set
1198  Data.Take(pBuf, iSize);
1199  // close
1200  close(f);
1201  // ok
1202  return true;
1203 }
1204 
1206 {
1207  assert(pRes); assert(pIO);
1208  const C4Network2ResCore &Core = pRes->getCore();
1209  // check
1210  if (iResID != pRes->getResID())
1211  {
1212 #ifdef C4NET2RES_DEBUG_LOG
1213  Application.InteractiveThread.ThreadLogS("C4Network2ResChunk(%d)::AddTo(%s [%d]): Resource ID mismatch!", (int) iResID, (const char *) Core.getFileName(), (int) pRes->getResID());
1214 #endif
1215  return false;
1216  }
1217  // calculate offset and size
1218  int32_t iOffset = iChunk * Core.getChunkSize();
1219  if (iOffset + Data.getSize() > Core.getFileSize())
1220  {
1221 #ifdef C4NET2RES_DEBUG_LOG
1222  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());
1223 #endif
1224  return false;
1225  }
1226  // open file
1227  int32_t f = pRes->OpenFileWrite();
1228  if (f == -1)
1229  {
1230 #ifdef C4NET2RES_DEBUG_LOG
1231  Application.InteractiveThread.ThreadLogS("C4Network2ResChunk(%d)::AddTo(%s [%d]): Open write file error: %s!", (int) iResID, (const char *) Core.getFileName(), (int) pRes->getResID(), strerror(errno));
1232 #endif
1233  return false;
1234  }
1235  // seek
1236  if (iOffset)
1237  if (lseek(f, iOffset, SEEK_SET) != iOffset)
1238  {
1239 #ifdef C4NET2RES_DEBUG_LOG
1240  Application.InteractiveThread.ThreadLogS("C4Network2ResChunk(%d)::AddTo(%s [%d]): lseek file error: %s!", (int) iResID, (const char *) Core.getFileName(), (int) pRes->getResID(), strerror(errno));
1241 #endif
1242  close(f);
1243  return false;
1244  }
1245  // write
1246  if (write(f, Data.getData(), Data.getSize()) != int32_t(Data.getSize()))
1247  {
1248 #ifdef C4NET2RES_DEBUG_LOG
1249  Application.InteractiveThread.ThreadLogS("C4Network2ResChunk(%d)::AddTo(%s [%d]): write error: %s!", (int) iResID, (const char *) Core.getFileName(), (int) pRes->getResID(), strerror(errno));
1250 #endif
1251  close(f);
1252  return false;
1253  }
1254  // ok, add chunks
1255  close(f);
1256  pRes->Chunks.AddChunk(iChunk);
1257  return true;
1258 }
1259 
1261 {
1262  // pack header
1263  pComp->Value(mkNamingAdapt(iResID, "ResID", -1));
1264  pComp->Value(mkNamingAdapt(iChunk, "Chunk", ~0U));
1265  // Data
1266  pComp->Value(mkNamingAdapt(Data, "Data"));
1267 }
1268 
1269 // *** C4Network2ResList
1270 
1272  : ResListCSec(this),
1273  iNextResID((-1) << 16)
1274 {
1275 
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 void C4Network2ResList::HandlePacket(char cStatus, const C4PacketBase *pPacket, C4Network2IOConnection *pConn)
1504 {
1505  // security
1506  if (!pConn) return;
1507 
1508 #define GETPKT(type, name) \
1509  assert(pPacket); const type &name = \
1510  static_cast<const type &>(*pPacket);
1511 
1512  switch (cStatus)
1513  {
1514 
1515  case PID_NetResDis: // resource discover
1516  {
1517  if (!pConn->isOpen()) break;
1519  // search matching resources
1520  CStdShareLock ResListLock(&ResListCSec);
1521  for (C4Network2Res *pRes = pFirst; pRes; pRes = pRes->pNext)
1522  if (Pkt.isIDPresent(pRes->getResID()))
1523  // must be binary compatible
1524  if (pRes->IsBinaryCompatible())
1525  pRes->OnDiscover(pConn);
1526  }
1527  break;
1528 
1529  case PID_NetResStat: // resource status
1530  {
1531  if (!pConn->isOpen()) break;
1532  GETPKT(C4PacketResStatus, Pkt);
1533  // matching resource?
1534  CStdShareLock ResListLock(&ResListCSec);
1535  C4Network2Res *pRes = getRes(Pkt.getResID());
1536  // present / being loaded? call handler
1537  if (pRes)
1538  pRes->OnStatus(Pkt.getChunks(), pConn);
1539  }
1540  break;
1541 
1542  case PID_NetResDerive: // resource derive
1543  {
1544  GETPKT(C4Network2ResCore, Core);
1545  if (Core.getDerID() < 0) break;
1546  // Check if there is a anonymous derived resource with matching parent.
1547  CStdShareLock ResListLock(&ResListCSec);
1548  for (C4Network2Res *pRes = pFirst; pRes; pRes = pRes->pNext)
1549  if (pRes->isAnonymous() && pRes->getCore().getDerID() == Core.getDerID())
1550  pRes->FinishDerive(Core);
1551  }
1552  break;
1553 
1554  case PID_NetResReq: // resource request
1555  {
1556  GETPKT(C4PacketResRequest, Pkt);
1557  // find resource
1558  CStdShareLock ResListLock(&ResListCSec);
1559  C4Network2Res *pRes = getRes(Pkt.getReqID());
1560  // send requested chunk
1561  if (pRes && pRes->IsBinaryCompatible()) pRes->SendChunk(Pkt.getReqChunk(), pConn->getClientID());
1562  }
1563  break;
1564 
1565  case PID_NetResData: // a chunk of data is coming in
1566  {
1567  GETPKT(C4Network2ResChunk, Chunk);
1568  // find resource
1569  CStdShareLock ResListLock(&ResListCSec);
1570  C4Network2Res *pRes = getRes(Chunk.getResID());
1571  // send requested chunk
1572  if (pRes) pRes->OnChunk(Chunk);
1573  }
1574  break;
1575  }
1576 #undef GETPKT
1577 }
1578 
1580 {
1581  CStdShareLock ResListLock(&ResListCSec);
1582  C4Network2Res *pRes;
1583  // do loads, check timeouts
1584  for (pRes = pFirst; pRes; pRes = pRes->pNext)
1585  if (pRes->isLoading() && !pRes->isRemoved())
1586  if (!pRes->DoLoad())
1587  pRes->Remove();
1588  // discovery time?
1589  if (!iLastDiscover || difftime(time(nullptr), iLastDiscover) >= C4NetResDiscoverInterval)
1590  {
1591  // needed?
1592  bool fSendDiscover = false;
1593  for (C4Network2Res *pRes = pFirst; pRes; pRes = pRes->pNext)
1594  if (pRes->isLoading() && !pRes->isRemoved())
1595  fSendDiscover |= pRes->NeedsDiscover();
1596  // send
1597  if (fSendDiscover)
1598  SendDiscover();
1599  }
1600  // status update?
1601  if (!iLastStatus || difftime(time(nullptr), iLastStatus) >= C4NetResStatusInterval)
1602  {
1603  // any?
1604  bool fStatusUpdates = false;
1605  for (pRes = pFirst; pRes; pRes = pRes->pNext)
1606  if (pRes->isDirty() && !pRes->isRemoved())
1607  fStatusUpdates |= pRes->SendStatus();
1608  // set time accordingly
1609  iLastStatus = fStatusUpdates ? time(nullptr) : 0;
1610  }
1611 }
1612 
1614 {
1615  if (pCSec == &ResListCSec)
1616  {
1617  // remove entries
1618  for (C4Network2Res *pRes = pFirst, *pNext, *pPrev = nullptr; pRes; pRes = pNext)
1619  {
1620  pNext = pRes->pNext;
1621  if (pRes->isRemoved() && (!pRes->getLastReqTime() || difftime(time(nullptr), pRes->getLastReqTime()) > C4NetResDeleteTime))
1622  {
1623  // unlink
1624  (pPrev ? pPrev->pNext : pFirst) = pNext;
1625  // remove
1626  pRes->pNext = nullptr;
1627  pRes->DelRef();
1628  }
1629  else
1630  pPrev = pRes;
1631  }
1632  }
1633 }
1634 
1636 {
1637  // make packet
1638  C4PacketResDiscover Pkt;
1639  // add special retrieves
1640  CStdShareLock ResListLock(&ResListCSec);
1641  for (C4Network2Res *pRes = pFirst; pRes; pRes = pRes->pNext)
1642  if (!pRes->isRemoved())
1643  if (pRes->isLoading())
1644  Pkt.AddDisID(pRes->getResID());
1645  ResListLock.Clear();
1646  // empty?
1647  if (!Pkt.getDisIDCnt()) return false;
1648  // broadcast?
1649  if (!pTo)
1650  {
1651  // save time
1652  iLastDiscover = time(nullptr);
1653  // send
1655  }
1656  else
1657  return pTo->Send(MkC4NetIOPacket(PID_NetResDis, Pkt));
1658 }
1659 
1661 {
1662  // log (network thread -> ThreadLog)
1663  Application.InteractiveThread.ThreadLogS("Network: %s received.", pRes->getCore().getFileName());
1664  // call handler (ctrl might wait for this resource)
1666 }
1667 
1669 {
1670  // get network path without trailing backslash
1671  char szNetworkPath[_MAX_PATH+1];
1672  SCopy(Config.AtNetworkPath(""), szNetworkPath, _MAX_PATH);
1673  TruncateBackslash(szNetworkPath);
1674  // but make sure that the configured path has one
1676  // does not exist?
1677  if (!DirectoryExists(szNetworkPath))
1678  {
1679  if (!CreatePath(szNetworkPath))
1680  { LogFatal("Network: could not create network path!"); return false; }
1681  return true;
1682  }
1683  return true;
1684 }
1685 
1686 bool C4Network2ResList::FindTempResFileName(const char *szFilename, char *pTarget)
1687 {
1688  char safeFilename[_MAX_PATH];
1689  char* safePos = safeFilename;
1690  while (*szFilename)
1691  {
1692  if ((*szFilename >= 'a' && *szFilename <= 'z') ||
1693  (*szFilename >= 'A' && *szFilename <= 'Z') ||
1694  (*szFilename >= '0' && *szFilename <= '9') ||
1695  (*szFilename == '.') || (*szFilename == '/'))
1696  *safePos = *szFilename;
1697  else
1698  *safePos = '_';
1699 
1700  ++safePos;
1701  ++szFilename;
1702  }
1703  *safePos = 0;
1704  szFilename = safeFilename;
1705 
1706  // create temporary file
1707  SCopy(Config.AtNetworkPath(GetFilename(szFilename)), pTarget, _MAX_PATH);
1708  // file name is free?
1709  if (!ItemExists(pTarget)) return true;
1710  // find another file name
1711  char szFileMask[_MAX_PATH+1];
1712  SCopy(pTarget, szFileMask, GetExtension(pTarget)-1-pTarget);
1713  SAppend("_%d", szFileMask, _MAX_PATH);
1714  SAppend(GetExtension(pTarget)-1, szFileMask, _MAX_PATH);
1715  for (int32_t i = 2; i < 1000; i++)
1716  {
1717  snprintf(pTarget, _MAX_PATH, szFileMask, i);
1718  // doesn't exist?
1719  if (!ItemExists(pTarget))
1720  return true;
1721  }
1722  // not found
1723  return false;
1724 }
C4Network2ResType
Definition: C4Network2Res.h:40
char * GetFilename(char *szPath)
Definition: StdFile.cpp:45
const char * getData() const
Definition: StdBuf.h:442
uint32_t getChunkSize() const
Definition: C4Network2Res.h:95
bool fStandaloneFailed
const int32_t C4NetResMaxLoad
Definition: C4Network2Res.h:33
bool FindEntry(const char *szWildCard, StdStrBuf *sFileName=nullptr, size_t *iSize=nullptr)
Definition: C4Group.cpp:1774
friend class C4Network2Res
virtual bool Separator(Sep eSep=SEP_SEP)
Definition: StdCompiler.h:119
int32_t MaxResSearchRecursion
Definition: C4Config.h:144
ChunkRange * pChunkRanges
#define SHA_DIGEST_LENGTH
int32_t iLastReqTime
const void * getData() const
Definition: StdBuf.h:99
bool SetByFile(const char *strFilePath, bool fTemp, C4Network2ResType eType, int32_t iResID, const char *szResName=nullptr, bool fSilent=false)
C4Config Config
Definition: C4Config.cpp:833
char * GrabPointer()
Definition: StdBuf.h:459
C4Network2Res::Ref AddByCore(const C4Network2ResCore &Core, bool fLoad=true)
void TruncateBackslash(char *szFilename)
Definition: StdFile.cpp:266
void SCopy(const char *szSource, char *sTarget, size_t iMaxL)
Definition: Standard.cpp:152
void RemoveLoad(C4Network2ResLoad *pLoad)
void Take(void *pnData, size_t inSize)
Definition: StdBuf.h:124
int32_t OpenFileRead()
C4Network2Res * pFirst
void OnShareFree(CStdCSecEx *pCSec) override
C4Network2ResLoad * Next() const
uint32_t getFileSize() const
Definition: C4Network2Res.h:89
void GetNegative(C4Network2ResChunkData &Target) const
StdNamingCountAdapt< int_t > mkNamingCountAdapt(int_t &iCount, const char *szName)
Definition: StdAdaptors.h:976
C4Network2ResChunkData Chunks
StdCopyStrBuf FileName
Definition: C4Network2Res.h:76
static bool GetDirSize(const char *szPath, uint32_t *pSize, uint32_t inMaxSize=~0)
void SAppend(const char *szSource, char *szTarget, int iMaxL)
Definition: Standard.cpp:257
bool ItemIdentical(const char *szFilename1, const char *szFilename2)
Definition: StdFile.cpp:855
uint32_t iContentsCRC
Definition: C4Network2Res.h:78
void excCorrupt(const char *szMessage,...)
Definition: StdCompiler.h:249
bool CreatePath(const std::string &path)
Definition: StdFile.cpp:632
uint32_t getContentsCRC() const
Definition: C4Network2Res.h:91
uint32_t UnsyncedRandom()
Definition: C4Random.cpp:58
void Clear()
Definition: StdBuf.h:466
int32_t getPresentChunkCnt() const
C4ConfigGeneral General
Definition: C4Config.h:251
bool ThreadLogS(const char *szMessage,...) GNUC_FORMAT_ATTRIBUTE_O
StdStrBuf::wchar_t_holder GetWideChar(const char *utf8, bool double_null_terminate=false)
void ChangeID(int32_t inID)
bool isLoadable() const
Definition: C4Network2Res.h:88
~C4Network2ResList() override
const int32_t C4NetResStatusInterval
Definition: C4Network2Res.h:32
const char * GetC4Filename(const char *szPath)
Definition: StdFile.cpp:71
bool isAnonymous() const
C4Network2IOConnection * GetMsgConnection(int iClientID)
bool AddDisID(int32_t iID)
Definition: C4Packet2.cpp:536
C4Network2ResType getType() const
bool GetFileSHA1(const char *szFilename, BYTE *pSHA1)
Definition: CStdFile.cpp:381
int32_t MaxLoadFileSize
Definition: C4Config.h:157
void CompileFunc(StdCompiler *pComp) override
void Clear()
Definition: StdSync.h:278
void AddChunkRange(int32_t iStart, int32_t iLength)
uint8_t BYTE
bool Send(const C4NetIOPacket &rPkt)
void Format(const char *szFmt,...) GNUC_FORMAT_ATTRIBUTE_O
Definition: StdBuf.cpp:174
virtual bool Name(const char *szName)
Definition: StdCompiler.h:77
void SetFileSHA(BYTE *pSHA)
bool Delete(const char *szFiles, bool fRecursive=false)
Definition: C4Group.cpp:1334
void Add(C4Network2Res *pRes)
#define _MAX_PATH
CStdCSec ResListAddCSec
C4Network2Res::Ref AddLoad(const C4Network2ResCore &Core)
bool SetLoad(const C4Network2ResCore &nCore)
bool SendChunk(uint32_t iChunk, int32_t iToClient)
bool SetDerived(const char *strName, const char *strFilePath, bool fTemp, C4Network2ResType eType, int32_t iDResID)
void CompileFunc(StdCompiler *pComp) override
#define GETPKT(type, name)
size_t SLen(const char *sptr)
Definition: Standard.h:74
void AppendBackslash(char *szFilename)
Definition: StdFile.cpp:257
C4Group * OpenAsGrp() const
bool SEqual(const char *szStr1, const char *szStr2)
Definition: Standard.h:93
bool SetByCore(const C4Network2ResCore &nCore, bool fSilent=false, const char *szAsFilename=nullptr, int32_t iRecursion=0)
const char * getFileName() const
Definition: C4Network2Res.h:94
time_t iDiscoverStartTime
#define O_CLOEXEC
~C4Network2ResChunk() override
C4Network2Res(C4Network2ResList *pnParent)
size_t getSize() const
Definition: StdBuf.h:101
const char * LoadResStr(const char *id)
Definition: C4Language.h:83
void RemoveAtClient(int32_t iClientID)
int32_t getChunkCnt() const
bool ThreadLog(const char *szMessage,...) GNUC_FORMAT_ATTRIBUTE_O
void OnResComplete(C4Network2Res *pRes)
StdNamingAdapt< T > mkNamingAdapt(T &&rValue, const char *szName)
Definition: StdAdaptors.h:92
const int32_t C4NetResIDAnonymous
Definition: C4Network2Res.h:38
bool isLoading() const
const int32_t C4NetResDiscoverTimeout
Definition: C4Network2Res.h:30
bool C4Group_PackDirectoryTo(const char *szFilename, const char *szFilenameTo)
Definition: C4Group.cpp:221
bool Init(int32_t iClientID, C4Network2IO *pIOClass)
bool IsBinaryCompatible()
void OnDiscover(C4Network2IOConnection *pBy)
const char * getFile() const
size_t FileSize(const char *fname)
int32_t GetChunkToRetrieve(const C4Network2ResChunkData &Available, int32_t iLoadingCnt, int32_t *pLoading) const
bool GetFileCRC(const char *szFilename, uint32_t *pCRC32)
Definition: CStdFile.cpp:355
int32_t getID() const
Definition: C4Network2Res.h:86
bool LogSilentF(const char *strMessage,...)
Definition: C4Log.cpp:260
C4Network2IO * getIOClass()
int32_t getResID() const
const C4Network2ResCore & getCore() const
C4Network2ResCore Core
void Set(C4Network2ResType eType, int32_t iResID, const char *strFileName, uint32_t iContentsCRC)
C4Network2Res::Ref AddByGroup(C4Group *pGrp, bool fTemp, C4Network2ResType eType, int32_t iResID=-1, const char *szResName=nullptr, bool fAllowUnloadable=false)
StdStrBuf GetFullName() const
Definition: C4Group.cpp:2078
void CompileFunc(StdCompiler *pComp) override
int32_t getDerID() const
Definition: C4Network2Res.h:87
C4GameControl Control
C4NetIOPacket MkC4NetIOPacket(char cStatus, const class C4PacketBase &Pkt, const C4NetIO::addr_t &addr=C4NetIO::addr_t())
Definition: C4PacketBase.h:40
int getClientID() const
Definition: C4Network2IO.h:271
uint32_t getChunkNr() const
bool Open(const char *szGroupName, bool fCreate=false)
Definition: C4Group.cpp:514
const char * AtNetworkPath(const char *szFilename)
Definition: C4Config.cpp:547
bool MergeRanges(ChunkRange *pRange)
void Take(char *pnData)
Definition: StdBuf.h:457
void Append(const char *pnData, size_t iChars)
Definition: StdBuf.h:519
bool C4Group_CopyItem(const char *szSource, const char *szTarget1, bool fNoSort, bool fResetAttributes)
Definition: C4Group.cpp:100
bool EraseDirectory(const char *szDirName)
Definition: StdFile.cpp:761
C4ConfigNetwork Network
Definition: C4Config.h:255
C4Network2ResChunkData & operator=(const C4Network2ResChunkData &Data2)
unsigned int EntryCRC32(const char *szWildCard=nullptr)
Definition: C4Group.cpp:1877
C4Network2Res::Ref Derive()
void Merge(const C4Network2ResChunkData &Data2)
bool Close()
Definition: C4Group.cpp:755
int32_t iLoadCnt
uint8_t FileSHA[SHA_DIGEST_LENGTH]
Definition: C4Network2Res.h:80
uint32_t getChunkCnt() const
Definition: C4Network2Res.h:96
bool Open(C4Group &hGroup, const char *filename) const
Definition: C4Reloc.cpp:82
bool LogFatal(const char *szMessage)
Definition: C4Log.cpp:227
C4Network2Res::Ref AddByFile(const char *strFilePath, bool fTemp, C4Network2ResType eType, int32_t iResID=-1, const char *szResName=nullptr, bool fAllowUnloadable=false)
bool SendStatus(C4Network2IOConnection *pTo=nullptr)
bool SendDiscover(C4Network2IOConnection *pTo=nullptr)
bool EraseFile(const char *szFileName)
C4Network2Res::Ref getRefRes(int32_t iResID)
char * GetExtension(char *szFilename)
Definition: StdFile.cpp:121
void SetDerived(int32_t inDerID)
void HandlePacket(char cStatus, const C4PacketBase *pPacket, C4Network2IOConnection *pConn)
void Value(const T &rStruct)
Definition: StdCompiler.h:161
bool isComplete() const
const int32_t C4ClientIDUnknown
Definition: C4Client.h:24
bool BroadcastMsg(const C4NetIOPacket &rPkt)
char WorkPath[CFG_MaxString+1]
Definition: C4Config.h:145
bool SetByGroup(C4Group *pGrp, bool fTemp, C4Network2ResType eType, int32_t iResID, const char *szResName=nullptr, bool fSilent=false)
bool FindTempResFileName(const char *szFilename, char *pTarget)
bool GetStandalone(char *pTo, int32_t iMaxL, bool fSetOfficial, bool fAllowUnloadable=false, bool fSilent=false)
C4Reloc Reloc
Definition: C4Reloc.cpp:21
int32_t getPresentChunk(int32_t iNr) const
struct C4Network2Res::ClientChunks * pCChunks
bool hasFileSHA() const
Definition: C4Network2Res.h:92
bool Set(C4Network2Res *pRes, uint32_t iChunk)
bool isRemoved() const
virtual bool isDeserializer()
Definition: StdCompiler.h:53
bool FileExists(const char *szFileName)
bool StartLoad(int32_t iFromClient, const C4Network2ResChunkData &Chunks)
void SetLoadable(uint32_t iSize, uint32_t iCRC)
char szStandalone[_MAX_PATH+1]
void OnClientConnect(C4Network2IOConnection *pConn)
C4Network2ResLoad * pNext
StdIntPackAdapt< T > mkIntPackAdapt(T &rVal)
Definition: StdAdaptors.h:759
StdHexAdapt mkHexAdapt(void *pData, size_t iSize)
Definition: StdAdaptors.h:1005
bool DirectoryExists(const char *szFilename)
Definition: StdFile.cpp:684
C4Network2ResType eType
Definition: C4Network2Res.h:74
C4Network2ResList * pParent
~C4Network2ResChunkData() override
const int32_t C4NetResDeleteTime
Definition: C4Network2Res.h:35
C4NetFilenameAdapt mkNetFilenameAdapt(StdStrBuf &FileName)
Definition: C4PacketBase.h:73
C4Network2ResType getType() const
Definition: C4Network2Res.h:84
#define C4CFN_BigIcon
Definition: C4Components.h:111
char szFile[_MAX_PATH+1]
C4GameControlNetwork Network
Definition: C4GameControl.h:67
bool EraseItem(const char *szItemName)
Definition: StdFile.cpp:809
C4Network2IOConnection * GetDataConnection(int iClientID)
#define _O_BINARY
int ForEachFile(const char *szDirName, bool(*fnCallback)(const char *))
Definition: StdFile.cpp:1043
bool Log(const char *szMessage)
Definition: C4Log.cpp:192
const int32_t C4NetResLoadTimeout
Definition: C4Network2Res.h:34
C4Network2ResChunkData Chunks
C4Network2Res * getRes(int32_t iResID)
StdCopyStrBuf ExePath
Definition: C4Config.h:52
void RemoveCChunks(ClientChunks *pChunks)
bool OptimizeStandalone(bool fSilent)
const int32_t C4NetResDiscoverInterval
Definition: C4Network2Res.h:31
bool isDirty() const
void AddChunk(int32_t iChunk)
C4Network2ResLoad * pLoads
void SetComplete(int32_t iChunkCnt)
C4Network2Res::Ref getRefNextRes(int32_t iResID)
const char * AtRelativePath(const char *szFilename)
Definition: C4Config.cpp:656
void SetID(int32_t inID)
Definition: C4Network2Res.h:99
std::atomic_long iRefCnt
C4Network2IO * pIO
CStdCSecEx ResListCSec
bool ItemExists(const char *szItemName)
Definition: StdFile.h:75
void OnStatus(const C4Network2ResChunkData &rChunkData, C4Network2IOConnection *pBy)
void SetIncomplete(int32_t iChunkCnt)
CStdCSec FileCSec
void OnResComplete(C4Network2Res *pRes)
#define DirectorySeparator
void SetLocalID(int32_t iClientID)
bool LogF(const char *strMessage,...)
Definition: C4Log.cpp:250
int32_t OpenFileWrite()
C4Network2ResLoad(int32_t iChunk, int32_t iByClient)
void Copy()
Definition: StdBuf.h:467
uint32_t getFileCRC() const
Definition: C4Network2Res.h:90
virtual void NameEnd(bool fBreak=false)
Definition: StdCompiler.h:78
C4Application Application
Definition: C4Globals.cpp:44
C4InteractiveThread InteractiveThread
Definition: C4Application.h:45
int32_t getDisIDCnt() const
bool AddTo(C4Network2Res *pRes, C4Network2IO *pIO) const
bool C4Group_PackDirectory(const char *szFilename)
Definition: C4Group.cpp:284
C4Network2Res * pNext
const int32_t C4NetResMaxBigicon
Definition: C4Network2Res.h:36
const StdEnumEntry< C4Network2ResType > C4Network2ResType_EnumMap[]
Definition: C4Network2Res.h:51
bool LocateItem(const char *filename, StdStrBuf &str) const
Definition: C4Reloc.cpp:93
bool isOpen() const
Definition: C4Network2IO.h:285
int32_t getResID() const
int iSize
Definition: TstC4NetIO.cpp:32
void OnChunk(const C4Network2ResChunk &rChunk)
const uint32_t C4NetResChunkSize
Definition: C4Network2Res.h:28