1/*
2 * LegacyClonk
3 *
4 * Copyright (c) RedWolf Design
5 * Copyright (c) 2017-2021, The LegacyClonk 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
17// statistics
18// by peter
19
20#include <C4Include.h>
21#include <C4Stat.h>
22
23#include <C4Game.h>
24
25// ** implemetation of C4MainStat
26
27C4MainStat::C4MainStat()
28 : bStatFileOpen(false), pFirst(nullptr) {}
29
30C4MainStat::~C4MainStat()
31{
32 CloseStatFile();
33}
34
35void C4MainStat::RegisterStat(C4Stat *pStat)
36{
37 // add to list
38 if (!pFirst)
39 {
40 pFirst = pStat;
41 pStat->pNext = nullptr;
42 pStat->pPrev = nullptr;
43 }
44 else
45 {
46 pStat->pNext = pFirst;
47 pFirst->pPrev = pStat;
48 pStat->pPrev = nullptr;
49 pFirst = pStat;
50 }
51}
52
53void C4MainStat::UnRegStat(C4Stat *pStat)
54{
55 // first item?
56 if (!pStat->pPrev)
57 {
58 pFirst = pStat->pNext;
59 pStat->pNext = nullptr;
60 }
61 // last item?
62 else if (!pStat->pNext)
63 {
64 pStat->pPrev->pNext = nullptr;
65 pStat->pPrev = nullptr;
66 }
67 else
68 {
69 pStat->pNext->pPrev = pStat->pPrev;
70 pStat->pPrev->pNext = pStat->pNext;
71 pStat->pNext = nullptr;
72 pStat->pPrev = nullptr;
73 }
74}
75
76void C4MainStat::Reset()
77{
78 CloseStatFile();
79 for (C4Stat *pAkt = pFirst; pAkt; pAkt = pAkt->pNext)
80 pAkt->Reset();
81}
82
83void C4MainStat::ResetPart()
84{
85 for (C4Stat *pAkt = pFirst; pAkt; pAkt = pAkt->pNext)
86 pAkt->ResetPart();
87}
88
89void C4MainStat::Show()
90{
91 // output the whole statistic (to stat.txt)
92
93 // open file
94 if (!bStatFileOpen)
95 OpenStatFile();
96
97 // count stats
98 unsigned int iCnt = 0;
99 C4Stat *pAkt;
100 for (pAkt = pFirst; pAkt; pAkt = pAkt->pNext)
101 iCnt++;
102
103 // create array
104 C4Stat **StatArray = new C4Stat *[iCnt];
105 bool *bHS = new bool[iCnt];
106
107 // sort it
108 unsigned int i, ii;
109 for (ii = 0; ii < iCnt; ii++) bHS[ii] = false;
110 for (i = 0; i < iCnt; i++)
111 {
112 C4Stat *pBestStat;
113 unsigned int iBestNr = ~0u;
114
115 for (ii = 0, pAkt = pFirst; ii < iCnt; ii++, pAkt = pAkt->pNext)
116 if (!bHS[ii])
117 if (iBestNr == ~0u)
118 {
119 iBestNr = ii;
120 pBestStat = pAkt;
121 }
122 else if (stricmp(s1: pBestStat->strName, s2: pAkt->strName) > 0)
123 {
124 iBestNr = ii;
125 pBestStat = pAkt;
126 }
127
128 if (iBestNr == ~0u)
129 break;
130 bHS[iBestNr] = true;
131
132 StatArray[i] = pBestStat;
133 }
134
135 delete[] bHS;
136
137 fprintf(stream: StatFile, format: "** Stat\n");
138
139 // output in order
140 for (i = 0; i < iCnt; i++)
141 {
142 pAkt = StatArray[i];
143
144 // output it!
145 if (pAkt->iCount)
146 fprintf(stream: StatFile, format: "%s: n = %d, t = %d, td = %.2f\n",
147 pAkt->strName, pAkt->iCount, pAkt->iTimeSum,
148 double(pAkt->iTimeSum) / std::max<int>(a: 1, b: pAkt->iCount - 100) * 1000);
149 }
150
151 // delete...
152 delete[] StatArray;
153
154 // ok. job done
155 fputs(s: "** Stat end\n", stream: StatFile);
156 fflush(stream: StatFile);
157}
158
159void C4MainStat::ShowPart()
160{
161 C4Stat *pAkt;
162
163 // open file
164 if (!bStatFileOpen)
165 OpenStatFile();
166
167 // insert tick nr
168 fprintf(stream: StatFile, format: "** PartStat begin %d\n", Game.FrameCounter);
169
170 // insert all stats
171 for (pAkt = pFirst; pAkt; pAkt = pAkt->pNext)
172 fprintf(stream: StatFile, format: "%s: n=%d, t=%d\n", pAkt->strName, pAkt->iCountPart, pAkt->iTimeSumPart);
173
174 // insert part stat end idtf
175 fprintf(stream: StatFile, format: "** PartStat end\n");
176 fflush(stream: StatFile);
177}
178
179// stat file handling
180void C4MainStat::OpenStatFile()
181{
182 if (bStatFileOpen) return;
183
184 // open & reset file
185 StatFile = fopen(filename: "stat.txt", modes: "w");
186
187 // success?
188 if (!StatFile)
189 return;
190
191 bStatFileOpen = true;
192}
193
194void C4MainStat::CloseStatFile()
195{
196 if (!bStatFileOpen) return;
197
198 // open & reset file
199 fclose(stream: StatFile);
200
201 bStatFileOpen = false;
202}
203
204// ** implemetation of C4Stat
205
206C4Stat::C4Stat(const char *strnName)
207 : strName(strnName)
208{
209 Reset();
210 getMainStat()->RegisterStat(pStat: this);
211}
212
213C4Stat::~C4Stat()
214{
215 getMainStat()->UnRegStat(pStat: this);
216}
217
218void C4Stat::Reset()
219{
220 iStartCalled = 0;
221
222 iTimeSum = 0;
223 iCount = 0;
224
225 ResetPart();
226}
227
228void C4Stat::ResetPart()
229{
230 iTimeSumPart = 0;
231 iCountPart = 0;
232}
233
234C4MainStat *C4Stat::getMainStat()
235{
236 static C4MainStat *pMainStat = new C4MainStat();
237 return pMainStat;
238}
239