OpenClonk
C4IDList.cpp
Go to the documentation of this file.
1 /*
2  * OpenClonk, http://www.openclonk.org
3  *
4  * Copyright (c) 1998-2000, Matthes Bender
5  * Copyright (c) 2001-2009, RedWolf Design GmbH, http://www.clonk.de/
6  * Copyright (c) 2009-2016, The OpenClonk Team and contributors
7  *
8  * Distributed under the terms of the ISC license; see accompanying file
9  * "COPYING" for details.
10  *
11  * "Clonk" is a registered trademark of Matthes Bender, used with permission.
12  * See accompanying file "TRADEMARK" for details.
13  *
14  * To redistribute this file separately, substitute the full license texts
15  * for the above references.
16  */
17 
18 /* At static list of C4IDs */
19 
20 #include "C4Include.h"
21 #include "object/C4IDList.h"
22 
23 #include "graphics/C4Draw.h"
25 #include "object/C4Def.h"
26 #include "object/C4DefList.h"
27 
29 {
30  // Prepare list
31  pNext = nullptr;
32 }
33 
35 {
36  // Empty the list
37  Clear();
38 }
39 
41 {
42  // Kill all chunks
43  C4IDListChunk *pChunk = pNext;
44  C4IDListChunk *pChunk2;
45  while (pChunk)
46  {
47  pChunk2 = pChunk->pNext;
48  pChunk->pNext = nullptr;
49  delete pChunk;
50  pChunk = pChunk2;
51  }
52  pNext = nullptr;
53 }
54 
56 {
57  Default();
58 }
59 
61 {
62  Default();
63  *this = rCopy;
64 }
65 
67 {
68  // Clear previous list
69  Clear();
70  // Copy primary
71  memcpy(this, &rCopy, sizeof(C4IDList));
72  // Copy all chunks
73  C4IDListChunk *pTo = this;
74  for (C4IDListChunk *pFrom = rCopy.pNext; pFrom; pFrom = pFrom->pNext)
75  {
76  C4IDListChunk *pNew = new C4IDListChunk(*pFrom);
77  pTo->pNext = pNew;
78  pTo = pNew;
79  }
80  // Finalize
81  pTo->pNext = nullptr;
82  return *this;
83 }
84 
86 {
87  // Destruction is done in chunk
88 }
89 
90 void C4IDList::Clear()
91 {
92  // Inherited
94  // Reset count
95  Count = 0;
96 }
97 
98 bool C4IDList::IsClear() const
99 {
100  return !Count;
101 }
102 
103 C4ID C4IDList::GetID(size_t index, int32_t *ipCount) const
104 {
105  // Outside list?
106  if (!Inside<size_t>(index + 1, 1u, Count))
107  {
108  return C4ID::None;
109  }
110  // Get chunk to query
111  const C4IDListChunk *pQueryChunk = this;
112  while (index >= C4IDListChunkSize)
113  {
114  pQueryChunk = pQueryChunk->pNext;
115  index -= C4IDListChunkSize;
116  }
117  // Query it
118  if (ipCount)
119  {
120  *ipCount = pQueryChunk->Count[index];
121  }
122  return pQueryChunk->id[index];
123 }
124 
125 int32_t C4IDList::GetCount(size_t index) const
126 {
127  // Outside list?
128  if (!Inside<size_t>(index + 1, 1u, Count))
129  {
130  return 0;
131  }
132  // Get chunk to query
133  const C4IDListChunk *pQueryChunk = this;
134  while (index >= C4IDListChunkSize)
135  {
136  pQueryChunk = pQueryChunk->pNext;
137  index -= C4IDListChunkSize;
138  }
139  // Query it
140  return pQueryChunk->Count[index];
141 }
142 
143 bool C4IDList::SetCount(size_t index, int32_t iCount)
144 {
145  // Outside list?
146  if (!Inside<size_t>(index + 1, 1u, Count))
147  {
148  return false;
149  }
150  // Get chunk to set in
151  C4IDListChunk *pQueryChunk = this;
152  while (index >= C4IDListChunkSize)
153  {
154  pQueryChunk = pQueryChunk->pNext;
155  index -= C4IDListChunkSize;
156  }
157  // Set it
158  pQueryChunk->Count[index] = iCount;
159  // Success
160  return true;
161 }
162 
163 int32_t C4IDList::GetIDCount(C4ID c_id, int32_t iZeroDefVal) const
164 {
165  // Find id
166  const C4IDListChunk *pQueryChunk = this;
167  size_t cnt = Count;
168  size_t cntl = 0;
169  while (cnt--)
170  {
171  if (pQueryChunk->id[cntl] == c_id)
172  {
173  int32_t iCount = pQueryChunk->Count[cntl];
174  return iCount ? iCount : iZeroDefVal;
175  }
176  if (++cntl == C4IDListChunkSize)
177  {
178  pQueryChunk = pQueryChunk->pNext;
179  cntl = 0;
180  }
181  }
182  // None found
183  return 0;
184 }
185 
186 bool C4IDList::SetIDCount(C4ID c_id, int32_t iCount, bool fAddNewID)
187 {
188  // Find id
189  C4IDListChunk *pQueryChunk = this;
190  size_t cnt = Count;
191  size_t cntl = 0;
192  while (cnt--)
193  {
194  if (pQueryChunk->id[cntl] == c_id)
195  {
196  pQueryChunk->Count[cntl] = iCount;
197  return true;
198  }
199  if (++cntl==C4IDListChunkSize)
200  {
201  pQueryChunk = pQueryChunk->pNext;
202  cntl = 0;
203  }
204  }
205  // None found: add new
206  if (fAddNewID)
207  {
208  // if end was reached, add new chunk
209  if (!pQueryChunk)
210  {
211  C4IDListChunk *pLast = this;
212  while (pLast->pNext)
213  {
214  pLast = pLast->pNext;
215  }
216  pQueryChunk = new C4IDListChunk();
217  pLast->pNext = pQueryChunk;
218  }
219  // Set id
220  pQueryChunk->id[cntl] = c_id;
221  pQueryChunk->Count[cntl] = iCount;
222  // Count id!
223  ++Count;
224  // Success
225  return true;
226  }
227  // Failure
228  return false;
229 }
230 
232 {
233  return Count;
234 }
235 
236 int32_t C4IDList::GetIndex(C4ID c_id) const
237 {
238  // Find id in list
239  const C4IDListChunk *pQueryChunk = this;
240  size_t cnt = Count;
241  size_t cntl = 0;
242  while (cnt--)
243  {
244  if (pQueryChunk->id[cntl] == c_id)
245  {
246  return Count-cnt-1;
247  }
248  if (++cntl == C4IDListChunkSize)
249  {
250  pQueryChunk = pQueryChunk->pNext;
251  cntl = 0;
252  }
253  }
254  // Not found
255  return -1;
256 }
257 
258 bool C4IDList::IncreaseIDCount(C4ID c_id, bool fAddNewID, int32_t IncreaseBy, bool fRemoveEmpty)
259 {
260  // Find id in list
261  C4IDListChunk *pQueryChunk = this;
262  size_t cnt = Count;
263  size_t cntl = 0;
264  while (cnt--)
265  {
266  if (pQueryChunk->id[cntl] == c_id)
267  {
268  // Increase count
269  pQueryChunk->Count[cntl] += IncreaseBy;
270  // Check count
271  if (fRemoveEmpty && !pQueryChunk->Count[cntl])
272  {
273  DeleteItem(Count - cnt - 1);
274  }
275  // Success
276  return true;
277  }
278  if (++cntl == C4IDListChunkSize)
279  {
280  pQueryChunk = pQueryChunk->pNext;
281  cntl = 0;
282  }
283  }
284  // Add desired?
285  if (!fAddNewID)
286  {
287  return true;
288  }
289  // Add it
290  // If end was reached, add new chunk
291  if (!pQueryChunk)
292  {
293  C4IDListChunk *pLast = this;
294  while (pLast->pNext)
295  {
296  pLast = pLast->pNext;
297  }
298  pQueryChunk = new C4IDListChunk();
299  pLast->pNext = pQueryChunk;
300  }
301  // Set id
302  pQueryChunk->id[cntl] = c_id;
303  pQueryChunk->Count[cntl] = IncreaseBy;
304  ++Count;
305  // Success
306  return true;
307 }
308 
309 
310 // Access by category-sorted index
311 C4ID C4IDList::GetID(C4DefList &rDefs, int32_t dwCategory, int32_t index, int32_t *ipCount) const
312 {
313  int32_t cindex = -1;
314  C4Def *cDef;
315  if (ipCount)
316  {
317  *ipCount = 0;
318  }
319  // Find id
320  const C4IDListChunk *pQueryChunk = this;
321  size_t cnt = Count;
322  size_t cntl = 0;
323  while (cnt--)
324  {
325  if ((dwCategory == C4D_All) || ( (cDef = rDefs.ID2Def(pQueryChunk->id[cntl])) && (cDef->Category & dwCategory) ) )
326  {
327  cindex++;
328  if (cindex==index)
329  {
330  if (ipCount)
331  {
332  *ipCount = pQueryChunk->Count[cntl];
333  }
334  return pQueryChunk->id[cntl];
335  }
336  }
337  if (++cntl == C4IDListChunkSize)
338  {
339  pQueryChunk = pQueryChunk->pNext;
340  cntl = 0;
341  }
342  }
343  return C4ID::None;
344 }
345 
346 int32_t C4IDList::GetCount(C4DefList &rDefs, int32_t dwCategory, int32_t index) const
347 {
348  int32_t cindex = -1;
349  C4Def *cDef;
350  const C4IDListChunk *pQueryChunk = this;
351  size_t cnt = Count;
352  size_t cntl = 0;
353  while (cnt--)
354  {
355  if ((dwCategory == C4D_All) || ( (cDef = rDefs.ID2Def(pQueryChunk->id[cntl])) && (cDef->Category & dwCategory) ) )
356  {
357  cindex++;
358  if (cindex == index)
359  {
360  return pQueryChunk->Count[cntl];
361  }
362  }
363  if (++cntl == C4IDListChunkSize)
364  {
365  pQueryChunk = pQueryChunk->pNext;
366  cntl = 0;
367  }
368  }
369  return 0;
370 }
371 
372 bool C4IDList::SetCount(C4DefList &rDefs, int32_t dwCategory, int32_t index, int32_t iCount)
373 {
374  int32_t cindex = -1;
375  C4Def *cDef;
376  C4IDListChunk *pQueryChunk = this;
377  size_t cnt = Count;
378  size_t cntl = 0;
379  while (cnt--)
380  {
381  if ((dwCategory == C4D_All) || ( (cDef = rDefs.ID2Def(pQueryChunk->id[cntl])) && (cDef->Category & dwCategory) ) )
382  {
383  cindex++;
384  if (cindex == index)
385  {
386  pQueryChunk->Count[cntl] = iCount;
387  return true;
388  }
389  }
390  if (++cntl == C4IDListChunkSize)
391  {
392  pQueryChunk = pQueryChunk->pNext;
393  cntl = 0;
394  }
395  }
396  return false;
397 }
398 
399 int32_t C4IDList::GetNumberOfIDs(C4DefList &rDefs, int32_t dwCategory) const
400 {
401  int32_t idnum = 0;
402  C4Def *cDef;
403  const C4IDListChunk *pQueryChunk = this;
404  size_t cnt = Count;
405  size_t cntl = 0;
406  while (cnt--)
407  {
408  if ((dwCategory == C4D_All) || ( (cDef = rDefs.ID2Def(pQueryChunk->id[cntl])) && (cDef->Category & dwCategory) ) )
409  {
410  idnum++;
411  }
412  if (++cntl == C4IDListChunkSize)
413  {
414  pQueryChunk = pQueryChunk->pNext;
415  cntl = 0;
416  }
417  }
418  return idnum;
419 }
420 // IDList merge
421 
423 {
424  C4IDListChunk *pQueryChunk = &rList;
425  size_t cnt = rList.Count;
426  size_t cntl = 0;
427  while (cnt--)
428  {
429  IncreaseIDCount(pQueryChunk->id[cntl], true, pQueryChunk->Count[cntl]);
430  if (++cntl == C4IDListChunkSize)
431  {
432  pQueryChunk = pQueryChunk->pNext;
433  cntl = 0;
434  }
435  }
436  return true;
437 }
438 
439 bool C4IDList::ConsolidateValids(C4DefList &rDefs, int32_t dwCategory)
440 {
441  bool fIDsRemoved = false;
442  C4IDListChunk *pQueryChunk = this;
443  size_t cnt = Count;
444  size_t cntl = 0;
445  C4Def* pDef;
446  while (cnt--)
447  {
448  // ID does not resolve to a valid def or category is specified and def does not match category
449  if (!(pDef = rDefs.ID2Def(pQueryChunk->id[cntl])) || (dwCategory && !(pDef->Category & dwCategory)))
450  {
451  // Delete it
452  DeleteItem(Count - cnt - 1);
453  // Handle this list index again!
454  --cntl;
455  // Something was done
456  fIDsRemoved = true;
457  }
458  if (++cntl == C4IDListChunkSize)
459  {
460  pQueryChunk = pQueryChunk->pNext;
461  cntl = 0;
462  }
463  }
464  return fIDsRemoved;
465 }
466 
467 void C4IDList::Draw(C4Facet &cgo, int32_t iSelection,
468  C4DefList &rDefs, DWORD dwCategory,
469  bool fCounts, int32_t iAlign) const
470 {
471 
472  int32_t sections = cgo.GetSectionCount();
473  int32_t idnum = GetNumberOfIDs(rDefs,dwCategory);
474  int32_t firstid = Clamp<int32_t>(iSelection - sections / 2, 0, std::max<int32_t>(idnum - sections, 0));
475  int32_t idcount;
476  C4ID c_id;
477  C4Facet cgo2;
478  char buf[10];
479  for (int32_t cnt = 0; (cnt<sections) && (c_id = GetID(rDefs, dwCategory, firstid + cnt, &idcount)); cnt++)
480  {
481  cgo2 = cgo.TruncateSection(iAlign);
482  rDefs.Draw(c_id, cgo2, (firstid + cnt == iSelection), 0);
483  sprintf(buf, "%dx", idcount);
484  if (fCounts)
485  {
487  }
488  }
489 }
490 
491 void C4IDList::Default()
492 {
493  Clear();
494 }
495 
496 // Clear index entry and shift all entries behind down by one.
497 
498 bool C4IDList::DeleteItem(size_t iIndex)
499 {
500  // Invalid index
501  if (!Inside<size_t>(iIndex + 1, 1u, Count))
502  {
503  return false;
504  }
505  // Get chunk to delete of
506  size_t index = iIndex;
507  C4IDListChunk *pQueryChunk = this;
508  while (index >= C4IDListChunkSize)
509  {
510  pQueryChunk = pQueryChunk->pNext;
511  index -= C4IDListChunkSize;
512  }
513  // Shift down all entries behind
514  size_t cnt = --Count - iIndex;
515  size_t cntl = index;
516  size_t cntl2 = cntl;
517  C4IDListChunk *pNextChunk = pQueryChunk;
518  while (cnt--)
519  {
520  // Check for list overlap
521  if (++cntl2 == C4IDListChunkSize)
522  {
523  pNextChunk = pQueryChunk->pNext;
524  cntl2 = 0;
525  }
526  // Move down
527  pQueryChunk->id[cntl] = pNextChunk->id[cntl2];
528  pQueryChunk->Count[cntl] = pNextChunk->Count[cntl2];
529  // Next item
530  pQueryChunk = pNextChunk;
531  cntl = cntl2;
532  }
533  // Done
534  return true;
535 }
536 
537 bool C4IDList::operator==(const C4IDList& rhs) const
538 {
539  // Compare counts
540  if (Count != rhs.Count)
541  {
542  return false;
543  }
544  // Compare all chunks
545  const C4IDListChunk *pChunk1 = this;
546  const C4IDListChunk *pChunk2 = &rhs;
547  int32_t cnt = Count;
548  while (pChunk1 && pChunk2)
549  {
550  if (memcmp(pChunk1->id, pChunk2->id, sizeof(C4ID)*std::min<int32_t>(cnt, C4IDListChunkSize)) )
551  {
552  return false;
553  }
554  if (memcmp(pChunk1->Count, pChunk2->Count, sizeof(int32_t)*std::min<int32_t>(cnt, C4IDListChunkSize)) )
555  {
556  return false;
557  }
558  pChunk1 = pChunk1->pNext;
559  pChunk2 = pChunk2->pNext;
560  cnt -= C4IDListChunkSize;
561  }
562  // Equal!
563  return true;
564 }
565 
566 void C4IDList::CompileFunc(StdCompiler *pComp, bool fValues)
567 {
568  // Get compiler characteristics
569  bool deserializing = pComp->isDeserializer();
570  bool fNaming = pComp->hasNaming();
571  // Compiling: Clear existing data first
572  if (deserializing)
573  {
574  Clear();
575  }
576  // Start
577  C4IDListChunk *pChunk = this;
578  size_t iNr = 0;
579  size_t iCNr = 0;
580  // Without naming: Compile Count
581  int32_t iCount = Count;
582  if (!fNaming)
583  {
584  pComp->Value(iCount);
585  }
586  Count = iCount;
587  // Read
588  for (;;)
589  {
590  // Prepare compiling of single mapping
591  if (!deserializing)
592  {
593  // End of list?
594  if (iNr >= Count)
595  {
596  break;
597  }
598  // End of chunk?
599  if (iCNr >= C4IDListChunkSize)
600  {
601  pChunk = pChunk->pNext;
602  iCNr = 0;
603  }
604  }
605  else
606  {
607  // End of list?
608  if (!fNaming && iNr >= Count)
609  {
610  break;
611  }
612  // End of chunk?
613  if (iCNr >= C4IDListChunkSize)
614  {
615  pChunk = pChunk->pNext = new C4IDListChunk();
616  iCNr = 0;
617  }
618  }
619  // Separator (';')
620  if (iNr > 0 && !pComp->Separator(StdCompiler::SEP_SEP2))
621  {
622  break;
623  }
624  // ID
625  pComp->Value(mkDefaultAdapt(pChunk->id[iCNr], C4ID::None));
626  // ID not valid? Note that C4ID::None is invalid.
627  if (pChunk->id[iCNr] == C4ID::None)
628  {
629  break;
630  }
631  // Value: Skip this part if requested
632  if (fValues)
633  {
634  // Separator ('=')
635  if (pComp->Separator(StdCompiler::SEP_SET))
636  {
637  // Count
638  pComp->Value(mkDefaultAdapt(pChunk->Count[iCNr], 0));
639  }
640  }
641  else if (deserializing)
642  {
643  pChunk->Count[iCNr] = 0;
644  }
645  // Goto next entry
646  iNr++;
647  iCNr++;
648  // Save back count
649  if (deserializing && fNaming)
650  {
651  Count = iNr;
652  }
653  }
654 }
const int32_t C4D_All
Definition: C4Def.h:39
C4Draw * pDraw
Definition: C4Draw.cpp:42
C4GraphicsResource GraphicsResource
const size_t C4IDListChunkSize
Definition: C4IDList.h:28
const int ARight
Definition: C4Surface.h:41
uint32_t DWORD
#define sprintf
Definition: Standard.h:162
StdDefaultAdapt< T, D > mkDefaultAdapt(T &&rValue, const D &rDefault)
Definition: StdAdaptors.h:64
Definition: C4Def.h:99
int32_t Category
Definition: C4Def.h:117
void Draw(C4ID id, C4Facet &cgo, bool fSelected, int32_t iColor)
Definition: C4DefList.cpp:407
C4Def * ID2Def(C4ID id)
@ DEFAULT_MESSAGE_COLOR
Definition: C4Draw.h:167
bool TextOut(const char *szText, CStdFont &rFont, float fZoom, C4Surface *sfcDest, float iTx, float iTy, DWORD dwFCol=0xffffffff, BYTE byForm=ALeft, bool fDoMarkup=true)
Definition: C4Draw.cpp:561
C4Surface * Surface
Definition: C4Facet.h:117
float Hgt
Definition: C4Facet.h:118
int32_t GetSectionCount()
Definition: C4Facet.cpp:46
C4Facet TruncateSection(int32_t iAlign=C4FCT_Left)
Definition: C4Facet.cpp:250
float Wdt
Definition: C4Facet.h:118
float Y
Definition: C4Facet.h:118
float X
Definition: C4Facet.h:118
Definition: C4Id.h:26
static const C4ID None
Definition: C4Id.h:39
void Clear()
Definition: C4IDList.cpp:40
C4IDListChunk * pNext
Definition: C4IDList.h:36
int32_t Count[C4IDListChunkSize]
Definition: C4IDList.h:34
C4ID id[C4IDListChunkSize]
Definition: C4IDList.h:33
int32_t GetCount(size_t index) const
Definition: C4IDList.cpp:125
bool IsClear() const
Definition: C4IDList.cpp:98
bool Add(C4IDList &rList)
Definition: C4IDList.cpp:422
void CompileFunc(StdCompiler *pComp, bool fValues=true)
Definition: stub-handle.cpp:69
bool SetCount(size_t index, int32_t iCount)
Definition: C4IDList.cpp:143
int32_t GetIndex(C4ID c_id) const
Definition: C4IDList.cpp:236
bool ConsolidateValids(C4DefList &rDefs, int32_t dwCategory=0)
Definition: C4IDList.cpp:439
bool SetIDCount(C4ID c_id, int32_t iCount, bool fAddNewID=false)
Definition: stub-handle.cpp:68
C4ID GetID(size_t index, int32_t *ipCount=nullptr) const
Definition: C4IDList.cpp:103
void Clear()
Definition: stub-handle.cpp:64
bool IncreaseIDCount(C4ID c_id, bool fAddNewID=true, int32_t IncreaseBy=1, bool fRemoveEmpty=false)
Definition: C4IDList.cpp:258
int32_t GetNumberOfIDs() const
Definition: C4IDList.cpp:231
bool operator==(const C4IDList &rhs) const
Definition: stub-handle.cpp:66
bool DeleteItem(size_t iIndex)
Definition: C4IDList.cpp:498
void Default()
Definition: stub-handle.cpp:63
size_t Count
Definition: C4IDList.h:55
void Draw(C4Facet &cgo, int32_t iSelection, C4DefList &rDefs, DWORD dwCategory, bool fCounts=true, int32_t iAlign=0) const
Definition: C4IDList.cpp:467
int32_t GetIDCount(C4ID c_id, int32_t iZeroDefVal=0) const
Definition: stub-handle.cpp:67
C4IDList & operator=(const C4IDList &rCopy)
Definition: stub-handle.cpp:65
int GetLineHeight() const
Definition: C4FontLoader.h:125
virtual bool Separator(Sep eSep=SEP_SEP)
Definition: StdCompiler.h:119
void Value(const T &rStruct)
Definition: StdCompiler.h:161
virtual bool isDeserializer()
Definition: StdCompiler.h:53
virtual bool hasNaming()
Definition: StdCompiler.h:58