1 /*
2 C-Dogs SDL
3 A port of the legendary (and fun) action/arcade cdogs.
4 Copyright (c) 2013-2016, 2019-2021 Cong Xu
5 All rights reserved.
6
7 Redistribution and use in source and binary forms, with or without
8 modification, are permitted provided that the following conditions are met:
9
10 Redistributions of source code must retain the above copyright notice, this
11 list of conditions and the following disclaimer.
12 Redistributions in binary form must reproduce the above copyright notice,
13 this list of conditions and the following disclaimer in the documentation
14 and/or other materials provided with the distribution.
15
16 THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
17 AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
18 IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
19 ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
20 LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
21 CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
22 SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
23 INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
24 CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
25 ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
26 POSSIBILITY OF SUCH DAMAGE.
27 */
28 #include "command_line.h"
29
30 #include <stdio.h>
31
32 #include <SDL_image.h>
33 #include <SDL_mixer.h>
34 #include <SDL_version.h>
35
36 #include <cdogs/XGetopt.h>
37 #include <cdogs/config.h>
38 #include <cdogs/log.h>
39 #include <cdogs/sys_config.h>
40 #include <cdogs/utils.h>
41
PrintTitle(void)42 void PrintTitle(void)
43 {
44 printf("C-Dogs SDL %s\n", CDOGS_SDL_VERSION);
45 printf(
46 "SDL version %d.%d.%d\n", SDL_MAJOR_VERSION, SDL_MINOR_VERSION,
47 SDL_PATCHLEVEL);
48 printf(
49 "SDL_image version %d.%d.%d\n", SDL_IMAGE_MAJOR_VERSION,
50 SDL_IMAGE_MINOR_VERSION, SDL_IMAGE_PATCHLEVEL);
51 printf(
52 "SDL_mixer version %d.%d.%d\n", SDL_MIXER_MAJOR_VERSION,
53 SDL_MIXER_MINOR_VERSION, SDL_MIXER_PATCHLEVEL);
54 }
55
PrintHelp(void)56 void PrintHelp(void)
57 {
58 printf(
59 "%s\n", "Video Options:\n"
60 " --fullscreen Try and use a fullscreen video mode.\n"
61 " --scale=n Set pixel size to a factor of n\n"
62 " Factors: 1 - 16\n"
63 " --screen=WxH Set window size to W x H\n");
64
65 printf(
66 "%s\n",
67 "Config:\n"
68 " --config=K,V Set arbitrary config option\n"
69 " Example: --config=Game.FriendlyFire=true\n"
70 " --config List all config options\n");
71
72 printf(
73 "Logging: logging is enabled per module and set at certain levels.\n"
74 "Log modules are: ");
75 for (int i = 0; i < (int)LM_COUNT; i++)
76 {
77 printf("%s", LogModuleName((LogModule)i));
78 if (i < (int)LM_COUNT - 1)
79 printf(", ");
80 else
81 printf("\n");
82 }
83 printf("Log levels are: ");
84 for (int i = 0; i < (int)LL_COUNT; i++)
85 {
86 printf("%s", LogLevelName((LogLevel)i));
87 if (i < (int)LL_COUNT - 1)
88 printf(", ");
89 else
90 printf("\n");
91 }
92 printf(
93 " --log=M,L Enable logging for module M at level L.\n\n"
94 " --log=L Enable logging for all modules at level L.\n\n"
95 " --logfile=F Log to file by filename\n\n");
96
97 printf(
98 "%s\n",
99 "Other:\n"
100 " --connect=host (Experimental) connect to a game server\n"
101 " --demo (Experimental) run game for 30 seconds\n");
102 }
103
ProcessCommandLine(char * buf,const int argc,char * argv[])104 void ProcessCommandLine(char *buf, const int argc, char *argv[])
105 {
106 buf[0] = '\0';
107 for (int i = 0; i < argc; i++)
108 {
109 strcat(buf, " ");
110 // HACK: for OS X, blank out the -psn_XXXX argument so that it doesn't
111 // break arg parsing
112 #ifdef __APPLE__
113 if (strncmp(argv[i], "-psn", strlen("-psn")) == 0)
114 {
115 argv[i] = "";
116 }
117 #endif
118 strcat(buf, argv[i]);
119 }
120 }
121
122 static void PrintConfig(const Config *c, const int indent);
ParseArgs(const int argc,char * argv[],ENetAddress * connectAddr,const char ** loadCampaign,int * demoQuitTimer)123 bool ParseArgs(
124 const int argc, char *argv[], ENetAddress *connectAddr,
125 const char **loadCampaign, int *demoQuitTimer)
126 {
127 struct option longopts[] = {
128 {"fullscreen", no_argument, NULL, 'f'},
129 {"scale", required_argument, NULL, 's'},
130 {"screen", required_argument, NULL, 'c'},
131 {"connect", required_argument, NULL, 'x'},
132 {"config", optional_argument, NULL, 'C'},
133 {"log", required_argument, NULL, 1000},
134 {"logfile", required_argument, NULL, 1001},
135 {"demo", no_argument, NULL, 1002},
136 {"help", no_argument, NULL, 'h'},
137 {0, 0, NULL, 0}};
138 int opt = 0;
139 int idx = 0;
140 while ((opt = getopt_long(
141 argc, argv, "fs:c:x:C::\0:\0:h", longopts, &idx)) != -1)
142 {
143 switch (opt)
144 {
145 case 'f':
146 ConfigGet(&gConfig, "Graphics.Fullscreen")->u.Bool.Value = true;
147 break;
148 case 's':
149 ConfigSetInt(&gConfig, "Graphics.ScaleFactor", atoi(optarg));
150 break;
151 case 'c':
152 sscanf(
153 optarg, "%dx%d",
154 &ConfigGet(&gConfig, "Graphics.WindowWidth")->u.Int.Value,
155 &ConfigGet(&gConfig, "Graphics.WindowHeight")->u.Int.Value);
156 LOG(LM_MAIN, LL_DEBUG, "Window size %dx%d set...",
157 ConfigGetInt(&gConfig, "Graphics.WindowWidth"),
158 ConfigGetInt(&gConfig, "Graphics.WindowHeight"));
159 break;
160 case 'm': {
161 ConfigGet(&gConfig, "Graphics.ShakeMultiplier")->u.Int.Value =
162 MAX(atoi(optarg), 0);
163 printf(
164 "Shake multiplier: %d\n",
165 ConfigGetInt(&gConfig, "Graphics.ShakeMultiplier"));
166 }
167 break;
168 case 'h':
169 PrintHelp();
170 return false;
171 case 1000: {
172 char *comma = strchr(optarg, ',');
173 if (comma)
174 {
175 // Set logging level for a single module
176 // The module and level are comma separated
177 *comma = '\0';
178 const LogLevel ll = StrLogLevel(comma + 1);
179 LogModuleSetLevel(StrLogModule(optarg), ll);
180 printf("Logging %s at %s\n", optarg, LogLevelName(ll));
181 }
182 else
183 {
184 // Set logging level for all modules
185 const LogLevel ll = StrLogLevel(optarg);
186 for (int i = 0; i < (int)LM_COUNT; i++)
187 {
188 LogModuleSetLevel((LogModule)i, ll);
189 }
190 printf("Logging everything at %s\n", LogLevelName(ll));
191 }
192 }
193 break;
194 case 1001:
195 LogOpenFile(optarg);
196 break;
197 case 1002:
198 *demoQuitTimer = 30 * 1000;
199 printf("Entering demo mode; will auto-quit in 30 seconds\n");
200 break;
201 case 'x':
202 if (enet_address_set_host(connectAddr, optarg) != 0)
203 {
204 printf("Error: unknown host %s\n", optarg);
205 }
206 break;
207 case 'C':
208 if (optarg == NULL)
209 {
210 PrintConfig(&gConfig, 0);
211 return false;
212 }
213 else
214 {
215 char *comma = strchr(optarg, ',');
216 if (comma == NULL)
217 {
218 const Config *c = ConfigGet(&gConfig, optarg);
219 if (c == NULL)
220 {
221 PrintHelp();
222 }
223 else
224 {
225 PrintConfig(c, 0);
226 }
227 return false;
228 }
229 *comma = '\0';
230 const char *value = comma + 1;
231 if (!ConfigTrySetFromString(&gConfig, optarg, value))
232 {
233 PrintHelp();
234 return false;
235 }
236 }
237 default:
238 PrintHelp();
239 // Ignore unknown arguments
240 break;
241 }
242 }
243 if (optind < argc)
244 {
245 // non-option ARGV-elements
246 for (; optind < argc; optind++)
247 {
248 // Load campaign
249 *loadCampaign = argv[optind];
250 }
251 }
252
253 return true;
254 }
PrintConfig(const Config * c,const int indent)255 static void PrintConfig(const Config *c, const int indent)
256 {
257 // Print this config
258 for (int i = 0; i < indent; i++)
259 {
260 printf("-");
261 }
262 if (c->Name != NULL)
263 {
264 printf("%s\n", c->Name);
265 }
266 if (c->Type == CONFIG_TYPE_GROUP)
267 {
268 CA_FOREACH(const Config, child, c->u.Group)
269 PrintConfig(child, indent + 1);
270 CA_FOREACH_END()
271 }
272 }
273