1 //------------------------------------------------------------------------
2 //  MAIN Program
3 //------------------------------------------------------------------------
4 //
5 //  DEH_EDGE  Copyright (C) 2004-2005  The EDGE Team
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 (in COPYING.txt) for more details.
16 //
17 //------------------------------------------------------------------------
18 //
19 //  DEH_EDGE is based on:
20 //
21 //  +  DeHackEd source code, by Greg Lewis.
22 //  -  DOOM source code (C) 1993-1996 id Software, Inc.
23 //  -  Linux DOOM Hack Editor, by Sam Lantinga.
24 //  -  PrBoom's DEH/BEX code, by Ty Halderman, TeamTNT.
25 //
26 //------------------------------------------------------------------------
27 
28 #include "i_defs.h"
29 
30 #include "ammo.h"
31 #include "attacks.h"
32 #include "buffer.h"
33 #include "dh_plugin.h"
34 #include "frames.h"
35 #include "info.h"
36 #include "misc.h"
37 #include "patch.h"
38 #include "rscript.h"
39 #include "sounds.h"
40 #include "storage.h"
41 #include "system.h"
42 #include "things.h"
43 #include "text.h"
44 #include "util.h"
45 #include "wad.h"
46 #include "weapons.h"
47 
48 #include <ctype.h>
49 #include <stdio.h>
50 #include <string.h>
51 #include <stdlib.h>
52 
53 namespace Deh_Edge
54 {
55 
56 class input_buffer_c
57 {
58 public:
59 	parse_buffer_api *buf;
60 	const char *infoname;
61 	bool is_lump;
62 
input_buffer_c(parse_buffer_api * _buf,const char * _info,bool _lump)63 	input_buffer_c(parse_buffer_api *_buf, const char *_info, bool _lump) :
64 		buf(_buf), is_lump(_lump)
65 	{
66 		infoname = StringDup(_info);
67 	}
68 
~input_buffer_c()69 	~input_buffer_c()
70 	{
71 		delete buf;
72 		buf = NULL;
73 
74 		free((void*)infoname);
75 	}
76 };
77 
78 #define MAX_INPUTS  32
79 
80 input_buffer_c *input_bufs[MAX_INPUTS];
81 int num_inputs = 0;
82 
83 const char *output_file = NULL;
84 
85 #define DEFAULT_TARGET  128
86 
87 int target_version;
88 bool quiet_mode;
89 bool all_mode;
90 
91 const dehconvfuncs_t *cur_funcs = NULL;
92 
93 
94 /* ----- user information ----------------------------- */
95 
96 #ifndef DEH_EDGE_PLUGIN
97 
ShowTitle(void)98 void ShowTitle(void)
99 {
100   PrintMsg(
101     "\n"
102 	"=================================================\n"
103     "|    DeHackEd -> EDGE Conversion Tool  V" DEH_EDGE_VERS "     |\n"
104 	"|                                               |\n"
105 	"|  The EDGE Team.  http://edge.sourceforge.net  |\n"
106 	"=================================================\n"
107 	"\n"
108   );
109 }
110 
ShowInfo(void)111 void ShowInfo(void)
112 {
113   PrintMsg(
114     "USAGE:  deh_edge  (Options...)  input.deh (...)  (-o output.wad)\n"
115 	"\n"
116 	"Available options:\n"
117 	"   -e --edge #.##   Specify EDGE version to target.\n"
118 	"   -o --output      Override output filename.\n"
119 	"   -q --quiet       Quiet mode, suppress warnings.\n"
120 	"\n"
121   );
122 }
123 
124 #endif
125 
Startup(void)126 void Startup(void)
127 {
128 	System_Startup();
129 
130 	Ammo::Startup();
131 	Frames::Startup();
132 	Misc::Startup();
133 	Rscript::Startup();
134 	Sounds::Startup();
135 	TextStr::Startup();
136 	Things::Startup();
137 	Weapons::Startup();
138 
139 	Storage::Startup();
140 	WAD::Startup();
141 
142 	/* reset parameters */
143 
144 	num_inputs = 0;
145 	output_file = NULL;
146 
147 	target_version = DEFAULT_TARGET;
148 	quiet_mode = false;
149 	all_mode = false;
150 }
151 
AddFile(const char * filename)152 dehret_e AddFile(const char *filename)
153 {
154 	if (num_inputs >= MAX_INPUTS)
155 	{
156 		SetErrorMsg("Too many input files !!\n");
157 		return DEH_E_BadArgs;
158 	}
159 
160 	if (strlen(ReplaceExtension(filename, NULL)) == 0)
161 	{
162 		SetErrorMsg("Illegal input filename: %s\n", filename);
163 		return DEH_E_BadArgs;
164 	}
165 
166 	if (CheckExtension(filename, "wad") || CheckExtension(filename, "hwa"))
167 	{
168 		SetErrorMsg("Input filename cannot be a WAD file.\n");
169 		return DEH_E_BadArgs;
170 	}
171 
172 	if (CheckExtension(filename, NULL))
173 	{
174 		// memory management here is "optimised" (i.e. a bit dodgy),
175 		// since the result of ReplaceExtension() is an internal static
176 		// buffer.
177 
178 		const char *bex_name = ReplaceExtension(filename, "bex");
179 
180 		if (FileExists(bex_name))
181 		{
182 			parse_buffer_api *buf = Buffer::OpenFile(bex_name);
183 
184 			if (! buf) return DEH_E_NoFile;  // normally won't happen
185 
186 			input_bufs[num_inputs++] = new input_buffer_c(buf,
187 				FileBaseName(bex_name), false);
188 
189 			return DEH_OK;
190 		}
191 
192 		const char *deh_name = ReplaceExtension(filename, "deh");
193 
194 		if (FileExists(deh_name))
195 		{
196 			parse_buffer_api *buf = Buffer::OpenFile(deh_name);
197 
198 			if (! buf) return DEH_E_NoFile;  // normally won't happen
199 
200 			input_bufs[num_inputs++] = new input_buffer_c(buf,
201 				FileBaseName(deh_name), false);
202 
203 			return DEH_OK;
204 		}
205 	}
206 
207 	parse_buffer_api *buf = Buffer::OpenFile(filename);
208 
209 	if (! buf)
210 		return DEH_E_NoFile;
211 
212 	input_bufs[num_inputs++] = new input_buffer_c(buf,
213 		FileBaseName(filename), false);
214 
215 	return DEH_OK;
216 }
217 
FreeInputBuffers(void)218 void FreeInputBuffers(void)
219 {
220 	for (int j = 0; j < num_inputs; j++)
221 	{
222 		if (input_bufs[j])
223 		{
224 			delete input_bufs[j];
225 			input_bufs[j] = NULL;
226 		}
227 	}
228 }
229 
Convert(void)230 dehret_e Convert(void)
231 {
232 	dehret_e result;
233 
234 	// load DEH patch file(s)
235 	for (int j = 0; j < num_inputs; j++)
236 	{
237 		char temp_text[256];
238 		sprintf(temp_text, "Parsing %s", input_bufs[j]->infoname);
239 
240 		ProgressText(temp_text);
241 		ProgressMajor(j * 70 / num_inputs, (j+1) * 70 / num_inputs);
242 
243 		PrintMsg("Loading patch file: %s\n", input_bufs[j]->infoname);
244 
245 		result = Patch::Load(input_bufs[j]->buf);
246 
247 		if (result != DEH_OK)
248 			return result;
249 	}
250 
251 	FreeInputBuffers();
252 
253 	ProgressText("Converting DEH");
254 	ProgressMajor(70, 80);
255 
256 	Storage::ApplyAll();
257 
258 	// do conversions into DDF...
259 	PrintMsg("Converting data into EDGE %d.%02d DDF...\n",
260 		target_version / 100, target_version % 100);
261 
262 	TextStr::SpriteDependencies();
263 	Frames::StateDependencies();
264 	Ammo::AmmoDependencies();
265 	ProgressMinor(1, 5);
266 
267 	Things::FixHeights();
268 	Things::ConvertTHING();
269 	Attacks::ConvertATK();
270 	ProgressMinor(2, 5);
271 
272 	Weapons::ConvertWEAP();
273 	Sounds::ConvertSFX();
274 	Sounds::ConvertMUS();
275 	ProgressMinor(3, 5);
276 
277 	TextStr::ConvertLDF();
278 	Rscript::ConvertRAD();
279 	ProgressMinor(4, 5);
280 
281 	Storage::RestoreAll();
282 
283 	PrintMsg("\n");
284 	PrintMsg("Writing WAD file: %s\n", output_file);
285 
286 	ProgressText("Writing HWA file");
287 	ProgressMajor(80, 100);
288 
289 	result = WAD::WriteFile(output_file);
290 
291 	PrintMsg("\n");
292 	ProgressMajor(100, 100);
293 
294 	return result;
295 }
296 
Shutdown(void)297 void Shutdown(void)
298 {
299 	WAD::Shutdown();
300 	System_Shutdown();
301 }
302 
303 
304 /* ----- option handling ----------------------------- */
305 
306 #ifndef DEH_EDGE_PLUGIN
307 
ParseArgs(int argc,char ** argv)308 void ParseArgs(int argc, char **argv)
309 {
310 	const char *first_input = NULL;
311 
312 	while (argc > 0)
313 	{
314 		const char *opt = *argv;
315 
316 		argv++, argc--;
317 
318 		// input filename ?
319 		if (*opt != '-')
320 		{
321 			if (! first_input)
322 				first_input = opt;
323 
324 			if (AddFile(opt) != DEH_OK)
325 				FatalError("%s", GetErrorMsg());
326 
327 			continue;
328 		}
329 
330 		if (opt[1] == '-') opt++;
331 
332 		if (! opt[1])
333 			FatalError("Illegal option %s\n", opt);
334 
335 		// output filename ?
336 		if (StrCaseCmp(opt, "-o") == 0 || StrCaseCmp(opt, "-output") == 0)
337 		{
338 			if (argc == 0 || argv[0][0] == '-')
339 				FatalError("Missing output filename !\n");
340 
341 			if (output_file)
342 				FatalError("Output file already given.\n");
343 
344 			output_file = StringDup(*argv);
345 
346 			argv++, argc--;
347 			continue;
348 		}
349 
350 		// version number
351 		if (StrCaseCmp(opt, "-e") == 0 || StrCaseCmp(opt, "-edge") == 0)
352 		{
353 			const char *ver = argv[0];
354 
355 			if (argc == 0 || ver[0] == '-')
356 				FatalError("Missing version number !\n");
357 
358 			if (isdigit(ver[0]) && ver[1] == '.' &&
359 				isdigit(ver[2]) && isdigit(ver[3]))
360 			{
361 				target_version = (ver[0] - '0') * 100 +
362 					(ver[2] - '0') * 10 + (ver[3] - '0');
363 			}
364 			else
365 				FatalError("Badly formed version number.\n");
366 
367 			argv++, argc--;
368 			continue;
369 		}
370 
371 		if (StrCaseCmp(opt, "-q") == 0 || StrCaseCmp(opt, "-quiet") == 0)
372 		{
373 			quiet_mode = true;
374 			continue;
375 		}
376 		if (StrCaseCmp(opt, "-full") == 0)
377 		{
378 			all_mode = true;
379 			continue;
380 		}
381 
382 		FatalError("Unknown option: %s\n", opt);
383 	}
384 
385 	// choose output filename when not specified
386 
387 	if (! output_file && first_input)
388 	{
389 		const char *base_name = ReplaceExtension(first_input, NULL);
390 
391 		output_file = StringNew(strlen(base_name) + 16);
392 
393 		strcpy((char *)output_file, base_name);
394 		strcat((char *)output_file, (target_version >= 128) ? ".hwa" : "_deh.wad");
395 	}
396 }
397 
398 #endif  // DEH_EDGE_PLUGIN
399 
ValidateArgs(void)400 dehret_e ValidateArgs(void)
401 {
402 	if (num_inputs == 0)
403 	{
404 		SetErrorMsg("Missing input filename !\n");
405 		return DEH_E_BadArgs;
406 	}
407 
408 	if (target_version < 123 || target_version >= 300)
409 	{
410 		SetErrorMsg("Illegal version number: %d.%02d\n", target_version / 100,
411 			target_version % 100);
412 		return DEH_E_BadArgs;
413 	}
414 
415 	if (! output_file)
416 	{
417 		SetErrorMsg("Missing output filename !\n");
418 		return DEH_E_BadArgs;
419 	}
420 
421 	if (CheckExtension(output_file, "deh") ||
422 	    CheckExtension(output_file, "bex"))
423 	{
424 		SetErrorMsg("Output filename cannot be a DEH file.\n");
425 		return DEH_E_BadArgs;
426 	}
427 
428 	if (CheckExtension(output_file, NULL))
429 	{
430 		output_file = StringDup(ReplaceExtension(output_file,
431 			(target_version >= 128) ? "hwa" : "wad"));
432 	}
433 
434 	return DEH_OK;
435 }
436 
437 }  // Deh_Edge
438 
439 //------------------------------------------------------------------------
440 
441 #ifndef DEH_EDGE_PLUGIN
442 
443 namespace Deh_Edge
444 {
445 
main(int argc,char ** argv)446 int main(int argc, char **argv)
447 {
448 	Startup();
449 	ShowTitle();
450 
451 	// skip program name itself
452 	argv++, argc--;
453 
454 	if (argc <= 0)
455 	{
456 		ShowInfo();
457 		System_Shutdown();
458 
459 		return 1;
460 	}
461 
462 	if (StrCaseCmp(argv[0], "/?") == 0 ||
463 		StrCaseCmp(argv[0], "-h") == 0 ||
464 		StrCaseCmp(argv[0], "-help") == 0 ||
465 		StrCaseCmp(argv[0], "--help") == 0)
466 	{
467 		ShowInfo();
468 		System_Shutdown();
469 
470 		return 1;
471 	}
472 
473 	ParseArgs(argc, argv);
474 
475 	if (ValidateArgs() != DEH_OK)
476 		FatalError("%s", GetErrorMsg());
477 
478 	if (Convert() != DEH_OK)
479 		FatalError("%s", GetErrorMsg());
480 
481 	Shutdown();
482 
483 	return 0;
484 }
485 
486 }  // Deh_Edge
487 
main(int argc,char ** argv)488 int main(int argc, char **argv)
489 {
490 	return Deh_Edge::main(argc, argv);
491 }
492 
493 #endif  // DEH_EDGE_PLUGIN
494 
495 //------------------------------------------------------------------------
496 
497 #ifdef DEH_EDGE_PLUGIN
498 
DehEdgeStartup(const dehconvfuncs_t * funcs)499 void DehEdgeStartup(const dehconvfuncs_t *funcs)
500 {
501 	Deh_Edge::Startup();
502 	Deh_Edge::cur_funcs = funcs;
503 
504 	Deh_Edge::PrintMsg("*** DeHackEd -> EDGE Conversion Tool V%s ***\n",
505 		DEH_EDGE_VERS);
506 }
507 
DehEdgeGetError(void)508 const char *DehEdgeGetError(void)
509 {
510 	return Deh_Edge::GetErrorMsg();
511 }
512 
DehEdgeSetVersion(int version)513 dehret_e DehEdgeSetVersion(int version)
514 {
515 	Deh_Edge::target_version = version;  // validated later
516 
517 	return DEH_OK;
518 }
519 
DehEdgeSetQuiet(int quiet)520 dehret_e DehEdgeSetQuiet(int quiet)
521 {
522 	Deh_Edge::quiet_mode = (quiet != 0);
523 
524 	return DEH_OK;
525 }
526 
DehEdgeAddFile(const char * filename)527 dehret_e DehEdgeAddFile(const char *filename)
528 {
529 	return Deh_Edge::AddFile(filename);
530 }
531 
DehEdgeAddLump(const char * data,int length,const char * infoname)532 dehret_e DehEdgeAddLump(const char *data, int length, const char *infoname)
533 {
534 	if (Deh_Edge::num_inputs >= MAX_INPUTS)
535 	{
536 		Deh_Edge::SetErrorMsg("Too many input lumps !!\n");
537 		return DEH_E_BadArgs;
538 	}
539 
540 	Deh_Edge::input_bufs[Deh_Edge::num_inputs++] =
541 		new Deh_Edge::input_buffer_c(
542 			Deh_Edge::Buffer::OpenLump(data, length), infoname, true);
543 
544 	return DEH_OK;
545 }
546 
DehEdgeRunConversion(const char * out_name)547 dehret_e DehEdgeRunConversion(const char *out_name)
548 {
549 	if (! out_name)
550 		Deh_Edge::FatalError("RunConversion: missing output name.\n");
551 
552 	Deh_Edge::output_file = out_name;
553 
554 	dehret_e result = Deh_Edge::ValidateArgs();
555 
556 	if (result != DEH_OK)
557 		return result;
558 
559 	return Deh_Edge::Convert();
560 }
561 
DehEdgeShutdown(void)562 void DehEdgeShutdown(void)
563 {
564 	Deh_Edge::Shutdown();
565 	Deh_Edge::cur_funcs = NULL;
566 }
567 
568 #endif  // DEH_EDGE_PLUGIN
569