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