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