OpenClonk
 All Classes Namespaces Files Functions Variables Typedefs Enumerations Enumerator Properties Friends Macros
C4GroupSet.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) 2010-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 // a set of group files
17 // manages system file overwriting by scearios or folders
18 
19 #include "C4Include.h"
20 #include "c4group/C4GroupSet.h"
21 
22 #include "c4group/C4Components.h"
23 #include "c4group/C4Group.h"
24 #include "lib/C4Log.h"
25 #include "c4group/C4Language.h"
26 
27 C4GroupSetNode::C4GroupSetNode(C4GroupSet &rParent, C4GroupSetNode *pPrev, C4Group &rGroup, bool fGrpOwned, int32_t id)
28 {
29  // set parent
30  pParent = &rParent;
31  // link into list
32  this->pPrev=pPrev;
33  if (pPrev) { pNext=pPrev->pNext; pPrev->pNext=this; }
34  else { pNext=pParent->pFirst; pParent->pFirst=this; }
35  if (pNext) pNext->pPrev=this; else pParent->pLast=this;
36  // set group
37  pGroup = &rGroup;
38  this->fGrpOwned = fGrpOwned;
39  // set id
40  this->id = id;
41 }
42 
44 {
45  // remove group
46  if (fGrpOwned) delete pGroup;
47  // unlink from list
48  (pPrev ? pPrev->pNext : pParent->pFirst) = pNext;
49  (pNext ? pNext->pPrev : pParent->pLast ) = pPrev;
50 }
51 
53 {
54  // clear nodes
55  while (pFirst) delete pFirst;
56  pFirst = nullptr;
57 }
58 
60 {
61  // zero fields
62  pFirst=pLast=nullptr;
63  // do not reset index here, because group set IDs are meant to be unique
64  // for each instance of the engine
65  // see also C4GraphicsResource::RegisterGlobalGraphics
66 }
67 
69 {
70  // zero fields
71  Default();
72  iIndex=0;
73 }
74 
76 {
77  // zero fields
78  Default();
79  iIndex=0;
80  // copy from other group set
82 }
83 
85 {
86  // clear nodes
87  Clear();
88 }
89 
90 bool C4GroupSet::RegisterGroup(C4Group &rGroup, bool fOwnGrp, int32_t Priority, int32_t Contents, bool fCheckContent)
91 {
92  // get node to sort in
93  // begin at back end and search for higher priority
94  C4GroupSetNode *pNode;
95  for (pNode=pLast; pNode; pNode=pNode->pPrev)
96  if (pNode->Priority > Priority) break;
97  // create new node
98  C4GroupSetNode *pNewNode = new C4GroupSetNode(*this, pNode, rGroup, fOwnGrp, ++iIndex);
99  // check content
100  if (fCheckContent) Contents = CheckGroupContents(rGroup, Contents);
101  // set priority and contents mask
102  pNewNode->Priority = Priority;
103  pNewNode->Contents = Contents;
104 
105  return true;
106 }
107 
108 int32_t C4GroupSet::CheckGroupContents(C4Group &rGroup, int32_t Contents)
109 {
110  // update mask
111  if (Contents & C4GSCnt_Graphics) if (!rGroup.FindEntry(C4CFN_Graphics)) Contents=Contents&~C4GSCnt_Graphics;
112  if (Contents & C4GSCnt_Loaders)
113  {
114  if (!rGroup.FindEntry("Loader*.bmp")
115  && !rGroup.FindEntry("Loader*.png")
116  && !rGroup.FindEntry("Loader*.jpg")
117  && !rGroup.FindEntry("Loader*.jpeg"))
118  {
119  Contents=Contents&~C4GSCnt_Loaders;
120  }
121  }
122  if (Contents & C4GSCnt_Material) if (!rGroup.FindEntry(C4CFN_Material)) Contents=Contents&~C4GSCnt_Material;
123  if (Contents & C4GSCnt_Music) if (!rGroup.FindEntry(C4CFN_Music)) Contents=Contents&~C4GSCnt_Music;
124  if (Contents & C4GSCnt_Definitions) if (!rGroup.FindEntry(C4CFN_DefFiles)) Contents=Contents&~C4GSCnt_Definitions;
125  if (Contents & C4GSCnt_FontDefs) if (!rGroup.FindEntry(C4CFN_FontFiles)) Contents=Contents&~C4GSCnt_FontDefs;
126  // return it
127  return Contents;
128 }
129 
130 bool C4GroupSet::RegisterGroups(const C4GroupSet &rCopy, int32_t Contents, const char *szFilename, int32_t iMaxSkipID)
131 {
132  // get all groups of rCopy
133  int32_t Contents2;
134  for (C4GroupSetNode *pNode=rCopy.pFirst; pNode; pNode=pNode->pNext)
135  if ((Contents2 = pNode->Contents & Contents))
136  if (pNode->id > iMaxSkipID)
137  {
138  if (!szFilename)
139  // add group but don't check the content again!
140  RegisterGroup(*pNode->pGroup, false, pNode->Priority, Contents2, false);
141  else
142  {
143  // if a filename is given, open the child group
144  C4Group *pGroup = new C4Group();
145  if (!pGroup->OpenAsChild(pNode->pGroup, szFilename))
146  { delete pGroup; continue; }
147  // add the child group to the local list; contents equal Contents2
148  // but this flag is not likely to be used
149  if (!RegisterGroup(*pGroup, true, pNode->Priority, Contents2, false))
150  delete pGroup;
151  }
152  }
153  // done, success
154  return true;
155 }
156 
157 C4Group *C4GroupSet::FindGroup(int32_t Contents, C4Group *pAfter, bool fSamePrio)
158 {
159  // get priority
160  int32_t iPriority=-1;
161  // find group by matching content mask
162  for (C4GroupSetNode *pNode=pFirst; pNode; pNode=pNode->pNext)
163  {
164  // check contents
165  if (!pAfter && (pNode->Contents & Contents))
166  // check priority
167  if (iPriority==-1 || iPriority==pNode->Priority)
168  // success, found an entry
169  return pNode->pGroup;
170  // find next clear flag
171  if (pNode->pGroup == pAfter) { pAfter=nullptr; if (fSamePrio) iPriority=pNode->Priority; }
172  }
173  // nothing found
174  return nullptr;
175 }
176 
177 C4Group *C4GroupSet::FindEntry(const char *szWildcard, int32_t *pPriority, int32_t *pID)
178 {
179  // find group that has this entry
180  for (C4GroupSetNode *pNode=pFirst; pNode; pNode=pNode->pNext)
181  if (pNode->pGroup->FindEntry(szWildcard))
182  {
183  // assign priority and ID, if ptrs is given
184  if (pPriority) *pPriority=pNode->Priority;
185  if (pID) *pID=pNode->id;
186  // return found group
187  return pNode->pGroup;
188  }
189  // nothing found
190  return nullptr;
191 }
192 
193 C4Group *C4GroupSet::FindSuitableFile(const char *szName, const char * const extensions[], char *szFileName, int32_t *pID)
194 {
195  C4Group *pGrp = 0;
196  C4Group *pGrp2;
197  int iPrio = -1;
198  int32_t iPrio2;
199  int32_t GroupID;
200  char FileName[_MAX_FNAME];
201  SCopy(szName, FileName);
202  for (int i = 0; extensions[i]; ++i)
203  {
204  EnforceExtension(FileName, extensions[i]);
205  pGrp2=FindEntry(FileName, &iPrio2, &GroupID);
206  if ((!pGrp || iPrio2 >= iPrio) && pGrp2)
207  {
208  if (pID) *pID = GroupID;
209  pGrp = pGrp2;
210  SCopy(FileName, szFileName);
211  }
212  }
213  // return found group, if any
214  return pGrp;
215 }
216 
217 bool C4GroupSet::LoadEntry(const char *szEntryName, char **lpbpBuf, size_t *ipSize, int32_t iAppendZeros)
218 {
219  // Load the entry from the first group that has it
220  C4Group *pGroup;
221  if ((pGroup = FindEntry(szEntryName)))
222  return pGroup->LoadEntry(szEntryName, lpbpBuf, ipSize, iAppendZeros);
223  // Didn't find it
224  return false;
225 }
226 
227 bool C4GroupSet::LoadEntryString(const char *szEntryName, StdStrBuf * rBuf)
228 {
229  // Load the entry from the first group that has it
230  C4Group *pGroup;
231  if ((pGroup = FindEntry(szEntryName)))
232  return pGroup->LoadEntryString(szEntryName, rBuf);
233  // Didn't find it
234  return false;
235 }
236 
238 {
239  // close everything that has folder-priority
240  for (C4GroupSetNode *pNode=pFirst,*pNext; pNode; pNode=pNext)
241  {
242  // get next, as pNode might be destroyed
243  pNext=pNode->pNext;
244  // check if priority matches
245  if (Inside<int32_t>(pNode->Priority, C4GSPrio_Folder, C4GSPrio_Folder2) || pNode->Priority==C4GSPrio_Scenario)
246  // clear it!
247  delete pNode;
248  }
249  // done, success
250  return true;
251 }
252 
254 {
255  int32_t iCount = 0;
256  for (C4GroupSetNode *pNode = pFirst; pNode; pNode = pNode->pNext)
257  iCount++;
258  return iCount;
259 }
260 
262 {
263  // Invalid index
264  if (iIndex < 0)
265  return nullptr;
266  // Find indicated group
267  for (C4GroupSetNode *pNode = pFirst; pNode; pNode = pNode->pNext)
268  if (iIndex == 0)
269  return pNode->pGroup;
270  else
271  iIndex--;
272  // Indicated group not found
273  return nullptr;
274 }
275 
276 bool C4GroupSet::UnregisterGroup(int32_t iIndex)
277 {
278  // Invalid index
279  if (iIndex < 0)
280  return false;
281  // Find indicated group
282  for (C4GroupSetNode *pNode = pFirst; pNode; pNode = pNode->pNext)
283  if (iIndex == 0)
284  {
285  // Delete found node
286  delete pNode;
287  return true;
288  }
289  else
290  iIndex--;
291  // Indicated group not found
292  return false;
293 
294 }
295 
296 C4Group *C4GroupSet::RegisterParentFolders(const char *szScenFilename)
297 {
298  // the scenario filename may be a scenario or directly a group folder
299  C4Group *pParentGroup=nullptr; bool fParentC4F;
300  char szParentfolder[_MAX_PATH+1];
301  if (SEqualNoCase(GetExtension(szScenFilename), "ocf"))
302  {
303  fParentC4F = true;
304  SCopy(szScenFilename, szParentfolder, _MAX_PATH);
305  }
306  else
307  {
308  GetParentPath(szScenFilename,szParentfolder);
309  fParentC4F = SEqualNoCase(GetExtension(szParentfolder), "ocf");
310  }
311  if (fParentC4F)
312  {
313  // replace all (back)slashes with zero-fields
314  int32_t iOriginalLen=SLen(szParentfolder);
315  for (int32_t i=0; i<iOriginalLen; ++i) if (szParentfolder[i]==DirectorySeparator || szParentfolder[i]=='/') szParentfolder[i]=0;
316  // trace back until the file extension is no more .ocf
317  int32_t iPos=iOriginalLen-1;
318  while (iPos)
319  {
320  // ignore additional zero fields (double-backslashes)
321  while (iPos && !szParentfolder[iPos]) --iPos;
322  // break if end has been reached
323  if (!iPos) break;
324  // trace back until next zero field
325  while (iPos && szParentfolder[iPos]) --iPos;
326  // check extension of this folder
327  if (!SEqualNoCase(GetExtension(szParentfolder+iPos+1), "ocf", 3)) break;
328  // continue
329  }
330  // trace backwards, putting the (back)slashes in place again
331  if (iPos)
332  {
333  szParentfolder[iPos+1+SLen(szParentfolder+iPos+1)]=szParentfolder[iPos]=DirectorySeparator;
334  while (iPos--) if (!szParentfolder[iPos]) szParentfolder[iPos]=DirectorySeparator;
335  ++iPos;
336  }
337  // trace forward again, opening all folders (iPos is 0 again)
338  int32_t iGroupIndex=0;
339  while (iPos<iOriginalLen)
340  {
341  // ignore additional zero-fields
342  while (iPos<iOriginalLen && !szParentfolder[iPos]) ++iPos;
343  // break if end has been reached
344  if (iPos>=iOriginalLen) break;
345  // open this folder
346  C4Group *pGroup = new C4Group();
347  if (pParentGroup)
348  {
349  if (!pGroup->OpenAsChild(pParentGroup, szParentfolder+iPos))
350  {
351  LogFatal(FormatString("%s: %s", LoadResStr("IDS_PRC_FILENOTFOUND"), szParentfolder+iPos).getData());
352  delete pGroup; return nullptr;
353  }
354  }
355  else if (!Reloc.Open(*pGroup, szParentfolder+iPos))
356  {
357  LogFatal(FormatString("%s: %s", LoadResStr("IDS_PRC_FILENOTFOUND"), szParentfolder+iPos).getData());
358  delete pGroup; return nullptr;
359  }
360  // set this group as new parent
361  pParentGroup=pGroup;
362  // add to group set, if this is a true scenario folder
363  int32_t iContentsMask;
364  if (WildcardMatch(C4CFN_FolderFiles, pParentGroup->GetName()))
365  iContentsMask = C4GSCnt_Folder;
366  else
367  iContentsMask = C4GSCnt_Directory;
368  if (!RegisterGroup(*pParentGroup, true, C4GSPrio_Folder+iGroupIndex++, iContentsMask))
369  { delete pParentGroup; LogFatal ("RegGrp: internal error"); return nullptr; }
370  // advance by file name length
371  iPos+=SLen(szParentfolder+iPos);
372  }
373  }
374  return pParentGroup;
375 }
376 
377 int32_t C4GroupSet::PreCacheEntries(const char *szEntryMask)
378 {
379  // pre-cache all entries matching mask for packed files in all groups
380  // note this does not catch overloads.
381  int32_t result = 0;
382  for (C4GroupSetNode *pNode = pFirst; pNode; pNode = pNode->pNext)
383  if (pNode->pGroup) result += pNode->pGroup->PreCacheEntries(szEntryMask);
384  return result;
385 }
bool FindEntry(const char *szWildCard, StdStrBuf *sFileName=nullptr, size_t *iSize=nullptr)
Definition: C4Group.cpp:1774
#define C4CFN_FolderFiles
Definition: C4Components.h:175
#define C4GSCnt_FontDefs
Definition: C4GroupSet.h:39
void Default()
Definition: C4GroupSet.cpp:59
void SCopy(const char *szSource, char *sTarget, size_t iMaxL)
Definition: Standard.cpp:122
bool RegisterGroup(C4Group &rGroup, bool fOwnGrp, int32_t Priority, int32_t Contents, bool fCheckContent=true)
Definition: C4GroupSet.cpp:90
class C4GroupSet * pParent
Definition: C4GroupSet.h:57
int32_t PreCacheEntries(const char *szEntryMask)
Definition: C4GroupSet.cpp:377
#define C4CFN_Material
Definition: C4Components.h:25
C4GroupSetNode * pPrev
Definition: C4GroupSet.h:58
C4Group * FindSuitableFile(const char *szName, const char *const extensions[], char *szFileName, int32_t *pID=nullptr)
Definition: C4GroupSet.cpp:193
#define C4GSCnt_Material
Definition: C4GroupSet.h:36
bool SEqualNoCase(const char *szStr1, const char *szStr2, int iLen)
Definition: Standard.cpp:177
bool RegisterGroups(const C4GroupSet &rCopy, int32_t Contents, const char *szFilename=nullptr, int32_t iMaxSkipID=0)
Definition: C4GroupSet.cpp:130
#define C4GSCnt_Definitions
Definition: C4GroupSet.h:38
bool LoadEntry(const char *szEntryName, char **lpbpBuf, size_t *ipSize=nullptr, int iAppendZeros=0)
Definition: C4Group.cpp:1893
#define C4GSCnt_All
Definition: C4GroupSet.h:51
C4GroupSetNode * pLast
Definition: C4GroupSet.h:79
int32_t GetGroupCount()
Definition: C4GroupSet.cpp:253
int32_t iIndex
Definition: C4GroupSet.h:80
#define C4GSCnt_Graphics
Definition: C4GroupSet.h:34
#define _MAX_PATH
size_t SLen(const char *sptr)
Definition: Standard.h:78
#define C4GSCnt_Loaders
Definition: C4GroupSet.h:35
bool UnregisterGroup(int32_t iIndex)
Definition: C4GroupSet.cpp:276
const char * LoadResStr(const char *id)
Definition: C4Language.h:83
bool GetParentPath(const char *szFilename, char *szBuffer)
Definition: StdFile.cpp:199
int32_t id
Definition: C4GroupSet.h:63
friend class C4GroupSetNode
Definition: C4GroupSet.h:109
#define C4CFN_FontFiles
Definition: C4Components.h:181
const char * GetName() const
Definition: C4Group.cpp:1845
C4Group * FindEntry(const char *szWildcard, int32_t *pPriority=nullptr, int32_t *pID=nullptr)
Definition: C4GroupSet.cpp:177
#define C4CFN_Music
Definition: C4Components.h:30
#define C4GSPrio_Scenario
Definition: C4GroupSet.h:31
#define C4GSCnt_Folder
Definition: C4GroupSet.h:43
bool Open(C4Group &hGroup, const char *filename) const
Definition: C4Reloc.cpp:69
bool LogFatal(const char *szMessage)
Definition: C4Log.cpp:230
#define _MAX_FNAME
int32_t Contents
Definition: C4GroupSet.h:70
char * GetExtension(char *szFilename)
Definition: StdFile.cpp:131
C4GroupSetNode * pFirst
Definition: C4GroupSet.h:79
#define C4GSPrio_Folder
Definition: C4GroupSet.h:29
C4Reloc Reloc
Definition: C4Reloc.cpp:22
C4Group * RegisterParentFolders(const char *szScenFilename)
Definition: C4GroupSet.cpp:296
#define C4CFN_Graphics
Definition: C4Components.h:28
void Clear()
Definition: C4GroupSet.cpp:52
bool LoadEntry(const char *szEntryName, char **lpbpBuf, size_t *ipSize=nullptr, int32_t iAppendZeros=0)
Definition: C4GroupSet.cpp:217
bool LoadEntryString(const char *szEntryName, StdStrBuf *rBuf)
Definition: C4GroupSet.cpp:227
int32_t Priority
Definition: C4GroupSet.h:69
bool LoadEntryString(const char *szEntryName, StdStrBuf *Buf)
Definition: C4Group.cpp:1932
C4GroupSetNode(class C4GroupSet &rParent, C4GroupSetNode *pPrev, C4Group &rGroup, bool fGrpOwned, int32_t id)
Definition: C4GroupSet.cpp:27
void EnforceExtension(char *szFilename, const char *szExtension)
Definition: StdFile.cpp:299
C4GroupSetNode * pNext
Definition: C4GroupSet.h:58
bool WildcardMatch(const char *szWildcard, const char *szString)
Definition: StdFile.cpp:384
C4Group * FindGroup(int32_t Contents, C4Group *pAfter=nullptr, bool fSamePrio=false)
Definition: C4GroupSet.cpp:157
#define C4CFN_DefFiles
Definition: C4Components.h:166
static int32_t CheckGroupContents(C4Group &rGroup, int32_t Contents)
Definition: C4GroupSet.cpp:108
bool CloseFolders()
Definition: C4GroupSet.cpp:237
#define C4GSCnt_Directory
Definition: C4GroupSet.h:45
#define DirectorySeparator
C4Group * pGroup
Definition: C4GroupSet.h:60
#define C4GSPrio_Folder2
Definition: C4GroupSet.h:30
bool OpenAsChild(C4Group *pMother, const char *szEntryName, bool fExclusive=false, bool fCreate=false)
Definition: C4Group.cpp:1585
StdStrBuf FormatString(const char *szFmt,...)
Definition: StdBuf.cpp:277
#define C4GSCnt_Music
Definition: C4GroupSet.h:37
C4Group * GetGroup(int32_t iIndex)
Definition: C4GroupSet.cpp:261