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