dehacked.c 15.2 KB
Newer Older
Alam Ed Arias committed
1 2 3
// SONIC ROBO BLAST 2
//-----------------------------------------------------------------------------
// Copyright (C) 1998-2000 by DooM Legacy Team.
James R. committed
4
// Copyright (C) 1999-2020 by Sonic Team Junior.
Alam Ed Arias committed
5 6 7 8 9 10 11 12 13 14
//
// This program is free software distributed under the
// terms of the GNU General Public License, version 2.
// See the 'LICENSE' file for more details.
//-----------------------------------------------------------------------------
/// \file  dehacked.c
/// \brief Load dehacked file and change tables and text

#include "doomdef.h"
#include "m_cond.h"
15 16
#include "deh_soc.h"
#include "deh_tables.h"
Alam Ed Arias committed
17

18
boolean deh_loaded = false;
Alam Ed Arias committed
19

20 21 22
boolean gamedataadded = false;
boolean titlechanged = false;
boolean introchanged = false;
Alam Ed Arias committed
23

24 25
static int dbg_line;
static INT32 deh_num_warning = 0;
Alam Ed Arias committed
26

27 28 29 30
FUNCPRINTF void deh_warning(const char *first, ...)
{
	va_list argptr;
	char *buf = Z_Malloc(1000, PU_STATIC, NULL);
Alam Ed Arias committed
31

32 33 34
	va_start(argptr, first);
	vsnprintf(buf, 1000, first, argptr); // sizeof only returned 4 here. it didn't like that pointer.
	va_end(argptr);
Alam Ed Arias committed
35

36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52
	if(dbg_line == -1) // Not in a SOC, line number unknown.
		CONS_Alert(CONS_WARNING, "%s\n", buf);
	else
		CONS_Alert(CONS_WARNING, "Line %u: %s\n", dbg_line, buf);

	deh_num_warning++;

	Z_Free(buf);
}

void deh_strlcpy(char *dst, const char *src, size_t size, const char *warntext)
{
	size_t len = strlen(src)+1; // Used to determine if truncation has been done
	if (len > size)
		deh_warning("%s exceeds max length of %s", warntext, sizeu1(size-1));
	strlcpy(dst, src, size);
}
Alam Ed Arias committed
53 54 55 56 57 58 59 60 61

ATTRINLINE static FUNCINLINE char myfget_color(MYFILE *f)
{
	char c = *f->curpos++;
	if (c == '^') // oh, nevermind then.
		return '^';

	if (c >= '0' && c <= '9')
		return 0x80+(c-'0');
62 63 64 65 66 67

	c = tolower(c);

	if (c >= 'a' && c <= 'f')
		return 0x80+10+(c-'a');

Alam Ed Arias committed
68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123
	return 0x80; // Unhandled -- default to no color
}

ATTRINLINE static FUNCINLINE char myfget_hex(MYFILE *f)
{
	char c = *f->curpos++, endchr = 0;
	if (c == '\\') // oh, nevermind then.
		return '\\';

	if (c >= '0' && c <= '9')
		endchr += (c-'0') << 4;
	else if (c >= 'A' && c <= 'F')
		endchr += ((c-'A') + 10) << 4;
	else if (c >= 'a' && c <= 'f')
		endchr += ((c-'a') + 10) << 4;
	else // invalid. stop and return a question mark.
		return '?';

	c = *f->curpos++;
	if (c >= '0' && c <= '9')
		endchr += (c-'0');
	else if (c >= 'A' && c <= 'F')
		endchr += ((c-'A') + 10);
	else if (c >= 'a' && c <= 'f')
		endchr += ((c-'a') + 10);
	else // invalid. stop and return a question mark.
		return '?';

	return endchr;
}

char *myfgets(char *buf, size_t bufsize, MYFILE *f)
{
	size_t i = 0;
	if (myfeof(f))
		return NULL;
	// we need one byte for a null terminated string
	bufsize--;
	while (i < bufsize && !myfeof(f))
	{
		char c = *f->curpos++;
		if (c == '^')
			buf[i++] = myfget_color(f);
		else if (c == '\\')
			buf[i++] = myfget_hex(f);
		else if (c != '\r')
			buf[i++] = c;
		if (c == '\n')
			break;
	}
	buf[i] = '\0';

	dbg_line++;
	return buf;
}

124
char *myhashfgets(char *buf, size_t bufsize, MYFILE *f)
Alam Ed Arias committed
125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142
{
	size_t i = 0;
	if (myfeof(f))
		return NULL;
	// we need one byte for a null terminated string
	bufsize--;
	while (i < bufsize && !myfeof(f))
	{
		char c = *f->curpos++;
		if (c == '^')
			buf[i++] = myfget_color(f);
		else if (c == '\\')
			buf[i++] = myfget_hex(f);
		else if (c != '\r')
			buf[i++] = c;
		if (c == '\n') // Ensure debug line is right...
			dbg_line++;
		if (c == '#')
143
		{
144 145
			if (i > 0) // don't let i wrap past 0
				i--; // don't include hash char in string
Alam Ed Arias committed
146
			break;
147 148
		}
	}
149 150
	if (buf[i] != '#') // don't include hash char in string
		i++;
151
	buf[i] = '\0';
152

Alam Ed Arias committed
153 154 155
	return buf;
}

156 157 158
// Used when you do something invalid like read a bad item number
// to prevent extra unnecessary errors
static void ignorelines(MYFILE *f)
Alam Ed Arias committed
159
{
160 161
	char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
	do
Alam Ed Arias committed
162
	{
163 164 165 166 167 168 169
		if (myfgets(s, MAXLINELEN, f))
		{
			if (s[0] == '\n')
				break;
		}
	} while (!myfeof(f));
	Z_Free(s);
Alam Ed Arias committed
170 171
}

172
static void DEH_LoadDehackedFile(MYFILE *f, boolean mainfile)
Alam Ed Arias committed
173 174
{
	char *s = Z_Malloc(MAXLINELEN, PU_STATIC, NULL);
175
	char textline[MAXLINELEN];
Alam Ed Arias committed
176 177 178 179
	char *word;
	char *word2;
	INT32 i;

180 181
	if (!deh_loaded)
		initfreeslots();
Jaime Ita Passos committed
182

183
	deh_num_warning = 0;
Jaime Ita Passos committed
184

185 186 187 188 189
	gamedataadded = titlechanged = introchanged = false;

	// it doesn't test the version of SRB2 and version of dehacked file
	dbg_line = -1; // start at -1 so the first line is 0.
	while (!myfeof(f))
Alam Ed Arias committed
190
	{
191 192 193 194 195 196 197 198 199 200 201 202
		char origpos[128];
		INT32 size = 0;
		char *traverse;

		myfgets(s, MAXLINELEN, f);
		memcpy(textline, s, MAXLINELEN);
		if (s[0] == '\n' || s[0] == '#')
			continue;

		traverse = s;

		while (traverse[0] != '\n')
Alam Ed Arias committed
203
		{
204 205 206 207 208 209
			traverse++;
			size++;
		}

		strncpy(origpos, s, size);
		origpos[size] = '\0';
Alam Ed Arias committed
210

211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242
		if (NULL != (word = strtok(s, " "))) {
			strupr(word);
			if (word[strlen(word)-1] == '\n')
				word[strlen(word)-1] = '\0';
		}
		if (word)
		{
			if (fastcmp(word, "FREESLOT"))
			{
				readfreeslots(f);
				continue;
			}
			else if (fastcmp(word, "MAINCFG"))
			{
				readmaincfg(f);
				continue;
			}
			else if (fastcmp(word, "WIPES"))
			{
				readwipes(f);
				continue;
			}
			word2 = strtok(NULL, " ");
			if (word2) {
				strupr(word2);
				if (word2[strlen(word2) - 1] == '\n')
					word2[strlen(word2) - 1] = '\0';
				i = atoi(word2);
			}
			else
				i = 0;
			if (fastcmp(word, "CHARACTER"))
Jaime Ita Passos committed
243
			{
244 245 246
				if (i >= 0 && i < 32)
					readPlayer(f, i);
				else
Jaime Ita Passos committed
247
				{
248 249
					deh_warning("Character %d out of range (0 - 31)", i);
					ignorelines(f);
Jaime Ita Passos committed
250
				}
251
				continue;
Jaime Ita Passos committed
252
			}
253
			else if (fastcmp(word, "EMBLEM"))
Alam Ed Arias committed
254
			{
255 256 257 258 259 260
				if (!mainfile && !gamedataadded)
				{
					deh_warning("You must define a custom gamedata to use \"%s\"", word);
					ignorelines(f);
				}
				else
Alam Ed Arias committed
261
				{
262 263 264 265 266 267 268 269 270 271
					if (!word2)
						i = numemblems + 1;

					if (i > 0 && i <= MAXEMBLEMS)
					{
						if (numemblems < i)
							numemblems = i;
						reademblemdata(f, i);
					}
					else
Alam Ed Arias committed
272
					{
273 274
						deh_warning("Emblem number %d out of range (1 - %d)", i, MAXEMBLEMS);
						ignorelines(f);
Alam Ed Arias committed
275 276
					}
				}
277 278 279 280 281
				continue;
			}
			else if (fastcmp(word, "EXTRAEMBLEM"))
			{
				if (!mainfile && !gamedataadded)
Alam Ed Arias committed
282
				{
283 284
					deh_warning("You must define a custom gamedata to use \"%s\"", word);
					ignorelines(f);
Alam Ed Arias committed
285 286 287
				}
				else
				{
288 289
					if (!word2)
						i = numextraemblems + 1;
Alam Ed Arias committed
290

291 292 293 294 295 296 297 298 299 300
					if (i > 0 && i <= MAXEXTRAEMBLEMS)
					{
						if (numextraemblems < i)
							numextraemblems = i;
						readextraemblemdata(f, i);
					}
					else
					{
						deh_warning("Extra emblem number %d out of range (1 - %d)", i, MAXEXTRAEMBLEMS);
						ignorelines(f);
Alam Ed Arias committed
301 302 303 304 305
					}
				}
				continue;
			}
			if (word2)
306
			{
307
				if (fastcmp(word, "THING") || fastcmp(word, "MOBJ") || fastcmp(word, "OBJECT"))
Jaime Ita Passos committed
308
				{
309 310 311 312 313
					if (i == 0 && word2[0] != '0') // If word2 isn't a number
						i = get_mobjtype(word2); // find a thing by name
					if (i < NUMMOBJTYPES && i > 0)
						readthing(f, i);
					else
Jaime Ita Passos committed
314
					{
315 316
						deh_warning("Thing %d out of range (1 - %d)", i, NUMMOBJTYPES-1);
						ignorelines(f);
Jaime Ita Passos committed
317 318
					}
				}
319
				else if (fastcmp(word, "SKINCOLOR") || fastcmp(word, "COLOR"))
Alam Ed Arias committed
320
				{
321 322 323 324 325
					if (i == 0 && word2[0] != '0') // If word2 isn't a number
						i = get_skincolor(word2); // find a skincolor by name
					if (i && i < numskincolors)
						readskincolor(f, i);
					else
Alam Ed Arias committed
326
					{
327 328
						deh_warning("Skincolor %d out of range (1 - %d)", i, numskincolors-1);
						ignorelines(f);
Alam Ed Arias committed
329 330
					}
				}
331 332 333 334 335 336 337 338 339 340
				else if (fastcmp(word, "SPRITE2"))
				{
					if (i == 0 && word2[0] != '0') // If word2 isn't a number
						i = get_sprite2(word2); // find a sprite by name
					if (i < (INT32)free_spr2 && i >= (INT32)SPR2_FIRSTFREESLOT)
						readsprite2(f, i);
					else
					{
						deh_warning("Sprite2 number %d out of range (%d - %d)", i, SPR2_FIRSTFREESLOT, free_spr2-1);
						ignorelines(f);
Alam Ed Arias committed
341
					}
342 343 344 345 346 347 348 349 350 351 352
				}
#ifdef HWRENDER
				else if (fastcmp(word, "LIGHT"))
				{
					// TODO: Read lights by name
					if (i > 0 && i < NUMLIGHTS)
						readlight(f, i);
					else
					{
						deh_warning("Light number %d out of range (1 - %d)", i, NUMLIGHTS-1);
						ignorelines(f);
Alam Ed Arias committed
353
					}
354 355 356 357 358 359 360 361 362 363 364 365
				}
#endif
				else if (fastcmp(word, "SPRITE") || fastcmp(word, "SPRITEINFO"))
				{
					if (i == 0 && word2[0] != '0') // If word2 isn't a number
						i = get_sprite(word2); // find a sprite by name
					if (i < NUMSPRITES && i > 0)
						readspriteinfo(f, i, false);
					else
					{
						deh_warning("Sprite number %d out of range (0 - %d)", i, NUMSPRITES-1);
						ignorelines(f);
James Hale committed
366
					}
367 368
				}
				else if (fastcmp(word, "SPRITE2INFO"))
Jaime Ita Passos committed
369
				{
370 371 372 373 374 375 376 377 378
					if (i == 0 && word2[0] != '0') // If word2 isn't a number
						i = get_sprite2(word2); // find a sprite by name
					if (i < NUMPLAYERSPRITES && i >= 0)
						readspriteinfo(f, i, true);
					else
					{
						deh_warning("Sprite2 number %d out of range (0 - %d)", i, NUMPLAYERSPRITES-1);
						ignorelines(f);
					}
Jaime Ita Passos committed
379
				}
380
				else if (fastcmp(word, "LEVEL"))
kaysrishaq committed
381
				{
382 383 384 385 386 387
					// Support using the actual map name,
					// i.e., Level AB, Level FZ, etc.

					// Convert to map number
					if (word2[0] >= 'A' && word2[0] <= 'Z')
						i = M_MapNumber(word2[0], word2[1]);
kaysrishaq committed
388

389 390 391
					if (i > 0 && i <= NUMMAPS)
						readlevelheader(f, i);
					else
kaysrishaq committed
392
					{
393 394
						deh_warning("Level number %d out of range (1 - %d)", i, NUMMAPS);
						ignorelines(f);
kaysrishaq committed
395
					}
396 397 398 399 400 401 402 403
				}
				else if (fastcmp(word, "GAMETYPE"))
				{
					// Get the gametype name from textline
					// instead of word2, so that gametype names
					// aren't allcaps
					INT32 c;
					for (c = 0; c < MAXLINELEN; c++)
kaysrishaq committed
404
					{
405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422
						if (textline[c] == '\0')
							break;
						if (textline[c] == ' ')
						{
							char *gtname = (textline+c+1);
							if (gtname)
							{
								// remove funny characters
								INT32 j;
								for (j = 0; j < (MAXLINELEN - c); j++)
								{
									if (gtname[j] == '\0')
										break;
									if (gtname[j] < 32)
										gtname[j] = '\0';
								}
								readgametype(f, gtname);
							}
kaysrishaq committed
423
							break;
424
						}
kaysrishaq committed
425 426
					}
				}
427 428 429 430 431 432 433 434 435
				else if (fastcmp(word, "CUTSCENE"))
				{
					if (i > 0 && i < 129)
						readcutscene(f, i - 1);
					else
					{
						deh_warning("Cutscene number %d out of range (1 - 128)", i);
						ignorelines(f);
					}
James Hale committed
436
				}
437
				else if (fastcmp(word, "PROMPT"))
438
				{
439 440
					if (i > 0 && i < MAX_PROMPTS)
						readtextprompt(f, i - 1);
441 442
					else
					{
443 444
						deh_warning("Prompt number %d out of range (1 - %d)", i, MAX_PROMPTS);
						ignorelines(f);
445 446
					}
				}
447
				else if (fastcmp(word, "FRAME") || fastcmp(word, "STATE"))
Jaime Ita Passos committed
448
				{
449 450 451 452 453
					if (i == 0 && word2[0] != '0') // If word2 isn't a number
						i = get_state(word2); // find a state by name
					if (i < NUMSTATES && i >= 0)
						readframe(f, i);
					else
Jaime Ita Passos committed
454
					{
455 456
						deh_warning("Frame %d out of range (0 - %d)", i, NUMSTATES-1);
						ignorelines(f);
Jaime Ita Passos committed
457 458
					}
				}
459
				else if (fastcmp(word, "SOUND"))
Jaime Ita Passos committed
460
				{
461 462 463 464 465 466 467 468 469
					if (i == 0 && word2[0] != '0') // If word2 isn't a number
						i = get_sfx(word2); // find a sound by name
					if (i < NUMSFX && i > 0)
						readsound(f, i);
					else
					{
						deh_warning("Sound %d out of range (1 - %d)", i, NUMSFX-1);
						ignorelines(f);
					}
Jaime Ita Passos committed
470
				}
471
				else if (fastcmp(word, "HUDITEM"))
Jaime Ita Passos committed
472
				{
473 474 475 476 477
					if (i == 0 && word2[0] != '0') // If word2 isn't a number
						i = get_huditem(word2); // find a huditem by name
					if (i >= 0 && i < NUMHUDITEMS)
						readhuditem(f, i);
					else
Jaime Ita Passos committed
478
					{
479 480
						deh_warning("HUD item number %d out of range (0 - %d)", i, NUMHUDITEMS-1);
						ignorelines(f);
Jaime Ita Passos committed
481 482
					}
				}
483
				else if (fastcmp(word, "MENU"))
Jaime Ita Passos committed
484
				{
485 486 487 488
					if (i == 0 && word2[0] != '0') // If word2 isn't a number
						i = get_menutype(word2); // find a huditem by name
					if (i >= 1 && i < NUMMENUTYPES)
						readmenu(f, i);
toaster committed
489
					else
Alam Ed Arias committed
490
					{
491 492 493 494
						// zero-based, but let's start at 1
						deh_warning("Menu number %d out of range (1 - %d)", i, NUMMENUTYPES-1);
						ignorelines(f);
					}
Alam Ed Arias committed
495
				}
496 497 498 499 500 501 502 503 504 505 506 507 508 509
				else if (fastcmp(word, "UNLOCKABLE"))
				{
					if (!mainfile && !gamedataadded)
					{
						deh_warning("You must define a custom gamedata to use \"%s\"", word);
						ignorelines(f);
					}
					else if (i > 0 && i <= MAXUNLOCKABLES)
						readunlockable(f, i - 1);
					else
					{
						deh_warning("Unlockable number %d out of range (1 - %d)", i, MAXUNLOCKABLES);
						ignorelines(f);
					}
Alam Ed Arias committed
510
				}
511 512 513 514 515 516 517 518 519 520 521 522 523 524
				else if (fastcmp(word, "CONDITIONSET"))
				{
					if (!mainfile && !gamedataadded)
					{
						deh_warning("You must define a custom gamedata to use \"%s\"", word);
						ignorelines(f);
					}
					else if (i > 0 && i <= MAXCONDITIONSETS)
						readconditionset(f, (UINT8)i);
					else
					{
						deh_warning("Condition set number %d out of range (1 - %d)", i, MAXCONDITIONSETS);
						ignorelines(f);
					}
James Hale committed
525
				}
526
				else if (fastcmp(word, "SRB2"))
527
				{
528 529 530 531 532 533 534 535 536 537 538 539 540 541 542 543 544 545 546 547 548
					if (isdigit(word2[0]))
					{
						i = atoi(word2);
						if (i != PATCHVERSION)
						{
							deh_warning(
									"Patch is for SRB2 version %d, "
									"only version %d is supported",
									i,
									PATCHVERSION
							);
						}
					}
					else
					{
						deh_warning(
								"SRB2 version definition has incorrect format, "
								"use \"SRB2 %d\"",
								PATCHVERSION
						);
					}
549
				}
550 551 552 553 554 555 556
				// Clear all data in certain locations (mostly for unlocks)
				// Unless you REALLY want to piss people off,
				// define a custom gamedata /before/ doing this!!
				// (then again, modifiedgame will prevent game data saving anyway)
				else if (fastcmp(word, "CLEAR"))
				{
					boolean clearall = (fastcmp(word2, "ALL"));
Alam Ed Arias committed
557

558 559 560 561 562
					if (!mainfile && !gamedataadded)
					{
						deh_warning("You must define a custom gamedata to use \"%s\"", word);
						continue;
					}
Alam Ed Arias committed
563

564 565
					if (clearall || fastcmp(word2, "UNLOCKABLES"))
						memset(&unlockables, 0, sizeof(unlockables));
Alam Ed Arias committed
566

567 568 569 570 571
					if (clearall || fastcmp(word2, "EMBLEMS"))
					{
						memset(&emblemlocations, 0, sizeof(emblemlocations));
						numemblems = 0;
					}
Alam Ed Arias committed
572

573 574 575 576
					if (clearall || fastcmp(word2, "EXTRAEMBLEMS"))
					{
						memset(&extraemblems, 0, sizeof(extraemblems));
						numextraemblems = 0;
577
					}
578 579 580 581 582 583

					if (clearall || fastcmp(word2, "CONDITIONSETS"))
						clear_conditionsets();

					if (clearall || fastcmp(word2, "LEVELS"))
						clear_levels();
584
				}
585 586
				else
					deh_warning("Unknown word: %s", word);
James Hale committed
587
			}
588 589
			else
				deh_warning("missing argument for '%s'", word);
James Hale committed
590
		}
591 592 593
		else
			deh_warning("No word in this line: %s", s);
	} // end while
Alam Ed Arias committed
594

595 596 597 598
	if (gamedataadded)
		G_LoadGameData();

	if (gamestate == GS_TITLESCREEN)
Alam Ed Arias committed
599
	{
600
		if (introchanged)
Alam Ed Arias committed
601
		{
602 603 604 605 606 607 608 609 610
			menuactive = false;
			I_UpdateMouseGrab();
			COM_BufAddText("playintro");
		}
		else if (titlechanged)
		{
			menuactive = false;
			I_UpdateMouseGrab();
			COM_BufAddText("exitgame"); // Command_ExitGame_f() but delayed
Alam Ed Arias committed
611 612 613
		}
	}

614 615
	dbg_line = -1;
	if (deh_num_warning)
616
	{
617 618 619 620 621
		CONS_Printf(M_GetText("%d warning%s in the SOC lump\n"), deh_num_warning, deh_num_warning == 1 ? "" : "s");
		if (devparm) {
			I_Error("%s%s",va(M_GetText("%d warning%s in the SOC lump\n"), deh_num_warning, deh_num_warning == 1 ? "" : "s"), M_GetText("See log.txt for details.\n"));
			//while (!I_GetKey())
				//I_OsPolling();
Alam Ed Arias committed
622
		}
623
	}
Alam Ed Arias committed
624

625 626
	deh_loaded = true;
	Z_Free(s);
Alam Ed Arias committed
627 628
}

629 630 631
// read dehacked lump in a wad (there is special trick for for deh
// file that are converted to wad in w_wad.c)
void DEH_LoadDehackedLumpPwad(UINT16 wad, UINT16 lump, boolean mainfile)
Alam Ed Arias committed
632
{
633 634 635 636 637 638 639 640 641
	MYFILE f;
	f.wad = wad;
	f.size = W_LumpLengthPwad(wad, lump);
	f.data = Z_Malloc(f.size + 1, PU_STATIC, NULL);
	W_ReadLumpPwad(wad, lump, f.data);
	f.curpos = f.data;
	f.data[f.size] = 0;
	DEH_LoadDehackedFile(&f, mainfile);
	Z_Free(f.data);
Alam Ed Arias committed
642 643
}

644
void DEH_LoadDehackedLump(lumpnum_t lumpnum)
645
{
646
	DEH_LoadDehackedLumpPwad(WADFILENUM(lumpnum),LUMPNUM(lumpnum), false);
647
}