| ---
tdopewars.c (101007B)
---
1 /************************************************************************
2 * dopewars.c dopewars - general purpose routines and init *
3 * Copyright (C) 1998-2021 Ben Webb *
4 * Email: benwebb@users.sf.net *
5 * WWW: https://dopewars.sourceforge.io/ *
6 * *
7 * This program is free software; you can redistribute it and/or *
8 * modify it under the terms of the GNU General Public License *
9 * as published by the Free Software Foundation; either version 2 *
10 * of the License, or (at your option) any later version. *
11 * *
12 * This program is distributed in the hope that it will be useful, *
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of *
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
15 * GNU General Public License for more details. *
16 * *
17 * You should have received a copy of the GNU General Public License *
18 * along with this program; if not, write to the Free Software *
19 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, *
20 * MA 02111-1307, USA. *
21 ************************************************************************/
22
23 #define _GNU_SOURCE
24
25 #ifdef HAVE_CONFIG_H
26 #include
27 #endif
28
29 #include
30 #include
31 #include
32 #include
33 #ifdef HAVE_UNISTD_H
34 #include
35 #endif
36 #ifdef HAVE_GETOPT_LONG
37 #include
38 #endif
39 #include
40 #include
41 #include
42 #include
43
44 #include "configfile.h"
45 #include "convert.h"
46 #include "dopewars.h"
47 #include "admin.h"
48 #include "log.h"
49 #include "message.h"
50 #include "nls.h"
51 #include "serverside.h"
52 #include "sound.h"
53 #include "tstring.h"
54 #include "AIPlayer.h"
55 #include "util.h"
56 #include "winmain.h"
57
58 #ifdef CURSES_CLIENT
59 #include "curses_client/curses_client.h"
60 #endif
61
62 #ifdef GUI_CLIENT
63 #include "gui_client/gtk_client.h"
64 #endif
65
66 #ifdef GUI_SERVER
67 #include "gtkport/gtkport.h"
68 #endif
69
70 int ClientSock, ListenSock;
71 gboolean Network, Client, Server, WantAntique = FALSE, UseSounds = TRUE;
72
73 /*
74 * dopewars acting as standalone TCP server:
75 * Network=Server=TRUE Client=FALSE
76 * dopewars acting as client, connecting to standalone server:
77 * Network=Client=TRUE Server=FALSE
78 * dopewars in single-player or antique mode:
79 * Network=Server=Client=FALSE
80 */
81 int Port = 7902;
82 gboolean Sanitized, ConfigVerbose, DrugValue, Antique = FALSE;
83 gchar *HiScoreFile = NULL, *ServerName = NULL;
84 gchar *ServerMOTD = NULL, *BindAddress = NULL, *PlayerName = NULL;
85
86 struct DATE StartDate = {
87 1, 12, 1984
88 };
89
90 #ifdef CYGWIN
91 gboolean MinToSysTray = TRUE;
92 #else
93 gboolean Daemonize = TRUE;
94 #endif
95
96 #ifdef CYGWIN
97 #define SNDPATH "sounds\\19.5degs\\"
98 #else
99 #define SNDPATH DPDATADIR"/dopewars/"
100 #endif
101
102 gchar *OurWebBrowser = NULL;
103 gint ConfigErrors = 0;
104 gboolean LocaleIsUTF8 = FALSE;
105
106 int NumLocation = 0, NumGun = 0, NumCop = 0, NumDrug = 0, NumSubway = 0;
107 int NumPlaying = 0, NumStoppedTo = 0;
108 int DebtInterest = 10, BankInterest = 5;
109 Player Noone;
110 int LoanSharkLoc, BankLoc, GunShopLoc, RoughPubLoc;
111 int DrugSortMethod = DS_ATOZ;
112 int FightTimeout = 5, IdleTimeout = 14400, ConnectTimeout = 300;
113 int MaxClients = 20, AITurnPause = 5;
114 price_t StartCash = 2000, StartDebt = 5500;
115 GSList *ServerList = NULL;
116
117 GScannerConfig ScannerConfig = {
118 " \t\n", /* Ignore these characters */
119
120 /* Valid characters for starting an identifier */
121 G_CSET_a_2_z "_" G_CSET_A_2_Z,
122
123 /* Valid characters for continuing an identifier */
124 G_CSET_a_2_z "._-0123456789" G_CSET_A_2_Z,
125
126 "#\n", /* Single line comments start with # and
127 * end with \n */
128 FALSE, /* Are symbols case sensitive? */
129 TRUE, /* Ignore C-style comments? */
130 TRUE, /* Ignore single-line comments? */
131 TRUE, /* Treat C-style comments as single tokens
132 * - do not break into words? */
133 TRUE, /* Read identifiers as tokens? */
134 TRUE, /* Read single-character identifiers as
135 * 1-character strings? */
136 TRUE, /* Allow the parsing of NULL as the
137 * G_TOKEN_IDENTIFIER_NULL ? */
138 FALSE, /* Allow symbols (defined by
139 * g_scanner_scope_add_symbol) ? */
140 TRUE, /* Allow binary numbers in 0b1110 format ? */
141 TRUE, /* Allow octal numbers in C-style e.g. 034 ? */
142 FALSE, /* Allow floats? */
143 TRUE, /* Allow hex numbers in C-style e.g. 0xFF ? */
144 TRUE, /* Allow hex numbers in $FF format ? */
145 TRUE, /* Allow '' strings (no escaping) ? */
146 TRUE, /* Allow "" strings (\ escapes parsed) ? */
147 TRUE, /* Convert octal, binary and hex to int? */
148 FALSE, /* Convert ints to floats? */
149 FALSE, /* Treat all identifiers as strings? */
150 TRUE, /* Leave single characters (e.g. {,=)
151 * unchanged, instead of returning
152 * G_TOKEN_CHAR ? */
153 FALSE, /* Replace read symbols with the token
154 * given by their value, instead of
155 * G_TOKEN_SYMBOL ? */
156 FALSE /* scope_0_fallback... */
157 };
158
159 struct LOCATION StaticLocation, *Location = NULL;
160 struct DRUG StaticDrug, *Drug = NULL;
161 struct GUN StaticGun, *Gun = NULL;
162 struct COP StaticCop, *Cop = NULL;
163 struct NAMES Names = {
164 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
165 };
166 struct SOUNDS Sounds = {
167 NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
168 NULL, NULL, NULL, NULL, NULL, NULL, NULL
169 };
170
171 /* N.B. The slightly over-enthusiastic comments here are for the benefit
172 * of translators ;) */
173 struct NAMES DefaultNames = {
174 /* Name of a single bitch - if you need to use different words for
175 "bitch" depending on where in the sentence it occurs (e.g. subject or
176 object) then read doc/i18n.html about the %tde (etc.) notation. N.B.
177 This notation can be used for most of the translatable strings in
178 dopewars. */
179 N_("nurse"),
180 /* Word used for two or more bitches */
181 N_("nurses"),
182 /* Word used for a single gun */
183 N_("syringe"),
184 /* Word used for two or more guns */
185 N_("syringes"),
186 /* Word used for a single drug */
187 N_("vaccine"),
188 /* Word used for two or more drugs */
189 N_("vaccines"),
190 /* String for displaying the game date or turn number. This is passed
191 to the strftime() function, with the exception that %T is used to
192 mean the turn number rather than the calendar date. */
193 N_("%m-%d-%Y"),
194 /* Names of the loan shark, the bank, the gun shop, and the pub,
195 respectively */
196 N_("the Bank"), N_("the EU"),
197 N_("the medical supply store"), N_("the University")
198 };
199
200 struct CURRENCY Currency = {
201 NULL, TRUE
202 };
203
204 struct PRICES Prices = {
205 20000, 10000
206 };
207
208 struct BITCH Bitch = {
209 50000, 150000
210 };
211
212 #ifdef NETWORKING
213 struct METASERVER MetaServer = {
214 FALSE, NULL, NULL, NULL, NULL
215 };
216
217 struct METASERVER DefaultMetaServer = {
218 TRUE, "https://dopewars.sourceforge.io/metaserver.php", "",
219 "", "dopewars server"
220 };
221
222 SocksServer Socks = { NULL, 0, 0, FALSE, NULL, NULL, NULL };
223 gboolean UseSocks;
224 #endif
225
226 int NumTurns = 31;
227
228 int PlayerArmor = 100, BitchArmor = 50;
229
230 struct LOG Log;
231
232 struct GLOBALS Globals[] = {
233 /* The following strings are the helptexts for all the options that can
234 be set in a dopewars configuration file, or in the server. See
235 doc/configfile.html for more detailed explanations. */
236 {&Port, NULL, NULL, NULL, NULL, "Port", N_("Network port to connect to"),
237 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 65535},
238 {NULL, NULL, NULL, &HiScoreFile, NULL, "HiScoreFile",
239 N_("Name of the high score file"), NULL, NULL, 0, "", NULL, NULL, FALSE,
240 0, 0},
241 {NULL, NULL, NULL, &ServerName, NULL, "Server",
242 N_("Name of the server to connect to"), NULL, NULL, 0, "", NULL,
243 NULL, FALSE, 0, 0},
244 {NULL, NULL, NULL, &ServerMOTD, NULL, "ServerMOTD",
245 N_("Server's welcome message of the day"), NULL, NULL, 0, "", NULL,
246 NULL, FALSE, 0, 0},
247 {NULL, NULL, NULL, &BindAddress, NULL, "BindAddress",
248 N_("Network address for the server to listen on"), NULL, NULL, 0, "",
249 NULL, NULL, FALSE, 0, 0},
250 #ifdef NETWORKING
251 {NULL, &UseSocks, NULL, NULL, NULL, "Socks.Active",
252 N_("TRUE if a SOCKS server should be used for networking"),
253 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
254 #ifndef CYGWIN
255 {NULL, &Socks.numuid, NULL, NULL, NULL, "Socks.NumUID",
256 N_("TRUE if numeric user IDs should be used for SOCKS4"),
257 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
258 #endif
259 {NULL, NULL, NULL, &Socks.user, NULL, "Socks.User",
260 N_("If not blank, the username to use for SOCKS4"),
261 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
262 {NULL, NULL, NULL, &Socks.name, NULL, "Socks.Name",
263 N_("The hostname of a SOCKS server to use"),
264 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
265 {&Socks.port, NULL, NULL, NULL, NULL, "Socks.Port",
266 N_("The port number of a SOCKS server to use"),
267 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 65535},
268 {&Socks.version, NULL, NULL, NULL, NULL, "Socks.Version",
269 N_("The version of the SOCKS protocol to use (4 or 5)"),
270 NULL, NULL, 0, "", NULL, NULL, FALSE, 4, 5},
271 {NULL, NULL, NULL, &Socks.authuser, NULL, "Socks.Auth.User",
272 N_("Username for SOCKS5 authentication"),
273 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
274 {NULL, NULL, NULL, &Socks.authpassword, NULL, "Socks.Auth.Password",
275 N_("Password for SOCKS5 authentication"),
276 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
277 {NULL, &MetaServer.Active, NULL, NULL, NULL, "MetaServer.Active",
278 N_("TRUE if server should report to a metaserver"),
279 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
280 {NULL, NULL, NULL, &MetaServer.URL, NULL, "MetaServer.URL",
281 N_("Metaserver URL to report/get server details to/from"),
282 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
283 {NULL, NULL, NULL, &MetaServer.LocalName, NULL, "MetaServer.LocalName",
284 N_("Preferred hostname of your server machine"), NULL, NULL, 0, "",
285 NULL, NULL, FALSE, 0, 0},
286 {NULL, NULL, NULL, &MetaServer.Password, NULL, "MetaServer.Password",
287 N_("Authentication for LocalName with the metaserver"), NULL, NULL, 0,
288 "", NULL, NULL, FALSE, 0, 0},
289 {NULL, NULL, NULL, &MetaServer.Comment, NULL, "MetaServer.Comment",
290 N_("Server description, reported to the metaserver"), NULL, NULL, 0, "",
291 NULL, NULL, FALSE, 0, 0},
292 #endif /* NETWORKING */
293 #ifdef CYGWIN
294 {NULL, &MinToSysTray, NULL, NULL, NULL, "MinToSysTray",
295 N_("If TRUE, the server minimizes to the System Tray"),
296 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
297 #else
298 {NULL, &Daemonize, NULL, NULL, NULL, "Daemonize",
299 N_("If TRUE, the server runs in the background"),
300 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
301 {NULL, NULL, NULL, &OurWebBrowser, NULL, "WebBrowser",
302 N_("The command used to start your web browser"),
303 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
304 #endif
305 {&NumTurns, NULL, NULL, NULL, NULL, "NumTurns",
306 N_("No. of game turns (if 0, game never ends)"),
307 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, -1},
308 {&StartDate.day, NULL, NULL, NULL, NULL, "StartDate.Day",
309 N_("Day of the month on which the game starts"),
310 NULL, NULL, 0, "", NULL, NULL, FALSE, 1, 31},
311 {&StartDate.month, NULL, NULL, NULL, NULL, "StartDate.Month",
312 N_("Month in which the game starts"),
313 NULL, NULL, 0, "", NULL, NULL, FALSE, 1, 12},
314 {&StartDate.year, NULL, NULL, NULL, NULL, "StartDate.Year",
315 N_("Year in which the game starts"),
316 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, -1},
317 {NULL, NULL, NULL, &Currency.Symbol, NULL, "Currency.Symbol",
318 N_("The currency symbol (e.g. $)"),
319 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
320 {NULL, &Currency.Prefix, NULL, NULL, NULL, "Currency.Prefix",
321 N_("If TRUE, the currency symbol precedes prices"),
322 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
323 {NULL, NULL, NULL, &Log.File, NULL, "Log.File",
324 N_("File to write log messages to"),
325 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
326 {&Log.Level, NULL, NULL, NULL, NULL, "Log.Level",
327 N_("Controls the number of log messages produced"),
328 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 5},
329 {NULL, NULL, NULL, &Log.Timestamp, NULL, "Log.Timestamp",
330 N_("strftime() format string for log timestamps"),
331 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
332 {NULL, &Sanitized, NULL, NULL, NULL, "Sanitized",
333 N_("Random events are sanitized"), NULL, NULL, 0, "", NULL, NULL, FALSE,
334 0, 0},
335 {NULL, &DrugValue, NULL, NULL, NULL, "DrugValue",
336 N_("TRUE if the value of bought drugs should be saved"),
337 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
338 {NULL, &ConfigVerbose, NULL, NULL, NULL, "ConfigVerbose",
339 N_("Be verbose in processing config file"), NULL, NULL, 0, "", NULL,
340 NULL, FALSE, 0, 0},
341 {&NumLocation, NULL, NULL, NULL, NULL, "NumLocation",
342 N_("Number of locations in the game"),
343 (void **)(&Location), NULL, sizeof(struct LOCATION), "", NULL,
344 ResizeLocations, FALSE, 1, -1},
345 {&NumCop, NULL, NULL, NULL, NULL, "NumCop",
346 N_("Number of types of cop in the game"),
347 (void **)(&Cop), NULL, sizeof(struct COP), "", NULL, ResizeCops, FALSE,
348 0, -1},
349 {&NumGun, NULL, NULL, NULL, NULL, "NumGun",
350 N_("Number of guns in the game"),
351 (void **)(&Gun), NULL, sizeof(struct GUN), "", NULL, ResizeGuns, FALSE,
352 0, -1},
353 {&NumDrug, NULL, NULL, NULL, NULL, "NumDrug",
354 N_("Number of drugs in the game"),
355 (void **)(&Drug), NULL, sizeof(struct DRUG), "", NULL, ResizeDrugs,
356 FALSE, 1, -1},
357 {&LoanSharkLoc, NULL, NULL, NULL, NULL, "LoanShark",
358 N_("Location of the Loan Shark"), NULL, NULL, 0, "", NULL, NULL, FALSE,
359 0, 0},
360 {&BankLoc, NULL, NULL, NULL, NULL, "Bank", N_("Location of the bank"),
361 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
362 {&GunShopLoc, NULL, NULL, NULL, NULL, "GunShop",
363 N_("Location of the gun shop"),
364 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
365 {&RoughPubLoc, NULL, NULL, NULL, NULL, "RoughPub",
366 N_("Location of the pub"),
367 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
368 {&DebtInterest, NULL, NULL, NULL, NULL, "DebtInterest",
369 N_("Daily interest rate on the loan shark debt"),
370 NULL, NULL, 0, "", NULL, NULL, FALSE, -100, -200},
371 {&BankInterest, NULL, NULL, NULL, NULL, "BankInterest",
372 N_("Daily interest rate on your bank balance"),
373 NULL, NULL, 0, "", NULL, NULL, FALSE, -100, -200},
374 {NULL, NULL, NULL, &Names.LoanSharkName, NULL, "LoanSharkName",
375 N_("Name of the loan shark"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
376 {NULL, NULL, NULL, &Names.BankName, NULL, "BankName",
377 N_("Name of the bank"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
378 {NULL, NULL, NULL, &Names.GunShopName, NULL, "GunShopName",
379 N_("Name of the gun shop"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
380 {NULL, NULL, NULL, &Names.RoughPubName, NULL, "RoughPubName",
381 N_("Name of the pub"), NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
382 {NULL, &UseSounds, NULL, NULL, NULL, "UseSounds",
383 N_("TRUE if sounds should be enabled"),
384 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
385 {NULL, NULL, NULL, &Sounds.FightHit, NULL, "Sounds.FightHit",
386 N_("Sound file played for a gun \"hit\""), NULL, NULL, 0, "",
387 NULL, NULL, FALSE, 0, 0},
388 {NULL, NULL, NULL, &Sounds.FightMiss, NULL, "Sounds.FightMiss",
389 N_("Sound file played for a gun \"miss\""), NULL, NULL, 0, "",
390 NULL, NULL, FALSE, 0, 0},
391 {NULL, NULL, NULL, &Sounds.FightReload, NULL, "Sounds.FightReload",
392 N_("Sound file played when guns are reloaded"), NULL, NULL, 0, "",
393 NULL, NULL, FALSE, 0, 0},
394 {NULL, NULL, NULL, &Sounds.EnemyBitchKilled, NULL, "Sounds.EnemyBitchKilled",
395 N_("Sound file played when an enemy bitch/deputy is killed"),
396 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
397 {NULL, NULL, NULL, &Sounds.BitchKilled, NULL, "Sounds.BitchKilled",
398 N_("Sound file played when one of your bitches is killed"),
399 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
400 {NULL, NULL, NULL, &Sounds.EnemyKilled, NULL, "Sounds.EnemyKilled",
401 N_("Sound file played when another player or cop is killed"),
402 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
403 {NULL, NULL, NULL, &Sounds.Killed, NULL, "Sounds.Killed",
404 N_("Sound file played when you are killed"),
405 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
406 {NULL, NULL, NULL, &Sounds.EnemyFailFlee, NULL, "Sounds.EnemyFailFlee",
407 N_("Sound file played when a player tries to escape, but fails"),
408 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
409 {NULL, NULL, NULL, &Sounds.FailFlee, NULL, "Sounds.FailFlee",
410 N_("Sound file played when you try to escape, but fail"),
411 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
412 {NULL, NULL, NULL, &Sounds.EnemyFlee, NULL, "Sounds.EnemyFlee",
413 N_("Sound file played when a player successfully escapes"),
414 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
415 {NULL, NULL, NULL, &Sounds.Flee, NULL, "Sounds.Flee",
416 N_("Sound file played when you successfully escape"),
417 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
418 {NULL, NULL, NULL, &Sounds.Jet, NULL, "Sounds.Jet",
419 N_("Sound file played on arriving at a new location"), NULL, NULL, 0, "",
420 NULL, NULL, FALSE, 0, 0},
421 {NULL, NULL, NULL, &Sounds.TalkToAll, NULL, "Sounds.TalkToAll",
422 N_("Sound file played when a player sends a public chat message"),
423 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
424 {NULL, NULL, NULL, &Sounds.TalkPrivate, NULL, "Sounds.TalkPrivate",
425 N_("Sound file played when a player sends a private chat message"),
426 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
427 {NULL, NULL, NULL, &Sounds.JoinGame, NULL, "Sounds.JoinGame",
428 N_("Sound file played when a player joins the game"),
429 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
430 {NULL, NULL, NULL, &Sounds.LeaveGame, NULL, "Sounds.LeaveGame",
431 N_("Sound file played when a player leaves the game"),
432 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
433 {NULL, NULL, NULL, &Sounds.StartGame, NULL, "Sounds.StartGame",
434 N_("Sound file played at the start of the game"),
435 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
436 {NULL, NULL, NULL, &Sounds.EndGame, NULL, "Sounds.EndGame",
437 N_("Sound file played at the end of the game"),
438 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
439 {&DrugSortMethod, NULL, NULL, NULL, NULL, "DrugSortMethod",
440 N_("Sort key for listing available drugs"),
441 NULL, NULL, 0, "", NULL, NULL, FALSE, 1, 4},
442 {&FightTimeout, NULL, NULL, NULL, NULL, "FightTimeout",
443 N_("No. of seconds in which to return fire"),
444 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, -1},
445 {&IdleTimeout, NULL, NULL, NULL, NULL, "IdleTimeout",
446 N_("Players are disconnected after this many seconds"),
447 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, -1},
448 {&ConnectTimeout, NULL, NULL, NULL, NULL, "ConnectTimeout",
449 N_("Time in seconds for connections to be made or broken"),
450 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, -1},
451 {&MaxClients, NULL, NULL, NULL, NULL, "MaxClients",
452 N_("Maximum number of TCP/IP connections"),
453 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, -1},
454 {&AITurnPause, NULL, NULL, NULL, NULL, "AITurnPause",
455 N_("Seconds between turns of AI players"),
456 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, -1},
457 {NULL, NULL, &StartCash, NULL, NULL, "StartCash",
458 N_("Amount of cash that each player starts with"),
459 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, -1},
460 {NULL, NULL, &StartDebt, NULL, NULL, "StartDebt",
461 N_("Amount of debt that each player starts with"),
462 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, -1},
463 {NULL, NULL, NULL, &StaticLocation.Name, NULL, "Name",
464 N_("Name of each location"), (void **)(&Location), &StaticLocation,
465 sizeof(struct LOCATION), "Location", &NumLocation, NULL, FALSE, 0, -1},
466 {&(StaticLocation.PolicePresence), NULL, NULL, NULL, NULL,
467 "PolicePresence",
468 N_("Police presence at each location (%)"),
469 (void **)(&Location), &StaticLocation,
470 sizeof(struct LOCATION), "Location", &NumLocation, NULL, FALSE, 0, 100},
471 {&(StaticLocation.MinDrug), NULL, NULL, NULL, NULL, "MinDrug",
472 N_("Minimum number of drugs at each location"),
473 (void **)(&Location), &StaticLocation,
474 sizeof(struct LOCATION), "Location", &NumLocation, NULL, FALSE, 1, -1},
475 {&(StaticLocation.MaxDrug), NULL, NULL, NULL, NULL, "MaxDrug",
476 N_("Maximum number of drugs at each location"),
477 (void **)(&Location), &StaticLocation,
478 sizeof(struct LOCATION), "Location", &NumLocation, NULL, FALSE, 1, -1},
479 {&PlayerArmor, NULL, NULL, NULL, NULL, "PlayerArmour",
480 N_("% resistance to gunshots of each player"),
481 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 100},
482 {&PlayerArmor, NULL, NULL, NULL, NULL, "PlayerArmor",
483 N_("% resistance to gunshots of each player"),
484 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 100},
485 {&BitchArmor, NULL, NULL, NULL, NULL, "BitchArmour",
486 N_("% resistance to gunshots of each bitch"),
487 NULL, NULL, 0, "", NULL, NULL, FALSE, 1, 100},
488 {&BitchArmor, NULL, NULL, NULL, NULL, "BitchArmor",
489 N_("% resistance to gunshots of each bitch"),
490 NULL, NULL, 0, "", NULL, NULL, FALSE, 1, 100},
491 {NULL, NULL, NULL, &StaticCop.Name, NULL, "Name",
492 N_("Name of each cop"),
493 (void **)(&Cop), &StaticCop, sizeof(struct COP), "Cop", &NumCop,
494 NULL, FALSE, 0, 0},
495 {NULL, NULL, NULL, &StaticCop.DeputyName, NULL, "DeputyName",
496 N_("Name of each cop's deputy"),
497 (void **)(&Cop), &StaticCop, sizeof(struct COP), "Cop", &NumCop,
498 NULL, FALSE, 0, 0},
499 {NULL, NULL, NULL, &StaticCop.DeputiesName, NULL, "DeputiesName",
500 N_("Name of each cop's deputies"),
501 (void **)(&Cop), &StaticCop, sizeof(struct COP), "Cop", &NumCop,
502 NULL, FALSE, 0, 0},
503 {&StaticCop.Armor, NULL, NULL, NULL, NULL, "Armour",
504 N_("% resistance to gunshots of each cop"),
505 (void **)(&Cop), &StaticCop, sizeof(struct COP), "Cop", &NumCop,
506 NULL, FALSE, 1, 100},
507 {&StaticCop.Armor, NULL, NULL, NULL, NULL, "Armor",
508 N_("% resistance to gunshots of each cop"),
509 (void **)(&Cop), &StaticCop, sizeof(struct COP), "Cop", &NumCop,
510 NULL, FALSE, 1, 100},
511 {&StaticCop.DeputyArmor, NULL, NULL, NULL, NULL, "DeputyArmour",
512 N_("% resistance to gunshots of each deputy"),
513 (void **)(&Cop), &StaticCop, sizeof(struct COP), "Cop", &NumCop,
514 NULL, FALSE, 1, 100},
515 {&StaticCop.DeputyArmor, NULL, NULL, NULL, NULL, "DeputyArmor",
516 N_("% resistance to gunshots of each deputy"),
517 (void **)(&Cop), &StaticCop, sizeof(struct COP), "Cop", &NumCop,
518 NULL, FALSE, 1, 100},
519 {&StaticCop.AttackPenalty, NULL, NULL, NULL, NULL, "AttackPenalty",
520 N_("Attack penalty relative to a player"),
521 (void **)(&Cop), &StaticCop, sizeof(struct COP), "Cop", &NumCop,
522 NULL, FALSE, 0, 100},
523 {&StaticCop.DefendPenalty, NULL, NULL, NULL, NULL, "DefendPenalty",
524 N_("Defend penalty relative to a player"),
525 (void **)(&Cop), &StaticCop, sizeof(struct COP), "Cop", &NumCop,
526 NULL, FALSE, 0, 100},
527 {&StaticCop.MinDeputies, NULL, NULL, NULL, NULL, "MinDeputies",
528 N_("Minimum number of accompanying deputies"),
529 (void **)(&Cop), &StaticCop, sizeof(struct COP), "Cop", &NumCop,
530 NULL, FALSE, 0, -1},
531 {&StaticCop.MaxDeputies, NULL, NULL, NULL, NULL, "MaxDeputies",
532 N_("Maximum number of accompanying deputies"),
533 (void **)(&Cop), &StaticCop, sizeof(struct COP), "Cop", &NumCop,
534 NULL, FALSE, 0, -1},
535 {&StaticCop.GunIndex, NULL, NULL, NULL, NULL, "GunIndex",
536 N_("Zero-based index of the gun that cops are armed with"),
537 (void **)(&Cop), &StaticCop, sizeof(struct COP), "Cop", &NumCop,
538 NULL, FALSE, 0, -1},
539 {&StaticCop.CopGun, NULL, NULL, NULL, NULL, "CopGun",
540 N_("Number of guns that each cop carries"),
541 (void **)(&Cop), &StaticCop, sizeof(struct COP), "Cop", &NumCop,
542 NULL, FALSE, 0, -1},
543 {&StaticCop.DeputyGun, NULL, NULL, NULL, NULL, "DeputyGun",
544 N_("Number of guns that each deputy carries"),
545 (void **)(&Cop), &StaticCop, sizeof(struct COP), "Cop", &NumCop,
546 NULL, FALSE, 0, -1},
547 {NULL, NULL, NULL, &StaticDrug.Name, NULL, "Name",
548 N_("Name of each drug"),
549 (void **)(&Drug), &StaticDrug,
550 sizeof(struct DRUG), "Drug", &NumDrug, NULL, FALSE, 0, 0},
551 {NULL, NULL, &(StaticDrug.MinPrice), NULL, NULL, "MinPrice",
552 N_("Minimum normal price of each drug"),
553 (void **)(&Drug), &StaticDrug,
554 sizeof(struct DRUG), "Drug", &NumDrug, NULL, FALSE, 1, -1},
555 {NULL, NULL, &(StaticDrug.MaxPrice), NULL, NULL, "MaxPrice",
556 N_("Maximum normal price of each drug"),
557 (void **)(&Drug), &StaticDrug,
558 sizeof(struct DRUG), "Drug", &NumDrug, NULL, FALSE, 1, -1},
559 {NULL, &(StaticDrug.Cheap), NULL, NULL, NULL, "Cheap",
560 N_("TRUE if this drug can be specially cheap"),
561 (void **)(&Drug), &StaticDrug,
562 sizeof(struct DRUG), "Drug", &NumDrug, NULL, FALSE, 0, 0},
563 {NULL, &(StaticDrug.Expensive), NULL, NULL, NULL, "Expensive",
564 N_("TRUE if this drug can be specially expensive"),
565 (void **)(&Drug), &StaticDrug,
566 sizeof(struct DRUG), "Drug", &NumDrug, NULL, FALSE, 0, 0},
567 {NULL, NULL, NULL, &StaticDrug.CheapStr, NULL, "CheapStr",
568 N_("Message displayed when this drug is specially cheap"),
569 (void **)(&Drug), &StaticDrug,
570 sizeof(struct DRUG), "Drug", &NumDrug, NULL, FALSE, 0, 0},
571 {NULL, NULL, NULL, &Drugs.ExpensiveStr1, NULL, "Drugs.ExpensiveStr1",
572 N_("Format string used for expensive drugs 50% of time"),
573 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
574 {NULL, NULL, NULL, &Drugs.ExpensiveStr2, NULL, "Drugs.ExpensiveStr2",
575 /* xgettext:no-c-format */
576 N_("Format string used for expensive drugs 50% of time"),
577 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
578 {&(Drugs.CheapDivide), NULL, NULL, NULL, NULL, "Drugs.CheapDivide",
579 N_("Divider for drug price when it's specially cheap"),
580 NULL, NULL, 0, "", NULL, NULL, FALSE, 1, -1},
581 {&(Drugs.ExpensiveMultiply), NULL, NULL, NULL, NULL,
582 "Drugs.ExpensiveMultiply",
583 N_("Multiplier for specially expensive drug prices"),
584 NULL, NULL, 0, "", NULL, NULL, FALSE, 1, -1},
585 {NULL, NULL, NULL, &StaticGun.Name, NULL, "Name",
586 N_("Name of each gun"),
587 (void **)(&Gun), &StaticGun,
588 sizeof(struct GUN), "Gun", &NumGun, NULL, FALSE, 0, 0},
589 {NULL, NULL, &(StaticGun.Price), NULL, NULL, "Price",
590 N_("Price of each gun"),
591 (void **)(&Gun), &StaticGun,
592 sizeof(struct GUN), "Gun", &NumGun, NULL, FALSE, 0, 0},
593 {&(StaticGun.Space), NULL, NULL, NULL, NULL, "Space",
594 N_("Space taken by each syringe"),
595 (void **)(&Gun), &StaticGun,
596 sizeof(struct GUN), "Gun", &NumGun, NULL, FALSE, 0, -1},
597 {&(StaticGun.Damage), NULL, NULL, NULL, NULL, "Damage",
598 N_("Damage done by each gun"),
599 (void **)(&Gun), &StaticGun,
600 sizeof(struct GUN), "Gun", &NumGun, NULL, FALSE, 0, -1},
601 {NULL, NULL, NULL, &Names.Bitch, NULL, "Names.Bitch",
602 N_("Word used to denote a single \"bitch\""), NULL, NULL, 0, "", NULL,
603 NULL, FALSE, 0, 0},
604 {NULL, NULL, NULL, &Names.Bitches, NULL, "Names.Bitches",
605 N_("Word used to denote two or more \"bitches\""),
606 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
607 {NULL, NULL, NULL, &Names.Gun, NULL, "Names.Gun",
608 N_("Word used to denote a single gun or equivalent"), NULL, NULL, 0, "",
609 NULL, NULL, FALSE, 0, 0},
610 {NULL, NULL, NULL, &Names.Guns, NULL, "Names.Guns",
611 N_("Word used to denote two or more guns"), NULL, NULL, 0, "", NULL,
612 NULL, FALSE, 0, 0},
613 {NULL, NULL, NULL, &Names.Drug, NULL, "Names.Drug",
614 N_("Word used to denote a single drug or equivalent"), NULL, NULL, 0,
615 "", NULL, NULL, FALSE, 0, 0},
616 {NULL, NULL, NULL, &Names.Drugs, NULL, "Names.Drugs",
617 N_("Word used to denote two or more drugs"), NULL, NULL, 0, "", NULL,
618 NULL, FALSE, 0, 0},
619 {NULL, NULL, NULL, &Names.Date, NULL, "Names.Date",
620 N_("strftime() format string for displaying the game turn"),
621 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, 0},
622 {NULL, NULL, &Prices.Spy, NULL, NULL, "Prices.Spy",
623 N_("Cost for a bitch to spy on the enemy"),
624 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, -1},
625 {NULL, NULL, &Prices.Tipoff, NULL, NULL, "Prices.Tipoff",
626 N_("Cost for a bitch to tipoff the cops to an enemy"),
627 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, -1},
628 {NULL, NULL, &Bitch.MinPrice, NULL, NULL, "Bitch.MinPrice",
629 N_("Minimum price to hire a bitch"),
630 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, -1},
631 {NULL, NULL, &Bitch.MaxPrice, NULL, NULL, "Bitch.MaxPrice",
632 N_("Maximum price to hire a bitch"),
633 NULL, NULL, 0, "", NULL, NULL, FALSE, 0, -1},
634 {NULL, NULL, NULL, NULL, &SubwaySaying, "SubwaySaying",
635 N_("List of things which you overhear on the internet"),
636 NULL, NULL, 0, "", &NumSubway, ResizeSubway, FALSE, 0, 0},
637 {&NumSubway, NULL, NULL, NULL, NULL, "NumSubwaySaying",
638 N_("Number of subway sayings"),
639 NULL, NULL, 0, "", NULL, ResizeSubway, FALSE, 0, -1},
640 {NULL, NULL, NULL, NULL, &Playing, "Playing",
641 N_("List of songs which you can hear playing"),
642 NULL, NULL, 0, "", &NumPlaying, ResizePlaying, FALSE, 0, 0},
643 {&NumPlaying, NULL, NULL, NULL, NULL, "NumPlaying",
644 N_("Number of playing songs"),
645 NULL, NULL, 0, "", NULL, ResizePlaying, FALSE, 0, -1},
646 {NULL, NULL, NULL, NULL, &StoppedTo, "StoppedTo",
647 N_("List of things which you can stop to do"),
648 NULL, NULL, 0, "", &NumStoppedTo, ResizeStoppedTo, FALSE, 0, 0},
649 {&NumStoppedTo, NULL, NULL, NULL, NULL, "NumStoppedTo",
650 N_("Number of things which you can stop to do"),
651 NULL, NULL, 0, "", NULL, ResizeStoppedTo, FALSE, 0, -1}
652 };
653 const int NUMGLOB = sizeof(Globals) / sizeof(Globals[0]);
654
655 char **Playing = NULL;
656 char *DefaultPlaying[] = {
657 /* Default list of songs that you can hear playing (N.B. this can be
658 overridden in the configuration file with the "Playing" variable) -
659 look for "You hear someone playing %s" to see how these are used. */
660 N_("`Are you Experienced` by Jimi Hendrix"),
661 N_("`Cheeba Cheeba` by Tone Loc"),
662 N_("`Comin` in to Los Angeles` by Arlo Guthrie"),
663 N_("`Commercial` by Spanky and Our Gang"),
664 N_("`Late in the Evening` by Paul Simon"),
665 N_("`Light Up` by Styx"),
666 N_("`Mexico` by Jefferson Airplane"),
667 N_("`One toke over the line` by Brewer & Shipley"),
668 N_("`The Smokeout` by Shel Silverstein"),
669 N_("`White Rabbit` by Jefferson Airplane"),
670 N_("`Itchycoo Park` by Small Faces"),
671 N_("`White Punks on Dope` by the Tubes"),
672 N_("`Legend of a Mind` by the Moody Blues"),
673 N_("`Eight Miles High` by the Byrds"),
674 N_("`Acapulco Gold` by Riders of the Purple Sage"),
675 N_("`Kicks` by Paul Revere & the Raiders"),
676 N_("the Nixon tapes"),
677 N_("`Legalize It` by Mojo Nixon & Skid Roper")
678 };
679
680 char **StoppedTo = NULL;
681 char *DefaultStoppedTo[] = {
682 /* Default list of things which you can "stop to do" (random events that
683 cost you a little money). These can be overridden with the "StoppedTo"
684 variable in the configuration file. See the later string "You stopped
685 to %s." to see how these strings are used. */
686 N_("have a beer"),
687 N_("COVID19 test a child"),
688 N_("check prostata"),
689 N_("start revolution"),
690 N_("yell at politicians")
691 };
692
693 struct COP DefaultCop[] = {
694 /* Name of the first police officer to attack you */
695 {N_("An antivaxxer"),
696 /* Name of a single deputy of the first police officer */
697 N_("facebook follower"),
698 /* Word used for more than one deputy of the first police officer */
699 N_("facebook followers"), 4, 3, 30, 30, 2, 8, 0, 1, 1},
700 /* Ditto, for the other police officers */
701 {N_("Bob"), N_("antivaxxer"), N_("antivaxxers"), 15, 4, 30, 20, 4, 10,
702 0, 2, 1},
703 {N_("Fietsopa"), N_("antivaxxer"), N_("antivaxxers"), 50, 6, 20, 20, 6, 18, 1, 3, 2}
704 };
705
706 struct GUN DefaultGun[] = {
707 /* The names of the default guns */
708 {N_("Needleless syringe"), 3000, 4, 5},
709 {N_("Nasal syringe"), 3500, 4, 9},
710 {N_("Safety syringe"), 2900, 4, 4},
711 {N_("Luer-Lock syringe"), 3100, 4, 7}
712 };
713
714 struct DRUG DefaultDrug[] = {
715 /* The names of the default drugs, and the messages displayed when they
716 are specially cheap or expensive */
717 {N_("AstraZenica"), 1000, 4400, TRUE, FALSE,
718 N_("The market is flooded with cheap home-made AstraZenica!")},
719 {N_("Pfizer/BioNTech"), 15000, 29000, FALSE, TRUE, ""},
720 {N_("SputnikLight"), 480, 1280, TRUE, FALSE,
721 N_("A large batch of Russian Sputnik Light is delivered!")},
722 {N_("Heroin"), 5500, 13000, FALSE, TRUE, ""},
723 {N_("Sputnik V"), 11, 60, TRUE, FALSE,
724 N_("Rival vaccine dealers raided a freezer and are selling cheap Sputnik V!")},
725 {N_("Sinopharm"), 1500, 4400, FALSE, FALSE, ""},
726 {N_("Moderna"), 540, 1250, FALSE, TRUE, ""},
727 {N_("RBD-Dimer"), 1000, 2500, FALSE, FALSE, ""},
728 {N_("CoviVac"), 220, 700, FALSE, FALSE, ""},
729 {N_("Covaxin"), 630, 1300, FALSE, FALSE, ""},
730 {N_("QazCovid-in"), 90, 250, FALSE, TRUE, ""},
731 {N_("Johns.&Johns."), 315, 890, TRUE, FALSE,
732 N_("Some countries have cancelled Johnson & Johnson! "
733 "Prices have bottomed out!")}
734 };
735
736 #define NUMDRUG (sizeof(DefaultDrug)/sizeof(DefaultDrug[0]))
737
738 struct LOCATION DefaultLocation[] = {
739 /* The names of the default locations */
740 {N_("Germany"), 10, NUMDRUG / 2 + 1, NUMDRUG},
741 {N_("Russia"), 5, NUMDRUG / 2 + 2, NUMDRUG},
742 {N_("USA"), 15, NUMDRUG / 2, NUMDRUG},
743 {N_("Myanmar"), 90, NUMDRUG / 2 - 2, NUMDRUG - 2},
744 {N_("Latvia"), 20, NUMDRUG / 2, NUMDRUG},
745 {N_("Denmark"), 70, NUMDRUG / 2 - 2, NUMDRUG - 1},
746 {N_("Trinidad and Tobago"), 50, NUMDRUG / 2, NUMDRUG},
747 {N_("Uganda"), 20, NUMDRUG / 2, NUMDRUG}
748 };
749
750 struct DRUGS Drugs = { NULL, NULL, 0, 0 };
751 struct DRUGS DefaultDrugs = {
752 /* Messages displayed for drug busts, etc. */
753 N_("Antivaxxers made a big %tde facebook campaign! Prices are outrageous!"),
754 N_("Doctors are buying %tde at ridiculous prices!"),
755 4, 4
756 };
757
758 char **SubwaySaying = NULL;
759 char *DefaultSubwaySaying[] = {
760 /* Default list of things which the "lady on the subway" can tell you
761 (N.B. can be overridden with the "SubwaySaying" config. file
762 variable). Look for "the lady next to you" to see how these strings
763 are used. */
764 N_("Oh hai Mark"),
765 N_("Anyway, how is your sex life?"),
766 N_("I\'ll bet you have some really interesting dreams"),
767 N_("So I think I\'m going to bitreichcon this year"),
768 N_("Son, you need a yellow haircut"),
769 N_("I think it\'s wonderful what they\'re doing with vaccines these days"),
770 N_("I wasn\'t always a woman, you know"),
771 N_("I feel a blood clod coming"),
772 N_("Are you high on something?"),
773 N_("Oh, you must be from Wales"),
774 N_("I used to be a hippie, myself"),
775 N_("There\'s nothing like having lots of money"),
776 N_("You look like an aardvark!"),
777 N_("I don\'t believe in Angela Merkel"),
778 N_("Courage! Bush is a noodle!"),
779 N_("Haven\'t I seen you on TV?"),
780 N_("I think hemorrhoid commercials are really neat!"),
781 N_("We\'re winning the war for drugs!"),
782 N_("A day without dope is like night"),
783 /* xgettext:no-c-format */
784 N_("We only use 20% of our brains, so why not burn out the other 80%"),
785 N_("I\'m soliciting contributions for Zombies for Christ"),
786 N_("I\'d like to sell you an edible poodle"),
787 N_("Winners don\'t do vaccines... unless they do"),
788 N_("Kill an antivaxxer for Christ!"),
789 N_("I am the walrus!"),
790 N_("Jesus loves you more than you will know"),
791 N_("I feel an unaccountable urge to dye my hair blue"),
792 N_("Wasn\'t Jane Fonda wonderful in Barbarella"),
793 N_("Just say No... well, maybe... ok, what the hell!"),
794 N_("Would you like a jelly baby?"),
795 N_("Vaccines can be your friend!")
796 };
797
798 static gboolean SetConfigValue(int GlobalIndex, int StructIndex,
799 gboolean IndexGiven, Converter *conv,
800 GScanner *scanner);
801 /*
802 * Returns a random integer not less than bot and less than top.
803 */
804 int brandom(int bot, int top)
805 {
806 return (int)((float)(top - bot) * rand() / (RAND_MAX + 1.0)) + bot;
807 }
808
809 /*
810 * Returns a random price not less than bot and less than top.
811 */
812 price_t prandom(price_t bot, price_t top)
813 {
814 return (price_t)((float)(top - bot) * rand() / (RAND_MAX + 1.0)) + bot;
815 }
816
817 /*
818 * Returns the total numbers of players in the list starting at "First";
819 * players still in the process of connecting or leaving, and those that
820 * are actually cops (server-created internal AI players) are ignored.
821 */
822 int CountPlayers(GSList *First)
823 {
824 GSList *list;
825 Player *Play;
826 int count = 0;
827
828 for (list = First; list; list = g_slist_next(list)) {
829 Play = (Player *)list->data;
830 if (strlen(GetPlayerName(Play)) > 0 && !IsCop(Play))
831 count++;
832 }
833 return count;
834 }
835
836 /*
837 * Adds the new Player structure "NewPlayer" to the linked list
838 * pointed to by "First", and initializes all fields. Returns the new
839 * start of the list. If this function is called by the server, then
840 * it should pass the file descriptor of the socket used to
841 * communicate with the client player.
842 */
843 GSList *AddPlayer(int fd, Player *NewPlayer, GSList *First)
844 {
845 Player *tmp;
846 GSList *list;
847
848 list = First;
849 NewPlayer->ID = 0;
850 /* Generate a unique player ID, if we're the server (clients get their
851 * IDs from the server, so don't need to generate IDs) */
852 if (Server) {
853 while (list) {
854 tmp = (Player *)list->data;
855 if (tmp->ID == NewPlayer->ID) {
856 NewPlayer->ID++;
857 list = First;
858 } else {
859 list = g_slist_next(list);
860 }
861 }
862 }
863 NewPlayer->Name = NULL;
864 SetPlayerName(NewPlayer, NULL);
865 NewPlayer->IsAt = 0;
866 NewPlayer->EventNum = E_NONE;
867 NewPlayer->FightTimeout = NewPlayer->ConnectTimeout =
868 NewPlayer->IdleTimeout = 0;
869 NewPlayer->Guns = (Inventory *)g_malloc0(NumGun * sizeof(Inventory));
870 NewPlayer->Drugs = (Inventory *)g_malloc0(NumDrug * sizeof(Inventory));
871 InitList(&(NewPlayer->SpyList));
872 InitList(&(NewPlayer->TipList));
873 NewPlayer->Turn = 1;
874 NewPlayer->date = g_date_new_dmy(StartDate.day, StartDate.month,
875 StartDate.year);
876 NewPlayer->Cash = StartCash;
877 NewPlayer->Debt = StartDebt;
878 NewPlayer->Bank = 0;
879 NewPlayer->Bitches.Carried = 8;
880 NewPlayer->CopIndex = 0;
881 NewPlayer->Health = 100;
882 NewPlayer->CoatSize = 100;
883 NewPlayer->Flags = 0;
884 #ifdef NETWORKING
885 InitNetworkBuffer(&NewPlayer->NetBuf, '\n', '\r',
886 UseSocks ? &Socks : NULL);
887 if (Server)
888 BindNetworkBufferToSocket(&NewPlayer->NetBuf, fd);
889 #endif
890 InitAbilities(NewPlayer);
891 NewPlayer->FightArray = NULL;
892 NewPlayer->Attacking = NULL;
893 return g_slist_append(First, (gpointer)NewPlayer);
894 }
895
896 /*
897 * Returns TRUE only if the given player has properly connected (i.e. has
898 * a valid name).
899 */
900 gboolean IsConnectedPlayer(Player *play)
901 {
902 return (play && play->Name && play->Name[0]);
903 }
904
905 /*
906 * Redimensions the Gun and Drug lists for "Play".
907 */
908 void UpdatePlayer(Player *Play)
909 {
910 Play->Guns =
911 (Inventory *)g_realloc(Play->Guns, NumGun * sizeof(Inventory));
912 Play->Drugs =
913 (Inventory *)g_realloc(Play->Drugs, NumDrug * sizeof(Inventory));
914 }
915
916 /*
917 * Removes the Player structure pointed to by "Play" from the linked
918 * list starting at "First". The client socket is freed if called
919 * from the server. The new start of the list is returned.
920 */
921 GSList *RemovePlayer(Player *Play, GSList *First)
922 {
923 g_assert(Play);
924 g_assert(First);
925
926 First = g_slist_remove(First, (gpointer)Play);
927 #ifdef NETWORKING
928 if (!IsCop(Play))
929 ShutdownNetworkBuffer(&Play->NetBuf);
930 #endif
931 ClearList(&(Play->SpyList));
932 ClearList(&(Play->TipList));
933 g_date_free(Play->date);
934 g_free(Play->Name);
935 g_free(Play->Guns);
936 g_free(Play->Drugs);
937 g_free(Play);
938 return First;
939 }
940
941 /*
942 * Copies player "Src" to player "Dest".
943 */
944 void CopyPlayer(Player *Dest, Player *Src)
945 {
946 if (!Dest || !Src)
947 return;
948 Dest->Turn = Src->Turn;
949 Dest->Cash = Src->Cash;
950 Dest->Debt = Src->Debt;
951 Dest->Bank = Src->Bank;
952 Dest->Health = Src->Health;
953 ClearInventory(Dest->Guns, Dest->Drugs);
954 AddInventory(Dest->Guns, Src->Guns, NumGun);
955 AddInventory(Dest->Drugs, Src->Drugs, NumDrug);
956 Dest->CoatSize = Src->CoatSize;
957 Dest->IsAt = Src->IsAt;
958 g_free(Dest->Name);
959 Dest->Name = g_strdup(Src->Name);
960 Dest->Bitches.Carried = Src->Bitches.Carried;
961 Dest->Flags = Src->Flags;
962 }
963
964 gboolean IsCop(Player *Play)
965 {
966 return (Play->CopIndex > 0);
967 }
968
969 char *GetPlayerName(Player *Play)
970 {
971 if (Play->Name)
972 return Play->Name;
973 else
974 return "";
975 }
976
977 void SetPlayerName(Player *Play, char *Name)
978 {
979 if (Play->Name)
980 g_free(Play->Name);
981 if (!Name)
982 Play->Name = g_strdup("");
983 else
984 Play->Name = g_strdup(Name);
985 }
986
987 /*
988 * Searches the linked list starting at "First" for a Player structure
989 * with the given ID. Returns a pointer to this structure, or NULL if
990 * no match can be found.
991 */
992 Player *GetPlayerByID(guint ID, GSList *First)
993 {
994 GSList *list;
995 Player *Play;
996
997 for (list = First; list; list = g_slist_next(list)) {
998 Play = (Player *)list->data;
999 if (Play->ID == ID)
1000 return Play;
1001 }
1002 return NULL;
1003 }
1004
1005 /*
1006 * Searches the linked list starting at "First" for a Player structure
1007 * with the name "Name". Returns a pointer to this structure, or NULL
1008 * if no match can be found.
1009 */
1010 Player *GetPlayerByName(char *Name, GSList *First)
1011 {
1012 GSList *list;
1013 Player *Play;
1014
1015 if (Name == NULL || Name[0] == 0)
1016 return &Noone;
1017 for (list = First; list; list = g_slist_next(list)) {
1018 Play = (Player *)list->data;
1019 if (!IsCop(Play) && strcmp(GetPlayerName(Play), Name) == 0)
1020 return Play;
1021 }
1022 return NULL;
1023 }
1024
1025 /*
1026 * Forms a price based on the string representation in "buf".
1027 */
1028 price_t strtoprice(char *buf)
1029 {
1030 guint i, buflen, FracNum;
1031 gchar digit, suffix;
1032 gboolean minus, InFrac;
1033 price_t val = 0;
1034
1035 minus = FALSE;
1036 if (!buf || !buf[0])
1037 return 0;
1038
1039 buflen = strlen(buf);
1040 suffix = buf[buflen - 1];
1041 suffix = toupper(suffix);
1042 if (suffix == 'M')
1043 FracNum = 6;
1044 else if (suffix == 'K')
1045 FracNum = 3;
1046 else
1047 FracNum = 0;
1048
1049 for (i = 0, InFrac = FALSE; i < buflen && (!InFrac || FracNum > 0); i++) {
1050 digit = buf[i];
1051 if (digit == '.' || digit == ',') {
1052 InFrac = TRUE;
1053 } else if (digit >= '0' && digit <= '9') {
1054 if (InFrac)
1055 FracNum--;
1056 val *= 10;
1057 val += (digit - '0');
1058 } else if (digit == '-')
1059 minus = TRUE;
1060 }
1061
1062 for (i = 0; i < FracNum; i++)
1063 val *= 10;
1064 if (minus)
1065 val = -val;
1066 return val;
1067 }
1068
1069 /*
1070 * Prints "price" directly into a dynamically-allocated string buffer
1071 * and returns a pointer to this buffer. It is the responsbility of
1072 * the user to g_free this buffer when it is finished with.
1073 */
1074 gchar *pricetostr(price_t price)
1075 {
1076 GString *PriceStr;
1077 gchar *NewBuffer;
1078 price_t absprice;
1079
1080 if (price < 0)
1081 absprice = -price;
1082 else
1083 absprice = price;
1084 PriceStr = g_string_new(NULL);
1085 while (absprice != 0) {
1086 g_string_prepend_c(PriceStr, '0' + (absprice % 10));
1087 absprice /= 10;
1088 if (absprice == 0) {
1089 if (price < 0)
1090 g_string_prepend_c(PriceStr, '-');
1091 }
1092 }
1093 NewBuffer = PriceStr->str;
1094 /* Free the string structure, but not the actual char array */
1095 g_string_free(PriceStr, FALSE);
1096 return NewBuffer;
1097 }
1098
1099 /*
1100 * Takes the number in "price" and prints it into a dynamically-allocated
1101 * string, adding commas to split up thousands, and adding a currency
1102 * symbol to the start. Returns a pointer to the string, which must be
1103 * g_free'd by the user when it is finished with.
1104 */
1105 gchar *FormatPrice(price_t price)
1106 {
1107 GString *PriceStr;
1108 gchar *NewBuffer;
1109 char thou[10];
1110 gboolean First = TRUE;
1111 price_t absprice;
1112
1113 PriceStr = g_string_new(NULL);
1114 if (price < 0)
1115 absprice = -price;
1116 else
1117 absprice = price;
1118 while (First || absprice > 0) {
1119 if (absprice >= 1000)
1120 sprintf(thou, "%03d", (int)(absprice % 1000l));
1121 else
1122 sprintf(thou, "%d", (int)(price % 1000l));
1123 price /= 1000l;
1124 absprice /= 1000l;
1125 if (!First)
1126 g_string_prepend_c(PriceStr, ',');
1127 g_string_prepend(PriceStr, thou);
1128 First = FALSE;
1129 }
1130 if (Currency.Prefix)
1131 g_string_prepend(PriceStr, Currency.Symbol);
1132 else
1133 g_string_append(PriceStr, Currency.Symbol);
1134
1135 NewBuffer = PriceStr->str;
1136 /* Free the string structure only, not the char data */
1137 g_string_free(PriceStr, FALSE);
1138 return NewBuffer;
1139 }
1140
1141 /*
1142 * Returns the total number of guns being carried by "Play".
1143 */
1144 int TotalGunsCarried(Player *Play)
1145 {
1146 int i, c;
1147
1148 c = 0;
1149 for (i = 0; i < NumGun; i++)
1150 c += Play->Guns[i].Carried;
1151 return c;
1152 }
1153
1154 /*
1155 * Capitalises the first character of "string" and writes the resultant
1156 * string into a dynamically-allocated copy; the user must g_free this
1157 * string (a pointer to which is returned) when it is no longer needed.
1158 */
1159 gchar *InitialCaps(gchar *string)
1160 {
1161 gchar *buf;
1162
1163 if (!string)
1164 return NULL;
1165 buf = g_strdup(string);
1166 if (strlen(buf) >= 1)
1167 buf[0] = toupper(buf[0]);
1168 return buf;
1169 }
1170
1171 /*
1172 * Returns TRUE if "string" starts with a vowel.
1173 */
1174 char StartsWithVowel(char *string)
1175 {
1176 int c;
1177
1178 if (!string || strlen(string) < 1)
1179 return FALSE;
1180 c = toupper(string[0]);
1181 return (c == 'A' || c == 'E' || c == 'I' || c == 'O' || c == 'U');
1182 }
1183
1184 /*
1185 * Reads a NULL-terminated string into the buffer "buf" from file "fp".
1186 * buf is sized to hold the string; this is a dynamic string and must be
1187 * freed by the calling routine. Returns 0 on success, EOF on failure.
1188 */
1189 int read_string(FILE *fp, char **buf)
1190 {
1191 int c;
1192 GString *text;
1193
1194 text = g_string_new("");
1195 do {
1196 c = fgetc(fp);
1197 if (c != EOF && c != 0)
1198 g_string_append_c(text, (char)c);
1199 } while (c != EOF && c != 0);
1200
1201 *buf = text->str;
1202
1203 /* Free the GString, but not the actual data text->str */
1204 g_string_free(text, FALSE);
1205 if (c == EOF)
1206 return EOF;
1207 else
1208 return 0;
1209 }
1210
1211 /*
1212 * This function simply clears the given inventories "Guns"
1213 * and "Drugs" if they are non-NULL.
1214 */
1215 void ClearInventory(Inventory *Guns, Inventory *Drugs)
1216 {
1217 int i;
1218
1219 if (Guns)
1220 for (i = 0; i < NumGun; i++) {
1221 Guns[i].Carried = 0;
1222 Guns[i].TotalValue = 0;
1223 }
1224 if (Drugs)
1225 for (i = 0; i < NumDrug; i++) {
1226 Drugs[i].Carried = 0;
1227 Drugs[i].TotalValue = 0;
1228 }
1229 }
1230
1231 /*
1232 * Returns TRUE only if "Guns" and "Drugs" contain no objects.
1233 */
1234 char IsInventoryClear(Inventory *Guns, Inventory *Drugs)
1235 {
1236 int i;
1237
1238 if (Guns)
1239 for (i = 0; i < NumGun; i++)
1240 if (Guns[i].Carried > 0)
1241 return FALSE;
1242 if (Drugs)
1243 for (i = 0; i < NumDrug; i++)
1244 if (Drugs[i].Carried > 0)
1245 return FALSE;
1246 return TRUE;
1247 }
1248
1249 /*
1250 * Adds inventory "Add" into the contents of inventory "Cumul"
1251 * Each inventory is of length "Length".
1252 * N.B. TotalValue is not modified, as it is assumed that the
1253 * new items are free (if this is not the case it must be
1254 * handled elsewhere).
1255 */
1256 void AddInventory(Inventory *Cumul, Inventory *Add, int Length)
1257 {
1258 int i;
1259
1260 for (i = 0; i < Length; i++)
1261 Cumul[i].Carried += Add[i].Carried;
1262 }
1263
1264 /*
1265 * Given the lists of "Guns" and "Drugs" (which the given player "Play"
1266 * must have sufficient room to carry) updates the player's space to
1267 * reflect carrying them.
1268 */
1269 void ChangeSpaceForInventory(Inventory *Guns, Inventory *Drugs,
1270 Player *Play)
1271 {
1272 int i;
1273
1274 if (Guns)
1275 for (i = 0; i < NumGun; i++) {
1276 Play->CoatSize -= Guns[i].Carried * Gun[i].Space;
1277 }
1278 if (Drugs)
1279 for (i = 0; i < NumDrug; i++) {
1280 Play->CoatSize -= Drugs[i].Carried;
1281 }
1282 }
1283
1284 /*
1285 * Discards items from "Guns" and/or "Drugs" (if non-NULL) if necessary
1286 * such that player "Play" is able to carry them all. The cheapest
1287 * objects are discarded.
1288 */
1289 void TruncateInventoryFor(Inventory *Guns, Inventory *Drugs, Player *Play)
1290 {
1291 int i, Total, CheapIndex;
1292 int CheapestGun;
1293
1294 Total = 0;
1295 if (Guns)
1296 for (i = 0; i < NumGun; i++)
1297 Total += Guns[i].Carried;
1298 Total += TotalGunsCarried(Play);
1299 while (Guns && Total > Play->Bitches.Carried + 2) {
1300 CheapIndex = -1;
1301 for (i = 0; i < NumGun; i++)
1302 if (Guns[i].Carried && (CheapIndex == -1
1303 || Gun[i].Price <= Gun[CheapIndex].Price)) {
1304 CheapIndex = i;
1305 }
1306 i = Total - Play->Bitches.Carried - 2;
1307 if (Guns[CheapIndex].Carried > i) {
1308 Guns[CheapIndex].Carried -= i;
1309 Total -= i;
1310 } else {
1311 Total -= Guns[CheapIndex].Carried;
1312 Guns[CheapIndex].Carried = 0;
1313 }
1314 }
1315
1316 Total = Play->CoatSize;
1317 if (Guns)
1318 for (i = 0; i < NumGun; i++)
1319 Total -= Guns[i].Carried * Gun[i].Space;
1320 if (Drugs)
1321 for (i = 0; i < NumDrug; i++)
1322 Total -= Drugs[i].Carried;
1323 while (Total < 0) {
1324 CheapestGun = -1;
1325 CheapIndex = -1;
1326 if (Guns)
1327 for (i = 0; i < NumGun; i++)
1328 if (Guns[i].Carried && (CheapIndex == -1
1329 || Gun[i].Price <= Gun[CheapIndex].Price)) {
1330 CheapIndex = i;
1331 CheapestGun = Gun[i].Price / Gun[i].Space;
1332 }
1333 if (Drugs)
1334 for (i = 0; i < NumDrug; i++)
1335 if (Drugs[i].Carried &&
1336 (CheapIndex == -1 ||
1337 (CheapestGun == -1
1338 && Drug[i].MinPrice <= Drug[CheapIndex].MinPrice)
1339 || (CheapestGun >= 0 && Drug[i].MinPrice <= CheapestGun))) {
1340 CheapIndex = i;
1341 CheapestGun = -1;
1342 }
1343 if (Guns && CheapestGun >= 0) {
1344 Guns[CheapIndex].Carried--;
1345 Total += Gun[CheapIndex].Space;
1346 } else {
1347 if (Drugs && Drugs[CheapIndex].Carried >= -Total) {
1348 Drugs[CheapIndex].TotalValue =
1349 Drugs[CheapIndex].TotalValue *
1350 (Drugs[CheapIndex].Carried + Total) /
1351 Drugs[CheapIndex].Carried;
1352 Drugs[CheapIndex].Carried += Total;
1353 Total = 0;
1354 } else {
1355 Total += Drugs[CheapIndex].Carried;
1356 Drugs[CheapIndex].Carried = 0;
1357 Drugs[CheapIndex].TotalValue = 0;
1358 }
1359 }
1360 }
1361 }
1362
1363 /*
1364 * Returns an index into the drugs array of a random drug that "Play" is
1365 * carrying at least "amount" of. If no suitable drug is found after 5
1366 * attempts, returns -1.
1367 */
1368 int IsCarryingRandom(Player *Play, int amount)
1369 {
1370 int i, ind;
1371
1372 for (i = 0; i < 5; i++) {
1373 ind = brandom(0, NumDrug);
1374 if (Play->Drugs[ind].Carried >= amount) {
1375 return ind;
1376 }
1377 }
1378 return -1;
1379 }
1380
1381 /*
1382 * Returns an index into the "Drugs" array maintained by player "Play"
1383 * of the next available drug after "OldIndex", following the current
1384 * sort method (defined globally as "DrugSortMethod").
1385 */
1386 int GetNextDrugIndex(int OldIndex, Player *Play)
1387 {
1388 int i, MaxIndex;
1389
1390 MaxIndex = -1;
1391 for (i = 0; i < NumDrug; i++) {
1392 if (Play->Drugs[i].Price != 0 && i != OldIndex && i != MaxIndex &&
1393 (MaxIndex == -1
1394 || (DrugSortMethod == DS_ATOZ
1395 && g_ascii_strncasecmp(Drug[MaxIndex].Name, Drug[i].Name, strlen(Drug[i].Name)) > 0)
1396 || (DrugSortMethod == DS_ZTOA
1397 && g_ascii_strncasecmp(Drug[MaxIndex].Name, Drug[i].Name, strlen(Drug[i].Name)) < 0)
1398 || (DrugSortMethod == DS_CHEAPFIRST
1399 && Play->Drugs[MaxIndex].Price > Play->Drugs[i].Price)
1400 || (DrugSortMethod == DS_CHEAPLAST
1401 && Play->Drugs[MaxIndex].Price < Play->Drugs[i].Price)) &&
1402 (OldIndex == -1
1403 || (DrugSortMethod == DS_ATOZ
1404 && g_ascii_strncasecmp(Drug[OldIndex].Name, Drug[i].Name, strlen(Drug[i].Name)) <= 0)
1405 || (DrugSortMethod == DS_ZTOA
1406 && g_ascii_strncasecmp(Drug[OldIndex].Name, Drug[i].Name, strlen(Drug[i].Name)) >= 0)
1407 || (DrugSortMethod == DS_CHEAPFIRST
1408 && Play->Drugs[OldIndex].Price <= Play->Drugs[i].Price)
1409 || (DrugSortMethod == DS_CHEAPLAST
1410 && Play->Drugs[OldIndex].Price >= Play->Drugs[i].Price))) {
1411 MaxIndex = i;
1412 }
1413 }
1414 return MaxIndex;
1415 }
1416
1417 /*
1418 * A DopeList is akin to a Vector class; it is a list of DopeEntry
1419 * structures, which can be dynamically extended or compressed. This
1420 * function initializes the newly-created list pointed to by "List"
1421 * (A DopeEntry contains a Player pointer and a counter, and is used
1422 * by the server to keep track of tipoffs and spies.)
1423 */
1424 void InitList(DopeList *List)
1425 {
1426 List->Data = NULL;
1427 List->Number = 0;
1428 }
1429
1430 /*
1431 * Clears the list pointed to by "List".
1432 */
1433 void ClearList(DopeList *List)
1434 {
1435 g_free(List->Data);
1436 InitList(List);
1437 }
1438
1439 /*
1440 * Adds a new DopeEntry (pointed to by "NewEntry") to the list "List".
1441 * A copy of NewEntry is placed into the list, so the original
1442 * structure pointed to by NewEntry can be reused.
1443 */
1444 void AddListEntry(DopeList *List, DopeEntry *NewEntry)
1445 {
1446 if (!NewEntry || !List)
1447 return;
1448 List->Number++;
1449 List->Data = (DopeEntry *)g_realloc(List->Data, List->Number *
1450 sizeof(DopeEntry));
1451 memmove(&(List->Data[List->Number - 1]), NewEntry, sizeof(DopeEntry));
1452 }
1453
1454 /*
1455 * Removes the DopeEntry at index "Index" from list "List".
1456 */
1457 void RemoveListEntry(DopeList *List, int Index)
1458 {
1459 if (!List || Index < 0 || Index >= List->Number)
1460 return;
1461
1462 if (Index < List->Number - 1) {
1463 memmove(&(List->Data[Index]), &(List->Data[Index + 1]),
1464 (List->Number - 1 - Index) * sizeof(DopeEntry));
1465 }
1466 List->Number--;
1467 List->Data = (DopeEntry *)g_realloc(List->Data, List->Number *
1468 sizeof(DopeEntry));
1469 if (List->Number == 0)
1470 List->Data = NULL;
1471 }
1472
1473 /*
1474 * Returns the index of the DopeEntry matching "Play" in list "List"
1475 * or -1 if this is not found.
1476 */
1477 int GetListEntry(DopeList *List, Player *Play)
1478 {
1479 int i;
1480
1481 for (i = List->Number - 1; i >= 0; i--) {
1482 if (List->Data[i].Play == Play)
1483 return i;
1484 }
1485 return -1;
1486 }
1487
1488 /*
1489 * Removes (if it exists) the DopeEntry in list "List" matching "Play".
1490 */
1491 void RemoveListPlayer(DopeList *List, Player *Play)
1492 {
1493 RemoveListEntry(List, GetListEntry(List, Play));
1494 }
1495
1496 /*
1497 * Similar to RemoveListPlayer, except that if the list contains "Play" more
1498 * than once, all the matching entries are removed, not just the first.
1499 */
1500 void RemoveAllEntries(DopeList *List, Player *Play)
1501 {
1502 int i;
1503
1504 do {
1505 i = GetListEntry(List, Play);
1506 if (i >= 0)
1507 RemoveListEntry(List, i);
1508 } while (i >= 0);
1509 }
1510
1511 void ResizeLocations(int NewNum)
1512 {
1513 int i;
1514
1515 if (NewNum < NumLocation)
1516 for (i = NewNum; i < NumLocation; i++) {
1517 g_free(Location[i].Name);
1518 }
1519 Location = g_realloc(Location, sizeof(struct LOCATION) * NewNum);
1520 if (NewNum > NumLocation) {
1521 memset(&Location[NumLocation], 0,
1522 (NewNum - NumLocation) * sizeof(struct LOCATION));
1523 for (i = NumLocation; i < NewNum; i++) {
1524 Location[i].Name = g_strdup("");
1525 }
1526 }
1527 NumLocation = NewNum;
1528 }
1529
1530 void ResizeCops(int NewNum)
1531 {
1532 int i;
1533
1534 if (NewNum < NumCop)
1535 for (i = NewNum; i < NumCop; i++) {
1536 g_free(Cop[i].Name);
1537 g_free(Cop[i].DeputyName);
1538 g_free(Cop[i].DeputiesName);
1539 }
1540 Cop = g_realloc(Cop, sizeof(struct COP) * NewNum);
1541 if (NewNum > NumCop) {
1542 memset(&Cop[NumCop], 0, (NewNum - NumCop) * sizeof(struct COP));
1543 for (i = NumCop; i < NewNum; i++) {
1544 Cop[i].Name = g_strdup("");
1545 Cop[i].DeputyName = g_strdup("");
1546 Cop[i].DeputiesName = g_strdup("");
1547 }
1548 }
1549 NumCop = NewNum;
1550 }
1551
1552 void ResizeGuns(int NewNum)
1553 {
1554 int i;
1555
1556 if (NewNum < NumGun)
1557 for (i = NewNum; i < NumGun; i++) {
1558 g_free(Gun[i].Name);
1559 }
1560 Gun = g_realloc(Gun, sizeof(struct GUN) * NewNum);
1561 if (NewNum > NumGun) {
1562 memset(&Gun[NumGun], 0, (NewNum - NumGun) * sizeof(struct GUN));
1563 for (i = NumGun; i < NewNum; i++) {
1564 Gun[i].Name = g_strdup("");
1565 }
1566 }
1567 NumGun = NewNum;
1568 }
1569
1570 void ResizeDrugs(int NewNum)
1571 {
1572 int i;
1573
1574 if (NewNum < NumDrug)
1575 for (i = NewNum; i < NumDrug; i++) {
1576 g_free(Drug[i].Name);
1577 g_free(Drug[i].CheapStr);
1578 }
1579 Drug = g_realloc(Drug, sizeof(struct DRUG) * NewNum);
1580 if (NewNum > NumDrug) {
1581 memset(&Drug[NumDrug], 0, (NewNum - NumDrug) * sizeof(struct DRUG));
1582 for (i = NumDrug; i < NewNum; i++) {
1583 Drug[i].Name = g_strdup("");
1584 Drug[i].CheapStr = g_strdup("");
1585 }
1586 }
1587 NumDrug = NewNum;
1588 }
1589
1590 void ResizeSubway(int NewNum)
1591 {
1592 int i;
1593
1594 if (NewNum < NumSubway)
1595 for (i = NewNum; i < NumSubway; i++) {
1596 g_free(SubwaySaying[i]);
1597 }
1598 SubwaySaying = g_realloc(SubwaySaying, sizeof(char *) * NewNum);
1599 if (NewNum > NumSubway)
1600 for (i = NumSubway; i < NewNum; i++) {
1601 SubwaySaying[i] = g_strdup("");
1602 }
1603 NumSubway = NewNum;
1604 }
1605
1606 void ResizePlaying(int NewNum)
1607 {
1608 int i;
1609
1610 if (NewNum < NumPlaying)
1611 for (i = NewNum; i < NumPlaying; i++) {
1612 g_free(Playing[i]);
1613 }
1614 Playing = g_realloc(Playing, sizeof(char *) * NewNum);
1615 if (NewNum > NumPlaying)
1616 for (i = NumPlaying; i < NewNum; i++) {
1617 Playing[i] = g_strdup("");
1618 }
1619 NumPlaying = NewNum;
1620 }
1621
1622 void ResizeStoppedTo(int NewNum)
1623 {
1624 int i;
1625
1626 if (NewNum < NumStoppedTo)
1627 for (i = NewNum; i < NumStoppedTo; i++) {
1628 g_free(StoppedTo[i]);
1629 }
1630 StoppedTo = g_realloc(StoppedTo, sizeof(char *) * NewNum);
1631 if (NewNum > NumStoppedTo)
1632 for (i = NumStoppedTo; i < NewNum; i++) {
1633 StoppedTo[i] = g_strdup("");
1634 }
1635 NumStoppedTo = NewNum;
1636 }
1637
1638 /*
1639 * Sets the dynamically-sized string pointed to by *dest to a copy of
1640 * "src" - src can safely be freed or reused afterwards. Any existing
1641 * string in "dest" is freed. The function returns immediately if src
1642 * and *dest are already the same.
1643 */
1644 void AssignName(gchar **dest, gchar *src)
1645 {
1646 if (*dest == src)
1647 return;
1648 g_free(*dest);
1649 *dest = g_strdup(src);
1650 }
1651
1652 void CopyNames(struct NAMES *dest, struct NAMES *src)
1653 {
1654 AssignName(&dest->Bitch, _(src->Bitch));
1655 AssignName(&dest->Bitches, _(src->Bitches));
1656 AssignName(&dest->Gun, _(src->Gun));
1657 AssignName(&dest->Guns, _(src->Guns));
1658 AssignName(&dest->Drug, _(src->Drug));
1659 AssignName(&dest->Drugs, _(src->Drugs));
1660 AssignName(&dest->Date, _(src->Date));
1661 AssignName(&dest->LoanSharkName, _(src->LoanSharkName));
1662 AssignName(&dest->BankName, _(src->BankName));
1663 AssignName(&dest->GunShopName, _(src->GunShopName));
1664 AssignName(&dest->RoughPubName, _(src->RoughPubName));
1665 }
1666
1667 #ifdef NETWORKING
1668 void CopyMetaServer(struct METASERVER *dest, struct METASERVER *src)
1669 {
1670 dest->Active = src->Active;
1671 AssignName(&dest->URL, src->URL);
1672 AssignName(&dest->LocalName, src->LocalName);
1673 AssignName(&dest->Password, src->Password);
1674 AssignName(&dest->Comment, src->Comment);
1675 }
1676 #endif
1677
1678 void CopyLocation(struct LOCATION *dest, struct LOCATION *src)
1679 {
1680 AssignName(&dest->Name, _(src->Name));
1681 dest->PolicePresence = src->PolicePresence;
1682 dest->MinDrug = src->MinDrug;
1683 dest->MaxDrug = src->MaxDrug;
1684 }
1685
1686 void CopyCop(struct COP *dest, struct COP *src)
1687 {
1688 AssignName(&dest->Name, _(src->Name));
1689 AssignName(&dest->DeputyName, _(src->DeputyName));
1690 AssignName(&dest->DeputiesName, _(src->DeputiesName));
1691 dest->Armor = src->Armor;
1692 dest->DeputyArmor = src->DeputyArmor;
1693 dest->AttackPenalty = src->AttackPenalty;
1694 dest->DefendPenalty = src->DefendPenalty;
1695 dest->MinDeputies = src->MinDeputies;
1696 dest->MaxDeputies = src->MaxDeputies;
1697 dest->GunIndex = src->GunIndex;
1698 dest->CopGun = src->CopGun;
1699 dest->DeputyGun = src->DeputyGun;
1700 }
1701
1702 void CopyGun(struct GUN *dest, struct GUN *src)
1703 {
1704 AssignName(&dest->Name, _(src->Name));
1705 dest->Price = src->Price;
1706 dest->Space = src->Space;
1707 dest->Damage = src->Damage;
1708 }
1709
1710 void CopyDrug(struct DRUG *dest, struct DRUG *src)
1711 {
1712 AssignName(&dest->Name, src->Name[0] ? _(src->Name) : "");
1713 dest->MinPrice = src->MinPrice;
1714 dest->MaxPrice = src->MaxPrice;
1715 dest->Cheap = src->Cheap;
1716 dest->Expensive = src->Expensive;
1717 AssignName(&dest->CheapStr, src->CheapStr[0] ? _(src->CheapStr) : "");
1718 }
1719
1720 void CopyDrugs(struct DRUGS *dest, struct DRUGS *src)
1721 {
1722 AssignName(&dest->ExpensiveStr1, _(src->ExpensiveStr1));
1723 AssignName(&dest->ExpensiveStr2, _(src->ExpensiveStr2));
1724 dest->CheapDivide = src->CheapDivide;
1725 dest->ExpensiveMultiply = src->ExpensiveMultiply;
1726 }
1727
1728 static struct PRICES BackupPrices;
1729 static struct NAMES BackupNames;
1730 static struct DRUG *BackupDrug = NULL;
1731 static struct GUN *BackupGun = NULL;
1732 static struct LOCATION *BackupLocation = NULL;
1733 static struct CURRENCY BackupCurrency = { NULL, TRUE };
1734 static gint NumBackupDrug = 0, NumBackupGun = 0, NumBackupLocation = 0;
1735
1736 void BackupConfig(void)
1737 {
1738 gint i;
1739
1740 BackupPrices.Spy = Prices.Spy;
1741 BackupPrices.Tipoff = Prices.Tipoff;
1742 AssignName(&BackupCurrency.Symbol, Currency.Symbol);
1743 BackupCurrency.Prefix = Currency.Prefix;
1744 CopyNames(&BackupNames, &Names);
1745
1746 /* Free existing backups of guns, drugs, and locations */
1747 for (i = 0; i < NumBackupGun; i++)
1748 g_free(BackupGun[i].Name);
1749 g_free(BackupGun);
1750 for (i = 0; i < NumBackupDrug; i++) {
1751 g_free(BackupDrug[i].Name);
1752 g_free(BackupDrug[i].CheapStr);
1753 }
1754 g_free(BackupDrug);
1755 for (i = 0; i < NumBackupLocation; i++)
1756 g_free(BackupLocation[i].Name);
1757 g_free(BackupLocation);
1758
1759 NumBackupGun = NumGun;
1760 BackupGun = g_new0(struct GUN, NumGun);
1761
1762 for (i = 0; i < NumGun; i++)
1763 CopyGun(&BackupGun[i], &Gun[i]);
1764
1765 NumBackupDrug = NumDrug;
1766 BackupDrug = g_new0(struct DRUG, NumDrug);
1767
1768 for (i = 0; i < NumDrug; i++)
1769 CopyDrug(&BackupDrug[i], &Drug[i]);
1770
1771 NumBackupLocation = NumLocation;
1772 BackupLocation = g_new0(struct LOCATION, NumLocation);
1773
1774 for (i = 0; i < NumLocation; i++)
1775 CopyLocation(&BackupLocation[i], &Location[i]);
1776 }
1777
1778 void RestoreConfig(void)
1779 {
1780 gint i;
1781
1782 Prices.Spy = BackupPrices.Spy;
1783 Prices.Tipoff = BackupPrices.Tipoff;
1784 CopyNames(&Names, &BackupNames);
1785 AssignName(&Currency.Symbol, BackupCurrency.Symbol);
1786 Currency.Prefix = BackupCurrency.Prefix;
1787
1788 ResizeGuns(NumBackupGun);
1789 for (i = 0; i < NumGun; i++)
1790 CopyGun(&Gun[i], &BackupGun[i]);
1791 ResizeDrugs(NumBackupDrug);
1792 for (i = 0; i < NumDrug; i++)
1793 CopyDrug(&Drug[i], &BackupDrug[i]);
1794 ResizeLocations(NumBackupLocation);
1795 for (i = 0; i < NumLocation; i++)
1796 CopyLocation(&Location[i], &BackupLocation[i]);
1797 }
1798
1799 void ScannerErrorHandler(GScanner *scanner, gchar *msg, gint error)
1800 {
1801 g_print("%s\n", msg);
1802 }
1803
1804 /*
1805 * On Windows systems, check the current config file referenced by "scanner"
1806 * for a UTF-8 header. If one is found, "conv" and "encoding" are set
1807 * for UTF-8 encoding.
1808 */
1809 static void CheckConfigHeader(GScanner *scanner, Converter *conv,
1810 gchar **encoding)
1811 {
1812 #ifdef CYGWIN
1813 GTokenType token;
1814
1815 token = g_scanner_peek_next_token(scanner);
1816 if (token == (guchar)'\357') {
1817 /* OK; assume this is a Windows-style \357 \273 \277 UTF-8 header */
1818 if (g_scanner_get_next_token(scanner) != (guchar)'\357'
1819 || g_scanner_get_next_token(scanner) != (guchar)'\273'
1820 || g_scanner_get_next_token(scanner) != (guchar)'\277') {
1821 return;
1822 }
1823 Conv_SetCodeset(conv, "UTF-8");
1824 if (encoding) {
1825 g_free(*encoding);
1826 *encoding = g_strdup("UTF-8");
1827 }
1828 }
1829 #endif
1830 }
1831
1832 /*
1833 * Read a configuration file given by "FileName"
1834 */
1835 static gboolean ReadConfigFile(char *FileName, gchar **encoding)
1836 {
1837 FILE *fp;
1838 Converter *conv;
1839
1840 GScanner *scanner;
1841
1842 fp = fopen(FileName, "r");
1843 if (fp) {
1844 conv = Conv_New();
1845 if (encoding) {
1846 *encoding = NULL;
1847 }
1848 scanner = g_scanner_new(&ScannerConfig);
1849 scanner->input_name = FileName;
1850 scanner->msg_handler = ScannerErrorHandler;
1851 g_scanner_input_file(scanner, fileno(fp));
1852 CheckConfigHeader(scanner, conv, encoding);
1853 while (!g_scanner_eof(scanner)) {
1854 if (!ParseNextConfig(scanner, conv, encoding, FALSE)) {
1855 ConfigErrors++;
1856 g_scanner_error(scanner,
1857 _("Unable to process configuration file %s, line %d"),
1858 FileName, g_scanner_cur_line(scanner));
1859 }
1860 }
1861 g_scanner_destroy(scanner);
1862 Conv_Free(conv);
1863 fclose(fp);
1864 return TRUE;
1865 } else {
1866 return FALSE;
1867 }
1868 }
1869
1870 gboolean ParseNextConfig(GScanner *scanner, Converter *conv,
1871 gchar **encoding, gboolean print)
1872 {
1873 GTokenType token;
1874 gchar *ID1, *ID2;
1875 gulong ind = 0;
1876 int GlobalIndex;
1877 gboolean IndexGiven = FALSE;
1878
1879 ID1 = ID2 = NULL;
1880 token = g_scanner_get_next_token(scanner);
1881 if (token == G_TOKEN_EOF)
1882 return TRUE;
1883 if (token != G_TOKEN_IDENTIFIER) {
1884 g_scanner_unexp_token(scanner, G_TOKEN_IDENTIFIER, NULL, NULL,
1885 NULL, NULL, FALSE);
1886 return FALSE;
1887 }
1888
1889 if (g_ascii_strncasecmp(scanner->value.v_identifier, "include", 7) == 0) {
1890 token = g_scanner_get_next_token(scanner);
1891 if (token == G_TOKEN_STRING) {
1892 if (!ReadConfigFile(scanner->value.v_string, NULL)) {
1893 g_scanner_error(scanner, _("Unable to open file %s"),
1894 scanner->value.v_string);
1895 }
1896 return TRUE;
1897 } else {
1898 g_scanner_unexp_token(scanner, G_TOKEN_STRING, NULL, NULL,
1899 NULL, NULL, FALSE);
1900 return FALSE;
1901 }
1902 } else if (g_ascii_strncasecmp(scanner->value.v_identifier, "encoding", 8) == 0) {
1903 token = g_scanner_get_next_token(scanner);
1904 if (token == G_TOKEN_STRING) {
1905 Conv_SetCodeset(conv, scanner->value.v_string);
1906 if (encoding) {
1907 g_free(*encoding);
1908 *encoding = g_strdup(scanner->value.v_string);
1909 }
1910 return TRUE;
1911 } else {
1912 g_scanner_unexp_token(scanner, G_TOKEN_STRING, NULL, NULL,
1913 NULL, NULL, FALSE);
1914 return FALSE;
1915 }
1916 }
1917
1918 ID1 = g_strdup(scanner->value.v_identifier);
1919 token = g_scanner_get_next_token(scanner);
1920 if (token == G_TOKEN_LEFT_BRACE) {
1921 token = g_scanner_get_next_token(scanner);
1922 if (token != G_TOKEN_INT) {
1923 g_scanner_unexp_token(scanner, G_TOKEN_INT, NULL, NULL,
1924 NULL, NULL, FALSE);
1925 return FALSE;
1926 }
1927 ind = scanner->value.v_int;
1928 IndexGiven = TRUE;
1929 token = g_scanner_get_next_token(scanner);
1930 if (token != G_TOKEN_RIGHT_BRACE) {
1931 g_scanner_unexp_token(scanner, G_TOKEN_RIGHT_BRACE, NULL, NULL,
1932 NULL, NULL, FALSE);
1933 return FALSE;
1934 }
1935 token = g_scanner_get_next_token(scanner);
1936 if (token == '.') {
1937 token = g_scanner_get_next_token(scanner);
1938 if (token != G_TOKEN_IDENTIFIER) {
1939 g_scanner_unexp_token(scanner, G_TOKEN_IDENTIFIER, NULL, NULL,
1940 NULL, NULL, FALSE);
1941 return FALSE;
1942 }
1943 ID2 = g_strdup(scanner->value.v_identifier);
1944 token = g_scanner_get_next_token(scanner);
1945 }
1946 }
1947 GlobalIndex = GetGlobalIndex(ID1, ID2);
1948 g_free(ID1);
1949 g_free(ID2);
1950 if (GlobalIndex == -1)
1951 return FALSE;
1952 if (token == G_TOKEN_EOF) {
1953 PrintConfigValue(GlobalIndex, (int)ind, IndexGiven, scanner);
1954 return TRUE;
1955 } else if (token == G_TOKEN_EQUAL_SIGN) {
1956 if (CountPlayers(FirstServer) > 0) {
1957 g_warning(_("Configuration can only be changed interactively "
1958 "when no\nplayers are logged on. Wait for all "
1959 "players to log off, or remove\nthem with the "
1960 "push or kill commands, and try again."));
1961 } else {
1962 if (SetConfigValue(GlobalIndex, (int)ind, IndexGiven, conv, scanner)
1963 && print) {
1964 PrintConfigValue(GlobalIndex, (int)ind, IndexGiven, scanner);
1965 }
1966 }
1967 return TRUE;
1968 } else {
1969 return FALSE;
1970 }
1971 return FALSE;
1972 }
1973
1974 int GetGlobalIndex(gchar *ID1, gchar *ID2)
1975 {
1976 int i;
1977 const int NumGlob = sizeof(Globals) / sizeof(Globals[0]);
1978
1979 if (!ID1)
1980 return -1;
1981 for (i = 0; i < NumGlob; i++) {
1982 if (!ID2 && !Globals[i].NameStruct[0]
1983 && g_ascii_strcasecmp(ID1, Globals[i].Name) == 0) {
1984 /* Just a bog-standard ID1=value */
1985 return i;
1986 }
1987 if (g_ascii_strcasecmp(ID1, Globals[i].NameStruct) == 0 && ID2
1988 && g_ascii_strcasecmp(ID2, Globals[i].Name) == 0
1989 && Globals[i].StructStaticPt && Globals[i].StructListPt) {
1990 /* ID1[index].ID2=value */
1991 return i;
1992 }
1993 }
1994 return -1;
1995 }
1996
1997 static void *GetGlobalPointer(int GlobalIndex, int StructIndex)
1998 {
1999 void *ValPt = NULL;
2000
2001 if (Globals[GlobalIndex].IntVal) {
2002 ValPt = (void *)Globals[GlobalIndex].IntVal;
2003 } else if (Globals[GlobalIndex].PriceVal) {
2004 ValPt = (void *)Globals[GlobalIndex].PriceVal;
2005 } else if (Globals[GlobalIndex].BoolVal) {
2006 ValPt = (void *)Globals[GlobalIndex].BoolVal;
2007 } else if (Globals[GlobalIndex].StringVal) {
2008 ValPt = (void *)Globals[GlobalIndex].StringVal;
2009 }
2010 if (!ValPt)
2011 return NULL;
2012
2013 if (Globals[GlobalIndex].StructStaticPt &&
2014 Globals[GlobalIndex].StructListPt) {
2015 return (char *)ValPt - (char *)Globals[GlobalIndex].StructStaticPt +
2016 (char *)*(Globals[GlobalIndex].StructListPt) +
2017 (StructIndex - 1) * Globals[GlobalIndex].LenStruct;
2018 } else {
2019 return ValPt;
2020 }
2021 }
2022
2023 gchar **GetGlobalString(int GlobalIndex, int StructIndex)
2024 {
2025 g_assert(Globals[GlobalIndex].StringVal);
2026
2027 return (gchar **)GetGlobalPointer(GlobalIndex, StructIndex);
2028 }
2029
2030 gint *GetGlobalInt(int GlobalIndex, int StructIndex)
2031 {
2032 g_assert(Globals[GlobalIndex].IntVal);
2033
2034 return (gint *)GetGlobalPointer(GlobalIndex, StructIndex);
2035 }
2036
2037 price_t *GetGlobalPrice(int GlobalIndex, int StructIndex)
2038 {
2039 g_assert(Globals[GlobalIndex].PriceVal);
2040
2041 return (price_t *)GetGlobalPointer(GlobalIndex, StructIndex);
2042 }
2043
2044 gboolean *GetGlobalBoolean(int GlobalIndex, int StructIndex)
2045 {
2046 g_assert(Globals[GlobalIndex].BoolVal);
2047
2048 return (gboolean *)GetGlobalPointer(GlobalIndex, StructIndex);
2049 }
2050
2051 gchar ***GetGlobalStringList(int GlobalIndex, int StructIndex)
2052 {
2053 g_assert(Globals[GlobalIndex].StringList);
2054
2055 return (gchar ***)GetGlobalPointer(GlobalIndex, StructIndex);
2056 }
2057
2058 gboolean CheckMaxIndex(GScanner *scanner, int GlobalIndex, int StructIndex,
2059 gboolean IndexGiven)
2060 {
2061 if (!Globals[GlobalIndex].MaxIndex ||
2062 (Globals[GlobalIndex].StringList && !IndexGiven) ||
2063 (IndexGiven && StructIndex >= 1 &&
2064 StructIndex <= *(Globals[GlobalIndex].MaxIndex))) {
2065 return TRUE;
2066 }
2067 /* Error message displayed when you try to set, for example,
2068 * Drug[10].Name when NumDrug<10 (%s="Drug" and %d=10 in this example) */
2069 g_scanner_error(scanner,
2070 _("Index into %s array should be between 1 and %d"),
2071 (Globals[GlobalIndex].NameStruct
2072 && Globals[GlobalIndex].
2073 NameStruct[0]) ? Globals[GlobalIndex].
2074 NameStruct : Globals[GlobalIndex].Name,
2075 *(Globals[GlobalIndex].MaxIndex));
2076 return FALSE;
2077 }
2078
2079 void PrintConfigValue(int GlobalIndex, int StructIndex,
2080 gboolean IndexGiven, GScanner *scanner)
2081 {
2082 gchar *GlobalName;
2083 int i;
2084
2085 if (!CheckMaxIndex(scanner, GlobalIndex, StructIndex, IndexGiven))
2086 return;
2087 if (Globals[GlobalIndex].NameStruct[0]) {
2088 GlobalName =
2089 g_strdup_printf("%s[%d].%s", Globals[GlobalIndex].NameStruct,
2090 StructIndex, Globals[GlobalIndex].Name);
2091 } else
2092 GlobalName = Globals[GlobalIndex].Name;
2093 if (Globals[GlobalIndex].IntVal) {
2094 /* Display of a numeric config. file variable - e.g. "NumDrug is 6" */
2095 g_print(_("%s is %d\n"), GlobalName,
2096 *GetGlobalInt(GlobalIndex, StructIndex));
2097 } else if (Globals[GlobalIndex].BoolVal) {
2098 /* Display of a boolean config. file variable - e.g. "DrugValue is
2099 TRUE" */
2100 g_print(_("%s is %s\n"), GlobalName,
2101 *GetGlobalBoolean(GlobalIndex, StructIndex) ?
2102 "TRUE" : "FALSE");
2103 } else if (Globals[GlobalIndex].PriceVal) {
2104 /* Display of a price config. file variable - e.g. "Bitch.MinPrice is
2105 $200" */
2106 dpg_print(_("%s is %P\n"), GlobalName,
2107 *GetGlobalPrice(GlobalIndex, StructIndex));
2108 } else if (Globals[GlobalIndex].StringVal) {
2109 /* Display of a string config. file variable - e.g. "LoanSharkName is
2110 \"the loan shark\"" */
2111 g_print(_("%s is \"%s\"\n"), GlobalName,
2112 *GetGlobalString(GlobalIndex, StructIndex));
2113 } else if (Globals[GlobalIndex].StringList) {
2114 if (IndexGiven) {
2115 /* Display of an indexed string list config. file variable - e.g.
2116 "StoppedTo[1] is have a beer" */
2117 g_print(_("%s[%d] is %s\n"), GlobalName, StructIndex,
2118 (*(Globals[GlobalIndex].StringList))[StructIndex - 1]);
2119 } else {
2120 GString *text;
2121
2122 text = g_string_new("");
2123 /* Display of the first part of an entire string list config. file
2124 variable - e.g. "StoppedTo is { " (followed by "have a beer",
2125 "smoke a joint" etc.) */
2126 g_string_printf(text, _("%s is { "), GlobalName);
2127 if (Globals[GlobalIndex].MaxIndex) {
2128 for (i = 0; i < *(Globals[GlobalIndex].MaxIndex); i++) {
2129 if (i > 0)
2130 g_string_append(text, ", ");
2131 g_string_append_printf(text, "\"%s\"",
2132 (*(Globals[GlobalIndex].StringList))[i]);
2133 }
2134 }
2135 g_string_append(text, " }\n");
2136
2137 g_print("%s", text->str);
2138 g_string_free(text, TRUE);
2139 }
2140 }
2141 if (Globals[GlobalIndex].NameStruct[0])
2142 g_free(GlobalName);
2143 }
2144
2145 static gboolean SetConfigValue(int GlobalIndex, int StructIndex,
2146 gboolean IndexGiven, Converter *conv,
2147 GScanner *scanner)
2148 {
2149 gchar *GlobalName, *tmpstr;
2150 GTokenType token;
2151 int IntVal, NewNum;
2152 Player *tmp;
2153 GSList *list, *StartList;
2154 gboolean parsed;
2155
2156 if (!CheckMaxIndex(scanner, GlobalIndex, StructIndex, IndexGiven))
2157 return FALSE;
2158 if (Globals[GlobalIndex].NameStruct[0]) {
2159 GlobalName =
2160 g_strdup_printf("%s[%d].%s", Globals[GlobalIndex].NameStruct,
2161 StructIndex, Globals[GlobalIndex].Name);
2162 } else {
2163 GlobalName = Globals[GlobalIndex].Name;
2164 }
2165 if (Globals[GlobalIndex].IntVal) {
2166 gboolean minus = FALSE;
2167
2168 /* GScanner doesn't understand negative numbers, so we need to
2169 * explicitly check for a prefixed minus sign */
2170 token = g_scanner_get_next_token(scanner);
2171 if (token == '-') {
2172 minus = TRUE;
2173 token = g_scanner_get_next_token(scanner);
2174 }
2175 if (token == G_TOKEN_INT) {
2176 IntVal = (int)scanner->value.v_int;
2177 if (minus) {
2178 IntVal = -IntVal;
2179 }
2180 if (IntVal < Globals[GlobalIndex].MinVal) {
2181 g_scanner_warn(scanner, _("%s can be no smaller than %d - ignoring!"),
2182 GlobalName, Globals[GlobalIndex].MinVal);
2183 return FALSE;
2184 }
2185 if (Globals[GlobalIndex].MaxVal > Globals[GlobalIndex].MinVal
2186 && IntVal > Globals[GlobalIndex].MaxVal) {
2187 g_scanner_warn(scanner, _("%s can be no larger than %d - ignoring!"),
2188 GlobalName, Globals[GlobalIndex].MaxVal);
2189 return FALSE;
2190 }
2191 if (Globals[GlobalIndex].ResizeFunc) {
2192 (*(Globals[GlobalIndex].ResizeFunc)) (IntVal);
2193 /* Displayed, for example, when you set NumDrug=10 to allow
2194 Drug[10].Name etc. to be set */
2195 if (ConfigVerbose)
2196 g_print(_("Resized structure list to %d elements\n"), IntVal);
2197 for (list = FirstClient; list; list = g_slist_next(list)) {
2198 tmp = (Player *)list->data;
2199 UpdatePlayer(tmp);
2200 }
2201 for (list = FirstServer; list; list = g_slist_next(list)) {
2202 tmp = (Player *)list->data;
2203 UpdatePlayer(tmp);
2204 }
2205 }
2206 *GetGlobalInt(GlobalIndex, StructIndex) = IntVal;
2207 } else {
2208 g_scanner_unexp_token(scanner, G_TOKEN_INT, NULL, NULL,
2209 NULL, NULL, FALSE);
2210 return FALSE;
2211 }
2212 } else if (Globals[GlobalIndex].BoolVal) {
2213 scanner->config->cset_identifier_first =
2214 G_CSET_a_2_z "01" G_CSET_A_2_Z;
2215 scanner->config->cset_identifier_nth = G_CSET_a_2_z G_CSET_A_2_Z;
2216 token = g_scanner_get_next_token(scanner);
2217 scanner->config->cset_identifier_first = G_CSET_a_2_z "_" G_CSET_A_2_Z;
2218 scanner->config->cset_identifier_nth =
2219 G_CSET_a_2_z "._0123456789" G_CSET_A_2_Z;
2220 parsed = FALSE;
2221 if (token == G_TOKEN_IDENTIFIER) {
2222 if (g_ascii_strncasecmp(scanner->value.v_identifier, "TRUE", 4) == 0 ||
2223 strcmp(scanner->value.v_identifier, "1") == 0) {
2224 parsed = TRUE;
2225 *GetGlobalBoolean(GlobalIndex, StructIndex) = TRUE;
2226 } else if (g_ascii_strncasecmp(scanner->value.v_identifier, "FALSE", 5) == 0
2227 || strcmp(scanner->value.v_identifier, "0") == 0) {
2228 parsed = TRUE;
2229 *GetGlobalBoolean(GlobalIndex, StructIndex) = FALSE;
2230 }
2231 }
2232 if (!parsed) {
2233 g_scanner_unexp_token(scanner, G_TOKEN_NONE, NULL, NULL, NULL,
2234 _("expected a boolean value (one of 0, FALSE, "
2235 "1, TRUE)"), FALSE);
2236 return FALSE;
2237 }
2238 } else if (Globals[GlobalIndex].PriceVal) {
2239 token = g_scanner_get_next_token(scanner);
2240 if (token == G_TOKEN_INT) {
2241 *GetGlobalPrice(GlobalIndex, StructIndex) =
2242 (price_t)scanner->value.v_int;
2243 } else {
2244 g_scanner_unexp_token(scanner, G_TOKEN_INT, NULL, NULL,
2245 NULL, NULL, FALSE);
2246 return FALSE;
2247 }
2248 } else if (Globals[GlobalIndex].StringVal) {
2249 scanner->config->identifier_2_string = TRUE;
2250 scanner->config->cset_identifier_first =
2251 G_CSET_a_2_z "._0123456789" G_CSET_A_2_Z G_CSET_LATINS
2252 G_CSET_LATINC;
2253 scanner->config->cset_identifier_nth =
2254 G_CSET_a_2_z " ._0123456789" G_CSET_A_2_Z G_CSET_LATINS
2255 G_CSET_LATINC;
2256 token = g_scanner_get_next_token(scanner);
2257 if (token == G_TOKEN_STRING) {
2258 tmpstr = Conv_ToInternal(conv, scanner->value.v_string, -1);
2259 AssignName(GetGlobalString(GlobalIndex, StructIndex), tmpstr);
2260 g_free(tmpstr);
2261 } else if (token == G_TOKEN_IDENTIFIER) {
2262 tmpstr = Conv_ToInternal(conv, scanner->value.v_identifier, -1);
2263 AssignName(GetGlobalString(GlobalIndex, StructIndex), tmpstr);
2264 g_free(tmpstr);
2265 } else {
2266 g_scanner_unexp_token(scanner, G_TOKEN_STRING, NULL, NULL,
2267 NULL, NULL, FALSE);
2268 }
2269 scanner->config->identifier_2_string = FALSE;
2270 scanner->config->cset_identifier_first = G_CSET_a_2_z "_" G_CSET_A_2_Z;
2271 scanner->config->cset_identifier_nth =
2272 G_CSET_a_2_z "._0123456789" G_CSET_A_2_Z;
2273 } else if (Globals[GlobalIndex].StringList) {
2274 token = g_scanner_get_next_token(scanner);
2275 if (IndexGiven) {
2276 if (token == G_TOKEN_STRING) {
2277 tmpstr = Conv_ToInternal(conv, scanner->value.v_string, -1);
2278 AssignName(&(*(Globals[GlobalIndex].StringList))[StructIndex - 1],
2279 tmpstr);
2280 g_free(tmpstr);
2281 } else {
2282 g_scanner_unexp_token(scanner, G_TOKEN_STRING, NULL, NULL,
2283 NULL, NULL, FALSE);
2284 return FALSE;
2285 }
2286 } else {
2287 StartList = NULL;
2288 if (token != G_TOKEN_LEFT_CURLY) {
2289 g_scanner_unexp_token(scanner, G_TOKEN_LEFT_CURLY, NULL, NULL,
2290 NULL, NULL, FALSE);
2291 return FALSE;
2292 }
2293 NewNum = 0;
2294 do {
2295 token = g_scanner_get_next_token(scanner);
2296 if (token == G_TOKEN_STRING) {
2297 tmpstr = g_strdup(scanner->value.v_string);
2298 NewNum++;
2299 StartList = g_slist_append(StartList, tmpstr);
2300 } else if (token != G_TOKEN_RIGHT_CURLY && token != G_TOKEN_COMMA) {
2301 g_scanner_unexp_token(scanner, G_TOKEN_STRING, NULL, NULL,
2302 NULL, NULL, FALSE);
2303 return FALSE;
2304 }
2305 } while (token != G_TOKEN_RIGHT_CURLY);
2306 (*Globals[GlobalIndex].ResizeFunc) (NewNum);
2307 NewNum = 0;
2308 for (list = StartList; list; NewNum++, list = g_slist_next(list)) {
2309 AssignName(&(*(Globals[GlobalIndex].StringList))[NewNum],
2310 (char *)list->data);
2311 g_free(list->data);
2312 }
2313 g_slist_free(StartList);
2314 }
2315 }
2316 if (Globals[GlobalIndex].NameStruct[0])
2317 g_free(GlobalName);
2318
2319 Globals[GlobalIndex].Modified = TRUE;
2320 return TRUE;
2321 }
2322
2323 /*
2324 * Returns the URL of the directory containing local HTML documentation.
2325 */
2326 gchar *GetDocRoot(void)
2327 {
2328 gchar *path;
2329 #ifdef CYGWIN
2330 gchar *bindir;
2331
2332 bindir = GetBinaryDir();
2333 path = g_strdup_printf("file://%s/doc/", bindir);
2334 g_free(bindir);
2335 #else
2336 path = g_strdup_printf("file://%s/", DPDOCDIR);
2337 #endif
2338 return path;
2339 }
2340
2341 /*
2342 * Returns the URL of the index file for the local HTML documentation.
2343 */
2344 gchar *GetDocIndex(void)
2345 {
2346 gchar *file, *root;
2347
2348 root = GetDocRoot();
2349 file = g_strdup_printf("%sindex.html", root);
2350 g_free(root);
2351 return file;
2352 }
2353
2354 #ifdef CYGWIN
2355 extern gchar *appdata_path;
2356 #endif
2357
2358 /*
2359 * Returns the pathname of the global (all users) configuration file,
2360 * as a dynamically-allocated string that must be later freed. On
2361 * error, NULL is returned.
2362 */
2363 gchar *GetGlobalConfigFile(void)
2364 {
2365 #ifdef CYGWIN
2366 gchar *bindir, *conf = NULL;
2367
2368 /* Global configuration is in the same directory as the dopewars binary */
2369 bindir = GetBinaryDir();
2370 if (bindir) {
2371 conf = g_strdup_printf("%s/dopewars-config.txt", bindir);
2372 g_free(bindir);
2373 }
2374 return conf;
2375 #else
2376 return g_strdup("/etc/dopewars");
2377 #endif
2378 }
2379
2380 /*
2381 * Returns the pathname of the local (per-user) configuration file,
2382 * as a dynamically-allocated string that must be later freed. On
2383 * error, NULL is returned.
2384 */
2385 gchar *GetLocalConfigFile(void)
2386 {
2387 #ifdef CYGWIN
2388 return g_strdup_printf("%s/dopewars-config.txt",
2389 appdata_path ? appdata_path : ".");
2390 #else
2391 gchar *home, *conf = NULL;
2392
2393 /* Local config is in the user's home directory */
2394 home = getenv("HOME");
2395 if (home) {
2396 conf = g_strdup_printf("%s/.dopewars", home);
2397 }
2398 return conf;
2399 #endif
2400 }
2401
2402 /*
2403 * Sets up data - such as the location of the high score file - to
2404 * hard-coded internal values, and then processes the global and
2405 * user-specific configuration files.
2406 */
2407 static void SetupParameters(GSList *extraconfigs, gboolean antique)
2408 {
2409 gchar *conf;
2410 GSList *list;
2411 int i, defloc;
2412
2413 DrugValue = TRUE;
2414 Sanitized = ConfigVerbose = FALSE;
2415
2416 g_free(Currency.Symbol);
2417 /* The currency symbol */
2418 Currency.Symbol = g_strdup(_("$"));
2419 Currency.Prefix = (strcmp("Currency.Prefix=TRUE",
2420 /* Translate this to "Currency.Prefix=FALSE" if you want your currency
2421 symbol to follow all prices. */
2422 _("Currency.Prefix=TRUE")) == 0);
2423
2424 /* Set hard-coded default values */
2425 AssignName(&ServerName, "localhost");
2426 AssignName(&ServerMOTD, "");
2427 AssignName(&BindAddress, "");
2428 AssignName(&OurWebBrowser, "/usr/bin/firefox");
2429
2430 AssignName(&Sounds.FightHit, SNDPATH"colt.wav");
2431 AssignName(&Sounds.EnemyBitchKilled, SNDPATH"shotdown.wav");
2432 AssignName(&Sounds.BitchKilled, SNDPATH"losebitch.wav");
2433 AssignName(&Sounds.EnemyKilled, SNDPATH"shotdown.wav");
2434 AssignName(&Sounds.Killed, SNDPATH"die.wav");
2435 AssignName(&Sounds.EnemyFlee, SNDPATH"run.wav");
2436 AssignName(&Sounds.Flee, SNDPATH"run.wav");
2437 AssignName(&Sounds.Jet, SNDPATH"train.wav");
2438 AssignName(&Sounds.TalkPrivate, SNDPATH"murmur.wav");
2439 AssignName(&Sounds.TalkToAll, SNDPATH"message.wav");
2440 AssignName(&Sounds.EndGame, SNDPATH"bye.wav");
2441
2442 LoanSharkLoc = DEFLOANSHARK;
2443 BankLoc = DEFBANK;
2444 if (antique) {
2445 GunShopLoc = RoughPubLoc = 0;
2446 } else {
2447 GunShopLoc = DEFGUNSHOP;
2448 RoughPubLoc = DEFROUGHPUB;
2449 }
2450
2451 CopyNames(&Names, &DefaultNames);
2452 CopyDrugs(&Drugs, &DefaultDrugs);
2453
2454 #ifdef NETWORKING
2455 CopyMetaServer(&MetaServer, &DefaultMetaServer);
2456 AssignName(&Socks.name, "socks");
2457 Socks.port = 1080;
2458 Socks.version = 4;
2459 g_free(Socks.user);
2460 g_free(Socks.authuser);
2461 g_free(Socks.authpassword);
2462 Socks.user = g_strdup("");
2463 Socks.numuid = FALSE;
2464 Socks.authuser = g_strdup("");
2465 Socks.authpassword = g_strdup("");
2466 UseSocks = FALSE;
2467 #endif
2468
2469 defloc = sizeof(DefaultLocation) / sizeof(DefaultLocation[0]);
2470 g_assert(defloc >= 6);
2471 if (antique) {
2472 defloc = 6;
2473 }
2474 ResizeLocations(defloc);
2475 for (i = 0; i < NumLocation; i++)
2476 CopyLocation(&Location[i], &DefaultLocation[i]);
2477 ResizeCops(sizeof(DefaultCop) / sizeof(DefaultCop[0]));
2478 for (i = 0; i < NumCop; i++)
2479 CopyCop(&Cop[i], &DefaultCop[i]);
2480 ResizeGuns(sizeof(DefaultGun) / sizeof(DefaultGun[0]));
2481 for (i = 0; i < NumGun; i++)
2482 CopyGun(&Gun[i], &DefaultGun[i]);
2483 ResizeDrugs(sizeof(DefaultDrug) / sizeof(DefaultDrug[0]));
2484 for (i = 0; i < NumDrug; i++)
2485 CopyDrug(&Drug[i], &DefaultDrug[i]);
2486 ResizeSubway(sizeof(DefaultSubwaySaying) /
2487 sizeof(DefaultSubwaySaying[0]));
2488 for (i = 0; i < NumSubway; i++) {
2489 AssignName(&SubwaySaying[i], _(DefaultSubwaySaying[i]));
2490 }
2491 ResizePlaying(sizeof(DefaultPlaying) / sizeof(DefaultPlaying[0]));
2492 for (i = 0; i < NumPlaying; i++) {
2493 AssignName(&Playing[i], _(DefaultPlaying[i]));
2494 }
2495 ResizeStoppedTo(sizeof(DefaultStoppedTo) / sizeof(DefaultStoppedTo[0]));
2496 for (i = 0; i < NumStoppedTo; i++) {
2497 AssignName(&StoppedTo[i], _(DefaultStoppedTo[i]));
2498 }
2499
2500 /* Replace nasty null pointers with null strings */
2501 for (i = 0; i < NUMGLOB; ++i) {
2502 if (Globals[i].StringVal && !*Globals[i].StringVal) {
2503 *Globals[i].StringVal = g_strdup("");
2504 }
2505 }
2506
2507 /* Now read in the global configuration file */
2508 conf = GetGlobalConfigFile();
2509 if (conf) {
2510 ReadConfigFile(conf, NULL);
2511 g_free(conf);
2512 }
2513
2514 /* Next, try the local configuration file */
2515 conf = GetLocalConfigFile();
2516 if (conf) {
2517 ReadConfigFile(conf, &LocalCfgEncoding);
2518 g_free(conf);
2519 }
2520
2521 /* Finally, any configuration files named on the command line */
2522 for (list = extraconfigs; list; list = g_slist_next(list)) {
2523 ReadConfigFile(list->data, NULL);
2524 }
2525 }
2526
2527 void GetDateString(GString *str, Player *Play)
2528 {
2529 gchar buf[200], *turn, *pt;
2530
2531 turn = g_strdup_printf("%d", Play->Turn);
2532 g_string_assign(str, Names.Date);
2533 while ((pt = strstr(str->str, "%T")) != NULL) {
2534 int ind = pt - str->str;
2535
2536 g_string_erase(str, ind, 2);
2537 g_string_insert(str, ind, turn);
2538 }
2539
2540 g_date_strftime(buf, sizeof(buf), str->str, Play->date);
2541 g_string_assign(str, buf);
2542 g_free(turn);
2543 }
2544
2545 static void PluginHelp(void)
2546 {
2547 gchar *plugins;
2548 #ifdef HAVE_GETOPT_LONG
2549 g_print(_(" -u, --plugin=FILE use sound plugin \"FILE\"\n"
2550 " "));
2551 #else
2552 g_print(_(" -u file use sound plugin \"file\"\n"
2553 " "));
2554 #endif
2555 plugins = GetPluginList();
2556 g_print(_("(%s available)\n"), plugins);
2557 g_free(plugins);
2558 }
2559
2560 void HandleHelpTexts(gboolean fullhelp)
2561 {
2562 g_print(_("dopewars version %s\n"), VERSION);
2563 if (!fullhelp) {
2564 return;
2565 }
2566
2567 g_print(
2568 #ifdef HAVE_GETOPT_LONG
2569 /* Usage information, printed when the user runs "dopewars -h"
2570 (version with support for GNU long options) */
2571 _("Usage: dopewars [OPTION]...\n\
2572 Drug dealing game based on \"Drug Wars\" by John E. Dell\n\
2573 -b, --no-color, \"black and white\" - i.e. do not use pretty colors\n\
2574 --no-colour (by default colors are used where available)\n\
2575 -n, --single-player be boring and don't connect to any available dopewars\n\
2576 servers (i.e. single player mode)\n\
2577 -a, --antique \"antique\" dopewars - keep as closely to the original\n\
2578 version as possible (no networking)\n\
2579 -f, --scorefile=FILE specify a file to use as the high score table (by\n\
2580 default %s/dopewars.sco is used)\n\
2581 -o, --hostname=ADDR specify a hostname where the server for multiplayer\n\
2582 dopewars can be found\n\
2583 -s, --public-server run in server mode (note: see the -A option for\n\
2584 configuring a server once it\'s running)\n\
2585 -S, --private-server run a \"private\" server (do not notify the metaserver)\n\
2586 -p, --port=PORT specify the network port to use (default: 7902)\n\
2587 -g, --config-file=FILE specify the pathname of a dopewars configuration file;\n\
2588 this file is read immediately when the -g option\n\
2589 is encountered\n\
2590 -r, --pidfile=FILE maintain pid file \"FILE\" while running the server\n\
2591 -l, --logfile=FILE write log information to \"FILE\"\n\
2592 -A, --admin connect to a locally-running server for administration\n\
2593 -c, --ai-player create and run a computer player\n\
2594 -w, --windowed-client force the use of a graphical (windowed)\n\
2595 client (GTK+ or Win32)\n\
2596 -t, --text-client force the use of a text-mode client (curses) (by\n\
2597 default, a windowed client is used when possible)\n\
2598 -P, --player=NAME set player name to \"NAME\"\n\
2599 -C, --convert=FILE convert an \"old format\" score file to the new format\n"), DPSCOREDIR);
2600 PluginHelp();
2601 g_print(_(" -h, --help display this help information\n\
2602 -v, --version output version information and exit\n\n\
2603 dopewars is Copyright (C) Ben Webb 1998-2021, and released under the GNU GPL\n\
2604 Report bugs to the author at benwebb@users.sf.net\n"));
2605 #else
2606 /* Usage information, printed when the user runs "dopewars -h"
2607 (short options only version) */
2608 _("Usage: dopewars [OPTION]...\n\
2609 Drug dealing game based on \"Drug Wars\" by John E. Dell\n\
2610 -b \"black and white\" - i.e. do not use pretty colors\n\
2611 (by default colors are used where the terminal supports them)\n\
2612 -n be boring and don't connect to any available dopewars servers\n\
2613 (i.e. single player mode)\n\
2614 -a \"antique\" dopewars - keep as closely to the original version as\n\
2615 possible (no networking)\n\
2616 -f file specify a file to use as the high score table\n\
2617 (by default %s/dopewars.sco is used)\n\
2618 -o addr specify a hostname where the server for multiplayer dopewars\n\
2619 can be found\n\
2620 -s run in server mode (note: see the -A option for configuring a\n\
2621 server once it\'s running)\n\
2622 -S run a \"private\" server (i.e. do not notify the metaserver)\n\
2623 -p port specify the network port to use (default: 7902)\n\
2624 -g file specify the pathname of a dopewars configuration file; this file\n\
2625 is read immediately when the -g option is encountered\n\
2626 -r file maintain pid file \"file\" while running the server\n\
2627 -l file write log information to \"file\"\n\
2628 -c create and run a computer player\n\
2629 -w force the use of a graphical (windowed) client (GTK+ or Win32)\n\
2630 -t force the use of a text-mode client (curses)\n\
2631 (by default, a windowed client is used when possible)\n\
2632 -P name set player name to \"name\"\n\
2633 -C file convert an \"old format\" score file to the new format\n\
2634 -A connect to a locally-running server for administration\n"),
2635 DPSCOREDIR);
2636 PluginHelp();
2637 g_print(_(" -h display this help information\n\
2638 -v output version information and exit\n\n\
2639 dopewars is Copyright (C) Ben Webb 1998-2021, and released under the GNU GPL\n\
2640 Report bugs to the author at benwebb@users.sf.net\n"));
2641 #endif
2642 }
2643
2644 struct CMDLINE *ParseCmdLine(int argc, char *argv[])
2645 {
2646 int c;
2647 struct CMDLINE *cmdline = g_new0(struct CMDLINE, 1);
2648 static const gchar *options = "anbchvf:o:sSp:g:r:wtC:l:NAu:P:";
2649
2650 #ifdef HAVE_GETOPT_LONG
2651 static const struct option long_options[] = {
2652 {"no-color", no_argument, NULL, 'b'},
2653 {"no-colour", no_argument, NULL, 'b'},
2654 {"single-player", no_argument, NULL, 'n'},
2655 {"antique", no_argument, NULL, 'a'},
2656 {"scorefile", required_argument, NULL, 'f'},
2657 {"hostname", required_argument, NULL, 'o'},
2658 {"public-server", no_argument, NULL, 's'},
2659 {"private-server", no_argument, NULL, 'S'},
2660 {"port", required_argument, NULL, 'p'},
2661 {"configfile", required_argument, NULL, 'g'},
2662 {"pidfile", required_argument, NULL, 'r'},
2663 {"ai-player", no_argument, NULL, 'c'},
2664 {"windowed-client", no_argument, NULL, 'w'},
2665 {"text-client", no_argument, NULL, 't'},
2666 {"player", required_argument, NULL, 'P'},
2667 {"convert", required_argument, NULL, 'C'},
2668 {"logfile", required_argument, NULL, 'l'},
2669 {"admin", no_argument, NULL, 'A'},
2670 {"plugin", required_argument, NULL, 'u'},
2671 {"help", no_argument, NULL, 'h'},
2672 {"version", no_argument, NULL, 'v'},
2673 {0, 0, 0, 0}
2674 };
2675 #endif
2676
2677 cmdline->scorefile = cmdline->servername = cmdline->pidfile
2678 = cmdline->logfile = cmdline->plugin = cmdline->convertfile
2679 = cmdline->playername = NULL;
2680 cmdline->configs = NULL;
2681 cmdline->color = cmdline->network = TRUE;
2682 cmdline->client = CLIENT_AUTO;
2683
2684 do {
2685 #ifdef HAVE_GETOPT_LONG
2686 c = getopt_long(argc, argv, options, long_options, NULL);
2687 #else
2688 c = getopt(argc, argv, options);
2689 #endif
2690 switch (c) {
2691 case 'n':
2692 cmdline->network = FALSE;
2693 break;
2694 case 'b':
2695 cmdline->color = FALSE;
2696 break;
2697 case 'c':
2698 cmdline->ai = TRUE;
2699 break;
2700 case 'a':
2701 cmdline->antique = TRUE;
2702 cmdline->network = FALSE;
2703 break;
2704 case 'v':
2705 cmdline->version = TRUE;
2706 break;
2707 case 'h':
2708 case 0:
2709 case '?':
2710 cmdline->help = TRUE;
2711 break;
2712 case 'f':
2713 AssignName(&cmdline->scorefile, optarg);
2714 break;
2715 case 'o':
2716 AssignName(&cmdline->servername, optarg);
2717 break;
2718 case 's':
2719 cmdline->server = TRUE;
2720 cmdline->notifymeta = TRUE;
2721 break;
2722 case 'S':
2723 cmdline->server = TRUE;
2724 cmdline->notifymeta = FALSE;
2725 break;
2726 case 'p':
2727 cmdline->setport = TRUE;
2728 cmdline->port = atoi(optarg);
2729 break;
2730 case 'g':
2731 cmdline->configs = g_slist_append(cmdline->configs, g_strdup(optarg));
2732 break;
2733 case 'r':
2734 AssignName(&cmdline->pidfile, optarg);
2735 break;
2736 case 'l':
2737 AssignName(&cmdline->logfile, optarg);
2738 break;
2739 case 'u':
2740 AssignName(&cmdline->plugin, optarg);
2741 break;
2742 case 'w':
2743 cmdline->client = CLIENT_WINDOW;
2744 break;
2745 case 't':
2746 cmdline->client = CLIENT_CURSES;
2747 break;
2748 case 'P':
2749 AssignName(&cmdline->playername, optarg);
2750 break;
2751 case 'C':
2752 AssignName(&cmdline->convertfile, optarg);
2753 cmdline->convert = TRUE;
2754 break;
2755 case 'A':
2756 cmdline->admin = TRUE;
2757 break;
2758 }
2759 } while (c != -1);
2760 cmdline->color = FALSE;
2761
2762 return cmdline;
2763 }
2764
2765 void FreeCmdLine(struct CMDLINE *cmdline)
2766 {
2767 GSList *list;
2768
2769 g_free(cmdline->scorefile);
2770 g_free(cmdline->servername);
2771 g_free(cmdline->pidfile);
2772 g_free(cmdline->logfile);
2773 g_free(cmdline->plugin);
2774 g_free(cmdline->convertfile);
2775 g_free(cmdline->playername);
2776
2777 for (list = cmdline->configs; list; list = g_slist_next(list)) {
2778 g_free(list->data);
2779 }
2780 g_slist_free(list);
2781 g_free(cmdline);
2782 }
2783
2784 static gchar *priv_hiscore = NULL;
2785
2786 /*
2787 * Does general startup stuff (command line, dropping privileges,
2788 * and high score init; config files are handled later)
2789 */
2790 struct CMDLINE *GeneralStartup(int argc, char *argv[])
2791 {
2792 /* First, open the hard-coded high score file with possibly
2793 * elevated privileges */
2794 #ifdef CYGWIN
2795 priv_hiscore = g_strdup_printf("%s/dopewars.sco",
2796 appdata_path ? appdata_path : DPSCOREDIR);
2797 #else
2798 priv_hiscore = g_strdup_printf("%s/dopewars.sco", DPSCOREDIR);
2799 #endif
2800 HiScoreFile = g_strdup(priv_hiscore);
2801 OpenHighScoreFile();
2802 DropPrivileges();
2803
2804 /* Initialize variables */
2805 Log.File = g_strdup("");
2806 Log.Level = 2;
2807 Log.Timestamp = g_strdup("[%H:%M:%S] ");
2808 srand((unsigned)time(NULL));
2809 Noone.Name = g_strdup("Noone");
2810 Server = Client = Network = FALSE;
2811
2812 return ParseCmdLine(argc, argv);
2813 }
2814
2815 void InitConfiguration(struct CMDLINE *cmdline)
2816 {
2817 ConfigErrors = 0;
2818 SetupParameters(cmdline->configs, cmdline->antique);
2819
2820 if (cmdline->scorefile) {
2821 AssignName(&HiScoreFile, cmdline->scorefile);
2822 }
2823 if (cmdline->servername) {
2824 AssignName(&ServerName, cmdline->servername);
2825 }
2826 if (cmdline->playername) {
2827 AssignName(&PlayerName, cmdline->playername);
2828 }
2829 if (cmdline->pidfile) {
2830 AssignName(&PidFile, cmdline->pidfile);
2831 }
2832 if (cmdline->logfile) {
2833 AssignName(&Log.File, cmdline->logfile);
2834 OpenLog();
2835 }
2836 if (cmdline->setport) {
2837 Port = cmdline->port;
2838 }
2839 #ifdef NETWORKING
2840 if (cmdline->server) {
2841 MetaServer.Active = cmdline->notifymeta;
2842 }
2843 #endif
2844 WantAntique = cmdline->antique;
2845
2846 if (!cmdline->version && !cmdline->help && !cmdline->ai
2847 && !cmdline->convert && !cmdline->admin) {
2848 /* Open a user-specified high score file with no privileges, if one
2849 * was given */
2850 if (strcmp(priv_hiscore, HiScoreFile) != 0) {
2851 CloseHighScoreFile();
2852 OpenHighScoreFile();
2853 }
2854 } else {
2855 CloseHighScoreFile();
2856 }
2857 }
2858
2859 /*
2860 * Removes any ^ or \n characters from the given string, which is
2861 * modified in place.
2862 */
2863 void StripTerminators(gchar *str)
2864 {
2865 guint i;
2866
2867 if (str) {
2868 for (i = 0; i < strlen(str); i++) {
2869 switch(str[i]) {
2870 case '^':
2871 case '\n':
2872 str[i] = '~';
2873 break;
2874 default:
2875 break;
2876 }
2877 }
2878 }
2879 }
2880
2881 #ifndef CYGWIN
2882
2883 #if defined(NETWORKING) && !defined(GUI_SERVER)
2884 static void ServerLogMessage(const gchar *log_domain,
2885 GLogLevelFlags log_level,
2886 const gchar *message, gpointer user_data)
2887 {
2888 GString *text;
2889
2890 text = GetLogString(log_level, message);
2891 if (text) {
2892 fprintf(Log.fp ? Log.fp : stdout, "%s\n", text->str);
2893 g_string_free(text, TRUE);
2894 }
2895 }
2896 #endif
2897
2898 #ifndef CURSES_CLIENT
2899 /*
2900 * Stub function to report an error if the Curses client is requested and
2901 * it isn't compiled in.
2902 */
2903 void CursesLoop(struct CMDLINE *cmdline)
2904 {
2905 g_print(_("No curses client available - rebuild the binary passing the\n"
2906 "--enable-curses-client option to configure, or use a windowed\n"
2907 "client (if available) instead!\n"));
2908 }
2909 #endif
2910
2911 #ifndef GUI_CLIENT
2912 /*
2913 * Stub function to report an error if the GTK+ client is requested and
2914 * it isn't compiled in.
2915 */
2916 #ifdef CYGWIN
2917 gboolean GtkLoop(HINSTANCE hInstance, HINSTANCE hPrevInstance,
2918 struct CMDLINE *cmdline, gboolean ReturnOnFail)
2919 #else
2920 gboolean GtkLoop(int *argc, char **argv[], struct CMDLINE *cmdline,
2921 gboolean ReturnOnFail)
2922 #endif
2923 {
2924 if (!ReturnOnFail) {
2925 g_print(_("No graphical client available - rebuild the binary\n"
2926 "passing the --enable-gui-client option to configure, or\n"
2927 "use the curses client (if available) instead!\n"));
2928 }
2929 return FALSE;
2930 }
2931 #endif
2932
2933 static void DefaultLogMessage(const gchar *log_domain,
2934 GLogLevelFlags log_level,
2935 const gchar *message, gpointer user_data)
2936 {
2937 GString *text;
2938
2939 text = GetLogString(log_level, message);
2940 if (text) {
2941 g_print("%s\n", text->str);
2942 g_string_free(text, TRUE);
2943 }
2944 }
2945
2946 /*
2947 * Standard program entry - Win32 uses WinMain() instead, in winmain.c
2948 */
2949 int main(int argc, char *argv[])
2950 {
2951 struct CMDLINE *cmdline;
2952 #ifdef ENABLE_NLS
2953 const char *charset;
2954 setlocale(LC_ALL, "");
2955 bindtextdomain(PACKAGE, LOCALEDIR);
2956 textdomain(PACKAGE);
2957 LocaleIsUTF8 = g_get_charset(&charset);
2958 #endif
2959 WantUTF8Errors(FALSE);
2960 g_log_set_handler(NULL, LogMask(), DefaultLogMessage, NULL);
2961 cmdline = GeneralStartup(argc, argv);
2962 if (cmdline->logfile) {
2963 AssignName(&Log.File, cmdline->logfile);
2964 }
2965 OpenLog();
2966 SoundInit();
2967 if (cmdline->version || cmdline->help) {
2968 HandleHelpTexts(cmdline->help);
2969 } else if (cmdline->admin) {
2970 #ifdef NETWORKING
2971 AdminServer(cmdline);
2972 #else
2973 g_print(_("This binary has been compiled without networking "
2974 "support, and thus cannot run\nin admin mode. "
2975 "Recompile passing --enable-networking to the "
2976 "configure script.\n"));
2977 #endif
2978 } else if (cmdline->convert) {
2979 ConvertHighScoreFile(cmdline->convertfile);
2980 } else {
2981 InitNetwork();
2982 if (cmdline->server) {
2983 #ifdef NETWORKING
2984 #ifdef GUI_SERVER
2985 Server = TRUE;
2986 gtk_set_locale();
2987 gtk_init(&argc, &argv);
2988 GuiServerLoop(cmdline, FALSE);
2989 #else
2990 g_log_set_handler(NULL, LogMask(), ServerLogMessage, NULL);
2991 ServerLoop(cmdline);
2992 #endif /* GUI_SERVER */
2993 #else
2994 g_print(_("This binary has been compiled without networking "
2995 "support, and thus cannot run\nin server mode. "
2996 "Recompile passing --enable-networking to the "
2997 "configure script.\n"));
2998 #endif /* NETWORKING */
2999 } else if (cmdline->ai) {
3000 AIPlayerLoop(cmdline);
3001 } else
3002 switch (cmdline->client) {
3003 case CLIENT_AUTO:
3004 if (!GtkLoop(&argc, &argv, cmdline, TRUE))
3005 CursesLoop(cmdline);
3006 break;
3007 case CLIENT_WINDOW:
3008 GtkLoop(&argc, &argv, cmdline, FALSE);
3009 break;
3010 case CLIENT_CURSES:
3011 CursesLoop(cmdline);
3012 break;
3013 }
3014 #ifdef NETWORKING
3015 StopNetworking();
3016 #endif
3017 }
3018 FreeCmdLine(cmdline);
3019 CloseLog();
3020 CloseHighScoreFile();
3021 g_free(PidFile);
3022 g_free(Log.File);
3023 SoundClose();
3024 return 0;
3025 }
3026
3027 #endif /* CYGWIN */ |