Subversion Repositories Games.Descent

Rev

Details | Last modification | View Log | RSS feed

Rev Author Line No. Line
1 pmbaty 1
/*
2
 * This file is part of the DXX-Rebirth project <https://www.dxx-rebirth.com/>.
3
 * It is copyright by its individual contributors, as recorded in the
4
 * project's Git history.  See COPYING.txt at the top level for license
5
 * terms and a link to the Git history.
6
 */
7
 
8
/*
9
 *
10
 * Functions for accessing arguments.
11
 *
12
 */
13
 
14
#include <string>
15
#include <vector>
16
#include <stdlib.h>
17
#include <string.h>
18
#include <SDL_stdinc.h>
19
#include "physfsx.h"
20
#include "args.h"
21
#include "u_mem.h"
22
#include "strutil.h"
23
#include "digi.h"
24
#include "game.h"
25
#include "gauges.h"
26
#include "console.h"
27
#include "mission.h"
28
#if DXX_USE_UDP
29
#include "net_udp.h"
30
#endif
31
 
32
#include "compiler-range_for.h"
33
#include "partial_range.h"
34
 
35
namespace dcx {
36
CArg CGameArg;
37
constexpr std::integral_constant<std::size_t, 1000> MAX_ARGS{};
38
typedef std::vector<std::string> Arglist;
39
 
40
namespace {
41
 
42
class ini_entry
43
{
44
public:
45
        const std::string filename;
46
        ini_entry(std::string &&f) :
47
                filename(std::move(f))
48
        {
49
        }
50
};
51
 
52
typedef std::vector<ini_entry> Inilist;
53
 
54
class argument_exception
55
{
56
public:
57
        const std::string arg;
58
        argument_exception(std::string &&a) :
59
                arg(std::move(a))
60
        {
61
        }
62
};
63
 
64
class missing_parameter : public argument_exception
65
{
66
public:
67
        using argument_exception::argument_exception;
68
};
69
 
70
class unhandled_argument : public argument_exception
71
{
72
public:
73
        using argument_exception::argument_exception;
74
};
75
 
76
class conversion_failure : public argument_exception
77
{
78
public:
79
        const std::string value;
80
        conversion_failure(std::string &&a, std::string &&v) :
81
                argument_exception(std::move(a)), value(std::move(v))
82
        {
83
        }
84
};
85
 
86
class nesting_depth_exceeded
87
{
88
};
89
 
90
}
91
 
92
static void AppendIniArgs(const char *filename, Arglist &Args)
93
{
94
        if (auto f = PHYSFSX_openReadBuffered(filename))
95
        {
96
                PHYSFSX_gets_line_t<1024> line;
97
                while (Args.size() < MAX_ARGS && PHYSFSX_fgets(line, f))
98
                {
99
                        const auto separator = " \t";
100
                        for(char *token = strtok(line, separator); token != NULL; token = strtok(NULL, separator))
101
                        {
102
                                if (*token == ';')
103
                                        break;
104
                                Args.push_back(token);
105
                        }
106
                }
107
        }
108
}
109
 
110
static std::string &&arg_string(Arglist::iterator &pp, Arglist::const_iterator end)
111
{
112
        auto arg = pp;
113
        if (++pp == end)
114
                throw missing_parameter(std::move(*arg));
115
        return std::move(*pp);
116
}
117
 
118
static long arg_integer(Arglist::iterator &pp, Arglist::const_iterator end)
119
{
120
        auto arg = pp;
121
        auto &&value = arg_string(pp, end);
122
        char *p;
123
        auto i = strtol(value.c_str(), &p, 10);
124
        if (*p)
125
                throw conversion_failure(std::move(*arg), std::move(value));
126
        return i;
127
}
128
 
129
template<typename E> E arg_enum(Arglist::iterator &pp, Arglist::const_iterator end)
130
{
131
        return static_cast<E>(arg_integer(pp, end));
132
}
133
 
134
#if DXX_USE_UDP
135
static void arg_port_number(Arglist::iterator &pp, Arglist::const_iterator end, uint16_t &out, bool allow_privileged)
136
{
137
        auto port = arg_integer(pp, end);
138
        if (static_cast<uint16_t>(port) == port && (allow_privileged || port >= 1024))
139
                out = port;
140
}
141
#endif
142
 
143
static void InitGameArg()
144
{
145
        CGameArg.SysMaxFPS = MAXIMUM_FPS;
146
#if defined(DXX_BUILD_DESCENT_II)
147
        GameArg.SndDigiSampleRate = SAMPLE_RATE_22K;
148
#endif
149
#if DXX_USE_UDP
150
        CGameArg.MplUdpHostAddr = UDP_MANUAL_ADDR_DEFAULT;
151
#if DXX_USE_TRACKER
152
        CGameArg.MplTrackerAddr = TRACKER_ADDR_DEFAULT;
153
        CGameArg.MplTrackerPort = TRACKER_PORT_DEFAULT;
154
#endif
155
#endif
156
        CGameArg.DbgVerbose = CON_NORMAL;
157
        CGameArg.DbgBpp = 32;
158
#if DXX_USE_OGL
159
        CGameArg.OglSyncMethod = OGL_SYNC_METHOD_DEFAULT;
160
        CGameArg.OglSyncWait = OGL_SYNC_WAIT_DEFAULT;
161
        CGameArg.DbgGlIntensity4Ok      = true;
162
        CGameArg.DbgGlLuminance4Alpha4Ok = true;
163
        CGameArg.DbgGlRGBA2Ok = true;
164
        CGameArg.DbgGlReadPixelsOk = true;
165
        CGameArg.DbgGlGetTexLevelParamOk = true;
166
#endif
167
}
168
 
169
}
170
 
171
namespace dsx {
172
 
173
Arg GameArg;
174
 
175
static void ReadCmdArgs(Inilist &ini, Arglist &Args);
176
 
177
static void ReadIniArgs(Inilist &ini)
178
{
179
        Arglist Args;
180
        AppendIniArgs(ini.back().filename.c_str(), Args);
181
        ReadCmdArgs(ini, Args);
182
        ini.pop_back();
183
}
184
 
185
static void ReadCmdArgs(Inilist &ini, Arglist &Args)
186
{
187
        for (Arglist::iterator pp = Args.begin(), end = Args.end(); pp != end; ++pp)
188
        {
189
                const char *p = pp->c_str();
190
        // System Options
191
 
192
                if (!d_stricmp(p, "-help") || !d_stricmp(p, "-h") || !d_stricmp(p, "-?") || !d_stricmp(p, "?"))
193
                        CGameArg.SysShowCmdHelp = true;
194
                else if (!d_stricmp(p, "-nonicefps"))
195
                        CGameArg.SysNoNiceFPS = true;
196
                else if (!d_stricmp(p, "-maxfps"))
197
                        CGameArg.SysMaxFPS = arg_integer(pp, end);
198
                else if (!d_stricmp(p, "-hogdir"))
199
                        CGameArg.SysHogDir = arg_string(pp, end);
200
#if PHYSFS_VER_MAJOR >= 2
201
                else if (!d_stricmp(p, "-add-missions-dir"))
202
                        CGameArg.SysMissionDir = arg_string(pp, end);
203
#endif
204
                else if (!d_stricmp(p, "-nohogdir"))
205
                {
206
                        /* No effect if no DXX_SHAREPATH.  Ignore it so that players can
207
                         * pass it via a cross-platform ini.
208
                         */
209
#if DXX_USE_SHAREPATH
210
                        GameArg.SysNoHogDir = true;
211
#endif
212
                }
213
                else if (!d_stricmp(p, "-use_players_dir"))
214
                        CGameArg.SysUsePlayersDir = static_cast<int8_t>(- (sizeof(PLAYER_DIRECTORY_TEXT) - 1));
215
                else if (!d_stricmp(p, "-lowmem"))
216
                        CGameArg.SysLowMem = true;
217
                else if (!d_stricmp(p, "-pilot"))
218
                        CGameArg.SysPilot = arg_string(pp, end);
219
                else if (!d_stricmp(p, "-record-demo-format"))
220
                        CGameArg.SysRecordDemoNameTemplate = arg_string(pp, end);
221
                else if (!d_stricmp(p, "-auto-record-demo"))
222
                        CGameArg.SysAutoRecordDemo = true;
223
                else if (!d_stricmp(p, "-window"))
224
                        CGameArg.SysWindow = true;
225
                else if (!d_stricmp(p, "-noborders"))
226
                        CGameArg.SysNoBorders = true;
227
                else if (!d_stricmp(p, "-notitles"))
228
                        CGameArg.SysNoTitles = true;
229
#if defined(DXX_BUILD_DESCENT_II)
230
                else if (!d_stricmp(p, "-nomovies"))
231
                        GameArg.SysNoMovies             = 1;
232
#endif
233
                else if (!d_stricmp(p, "-autodemo"))
234
                        CGameArg.SysAutoDemo = true;
235
 
236
        // Control Options
237
 
238
                else if (!d_stricmp(p, "-nocursor"))
239
                        CGameArg.CtlNoCursor            = true;
240
                else if (!d_stricmp(p, "-nomouse"))
241
                        CGameArg.CtlNoMouse             = true;
242
                else if (!d_stricmp(p, "-nojoystick"))
243
                {
244
#if DXX_MAX_JOYSTICKS
245
                        CGameArg.CtlNoJoystick          = 1;
246
#endif
247
                }
248
                else if (!d_stricmp(p, "-nostickykeys"))
249
                        CGameArg.CtlNoStickyKeys        = true;
250
 
251
        // Sound Options
252
 
253
                else if (!d_stricmp(p, "-nosound"))
254
                        CGameArg.SndNoSound             = 1;
255
                else if (!d_stricmp(p, "-nomusic"))
256
                        CGameArg.SndNoMusic = true;
257
#if defined(DXX_BUILD_DESCENT_II)
258
                else if (!d_stricmp(p, "-sound11k"))
259
                        GameArg.SndDigiSampleRate               = SAMPLE_RATE_11K;
260
#endif
261
                else if (!d_stricmp(p, "-nosdlmixer"))
262
                {
263
#if DXX_USE_SDLMIXER
264
                        CGameArg.SndDisableSdlMixer = true;
265
#endif
266
                }
267
 
268
        // Graphics Options
269
 
270
                else if (!d_stricmp(p, "-lowresfont"))
271
                        CGameArg.GfxSkipHiresFNT = true;
272
#if defined(DXX_BUILD_DESCENT_II)
273
                else if (!d_stricmp(p, "-lowresgraphics"))
274
                        GameArg.GfxSkipHiresGFX = 1;
275
                else if (!d_stricmp(p, "-lowresmovies"))
276
                        GameArg.GfxSkipHiresMovie               = 1;
277
#endif
278
#if DXX_USE_OGL
279
        // OpenGL Options
280
 
281
                else if (!d_stricmp(p, "-gl_fixedfont"))
282
                        CGameArg.OglFixedFont = true;
283
                else if (!d_stricmp(p, "-gl_syncmethod"))
284
                        CGameArg.OglSyncMethod = arg_enum<SyncGLMethod>(pp, end);
285
                else if (!d_stricmp(p, "-gl_syncwait"))
286
                        CGameArg.OglSyncWait = arg_integer(pp, end);
287
                else if (!d_stricmp(p, "-gl_darkedges"))
288
                        CGameArg.OglDarkEdges = true;
289
#endif
290
 
291
        // Multiplayer Options
292
 
293
#if DXX_USE_UDP
294
                else if (!d_stricmp(p, "-udp_hostaddr"))
295
                        CGameArg.MplUdpHostAddr = arg_string(pp, end);
296
                else if (!d_stricmp(p, "-udp_hostport"))
297
                        /* Peers use -udp_myport to change, so peer cannot set a
298
                         * privileged port.
299
                         */
300
                        arg_port_number(pp, end, CGameArg.MplUdpHostPort, false);
301
                else if (!d_stricmp(p, "-udp_myport"))
302
                {
303
                        arg_port_number(pp, end, CGameArg.MplUdpMyPort, false);
304
                }
305
                else if (!d_stricmp(p, "-no-tracker"))
306
                {
307
                        /* Always recognized.  No-op if tracker support compiled
308
                         * out. */
309
#if DXX_USE_TRACKER
310
                        CGameArg.MplTrackerAddr.clear();
311
#endif
312
                }
313
#if DXX_USE_TRACKER
314
                else if (!d_stricmp(p, "-tracker_hostaddr"))
315
                {
316
                        CGameArg.MplTrackerAddr = arg_string(pp, end);
317
                }
318
                else if (!d_stricmp(p, "-tracker_hostport"))
319
                        arg_port_number(pp, end, CGameArg.MplTrackerPort, true);
320
#endif
321
#endif
322
 
323
#if defined(DXX_BUILD_DESCENT_I)
324
                else if (!d_stricmp(p, "-nobm"))
325
                        GameArg.EdiNoBm                 = 1;
326
#elif defined(DXX_BUILD_DESCENT_II)
327
#if DXX_USE_EDITOR
328
        // Editor Options
329
 
330
                else if (!d_stricmp(p, "-autoload"))
331
                        GameArg.EdiAutoLoad = arg_string(pp, end);
332
                else if (!d_stricmp(p, "-macdata"))
333
                        GameArg.EdiMacData              = 1;
334
                else if (!d_stricmp(p, "-hoarddata"))
335
                        GameArg.EdiSaveHoardData        = 1;
336
#endif
337
#endif
338
 
339
        // Debug Options
340
 
341
                else if (!d_stricmp(p, "-debug"))
342
                        CGameArg.DbgVerbose     = CON_DEBUG;
343
                else if (!d_stricmp(p, "-verbose"))
344
                        CGameArg.DbgVerbose     = CON_VERBOSE;
345
 
346
                else if (!d_stricmp(p, "-no-grab"))
347
                        CGameArg.DbgForbidConsoleGrab = true;
348
                else if (!d_stricmp(p, "-safelog"))
349
                        CGameArg.DbgSafelog = true;
350
                else if (!d_stricmp(p, "-norun"))
351
                        CGameArg.DbgNoRun = true;
352
                else if (!d_stricmp(p, "-renderstats"))
353
                        CGameArg.DbgRenderStats = true;
354
                else if (!d_stricmp(p, "-text"))
355
                        CGameArg.DbgAltTex = arg_string(pp, end);
356
                else if (!d_stricmp(p, "-showmeminfo"))
357
                        CGameArg.DbgShowMemInfo                 = 1;
358
                else if (!d_stricmp(p, "-nodoublebuffer"))
359
                        CGameArg.DbgNoDoubleBuffer = true;
360
                else if (!d_stricmp(p, "-bigpig"))
361
                        CGameArg.DbgNoCompressPigBitmap = true;
362
                else if (!d_stricmp(p, "-16bpp"))
363
                        CGameArg.DbgBpp         = 16;
364
 
365
#if DXX_USE_OGL
366
                else if (!d_stricmp(p, "-gl_oldtexmerge"))
367
                        CGameArg.DbgUseOldTextureMerge = true;
368
                else if (!d_stricmp(p, "-gl_intensity4_ok"))
369
                        CGameArg.DbgGlIntensity4Ok = arg_integer(pp, end);
370
                else if (!d_stricmp(p, "-gl_luminance4_alpha4_ok"))
371
                        CGameArg.DbgGlLuminance4Alpha4Ok = arg_integer(pp, end);
372
                else if (!d_stricmp(p, "-gl_rgba2_ok"))
373
                        CGameArg.DbgGlRGBA2Ok = arg_integer(pp, end);
374
                else if (!d_stricmp(p, "-gl_readpixels_ok"))
375
                        CGameArg.DbgGlReadPixelsOk = arg_integer(pp, end);
376
                else if (!d_stricmp(p, "-gl_gettexlevelparam_ok"))
377
                        CGameArg.DbgGlGetTexLevelParamOk = arg_integer(pp, end);
378
#else
379
                else if (!d_stricmp(p, "-tmap"))
380
                        CGameArg.DbgTexMap = arg_string(pp, end);
381
                else if (!d_stricmp(p, "-hwsurface"))
382
                        CGameArg.DbgSdlHWSurface = true;
383
                else if (!d_stricmp(p, "-asyncblit"))
384
                        CGameArg.DbgSdlASyncBlit = true;
385
#endif
386
                else if (!d_stricmp(p, "-ini"))
387
                {
388
                        ini.emplace_back(arg_string(pp, end));
389
                        if (ini.size() > 10)
390
                                throw nesting_depth_exceeded();
391
                        ReadIniArgs(ini);
392
                }
393
#if defined(__APPLE__) && defined(__MACH__)
394
                else if (!strncmp(p, "-psn", 4))
395
                {
396
                        //do nothing/gobble it up
397
                }
398
#endif
399
                else
400
                        throw unhandled_argument(std::move(*pp));
401
        }
402
}
403
 
404
}
405
 
406
namespace dcx {
407
 
408
static void PostProcessGameArg()
409
{
410
        if (CGameArg.SysMaxFPS < MINIMUM_FPS)
411
                CGameArg.SysMaxFPS = MINIMUM_FPS;
412
        else if (CGameArg.SysMaxFPS > MAXIMUM_FPS)
413
                CGameArg.SysMaxFPS = MAXIMUM_FPS;
414
#if PHYSFS_VER_MAJOR >= 2
415
        if (!CGameArg.SysMissionDir.empty())
416
                PHYSFS_mount(CGameArg.SysMissionDir.c_str(), MISSION_DIR, 1);
417
#endif
418
 
419
#if SDL_MAJOR_VERSION == 1
420
        static char sdl_disable_lock_keys[] = "SDL_DISABLE_LOCK_KEYS=0";
421
        if (CGameArg.CtlNoStickyKeys) // Must happen before SDL_Init!
422
                sdl_disable_lock_keys[sizeof(sdl_disable_lock_keys) - 2] = '1';
423
        SDL_putenv(sdl_disable_lock_keys);
424
#endif
425
}
426
 
427
static std::string ConstructIniStackExplanation(const Inilist &ini)
428
{
429
        Inilist::const_reverse_iterator i = ini.rbegin(), e = ini.rend();
430
        if (i == e)
431
                return " while processing <command line>";
432
        std::string result;
433
        result.reserve(ini.size() * 128);
434
        result += " while processing \"";
435
        for (;;)
436
        {
437
                result += i->filename;
438
                if (++ i == e)
439
                        return result += "\"";
440
                result += "\"\n    included from \"";
441
        }
442
}
443
 
444
}
445
 
446
namespace dsx {
447
 
448
bool InitArgs( int argc,char **argv )
449
{
450
        InitGameArg();
451
 
452
        Inilist ini;
453
        try {
454
                {
455
                        assert(ini.empty());
456
#if defined(DXX_BUILD_DESCENT_I)
457
                        const auto INI_FILENAME = "d1x.ini";
458
#elif defined(DXX_BUILD_DESCENT_II)
459
                        const auto INI_FILENAME = "d2x.ini";
460
#endif
461
                        ini.emplace_back(INI_FILENAME);
462
                        ReadIniArgs(ini);
463
                }
464
                {
465
                        Arglist Args;
466
                        Args.reserve(argc);
467
                        range_for (auto &i, unchecked_partial_range(argv, 1u, static_cast<unsigned>(argc)))
468
                                Args.push_back(i);
469
                        ReadCmdArgs(ini, Args);
470
                }
471
                PostProcessGameArg();
472
                return true;
473
        } catch(const missing_parameter& e) {
474
                UserError("Missing parameter for argument \"%s\"%s", e.arg.c_str(), ConstructIniStackExplanation(ini).c_str());
475
        } catch(const unhandled_argument& e) {
476
                UserError("Unhandled argument \"%s\"%s", e.arg.c_str(), ConstructIniStackExplanation(ini).c_str());
477
        } catch(const conversion_failure& e) {
478
                UserError("Failed to convert argument \"%s\" parameter \"%s\"%s", e.arg.c_str(), e.value.c_str(), ConstructIniStackExplanation(ini).c_str());
479
        } catch(const nesting_depth_exceeded &) {
480
                UserError("Nesting depth exceeded%s", ConstructIniStackExplanation(ini).c_str());
481
        }
482
        return false;
483
}
484
 
485
}