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