Richard Boegli's CnC_Generals_Zero_Hour Fork WIP
This is documentation of Richard Boegil's Zero Hour Fork
 
Loading...
Searching...
No Matches
configfile.cpp
Go to the documentation of this file.
1/*
2** Command & Conquer Generals Zero Hour(tm)
3** Copyright 2025 Electronic Arts Inc.
4**
5** This program is free software: you can redistribute it and/or modify
6** it under the terms of the GNU General Public License as published by
7** the Free Software Foundation, either version 3 of the License, or
8** (at your option) any later version.
9**
10** This program is distributed in the hope that it will be useful,
11** but WITHOUT ANY WARRANTY; without even the implied warranty of
12** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13** GNU General Public License for more details.
14**
15** You should have received a copy of the GNU General Public License
16** along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19/****************************************************************************\
20* C O N F I D E N T I A L --- W E S T W O O D S T U D I O S *
21******************************************************************************
22Project Name: Carpenter (The RedAlert ladder creator)
23File Name : configfile.cpp
24Author : Neal Kettler
25Start Date : June 9, 1997
26Last Update : June 17, 1997
27
28
29This class will read in a config file and store the key value pairs for
30later access. This is a fairly simple class, the config file is assumed
31to be of the form:
32
33#comment
34key = value
35
36The value can then be retrieved as a string or an integer. The key on
37the left is used for retrieval and it must be specified in uppercase
38for the 'get' functions. E.g. getString("KEY",valWstring);
39\***************************************************************************/
40
41#include <stdlib.h>
42#include <stdio.h>
43#include <ctype.h>
44
45#include "configfile.h"
46#include "wdebug.h"
47
48static uint32 Wstring_Hash(const Wstring &string);
49static char *Eat_Spaces(char *string);
50
51ConfigFile::ConfigFile() : Dictionary_(Wstring_Hash)
52{ }
53
55{ }
56
57// Read and parse the config file. The key value pairs will be stored
58// for later access by the getString/getInt functions.
60{
61 char string[256];
62 char sectionname[256]; // section name like '[user parameters]'
63 Wstring key;
64 Wstring value;
65 char *cptr;
66
67 memset(string,0,256);
68 memset(sectionname,0,256);
69 sectionList.clear();
70
71 while (fgets(string,256,in))
72 {
73 cptr=Eat_Spaces(string);
74 if ((*cptr==0)||(*cptr=='#')) // '#' signals a comment
75 continue;
76
77 if (*cptr=='[') // new section
78 {
79 key=cptr;
80 key.truncate(']'); // remove after & including the ]
81 key.cat("]"); // put the ] back
82 strcpy(sectionname,key.get()); // set the current section name
83 Wstring wssectionname;
84
85 if (strlen(sectionname)==2) // clear section with a "[]"
86 {
87 sectionname[0]=0;
88 wssectionname.set("");
89 }
90 else
91 wssectionname.set(sectionname+1);
92
93 wssectionname.truncate(']');
94 sectionList.addTail(wssectionname);
95
96
97 continue;
98 }
99
100 if (strchr(cptr,'=')==NULL) // All config entries must have a '='
101 continue;
102 key=cptr;
103 key.truncate('=');
104 key.removeSpaces(); // No spaces allowed in the key
105 key.toUpper(); // make key all caps
106
107 // Add the section name to the end of the key
108 if (strlen(sectionname))
109 key.cat(sectionname);
110
111 cptr=Eat_Spaces(strchr(cptr,'=')+1); // Jump to after the '='
112 value=cptr;
113 value.truncate('\r');
114 value.truncate('\n');
115 value.truncate('#');
116
117 // Remove trailing spaces
118 while(isgraph(value.get()[strlen(value.get())-1])==0)
119 value.get()[strlen(value.get())-1]=0;
120
121 Critsec_.lock();
122 Dictionary_.add(key,value);
123 Critsec_.unlock();
124 }
125 return(TRUE);
126}
127
128
129//
130// Enum through the config strings. To start, index & offset should be 0
131// If retval is false you're done, ignore whatever's in key & value.
132//
133// Section specifies the configfile section. Set to NULL if you don't care.
134//
135bit8 ConfigFile::enumerate(int &index, int &offset, Wstring &key, Wstring &value, IN char *section) const
136{
137 int seclen = strlen(section);
138 while(1)
139 {
140 Critsec_.lock();
141 if (Dictionary_.iterate(index,offset,key,value)==FALSE) // out of keys?
142 {
143 Critsec_.unlock();
144 return(FALSE);
145 }
146 Critsec_.unlock();
147
148 if (section==NULL) // no specified section, so any will do...
149 break;
150
151 if (strlen(section)+2 >= strlen(key.get())) // key should have form: X[section]
152 continue;
153
154 // Is this key part of our section?
155 const char *keystr = key.get() + strlen(key.get())-seclen-1;
156 if (strncmp(keystr,section,strlen(section))==0)
157 break;
158 }
159 key.truncate('['); // remove the section name
160 return(TRUE);
161}
162
163
164
165// Get a config entry as a string
166bit8 ConfigFile::getString(IN Wstring &_key, Wstring &value, IN char *section) const
167{
168 Wstring key(_key);
169 key.toUpper();
170
171 if (section) // append section name to key
172 {
173 key+="[";
174 key+=section;
175 key+="]";
176 }
177
178 Critsec_.lock();
179 bit8 retval=Dictionary_.getValue(key,value);
180 Critsec_.unlock();
181
182 if (retval==FALSE)
183 {
184 DBGMSG("Config entry missing: "<<key.get());
185 }
186
187 return(retval);
188}
189
190// Get a config entry as a string
191bit8 ConfigFile::getString(IN char *key,Wstring &value, IN char *section) const
192{
193 Wstring sKey;
194 sKey.set(key);
195 return(getString(sKey,value,section));
196}
197
198// Get a config entry as an integer
199bit8 ConfigFile::getInt(IN Wstring &_key,sint32 &value, IN char *section) const
200{
201 Wstring key(_key);
202 key.toUpper();
203
204 if (section) // append section name to key
205 {
206 key+="[";
207 key+=section;
208 key+="]";
209 }
210
211 Wstring svalue;
212 Critsec_.lock();
213 bit8 retval=Dictionary_.getValue(key,svalue);
214 Critsec_.unlock();
215
216 if (retval==FALSE)
217 { DBGMSG("Config entry missing: "<<key.get()); }
218
219 if (retval==FALSE)
220 return(FALSE);
221 value=atol(svalue.get());
222 return(TRUE);
223}
224
225// Get a config entry as an integer
226bit8 ConfigFile::getInt(IN char *key,sint32 &value, IN char *section) const
227{
228 Wstring sKey;
229 sKey.set(key);
230
231 return(getInt(sKey,value,section));
232}
233
234
235
236// Get a config entry as an integer
237bit8 ConfigFile::getInt(IN Wstring &_key,sint16 &value, IN char *section) const
238{
239 Wstring key(_key);
240 key.toUpper();
241
242 if (section) // append section name to key
243 {
244 key+="[";
245 key+=section;
246 key+="]";
247 }
248
249 Wstring svalue;
250 Critsec_.lock();
251 bit8 retval=Dictionary_.getValue(key,svalue);
252 Critsec_.unlock();
253
254 if (retval==FALSE)
255 { DBGMSG("Config entry missing: "<<key.get()); }
256
257 if (retval==FALSE)
258 return(FALSE);
259 value=atoi(svalue.get());
260 return(TRUE);
261}
262
263// Get a config entry as an integer
264bit8 ConfigFile::getInt(IN char *key,sint16 &value, IN char *section) const
265{
266 Wstring sKey;
267 sKey.set(key);
268
269 return(getInt(sKey,value,section));
270}
271
272
273
274/************* MDC; Added functionality for updating and saving config files ************/
275
276// Remove an entry
277bit8 ConfigFile::removeEntry(IN Wstring &_key, IN char *section)
278{
279 Wstring key(_key);
280 key.toUpper();
281
282 if (section) // append section name to key
283 {
284 key+="[";
285 key+=section;
286 key+="]";
287 }
288
289 Critsec_.lock();
290 bit8 retval=Dictionary_.remove(key);
291 Critsec_.unlock();
292
293 if (retval==FALSE)
294 { DBGMSG("Config entry missing: "<<key.get()); }
295
296 if (retval==FALSE)
297 return(FALSE);
298 return(TRUE);
299}
300
301// Remove an entry
302bit8 ConfigFile::removeEntry(IN char *key, IN char *section)
303{
304 Wstring sKey;
305 sKey.set(key);
306 return removeEntry(sKey, section);
307}
308
309// Set a config entry as a string
310bit8 ConfigFile::setString(IN Wstring &_key, IN Wstring &value, IN char *section)
311{
312 Wstring key(_key);
313 key.toUpper();
314
315 if (section) // append section name to key
316 {
317 key+="[";
318 key+=section;
319 key+="]";
320 }
321 else
322 {
323 section = ""; // give it a default
324 }
325
326 Critsec_.lock();
327 Dictionary_.remove(key);
328 bit8 retval=Dictionary_.add(key,value);
329
330 // Test for a new section
331 Wstring test;
332 int i;
333 for (i=0; i<sectionList.length(); i++)
334 {
335 sectionList.get(test, i);
336 if (!strcmp(test.get(), section))
337 break;
338 }
339 if (i == sectionList.length())
340 {
341 // New section!
342 //DBGMSG("New section " << section << ", " << sectionList.length() << " entries");
343 test.set(section);
344 sectionList.addTail(test);
345 }
346 Critsec_.unlock();
347
348 if (retval==FALSE)
349 {
350 DBGMSG("Config could not set entry: "<<key.get());
351 }
352
353 return(retval);
354}
355
356// Set a config entry as a string
357bit8 ConfigFile::setString(IN char *key,IN Wstring &value, IN char *section)
358{
359 Wstring sKey;
360 sKey.set(key);
361 return(setString(sKey,value,section));
362}
363
364// Set a config entry as an integer
365bit8 ConfigFile::setInt(IN Wstring &_key,IN sint32 &value, IN char *section)
366{
367 Wstring key(_key);
368 key.toUpper();
369
370 if (section) // append section name to key
371 {
372 key+="[";
373 key+=section;
374 key+="]";
375 }
376 else
377 {
378 section = ""; // give it a default
379 }
380
381 Wstring svalue;
382 svalue.setFormatted("%d", value);
383 Critsec_.lock();
384 Dictionary_.remove(key);
385 bit8 retval=Dictionary_.add(key,svalue);
386 // Test for a new section
387 Wstring test;
388 //DBGMSG("Testing " << sectionList.length() << " entries for " << section);
389 int i;
390 for (i=0; i<sectionList.length(); i++)
391 {
392 sectionList.get(test, i);
393 //DBGMSG("Looking at " << test.get());
394 if (!strcmp(test.get(), section))
395 break;
396 }
397 if (i == sectionList.length() && section)
398 {
399 // New section!
400 //DBGMSG("New section " << section << ", " << sectionList.length() << " entries");
401 test.set(section);
402 sectionList.addTail(test);
403 }
404 Critsec_.unlock();
405
406 if (retval==FALSE)
407 { DBGMSG("Config could not set entry: "<<key.get()); }
408
409 if (retval==FALSE)
410 return(FALSE);
411 return(TRUE);
412}
413
414// Set a config entry as an integer
415bit8 ConfigFile::setInt(IN char *key,IN sint32 &value, IN char *section)
416{
417 Wstring sKey;
418 sKey.set(key);
419 return(setInt(sKey,value,section));
420}
421
422
423// Write config file to disk. Does not preserve comments, etc.
424bit8 ConfigFile::writeFile(FILE *config)
425{
426 if (!config)
427 {
428 ERRMSG("No FP on config file write!");
429 return FALSE;
430 }
431 int index = 0;
432 int offset = 0;
433 Wstring key;
434 Wstring value;
435
436 Wstring section;
437 //DBGMSG(sectionList.length() << " entries");
438 for (int i=0; i<sectionList.length(); i++)
439 {
440 sectionList.get(section, i);
441 //DBGMSG("Writing " << section.get());
442 fprintf(config, "[%s]\n", section.get());
443 index = 0;
444 offset = 0;
445 while (enumerate(index, offset, key, value, section.get())!=FALSE)
446 {
447 fprintf(config, "%s=%s\n", key.get(), value.get());
448 }
449 fprintf(config, "\n");
450 }
451 return TRUE;
452}
453
454
455
456
457
458
459
460/************* Static functions below **************/
461
462// Given a Wstring, return a 32 bit integer that has a good numeric
463// distributation for the purposes of indexing into a hash table.
464static uint32 Wstring_Hash(const Wstring &string)
465{
466 uint32 retval=0;
467 retval=string.length();
468 for (uint32 i=0; i<string.length(); i++)
469 {
470 retval+=*(string.get()+i);
471 retval+=i;
472 retval=(retval<<8)^(retval>>24); // ROL 8
473 }
474 return(retval);
475}
476
477static char *Eat_Spaces(char *string)
478{
479 char *retval=string;
480 while (isspace(*retval))
481 retval++;
482 return(retval);
483}
#define NULL
Definition BaseType.h:92
#define TRUE
Definition BaseType.h:109
#define FALSE
Definition BaseType.h:113
void const char * value
#define ERRMSG(X)
Definition wdebug.h:86
#define DBGMSG(X)
Definition wdebug.h:128
char bit8
Definition wstypes.h:61
#define IN
Definition wstypes.h:57
unsigned long uint32
Definition bittype.h:46
signed short sint16
Definition bittype.h:50
signed long sint32
Definition bittype.h:51
bit8 enumerate(int &index, int &offset, Wstring &key, Wstring &value, IN char *section=NULL) const
bit8 getInt(IN Wstring &key, OUT sint32 &value)
bit8 readFile(IN FILE *config)
ArrayList< Wstring > sectionList
Definition configfile.h:65
bit8 setString(IN Wstring &key, IN Wstring &value, IN char *section=NULL)
bit8 removeEntry(IN Wstring &key, IN char *section=NULL)
bit8 setInt(IN Wstring &key, IN sint32 &value, IN char *section=NULL)
bit8 writeFile(FILE *config)
bit8 getString(IN Wstring &key, OUT Wstring &value)
bit8 cat(IN char *string)
Definition wstring.cpp:123
void toUpper(void)
Definition wstring.cpp:525
char * get(void)
Definition wstring.cpp:336
void removeSpaces(void)
Definition wstring.cpp:304
char setFormatted(IN char *str,...)
Definition wstring.cpp:447
char set(IN char *str)
Definition wstring.cpp:458
bit8 truncate(uint32 len)
Definition wstring.cpp:539
int test
Definition test6.cpp:32