mirror of
https://github.com/ZDoom/gzdoom.git
synced 2025-05-07 17:41:02 +00:00
1642 lines
No EOL
41 KiB
C
1642 lines
No EOL
41 KiB
C
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <malloc.h>
|
|
#include <string.h>
|
|
#include <stddef.h>
|
|
|
|
#include "doomtype.h"
|
|
#include "doomstat.h"
|
|
#include "info.h"
|
|
#include "d_dehack.h"
|
|
#include "s_sound.h"
|
|
#include "d_items.h"
|
|
#include "g_level.h"
|
|
#include "m_cheat.h"
|
|
#include "cmdlib.h"
|
|
#include "dstrings.h"
|
|
#include "m_alloc.h"
|
|
#include "m_misc.h"
|
|
#include "w_wad.h"
|
|
|
|
|
|
// Miscellaneous info that used to be constant
|
|
struct DehInfo deh = {
|
|
100, // .StartHealth
|
|
50, // .StartBullets
|
|
100, // .MaxHealth
|
|
200, // .MaxArmor
|
|
1, // .GreenAC
|
|
2, // .BlueAC
|
|
200, // .MaxSoulsphere
|
|
100, // .SoulsphereHealth
|
|
200, // .MegasphereHealth
|
|
100, // .GodHealth
|
|
200, // .FAArmor
|
|
2, // .FAAC
|
|
200, // .KFAArmor
|
|
2, // .KFAAC
|
|
40, // .BFGCells
|
|
0, // .Infight
|
|
};
|
|
|
|
// These are the original heights of every Doom 2 thing. They are used if a patch
|
|
// specifies that a thing should be hanging from the ceiling but doesn't specify
|
|
// a height for the thing, since these are the heights it probably wants.
|
|
|
|
static byte OrgHeights[] = {
|
|
56, 56, 56, 56, 16, 56, 8, 16, 64, 8, 56, 56,
|
|
56, 56, 56, 64, 8, 64, 56, 100, 64, 110, 56, 56,
|
|
72, 16, 32, 32, 32, 16, 42, 8, 8, 8,
|
|
8, 8, 8, 16, 16, 16, 16, 16, 16, 16,
|
|
16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
|
16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
|
16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
|
16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
|
16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
|
16, 16, 16, 16, 16, 16, 16, 68, 84, 84,
|
|
68, 52, 84, 68, 52, 52, 68, 16, 16, 16,
|
|
16, 16, 16, 16, 16, 16, 16, 16, 16, 16,
|
|
16, 16, 16, 16, 88, 88, 64, 64, 64, 64,
|
|
16, 16, 16
|
|
};
|
|
|
|
#define LINESIZE 2048
|
|
|
|
#define CHECKKEY(a,b) if (!stricmp (Line1, (a))) (b) = atoi(Line2);
|
|
|
|
static char *PatchFile, *PatchPt;
|
|
static char *Line1, *Line2;
|
|
static int dversion, pversion;
|
|
static BOOL including, includenotext;
|
|
|
|
static const char *unknown_str = "Unknown key %s encountered in %s %d.\n";
|
|
|
|
// This is an offset to be used for computing the text stuff.
|
|
// Straight from the DeHackEd source which was
|
|
// Written by Greg Lewis, gregl@umich.edu.
|
|
static int toff[] = {129044, 129044, 129044, 129284, 129380};
|
|
|
|
// A conversion array to convert from the 448 code pointers to the 966
|
|
// Frames that exist.
|
|
// Again taken from the DeHackEd source.
|
|
static short codepconv[448] = { 1, 2, 3, 4, 6, 9, 10, 11, 12, 14,
|
|
16, 17, 18, 19, 20, 22, 29, 30, 31, 32,
|
|
33, 34, 36, 38, 39, 41, 43, 44, 47, 48,
|
|
49, 50, 51, 52, 53, 54, 55, 56, 57, 58,
|
|
59, 60, 61, 62, 63, 65, 66, 67, 68, 69,
|
|
70, 71, 72, 73, 74, 75, 76, 77, 78, 79,
|
|
80, 81, 82, 83, 84, 85, 86, 87, 88, 89,
|
|
119, 127, 157, 159, 160, 166, 167, 174, 175, 176,
|
|
177, 178, 179, 180, 181, 182, 183, 184, 185, 188,
|
|
190, 191, 195, 196, 207, 208, 209, 210, 211, 212,
|
|
213, 214, 215, 216, 217, 218, 221, 223, 224, 228,
|
|
229, 241, 242, 243, 244, 245, 246, 247, 248, 249,
|
|
250, 251, 252, 253, 254, 255, 256, 257, 258, 259,
|
|
260, 261, 262, 263, 264, 270, 272, 273, 281, 282,
|
|
283, 284, 285, 286, 287, 288, 289, 290, 291, 292,
|
|
293, 294, 295, 296, 297, 298, 299, 300, 301, 302,
|
|
303, 304, 305, 306, 307, 308, 309, 310, 316, 317,
|
|
321, 322, 323, 324, 325, 326, 327, 328, 329, 330,
|
|
331, 332, 333, 334, 335, 336, 337, 338, 339, 340,
|
|
341, 342, 344, 347, 348, 362, 363, 364, 365, 366,
|
|
367, 368, 369, 370, 371, 372, 373, 374, 375, 376,
|
|
377, 378, 379, 380, 381, 382, 383, 384, 385, 387,
|
|
389, 390, 397, 406, 407, 408, 409, 410, 411, 412,
|
|
413, 414, 415, 416, 417, 418, 419, 421, 423, 424,
|
|
430, 431, 442, 443, 444, 445, 446, 447, 448, 449,
|
|
450, 451, 452, 453, 454, 456, 458, 460, 463, 465,
|
|
475, 476, 477, 478, 479, 480, 481, 482, 483, 484,
|
|
485, 486, 487, 489, 491, 493, 502, 503, 504, 505,
|
|
506, 508, 511, 514, 527, 528, 529, 530, 531, 532,
|
|
533, 534, 535, 536, 537, 538, 539, 541, 543, 545,
|
|
548, 556, 557, 558, 559, 560, 561, 562, 563, 564,
|
|
565, 566, 567, 568, 570, 572, 574, 585, 586, 587,
|
|
588, 589, 590, 594, 596, 598, 601, 602, 603, 604,
|
|
605, 606, 607, 608, 609, 610, 611, 612, 613, 614,
|
|
615, 616, 617, 618, 620, 621, 622, 631, 632, 633,
|
|
635, 636, 637, 638, 639, 640, 641, 642, 643, 644,
|
|
645, 646, 647, 648, 650, 652, 653, 654, 659, 674,
|
|
675, 676, 677, 678, 679, 680, 681, 682, 683, 684,
|
|
685, 686, 687, 688, 689, 690, 692, 696, 700, 701,
|
|
702, 703, 704, 705, 706, 707, 708, 709, 710, 711,
|
|
713, 715, 718, 726, 727, 728, 729, 730, 731, 732,
|
|
733, 734, 735, 736, 737, 738, 739, 740, 741, 743,
|
|
745, 746, 750, 751, 766, 774, 777, 779, 780, 783,
|
|
784, 785, 786, 787, 788, 789, 790, 791, 792, 793,
|
|
794, 795, 796, 797, 798, 801, 809, 811};
|
|
|
|
BOOL BackedUpData = false;
|
|
// This is the original data before it gets replaced by a patch.
|
|
char *OrgSprNames[NUMSPRITES];
|
|
actionf_t OrgActionPtrs[NUMSTATES];
|
|
|
|
// Sound equivalences. When a patch tries to change a sound,
|
|
// use these sound names.
|
|
char *SoundMap[] = {
|
|
NULL,
|
|
"weapons/pistol",
|
|
"weapons/shotgf",
|
|
"weapons/shotgr",
|
|
"weapons/sshotf",
|
|
"weapons/sshoto",
|
|
"weapons/sshotc",
|
|
"weapons/sshotl",
|
|
"weapons/plasmaf",
|
|
"weapons/bfgf",
|
|
"weapons/sawup",
|
|
"weapons/sawidle",
|
|
"weapons/sawfull",
|
|
"weapons/sawhit",
|
|
"weapons/rocklf",
|
|
"weapons/bfgx",
|
|
"imp/attack",
|
|
"imp/shotx",
|
|
"plats/pt1_strt",
|
|
"plats/pt1_stop",
|
|
"doors/dr1_open",
|
|
"doors/dr1_clos",
|
|
"plats/pt1_mid",
|
|
"switches/normbutn",
|
|
"switches/exitbutn",
|
|
"*pain100_1",
|
|
"demon/pain",
|
|
"grunt/pain",
|
|
"vile/pain",
|
|
"fatso/pain",
|
|
"pain/pain",
|
|
"misc/gibbed",
|
|
"misc/i_pkup",
|
|
"misc/w_pkup",
|
|
"*land1",
|
|
"misc/teleport",
|
|
"grunt/sight1",
|
|
"grunt/sight2",
|
|
"grunt/sight3",
|
|
"imp/sight1",
|
|
"imp/sight2",
|
|
"demon/sight",
|
|
"caco/sight",
|
|
"baron/sight",
|
|
"cyber/sight",
|
|
"spider/sight",
|
|
"baby/sight",
|
|
"knight/sight",
|
|
"vile/sight",
|
|
"fatso/sight",
|
|
"pain/sight",
|
|
"skull/melee",
|
|
"demon/melee",
|
|
"skeleton/melee",
|
|
"vile/start",
|
|
"imp/melee",
|
|
"skeleton/swing",
|
|
"*death1",
|
|
"*xdeath1",
|
|
"grunt/death1",
|
|
"grunt/death2",
|
|
"grunt/death3",
|
|
"imp/death1",
|
|
"imp/death2",
|
|
"demon/death",
|
|
"caco/death",
|
|
"misc/unused",
|
|
"baron/death",
|
|
"cyber/death",
|
|
"spider/death",
|
|
"baby/death",
|
|
"vile/death",
|
|
"knight/death",
|
|
"pain/death",
|
|
"skeleton/death",
|
|
"grunt/active",
|
|
"imp/active",
|
|
"demon/active",
|
|
"baby/active",
|
|
"baby/walk",
|
|
"vile/active",
|
|
"*grunt1",
|
|
"world/barrelx",
|
|
"*fist",
|
|
"cyber/hoof",
|
|
"spider/walk",
|
|
"weapons/chngun",
|
|
"misc/chat2",
|
|
"doors/dr2_open",
|
|
"doors/dr2_clos",
|
|
"misc/spawn",
|
|
"vile/firecrkl",
|
|
"vile/firestrt",
|
|
"misc/p_pkup",
|
|
"brain/spit",
|
|
"brain/cube",
|
|
"brain/sight",
|
|
"brain/pain",
|
|
"brain/death",
|
|
"fatso/attack",
|
|
"gatso/death",
|
|
"wolfss/sight",
|
|
"wolfss/death",
|
|
"keen/pain",
|
|
"keen/death",
|
|
"skeleton/active",
|
|
"skeleton/sight",
|
|
"skeleton/attack",
|
|
"misc/chat"
|
|
};
|
|
|
|
// Functions used in a .bex [CODEPTR] chunk
|
|
void A_FireRailgun(player_t*, pspdef_t*);
|
|
void A_FireRailgunLeft(player_t*, pspdef_t*);
|
|
void A_FireRailgunRight(player_t*, pspdef_t*);
|
|
void A_RailWait(player_t*, pspdef_t*);
|
|
void A_Light0(player_t*, pspdef_t*);
|
|
void A_WeaponReady(player_t*, pspdef_t*);
|
|
void A_Lower(player_t*, pspdef_t*);
|
|
void A_Raise(player_t*, pspdef_t*);
|
|
void A_Punch(player_t*, pspdef_t*);
|
|
void A_ReFire(player_t*, pspdef_t*);
|
|
void A_FirePistol(player_t*, pspdef_t*);
|
|
void A_Light1(player_t*, pspdef_t*);
|
|
void A_FireShotgun(player_t*, pspdef_t*);
|
|
void A_Light2(player_t*, pspdef_t*);
|
|
void A_FireShotgun2(player_t*, pspdef_t*);
|
|
void A_CheckReload(player_t*, pspdef_t*);
|
|
void A_OpenShotgun2(player_t*, pspdef_t*);
|
|
void A_LoadShotgun2(player_t*, pspdef_t*);
|
|
void A_CloseShotgun2(player_t*, pspdef_t*);
|
|
void A_FireCGun(player_t*, pspdef_t*);
|
|
void A_GunFlash(player_t*, pspdef_t*);
|
|
void A_FireMissile(player_t*, pspdef_t*);
|
|
void A_Saw(player_t*, pspdef_t*);
|
|
void A_FirePlasma(player_t*, pspdef_t*);
|
|
void A_BFGsound(player_t*, pspdef_t*);
|
|
void A_FireBFG(player_t*, pspdef_t*);
|
|
void A_BFGSpray(mobj_t*);
|
|
void A_Explode(mobj_t*);
|
|
void A_Pain(mobj_t*);
|
|
void A_PlayerScream(mobj_t*);
|
|
void A_Fall(mobj_t*);
|
|
void A_XScream(mobj_t*);
|
|
void A_Look(mobj_t*);
|
|
void A_Chase(mobj_t*);
|
|
void A_FaceTarget(mobj_t*);
|
|
void A_PosAttack(mobj_t*);
|
|
void A_Scream(mobj_t*);
|
|
void A_SPosAttack(mobj_t*);
|
|
void A_VileChase(mobj_t*);
|
|
void A_VileStart(mobj_t*);
|
|
void A_VileTarget(mobj_t*);
|
|
void A_VileAttack(mobj_t*);
|
|
void A_StartFire(mobj_t*);
|
|
void A_Fire(mobj_t*);
|
|
void A_FireCrackle(mobj_t*);
|
|
void A_Tracer(mobj_t*);
|
|
void A_SkelWhoosh(mobj_t*);
|
|
void A_SkelFist(mobj_t*);
|
|
void A_SkelMissile(mobj_t*);
|
|
void A_FatRaise(mobj_t*);
|
|
void A_FatAttack1(mobj_t*);
|
|
void A_FatAttack2(mobj_t*);
|
|
void A_FatAttack3(mobj_t*);
|
|
void A_BossDeath(mobj_t*);
|
|
void A_CPosAttack(mobj_t*);
|
|
void A_CPosRefire(mobj_t*);
|
|
void A_TroopAttack(mobj_t*);
|
|
void A_SargAttack(mobj_t*);
|
|
void A_HeadAttack(mobj_t*);
|
|
void A_BruisAttack(mobj_t*);
|
|
void A_SkullAttack(mobj_t*);
|
|
void A_Metal(mobj_t*);
|
|
void A_SpidRefire(mobj_t*);
|
|
void A_BabyMetal(mobj_t*);
|
|
void A_BspiAttack(mobj_t*);
|
|
void A_Hoof(mobj_t*);
|
|
void A_CyberAttack(mobj_t*);
|
|
void A_PainAttack(mobj_t*);
|
|
void A_PainDie(mobj_t*);
|
|
void A_KeenDie(mobj_t*);
|
|
void A_BrainPain(mobj_t*);
|
|
void A_BrainScream(mobj_t*);
|
|
void A_BrainDie(mobj_t*);
|
|
void A_BrainAwake(mobj_t*);
|
|
void A_BrainSpit(mobj_t*);
|
|
void A_SpawnSound(mobj_t*);
|
|
void A_SpawnFly(mobj_t*);
|
|
void A_BrainExplode(mobj_t*);
|
|
void A_Die(mobj_t*);
|
|
void A_Detonate(mobj_t*);
|
|
void A_Mushroom(mobj_t*);
|
|
void A_MonsterRail(mobj_t*);
|
|
|
|
struct CodePtr {
|
|
char *name;
|
|
actionf_t func;
|
|
};
|
|
|
|
static const struct CodePtr CodePtrs[] = {
|
|
{ "NULL", {(actionf_p1)NULL} },
|
|
{ "MonsterRail", {(actionf_p1)A_MonsterRail} },
|
|
{ "FireRailgun", {(actionf_p1)A_FireRailgun} },
|
|
{ "FireRailgunLeft",{(actionf_p1)A_FireRailgunLeft} },
|
|
{ "FireRailgunRight",{(actionf_p1)A_FireRailgunRight} },
|
|
{ "RailWait", {(actionf_p1)A_RailWait} },
|
|
{ "Light0", {(actionf_p1)A_Light0} },
|
|
{ "WeaponReady", {(actionf_p1)A_WeaponReady} },
|
|
{ "Lower", {(actionf_p1)A_Lower} },
|
|
{ "Raise", {(actionf_p1)A_Raise} },
|
|
{ "Punch", {(actionf_p1)A_Punch} },
|
|
{ "ReFire", {(actionf_p1)A_ReFire} },
|
|
{ "FirePistol", {(actionf_p1)A_FirePistol} },
|
|
{ "Light1", {(actionf_p1)A_Light1} },
|
|
{ "FireShotgun", {(actionf_p1)A_FireShotgun} },
|
|
{ "Light2", {(actionf_p1)A_Light2} },
|
|
{ "FireShotgun2", {(actionf_p1)A_FireShotgun2} },
|
|
{ "CheckReload", {(actionf_p1)A_CheckReload} },
|
|
{ "OpenShotgun2", {(actionf_p1)A_OpenShotgun2} },
|
|
{ "LoadShotgun2", {(actionf_p1)A_LoadShotgun2} },
|
|
{ "CloseShotgun2", {(actionf_p1)A_CloseShotgun2} },
|
|
{ "FireCGun", {(actionf_p1)A_FireCGun} },
|
|
{ "A_GunFlash", {(actionf_p1)A_GunFlash} },
|
|
{ "FireMissile", {(actionf_p1)A_FireMissile} },
|
|
{ "Saw", {(actionf_p1)A_Saw} },
|
|
{ "FirePlasma", {(actionf_p1)A_FirePlasma} },
|
|
{ "BFGsound", {(actionf_p1)A_BFGsound} },
|
|
{ "FireBFG", {(actionf_p1)A_FireBFG} },
|
|
{ "BFGSpray", {(actionf_p1)A_BFGSpray} },
|
|
{ "Explode", {(actionf_p1)A_Explode} },
|
|
{ "Pain", {(actionf_p1)A_Pain} },
|
|
{ "PlayerScream", {(actionf_p1)A_PlayerScream} },
|
|
{ "Fall", {(actionf_p1)A_Fall} },
|
|
{ "XScream", {(actionf_p1)A_XScream} },
|
|
{ "Look", {(actionf_p1)A_Look} },
|
|
{ "Chase", {(actionf_p1)A_Chase} },
|
|
{ "FaceTarget", {(actionf_p1)A_FaceTarget} },
|
|
{ "PosAttack", {(actionf_p1)A_PosAttack} },
|
|
{ "Scream", {(actionf_p1)A_Scream} },
|
|
{ "SPosAttack", {(actionf_p1)A_SPosAttack} },
|
|
{ "VileChase", {(actionf_p1)A_VileChase} },
|
|
{ "VileStart", {(actionf_p1)A_VileStart} },
|
|
{ "VileTarget", {(actionf_p1)A_VileTarget} },
|
|
{ "VileAttack", {(actionf_p1)A_VileAttack} },
|
|
{ "StartFire", {(actionf_p1)A_StartFire} },
|
|
{ "Fire", {(actionf_p1)A_Fire} },
|
|
{ "FireCrackle", {(actionf_p1)A_FireCrackle} },
|
|
{ "Tracer", {(actionf_p1)A_Tracer} },
|
|
{ "SkelWhoosh", {(actionf_p1)A_SkelWhoosh} },
|
|
{ "SkelFist", {(actionf_p1)A_SkelFist} },
|
|
{ "SkelMissile", {(actionf_p1)A_SkelMissile} },
|
|
{ "FatRaise", {(actionf_p1)A_FatRaise} },
|
|
{ "FatAttack1", {(actionf_p1)A_FatAttack1} },
|
|
{ "FatAttack2", {(actionf_p1)A_FatAttack2} },
|
|
{ "FatAttack3", {(actionf_p1)A_FatAttack3} },
|
|
{ "BossDeath", {(actionf_p1)A_BossDeath} },
|
|
{ "CPosAttack", {(actionf_p1)A_CPosAttack} },
|
|
{ "CPosRefire", {(actionf_p1)A_CPosRefire} },
|
|
{ "TroopAttack", {(actionf_p1)A_TroopAttack} },
|
|
{ "SargAttack", {(actionf_p1)A_SargAttack} },
|
|
{ "HeadAttack", {(actionf_p1)A_HeadAttack} },
|
|
{ "BruisAttack", {(actionf_p1)A_BruisAttack} },
|
|
{ "SkullAttack", {(actionf_p1)A_SkullAttack} },
|
|
{ "Metal", {(actionf_p1)A_Metal} },
|
|
{ "SpidRefire", {(actionf_p1)A_SpidRefire} },
|
|
{ "BabyMetal", {(actionf_p1)A_BabyMetal} },
|
|
{ "BspiAttack", {(actionf_p1)A_BspiAttack} },
|
|
{ "Hoof", {(actionf_p1)A_Hoof} },
|
|
{ "CyberAttack", {(actionf_p1)A_CyberAttack} },
|
|
{ "PainAttack", {(actionf_p1)A_PainAttack} },
|
|
{ "PainDie", {(actionf_p1)A_PainDie} },
|
|
{ "KeenDie", {(actionf_p1)A_KeenDie} },
|
|
{ "BrainPain", {(actionf_p1)A_BrainPain} },
|
|
{ "BrainScream", {(actionf_p1)A_BrainScream} },
|
|
{ "BrainDie", {(actionf_p1)A_BrainDie} },
|
|
{ "BrainAwake", {(actionf_p1)A_BrainAwake} },
|
|
{ "BrainSpit", {(actionf_p1)A_BrainSpit} },
|
|
{ "SpawnSound", {(actionf_p1)A_SpawnSound} },
|
|
{ "SpawnFly", {(actionf_p1)A_SpawnFly} },
|
|
{ "BrainExplode", {(actionf_p1)A_BrainExplode} },
|
|
{ "Die", {(actionf_p1)A_Die} },
|
|
{ "Detonate", {(actionf_p1)A_Detonate} },
|
|
{ "Mushroom", {(actionf_p1)A_Mushroom} },
|
|
{ NULL, }
|
|
};
|
|
|
|
struct Key {
|
|
char *name;
|
|
ptrdiff_t offset;
|
|
};
|
|
|
|
extern byte cheat_mus_seq[9];
|
|
extern byte cheat_choppers_seq[11];
|
|
extern byte cheat_god_seq[6];
|
|
extern byte cheat_ammo_seq[6];
|
|
extern byte cheat_ammonokey_seq[5];
|
|
extern byte cheat_noclip_seq[11];
|
|
extern byte cheat_commercial_noclip_seq[7];
|
|
extern byte cheat_powerup_seq[7][10];
|
|
extern byte cheat_clev_seq[10];
|
|
extern byte cheat_mypos_seq[8];
|
|
extern byte cheat_amap_seq[5];
|
|
|
|
static int PatchThing (int);
|
|
static int PatchSound (int);
|
|
static int PatchFrame (int);
|
|
static int PatchSprite (int);
|
|
static int PatchAmmo (int);
|
|
static int PatchWeapon (int);
|
|
static int PatchPointer (int);
|
|
static int PatchCheats (int);
|
|
static int PatchMisc (int);
|
|
static int PatchText (int);
|
|
static int PatchStrings (int);
|
|
static int PatchPars (int);
|
|
static int PatchCodePtrs (int);
|
|
static int DoInclude (int);
|
|
|
|
static const struct {
|
|
char *name;
|
|
int (*func)(int);
|
|
} Modes[] = {
|
|
// These appear in .deh and .bex files
|
|
{ "Thing", PatchThing },
|
|
{ "Sound", PatchSound },
|
|
{ "Frame", PatchFrame },
|
|
{ "Sprite", PatchSprite },
|
|
{ "Ammo", PatchAmmo },
|
|
{ "Weapon", PatchWeapon },
|
|
{ "Pointer", PatchPointer },
|
|
{ "Cheat", PatchCheats },
|
|
{ "Misc", PatchMisc },
|
|
{ "Text", PatchText },
|
|
// These appear in .bex files
|
|
{ "include", DoInclude },
|
|
{ "[STRINGS]", PatchStrings },
|
|
{ "[PARS]", PatchPars },
|
|
{ "[CODEPTR]", PatchCodePtrs },
|
|
{ NULL, },
|
|
};
|
|
|
|
static int HandleMode (const char *mode, int num);
|
|
static BOOL HandleKey (const struct Key *keys, void *structure, const char *key, int value);
|
|
static void BackupData (void);
|
|
static void ChangeCheat (byte *newcheat, byte *cheatseq, BOOL needsval);
|
|
static BOOL ReadChars (char **stuff, int size);
|
|
static char *igets (void);
|
|
static int GetLine (void);
|
|
|
|
|
|
static int HandleMode (const char *mode, int num)
|
|
{
|
|
int i = 0;
|
|
|
|
while (Modes[i].name && stricmp (Modes[i].name, mode))
|
|
i++;
|
|
|
|
if (Modes[i].name)
|
|
return Modes[i].func (num);
|
|
|
|
// Handle unknown or unimplemented data
|
|
Printf (PRINT_HIGH, "Unknown chunk %s encountered. Skipping.\n", mode);
|
|
do
|
|
i = GetLine ();
|
|
while (i == 1);
|
|
|
|
return i;
|
|
}
|
|
|
|
static BOOL HandleKey (const struct Key *keys, void *structure, const char *key, int value)
|
|
{
|
|
while (keys->name && stricmp (keys->name, key))
|
|
keys++;
|
|
|
|
if (keys->name) {
|
|
*((int *)(((byte *)structure) + keys->offset)) = value;
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
static void BackupData (void)
|
|
{
|
|
int i;
|
|
|
|
if (BackedUpData)
|
|
return;
|
|
|
|
// for (i = 0; i < NUMSFX; i++)
|
|
// OrgSfxNames[i] = S_sfx[i].name;
|
|
|
|
for (i = 0; i < NUMSPRITES; i++)
|
|
OrgSprNames[i] = sprnames[i];
|
|
|
|
for (i = 0; i < NUMSTATES; i++)
|
|
OrgActionPtrs[i] = states[i].action;
|
|
}
|
|
|
|
static void ChangeCheat (byte *newcheat, byte *cheatseq, BOOL needsval)
|
|
{
|
|
while (*cheatseq != 0xff && *cheatseq != 1 && *newcheat) {
|
|
*cheatseq++ = SCRAMBLE(*newcheat);
|
|
newcheat++;
|
|
}
|
|
|
|
if (needsval) {
|
|
*cheatseq++ = 1;
|
|
*cheatseq++ = 0;
|
|
*cheatseq++ = 0;
|
|
}
|
|
|
|
*cheatseq = 0xff;
|
|
}
|
|
|
|
static BOOL ReadChars (char **stuff, int size)
|
|
{
|
|
char *str = *stuff;
|
|
|
|
if (!size) {
|
|
*str = 0;
|
|
return true;
|
|
}
|
|
|
|
do {
|
|
// Ignore carriage returns
|
|
if (*PatchPt != '\r')
|
|
*str++ = *PatchPt;
|
|
else
|
|
size++;
|
|
|
|
PatchPt++;
|
|
} while (--size);
|
|
|
|
*str = 0;
|
|
return true;
|
|
}
|
|
|
|
static void ReplaceSpecialChars (char *str)
|
|
{
|
|
char *p = str, c;
|
|
int i;
|
|
|
|
while ( (c = *p++) ) {
|
|
if (c != '\\') {
|
|
*str++ = c;
|
|
} else {
|
|
switch (*p) {
|
|
case 'n':
|
|
case 'N':
|
|
*str++ = '\n';
|
|
break;
|
|
case 't':
|
|
case 'T':
|
|
*str++ = '\t';
|
|
break;
|
|
case 'r':
|
|
case 'R':
|
|
*str++ = '\r';
|
|
break;
|
|
case 'x':
|
|
case 'X':
|
|
c = 0;
|
|
p++;
|
|
for (i = 0; i < 2; i++) {
|
|
c <<= 4;
|
|
if (*p >= '0' && *p <= '9')
|
|
c += *p-'0';
|
|
else if (*p >= 'a' && *p <= 'f')
|
|
c += 10 + *p-'a';
|
|
else if (*p >= 'A' && *p <= 'F')
|
|
c += 10 + *p-'A';
|
|
else
|
|
break;
|
|
p++;
|
|
}
|
|
*str++ = c;
|
|
break;
|
|
case '0':
|
|
case '1':
|
|
case '2':
|
|
case '3':
|
|
case '4':
|
|
case '5':
|
|
case '6':
|
|
case '7':
|
|
c = 0;
|
|
for (i = 0; i < 3; i++) {
|
|
c <<= 3;
|
|
if (*p >= '0' && *p <= '7')
|
|
c += *p-'0';
|
|
else
|
|
break;
|
|
p++;
|
|
}
|
|
*str++ = c;
|
|
break;
|
|
default:
|
|
*str++ = *p;
|
|
break;
|
|
}
|
|
p++;
|
|
}
|
|
}
|
|
*str = 0;
|
|
}
|
|
|
|
static char *skipwhite (char *str)
|
|
{
|
|
if (str)
|
|
while (*str && isspace(*str))
|
|
str++;
|
|
return str;
|
|
}
|
|
|
|
static void stripwhite (char *str)
|
|
{
|
|
char *end = str + strlen(str) - 1;
|
|
|
|
while (end >= str && isspace(*end))
|
|
end--;
|
|
|
|
end[1] = '\0';
|
|
}
|
|
|
|
static char *igets (void)
|
|
{
|
|
char *line;
|
|
|
|
if (*PatchPt == '\0')
|
|
return NULL;
|
|
|
|
line = PatchPt;
|
|
|
|
while (*PatchPt != '\n' && *PatchPt != '\0')
|
|
PatchPt++;
|
|
|
|
if (*PatchPt == '\n')
|
|
*PatchPt++ = 0;
|
|
|
|
return line;
|
|
}
|
|
|
|
static int GetLine (void)
|
|
{
|
|
char *line, *line2;
|
|
|
|
do {
|
|
while ( (line = igets ()) )
|
|
if (line[0] != '#') // Skip comment lines
|
|
break;
|
|
|
|
if (!line)
|
|
return 0;
|
|
|
|
Line1 = skipwhite (line);
|
|
} while (Line1 && *Line1 == 0); // Loop until we get a line with
|
|
// more than just whitespace.
|
|
line = strchr (Line1, '=');
|
|
|
|
if (line) { // We have an '=' in the input line
|
|
line2 = line;
|
|
while (--line2 >= Line1)
|
|
if (*line2 > ' ')
|
|
break;
|
|
|
|
if (line2 < Line1)
|
|
return 0; // Nothing before '='
|
|
|
|
*(line2 + 1) = 0;
|
|
|
|
line++;
|
|
while (*line && *line <= ' ')
|
|
line++;
|
|
|
|
if (*line == 0)
|
|
return 0; // Nothing after '='
|
|
|
|
Line2 = line;
|
|
|
|
return 1;
|
|
} else { // No '=' in input line
|
|
line = Line1 + 1;
|
|
while (*line > ' ')
|
|
line++; // Get beyond first word
|
|
|
|
*line++ = 0;
|
|
while (*line && *line <= ' ')
|
|
line++; // Skip white space
|
|
|
|
//.bex files don't have this restriction
|
|
//if (*line == 0)
|
|
// return 0; // No second word
|
|
|
|
Line2 = line;
|
|
|
|
return 2;
|
|
}
|
|
}
|
|
|
|
static int PatchThing (int thingNum)
|
|
{
|
|
static const struct Key keys[] = {
|
|
{ "ID #", myoffsetof(mobjinfo_t,doomednum) },
|
|
{ "Initial frame", myoffsetof(mobjinfo_t,spawnstate) },
|
|
{ "Hit points", myoffsetof(mobjinfo_t,spawnhealth) },
|
|
{ "First moving frame", myoffsetof(mobjinfo_t,seestate) },
|
|
{ "Reaction time", myoffsetof(mobjinfo_t,reactiontime) },
|
|
{ "Injury frame", myoffsetof(mobjinfo_t,painstate) },
|
|
{ "Pain chance", myoffsetof(mobjinfo_t,painchance) },
|
|
{ "Close attack frame", myoffsetof(mobjinfo_t,meleestate) },
|
|
{ "Far attack frame", myoffsetof(mobjinfo_t,missilestate) },
|
|
{ "Death frame", myoffsetof(mobjinfo_t,deathstate) },
|
|
{ "Exploding frame", myoffsetof(mobjinfo_t,xdeathstate) },
|
|
{ "Speed", myoffsetof(mobjinfo_t,speed) },
|
|
{ "Width", myoffsetof(mobjinfo_t,radius) },
|
|
{ "Height", myoffsetof(mobjinfo_t,height) },
|
|
{ "Mass", myoffsetof(mobjinfo_t,mass) },
|
|
{ "Missile damage", myoffsetof(mobjinfo_t,damage) },
|
|
{ "Respawn frame", myoffsetof(mobjinfo_t,raisestate) },
|
|
{ NULL, }
|
|
};
|
|
|
|
// flags can be specified by name (a .bex extension):
|
|
static const struct {
|
|
short bit;
|
|
short whichflags;
|
|
const char *name;
|
|
} bitnames[] = {
|
|
{ 0, 0, "SPECIAL"},
|
|
{ 1, 0, "SOLID"},
|
|
{ 2, 0, "SHOOTABLE"},
|
|
{ 3, 0, "NOSECTOR"},
|
|
{ 4, 0, "NOBLOCKMAP"},
|
|
{ 5, 0, "AMBUSH"},
|
|
{ 6, 0, "JUSTHIT"},
|
|
{ 7, 0, "JUSTATTACKED"},
|
|
{ 8, 0, "SPAWNCEILING"},
|
|
{ 9, 0, "NOGRAVITY"},
|
|
{10, 0, "DROPOFF"},
|
|
{11, 0, "PICKUP"},
|
|
{12, 0, "NOCLIP"},
|
|
{14, 0, "FLOAT"},
|
|
{15, 0, "TELEPORT"},
|
|
{16, 0, "MISSILE"},
|
|
{17, 0, "DROPPED"},
|
|
{18, 0, "SHADOW"},
|
|
{19, 0, "NOBLOOD"},
|
|
{20, 0, "CORPSE"},
|
|
{21, 0, "INFLOAT"},
|
|
{22, 0, "COUNTKILL"},
|
|
{23, 0, "COUNTITEM"},
|
|
{24, 0, "SKULLFLY"},
|
|
{25, 0, "NOTDMATCH"},
|
|
{26, 0, "TRANSLATION1"},
|
|
{26, 0, "TRANSLATION"}, // BOOM compatibility
|
|
{27, 0, "TRANSLATION2"},
|
|
{27, 0, "UNUSED1"}, // BOOM compatibility
|
|
{28, 0, "STEALTH"},
|
|
{28, 0, "UNUSED2"}, // BOOM compatibility
|
|
{29, 0, "TRANSLUC25"},
|
|
{29, 0, "UNUSED3"}, // BOOM compatibility
|
|
{30, 0, "TRANSLUC50"},
|
|
{(29<<8)|30, 0, "TRANSLUC75"},
|
|
{30, 0, "UNUSED4"}, // BOOM compatibility
|
|
{30, 0, "TRANSLUCENT"}, // BOOM compatibility?
|
|
{31, 0, "RESERVED"},
|
|
|
|
// Names for flags2
|
|
{ 0, 1, "LOGRAV"},
|
|
{ 1, 1, "WINDTHRUST"},
|
|
{ 2, 1, "FLOORBOUNCE"},
|
|
{ 3, 1, "BLASTED"},
|
|
{ 4, 1, "FLY"},
|
|
{ 5, 1, "FLOORCLIP"},
|
|
{ 6, 1, "SPAWNFLOAT"},
|
|
{ 7, 1, "NOTELEPORT"},
|
|
{ 8, 1, "RIP"},
|
|
{ 9, 1, "PUSHABLE"},
|
|
{10, 1, "CANSLIDE"}, // Avoid conflict with SLIDE from BOOM
|
|
{11, 1, "ONMOBJ"},
|
|
{12, 1, "PASSMOBJ"},
|
|
{13, 1, "CANNOTPUSH"},
|
|
{14, 1, "DROPPED"},
|
|
{15, 1, "BOSS"},
|
|
{16, 1, "FIREDAMAGE"},
|
|
{17, 1, "NODMGTHRUST"},
|
|
{18, 1, "TELESTOMP"},
|
|
{19, 1, "FLOATBOB"},
|
|
{20, 1, "DONTDRAW"},
|
|
{21, 1, "IMPACT"},
|
|
{22, 1, "PUSHWALL"},
|
|
{23, 1, "MCROSS"},
|
|
{24, 1, "PCROSS"},
|
|
{25, 1, "CANTLEAVEFLOORPIC"},
|
|
{26, 1, "NONSHOOTABLE"},
|
|
{27, 1, "INVULNERABLE"},
|
|
{28, 1, "DORMANT"},
|
|
{29, 1, "ICEDAMAGE"},
|
|
{30, 1, "SEEKERMISSILE"},
|
|
{31, 1, "REFLECTIVE"}
|
|
};
|
|
int result;
|
|
mobjinfo_t *info, dummy;
|
|
BOOL hadHeight = false;
|
|
|
|
thingNum--;
|
|
if (thingNum >= 0 && thingNum < NUMMOBJTYPES) {
|
|
info = &mobjinfo[thingNum];
|
|
DPrintf ("Thing %d\n", thingNum);
|
|
} else {
|
|
info = &dummy;
|
|
Printf (PRINT_HIGH, "Thing %d out of range.\n", thingNum + 1);
|
|
}
|
|
|
|
// Turn off transparency for the mobj, since original DOOM
|
|
// didn't have any. If some is wanted, let the patch specify it.
|
|
// Uncomment this if you want to make it so.
|
|
//info->flags &= ~MF_TRANSLUCBITS;
|
|
|
|
while ((result = GetLine ()) == 1) {
|
|
int sndmap = atoi (Line2);
|
|
|
|
if (sndmap >= sizeof(SoundMap))
|
|
sndmap = 0;
|
|
|
|
if (HandleKey (keys, info, Line1, atoi (Line2))) {
|
|
if (!stricmp (Line1, "Alert sound"))
|
|
info->seesound = SoundMap[sndmap];
|
|
else if (!stricmp (Line1, "Attack sound"))
|
|
info->attacksound = SoundMap[sndmap];
|
|
else if (!stricmp (Line1, "Pain sound"))
|
|
info->painsound = SoundMap[sndmap];
|
|
else if (!stricmp (Line1, "Death sound"))
|
|
info->deathsound = SoundMap[sndmap];
|
|
else if (!stricmp (Line1, "Action sound"))
|
|
info->activesound = SoundMap[sndmap];
|
|
else if (!stricmp (Line1, "Bits"))
|
|
{
|
|
int value = 0, value2 = 0;
|
|
BOOL vchanged = false, v2changed = false;
|
|
char *strval;
|
|
|
|
for (strval = Line2; (strval = strtok (strval, ",+| \t\f\r")); strval = NULL)
|
|
{
|
|
int iy;
|
|
|
|
if (IsNum (strval))
|
|
{
|
|
// Force the top 4 bits to 0 so that the user is forced
|
|
// to use the mnemonics to change them.
|
|
value |= (atoi(strval) & 0x0fffffff);
|
|
vchanged = true;
|
|
}
|
|
else
|
|
{
|
|
for (iy = 0; iy < sizeof(bitnames)/sizeof(bitnames[0]); iy++)
|
|
{
|
|
if (!stricmp (strval, bitnames[iy].name))
|
|
{
|
|
if (bitnames[iy].whichflags)
|
|
{
|
|
v2changed = true;
|
|
if (bitnames[iy].bit & 0xff00)
|
|
value2 |= 1 << (bitnames[iy].bit >> 8);
|
|
value2 |= 1 << (bitnames[iy].bit & 0xff);
|
|
}
|
|
else
|
|
{
|
|
vchanged = true;
|
|
if (bitnames[iy].bit & 0xff00)
|
|
value |= 1 << (bitnames[iy].bit >> 8);
|
|
value |= 1 << (bitnames[iy].bit & 0xff);
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
if (iy >= sizeof(bitnames)/sizeof(bitnames[0]))
|
|
DPrintf("Unknown bit mnemonic %s\n", strval);
|
|
}
|
|
}
|
|
if (vchanged)
|
|
info->flags = value;
|
|
if (v2changed)
|
|
info->flags2 = value2;
|
|
DPrintf ("Bits: %d,%d (0x%08x,0x%08x)\n", info->flags, info->flags2,
|
|
info->flags, info->flags2);
|
|
}
|
|
else Printf (PRINT_HIGH, unknown_str, Line1, "Thing", thingNum);
|
|
} else if (!stricmp (Line1, "Height")) {
|
|
hadHeight = true;
|
|
}
|
|
}
|
|
|
|
if (info->flags & MF_SPAWNCEILING && !hadHeight && thingNum < sizeof(OrgHeights))
|
|
info->height = OrgHeights[thingNum] * FRACUNIT;
|
|
|
|
return result;
|
|
}
|
|
|
|
static int PatchSound (int soundNum)
|
|
{
|
|
int result;
|
|
|
|
DPrintf ("Sound %d (no longer supported)\n", soundNum);
|
|
/*
|
|
sfxinfo_t *info, dummy;
|
|
int offset = 0;
|
|
if (soundNum >= 1 && soundNum <= NUMSFX) {
|
|
info = &S_sfx[soundNum];
|
|
} else {
|
|
info = &dummy;
|
|
Printf ("Sound %d out of range.\n");
|
|
}
|
|
*/
|
|
while ((result = GetLine ()) == 1) {
|
|
/*
|
|
if (!stricmp ("Offset", Line1))
|
|
offset = atoi (Line2);
|
|
else CHECKKEY ("Zero/One", info->singularity)
|
|
else CHECKKEY ("Value", info->priority)
|
|
else CHECKKEY ("Zero 1", info->link)
|
|
else CHECKKEY ("Neg. One 1", info->pitch)
|
|
else CHECKKEY ("Neg. One 2", info->volume)
|
|
else CHECKKEY ("Zero 2", info->data)
|
|
else CHECKKEY ("Zero 3", info->usefulness)
|
|
else CHECKKEY ("Zero 4", info->lumpnum)
|
|
else Printf (unknown_str, Line1, "Sound", soundNum);
|
|
*/
|
|
}
|
|
/*
|
|
if (offset) {
|
|
// Calculate offset from start of sound names
|
|
offset -= toff[dversion] + 21076;
|
|
|
|
if (offset <= 64) // pistol .. bfg
|
|
offset >>= 3;
|
|
else if (offset <= 260) // sawup .. oof
|
|
offset = (offset + 4) >> 3;
|
|
else // telept .. skeatk
|
|
offset = (offset + 8) >> 3;
|
|
|
|
if (offset >= 0 && offset < NUMSFX) {
|
|
S_sfx[soundNum].name = OrgSfxNames[offset + 1];
|
|
} else {
|
|
Printf ("Sound name %d out of range.\n", offset + 1);
|
|
}
|
|
}
|
|
*/
|
|
return result;
|
|
}
|
|
|
|
static int PatchFrame (int frameNum)
|
|
{
|
|
static const struct Key keys[] = {
|
|
{ "Sprite number", myoffsetof(state_t,sprite) },
|
|
{ "Sprite subnumber", myoffsetof(state_t,frame) },
|
|
{ "Duration", myoffsetof(state_t,tics) },
|
|
{ "Next frame", myoffsetof(state_t,nextstate) },
|
|
{ "Unknown 1", myoffsetof(state_t,misc1) },
|
|
{ "Unknown 2", myoffsetof(state_t,misc2) },
|
|
{ NULL, }
|
|
};
|
|
int result;
|
|
state_t *info, dummy;
|
|
|
|
if (frameNum >= 0 && frameNum < NUMSTATES) {
|
|
info = &states[frameNum];
|
|
DPrintf ("Frame %d\n", frameNum);
|
|
} else {
|
|
info = &dummy;
|
|
Printf (PRINT_HIGH, "Frame %d out of range\n", frameNum);
|
|
}
|
|
|
|
while ((result = GetLine ()) == 1)
|
|
if (HandleKey (keys, info, Line1, atoi (Line2)))
|
|
Printf (PRINT_HIGH, unknown_str, Line1, "Frame", frameNum);
|
|
|
|
return result;
|
|
}
|
|
|
|
static int PatchSprite (int sprNum)
|
|
{
|
|
int result;
|
|
int offset = 0;
|
|
|
|
if (sprNum >= 0 && sprNum < NUMSPRITES) {
|
|
DPrintf ("Sprite %d\n", sprNum);
|
|
} else {
|
|
Printf (PRINT_HIGH, "Sprite %d out of range.\n", sprNum);
|
|
sprNum = -1;
|
|
}
|
|
|
|
while ((result = GetLine ()) == 1) {
|
|
if (!stricmp ("Offset", Line1))
|
|
offset = atoi (Line2);
|
|
else Printf (PRINT_HIGH, unknown_str, Line1, "Sprite", sprNum);
|
|
}
|
|
|
|
if (offset > 0 && sprNum != -1) {
|
|
// Calculate offset from beginning of sprite names.
|
|
offset = (offset - toff[dversion] - 22044) / 8;
|
|
|
|
if (offset >= 0 && offset < NUMSPRITES) {
|
|
sprnames[sprNum] = OrgSprNames[offset];
|
|
} else {
|
|
Printf (PRINT_HIGH, "Sprite name %d out of range.\n", offset);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static int PatchAmmo (int ammoNum)
|
|
{
|
|
extern int clipammo[NUMAMMO];
|
|
|
|
int result;
|
|
int *max;
|
|
int *per;
|
|
int dummy;
|
|
|
|
if (ammoNum >= 0 && ammoNum < NUMAMMO) {
|
|
DPrintf ("Ammo %d.\n", ammoNum);
|
|
max = &maxammo[ammoNum];
|
|
per = &clipammo[ammoNum];
|
|
} else {
|
|
Printf (PRINT_HIGH, "Ammo %d out of range.\n", ammoNum);
|
|
max = per = &dummy;
|
|
}
|
|
|
|
while ((result = GetLine ()) == 1) {
|
|
CHECKKEY ("Max ammo", *max)
|
|
else CHECKKEY ("Per ammo", *per)
|
|
else Printf (PRINT_HIGH, unknown_str, Line1, "Ammo", ammoNum);
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static int PatchWeapon (int weapNum)
|
|
{
|
|
static const struct Key keys[] = {
|
|
{ "Ammo type", myoffsetof(weaponinfo_t,ammo) },
|
|
{ "Deselect frame", myoffsetof(weaponinfo_t,upstate) },
|
|
{ "Select frame", myoffsetof(weaponinfo_t,downstate) },
|
|
{ "Bobbing frame", myoffsetof(weaponinfo_t,readystate) },
|
|
{ "Shooting frame", myoffsetof(weaponinfo_t,atkstate) },
|
|
{ "Firing frame", myoffsetof(weaponinfo_t,flashstate) },
|
|
{ NULL, }
|
|
};
|
|
int result;
|
|
weaponinfo_t *info, dummy;
|
|
|
|
if (weapNum >= 0 && weapNum < NUMWEAPONS) {
|
|
info = &weaponinfo[weapNum];
|
|
DPrintf ("Weapon %d\n", weapNum);
|
|
} else {
|
|
info = &dummy;
|
|
Printf (PRINT_HIGH, "Weapon %d out of range.\n", weapNum);
|
|
}
|
|
|
|
while ((result = GetLine ()) == 1)
|
|
if (HandleKey (keys, info, Line1, atoi (Line2)))
|
|
Printf (PRINT_HIGH, unknown_str, Line1, "Weapon", weapNum);
|
|
|
|
return result;
|
|
}
|
|
|
|
static int PatchPointer (int ptrNum)
|
|
{
|
|
int result;
|
|
|
|
if (ptrNum >= 0 && ptrNum < 448) {
|
|
DPrintf ("Pointer %d\n", ptrNum);
|
|
} else {
|
|
Printf (PRINT_HIGH, "Pointer %d out of range.\n", ptrNum);
|
|
ptrNum = -1;
|
|
}
|
|
|
|
while ((result = GetLine ()) == 1) {
|
|
if ((ptrNum != -1) && (!stricmp (Line1, "Codep Frame")))
|
|
states[codepconv[ptrNum]].action = OrgActionPtrs[atoi (Line2)];
|
|
else Printf (PRINT_HIGH, unknown_str, Line1, "Pointer", ptrNum);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static int PatchCheats (int dummy)
|
|
{
|
|
static const struct {
|
|
char *name;
|
|
byte *cheatseq;
|
|
BOOL needsval;
|
|
} keys[] = {
|
|
{ "Change music", cheat_mus_seq, true },
|
|
{ "Chainsaw", cheat_choppers_seq, false },
|
|
{ "God mode", cheat_god_seq, false },
|
|
{ "Ammo & Keys", cheat_ammo_seq, false },
|
|
{ "Ammo", cheat_ammonokey_seq, false },
|
|
{ "No Clipping 1", cheat_noclip_seq, false },
|
|
{ "No Clipping 2", cheat_commercial_noclip_seq, false },
|
|
{ "Invincibility", cheat_powerup_seq[0], false },
|
|
{ "Berserk", cheat_powerup_seq[1], false },
|
|
{ "Invisibility", cheat_powerup_seq[2], false },
|
|
{ "Radiation Suit", cheat_powerup_seq[3], false },
|
|
{ "Auto-map", cheat_powerup_seq[4], false },
|
|
{ "Lite-Amp Goggles", cheat_powerup_seq[5], false },
|
|
{ "BEHOLD menu", cheat_powerup_seq[6], false },
|
|
{ "Level Warp", cheat_clev_seq, true },
|
|
{ "Player Position", cheat_mypos_seq, false },
|
|
{ "Map cheat", cheat_amap_seq, false },
|
|
{ NULL, }
|
|
};
|
|
int result;
|
|
|
|
DPrintf ("Cheats\n");
|
|
|
|
while ((result = GetLine ()) == 1) {
|
|
int i = 0;
|
|
while (keys[i].name && stricmp (keys[i].name, Line1))
|
|
i++;
|
|
|
|
if (!keys[i].name)
|
|
Printf (PRINT_HIGH, "Unknown cheat %s.\n", Line2);
|
|
else
|
|
ChangeCheat (Line2, keys[i].cheatseq, keys[i].needsval);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static int PatchMisc (int dummy)
|
|
{
|
|
static const struct Key keys[] = {
|
|
{ "Initial Health", myoffsetof(struct DehInfo,StartHealth) },
|
|
{ "Initial Bullets", myoffsetof(struct DehInfo,StartBullets) },
|
|
{ "Max Health", myoffsetof(struct DehInfo,MaxHealth) },
|
|
{ "Max Armor", myoffsetof(struct DehInfo,MaxArmor) },
|
|
{ "Green Armor Class", myoffsetof(struct DehInfo,GreenAC) },
|
|
{ "Blue Armor Class", myoffsetof(struct DehInfo,BlueAC) },
|
|
{ "Max Soulsphere", myoffsetof(struct DehInfo,BlueAC) },
|
|
{ "Soulsphere Health", myoffsetof(struct DehInfo,SoulsphereHealth) },
|
|
{ "Megasphere Health", myoffsetof(struct DehInfo,MegasphereHealth) },
|
|
{ "God Mode Health", myoffsetof(struct DehInfo,GodHealth) },
|
|
{ "IDFA Armor", myoffsetof(struct DehInfo,FAArmor) },
|
|
{ "IDFA Armor Class", myoffsetof(struct DehInfo,FAAC) },
|
|
{ "IDKFA Armor", myoffsetof(struct DehInfo,KFAArmor) },
|
|
{ "IDKFA Armor Class", myoffsetof(struct DehInfo,KFAAC) },
|
|
{ "BFG Cells/Shot", myoffsetof(struct DehInfo,BFGCells) },
|
|
{ "Monsters Infight", myoffsetof(struct DehInfo,Infight) },
|
|
{ NULL, }
|
|
};
|
|
int result;
|
|
gitem_t *item;
|
|
|
|
DPrintf ("Misc\n");
|
|
|
|
while ((result = GetLine()) == 1)
|
|
if (HandleKey (keys, &deh, Line1, atoi (Line2)))
|
|
Printf (PRINT_HIGH, "Unknown miscellaneous info %s.\n", Line2);
|
|
|
|
if ( (item = FindItem ("Basic Armor")) )
|
|
item->offset = deh.GreenAC;
|
|
|
|
if ( (item = FindItem ("Mega Armor")) )
|
|
item->offset = deh.BlueAC;
|
|
|
|
// 0xDD == enable infighting
|
|
deh.Infight = deh.Infight == 0xDD ? 1 : 0;
|
|
|
|
return result;
|
|
}
|
|
|
|
static int PatchPars (int dummy)
|
|
{
|
|
char *space, mapname[8], *moredata;
|
|
level_info_t *info;
|
|
int result, par;
|
|
|
|
DPrintf ("[Pars]\n");
|
|
|
|
while ( (result = GetLine()) ) {
|
|
// Argh! .bex doesn't follow the same rules as .deh
|
|
if (result == 1) {
|
|
Printf (PRINT_HIGH, "Unknown key in [PARS] section: %s\n", Line1);
|
|
continue;
|
|
}
|
|
if (stricmp ("par", Line1))
|
|
return result;
|
|
|
|
space = strchr (Line2, ' ');
|
|
|
|
if (!space) {
|
|
Printf (PRINT_HIGH, "Need data after par.\n");
|
|
continue;
|
|
}
|
|
|
|
*space++ = '\0';
|
|
|
|
while (*space && isspace(*space))
|
|
space++;
|
|
|
|
moredata = strchr (space, ' ');
|
|
|
|
if (moredata) {
|
|
// At least 3 items on this line, must be E?M? format
|
|
sprintf (mapname, "E%cM%c", *Line2, *space);
|
|
par = atoi (moredata + 1);
|
|
} else {
|
|
// Only 2 items, must be MAP?? format
|
|
sprintf (mapname, "MAP%02d", atoi(Line2) % 100);
|
|
par = atoi (space);
|
|
}
|
|
|
|
if (!(info = FindLevelInfo (mapname)) ) {
|
|
Printf (PRINT_HIGH, "No map %s\n", mapname);
|
|
continue;
|
|
}
|
|
|
|
info->partime = par;
|
|
DPrintf ("Par for %s changed to %d\n", mapname, par);
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static int PatchCodePtrs (int dummy)
|
|
{
|
|
int result;
|
|
|
|
DPrintf ("[CodePtr]\n");
|
|
|
|
while ((result = GetLine()) == 1) {
|
|
if (!strnicmp ("Frame", Line1, 5) && isspace(Line1[5])) {
|
|
int frame = atoi (Line1 + 5);
|
|
|
|
if (frame < 0 || frame >= NUMSTATES) {
|
|
Printf (PRINT_HIGH, "Frame %d out of range\n", frame);
|
|
} else {
|
|
int i = 0;
|
|
char *data;
|
|
|
|
COM_Parse (Line2);
|
|
|
|
if ((com_token[0] == 'A' || com_token[0] == 'a') && com_token[1] == '_')
|
|
data = com_token + 2;
|
|
else
|
|
data = com_token;
|
|
|
|
while (CodePtrs[i].name && stricmp (CodePtrs[i].name, data))
|
|
i++;
|
|
|
|
if (CodePtrs[i].name) {
|
|
states[frame].action.acp1 = CodePtrs[i].func.acp1;
|
|
DPrintf ("Frame %d set to %s\n", frame, CodePtrs[i].name);
|
|
} else {
|
|
states[frame].action.acp1 = NULL;
|
|
DPrintf ("Unknown code pointer: %s\n", com_token);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
return result;
|
|
}
|
|
|
|
static int PatchText (int oldSize)
|
|
{
|
|
int newSize;
|
|
char *oldStr;
|
|
char *newStr;
|
|
char *temp;
|
|
BOOL good;
|
|
int result;
|
|
int i;
|
|
|
|
temp = COM_Parse (Line2); // Skip old size, since we already have it
|
|
if (!COM_Parse (temp)) {
|
|
Printf (PRINT_HIGH, "Text chunk is missing size of new string.\n");
|
|
return 2;
|
|
}
|
|
newSize = atoi (com_token);
|
|
|
|
oldStr = malloc (oldSize + 1);
|
|
newStr = malloc (newSize + 1);
|
|
|
|
if (!oldStr || !newStr) {
|
|
Printf (PRINT_HIGH, "Out of memory.\n");
|
|
goto donewithtext;
|
|
}
|
|
|
|
good = ReadChars (&oldStr, oldSize);
|
|
good += ReadChars (&newStr, newSize);
|
|
|
|
if (!good) {
|
|
free (newStr);
|
|
free (oldStr);
|
|
Printf (PRINT_HIGH, "Unexpected end-of-file.\n");
|
|
return 0;
|
|
}
|
|
|
|
if (includenotext) {
|
|
Printf (PRINT_HIGH, "Skipping text chunk in included patch.\n");
|
|
goto donewithtext;
|
|
}
|
|
|
|
DPrintf ("Searching for text:\n%s\n", oldStr);
|
|
good = false;
|
|
|
|
|
|
// Search through sfx names
|
|
#if 0
|
|
for (i = 0; i < NUMSFX; i++) {
|
|
if (S_sfx[i].name) {
|
|
if (!strcmp (S_sfx[i].name, oldStr)) {
|
|
S_sfx[i].name = copystring (newStr);
|
|
good = true;
|
|
// Yes, we really need to check them all. A sound patch could
|
|
// have created two sounds that point to the same name, and
|
|
// stopping at the first would miss the change to the second.
|
|
}
|
|
}
|
|
}
|
|
if (good)
|
|
goto donewithtext;
|
|
#endif
|
|
|
|
|
|
// Search through sprite names
|
|
for (i = 0; i < NUMSPRITES; i++) {
|
|
if (!strcmp (sprnames[i], oldStr)) {
|
|
sprnames[i] = copystring (newStr);
|
|
good = true;
|
|
// See above.
|
|
}
|
|
}
|
|
|
|
if (good)
|
|
goto donewithtext;
|
|
|
|
|
|
// Search through music names.
|
|
// This is something of an even bigger hack
|
|
// since I changed the way music is handled.
|
|
if (oldSize < 7) { // Music names are never >6 chars
|
|
if ( (temp = malloc (oldSize + 3)) ) {
|
|
level_info_t *info = LevelInfos;
|
|
sprintf (temp, "d_%s", oldStr);
|
|
|
|
while (info->level_name) {
|
|
if (!strnicmp (info->music, temp, 8)) {
|
|
good = true;
|
|
strncpy (info->music, temp, 8);
|
|
}
|
|
info++;
|
|
}
|
|
|
|
free (temp);
|
|
}
|
|
}
|
|
|
|
if (good)
|
|
goto donewithtext;
|
|
|
|
|
|
// Search through most other texts
|
|
for (i = 0; i < NUMSTRINGS; i++) {
|
|
if (!stricmp (Strings[i].builtin, oldStr)) {
|
|
ReplaceString (&Strings[i].string, newStr);
|
|
Strings[i].type = str_patched;
|
|
good = true;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (!good)
|
|
DPrintf (" (Unmatched)\n");
|
|
|
|
donewithtext:
|
|
if (newStr)
|
|
free (newStr);
|
|
if (oldStr)
|
|
free (oldStr);
|
|
|
|
// Fetch next identifier for main loop
|
|
while ((result = GetLine ()) == 1)
|
|
;
|
|
|
|
return result;
|
|
}
|
|
|
|
static int PatchStrings (int dummy)
|
|
{
|
|
static int maxstrlen = 128;
|
|
static char *holdstring;
|
|
int result;
|
|
|
|
DPrintf ("[Strings]\n");
|
|
|
|
if (!holdstring)
|
|
holdstring = Malloc (maxstrlen);
|
|
|
|
while ((result = GetLine()) == 1) {
|
|
int i;
|
|
|
|
*holdstring = '\0';
|
|
do {
|
|
while (maxstrlen < strlen (holdstring) + strlen (Line2)) {
|
|
maxstrlen += 128;
|
|
holdstring = Realloc (holdstring, maxstrlen);
|
|
}
|
|
strcat (holdstring, skipwhite (Line2));
|
|
stripwhite (holdstring);
|
|
if (holdstring[strlen(holdstring)-1] == '\\') {
|
|
holdstring[strlen(holdstring)-1] = '\0';
|
|
Line2 = igets ();
|
|
} else
|
|
Line2 = NULL;
|
|
} while (Line2 && *Line2);
|
|
|
|
for (i = 0; i < NUMSTRINGS; i++)
|
|
if (!stricmp (Strings[i].name, Line1))
|
|
break;
|
|
|
|
if (i == NUMSTRINGS) {
|
|
Printf (PRINT_HIGH, "Unknown string: %s\n", Line1);
|
|
} else {
|
|
ReplaceSpecialChars (holdstring);
|
|
ReplaceString (&Strings[i].string, copystring (holdstring));
|
|
Strings[i].type = str_patched;
|
|
Printf (PRINT_HIGH, "%s set to:\n%s\n", Line1, holdstring);
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
static int DoInclude (int dummy)
|
|
{
|
|
char *data;
|
|
int savedversion, savepversion;
|
|
char *savepatchfile, *savepatchpt;
|
|
|
|
if (including) {
|
|
Printf (PRINT_HIGH, "Sorry, can't nest includes\n");
|
|
goto endinclude;
|
|
}
|
|
|
|
data = COM_Parse (Line2);
|
|
if (!stricmp (com_token, "notext")) {
|
|
includenotext = true;
|
|
data = COM_Parse (data);
|
|
}
|
|
|
|
if (!com_token[0]) {
|
|
includenotext = false;
|
|
Printf (PRINT_HIGH, "Include directive is missing filename\n");
|
|
goto endinclude;
|
|
}
|
|
|
|
DPrintf ("Including %s\n", com_token);
|
|
savepatchfile = PatchFile;
|
|
savepatchpt = PatchPt;
|
|
savedversion = dversion;
|
|
savepversion = pversion;
|
|
including = true;
|
|
|
|
DoDehPatch (com_token, false);
|
|
|
|
DPrintf ("Done with include\n");
|
|
PatchFile = savepatchfile;
|
|
PatchPt = savepatchpt;
|
|
dversion = savedversion;
|
|
pversion = savepversion;
|
|
|
|
endinclude:
|
|
including = false;
|
|
includenotext = false;
|
|
return GetLine();
|
|
}
|
|
|
|
void DoDehPatch (char *patchfile, BOOL autoloading)
|
|
{
|
|
char file[256];
|
|
int cont;
|
|
int filelen = 0; // Be quiet, gcc
|
|
int lump;
|
|
|
|
BackupData ();
|
|
PatchFile = NULL;
|
|
|
|
lump = W_CheckNumForName ("DEHACKED");
|
|
|
|
if (lump >= 0 && autoloading) {
|
|
// Execute the DEHACKED lump as a patch.
|
|
filelen = W_LumpLength (lump);
|
|
if ( (PatchFile = malloc (filelen + 1)) ) {
|
|
W_ReadLump (lump, PatchFile);
|
|
} else {
|
|
Printf (PRINT_HIGH, "Not enough memory to apply patch\n");
|
|
return;
|
|
}
|
|
} else if (patchfile) {
|
|
// Try to use patchfile as a patch.
|
|
FILE *deh;
|
|
|
|
strcpy (file, patchfile);
|
|
FixPathSeperator (file);
|
|
DefaultExtension (file, ".deh");
|
|
|
|
if ( !(deh = fopen (file, "rb")) ) {
|
|
strcpy (file, patchfile);
|
|
FixPathSeperator (file);
|
|
DefaultExtension (file, ".bex");
|
|
deh = fopen (file, "rb");
|
|
}
|
|
|
|
if (deh) {
|
|
filelen = Q_filelength (deh);
|
|
if ( (PatchFile = malloc (filelen + 1)) ) {
|
|
fread (PatchFile, 1, filelen, deh);
|
|
fclose (deh);
|
|
}
|
|
}
|
|
|
|
if (!PatchFile) {
|
|
// Couldn't find it on disk, try reading it from a lump
|
|
strcpy (file, patchfile);
|
|
FixPathSeperator (file);
|
|
ExtractFileBase (file, file);
|
|
file[8] = 0;
|
|
lump = W_CheckNumForName (file);
|
|
if (lump >= 0) {
|
|
filelen = W_LumpLength (lump);
|
|
if ( (PatchFile = malloc (filelen + 1)) ) {
|
|
W_ReadLump (lump, PatchFile);
|
|
} else {
|
|
Printf (PRINT_HIGH, "Not enough memory to apply patch\n");
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (!PatchFile) {
|
|
Printf (PRINT_HIGH, "Could not open DeHackEd patch \"%s\"\n", file);
|
|
return;
|
|
}
|
|
} else {
|
|
// Nothing to do.
|
|
return;
|
|
}
|
|
|
|
// End file with a NULL for our parser
|
|
PatchFile[filelen] = 0;
|
|
|
|
dversion = pversion = -1;
|
|
|
|
cont = 0;
|
|
if (!strncmp (PatchFile, "Patch File for DeHackEd v", 25)) {
|
|
PatchPt = strchr (PatchFile, '\n');
|
|
while ((cont = GetLine()) == 1) {
|
|
CHECKKEY ("Doom version", dversion)
|
|
else CHECKKEY ("Patch format", pversion)
|
|
}
|
|
if (!cont || dversion == -1 || pversion == -1) {
|
|
free (PatchFile);
|
|
Printf (PRINT_HIGH, "\"%s\" is not a DeHackEd patch file\n");
|
|
return;
|
|
}
|
|
} else {
|
|
DPrintf ("Patch does not have DeHackEd signature. Assuming .bex\n");
|
|
dversion = 19;
|
|
pversion = 6;
|
|
PatchPt = PatchFile;
|
|
while ((cont = GetLine()) == 1)
|
|
;
|
|
}
|
|
|
|
if (pversion != 6) {
|
|
Printf (PRINT_HIGH, "DeHackEd patch version is %d.\nUnexpected results may occur.\n", pversion);
|
|
}
|
|
|
|
if (dversion == 16)
|
|
dversion = 0;
|
|
else if (dversion == 17)
|
|
dversion = 2;
|
|
else if (dversion == 19)
|
|
dversion = 3;
|
|
else if (dversion == 20)
|
|
dversion = 1;
|
|
else if (dversion == 21)
|
|
dversion = 4;
|
|
else {
|
|
Printf (PRINT_HIGH, "Patch created with unknown DOOM version.\nAssuming version 1.9.\n");
|
|
dversion = 3;
|
|
}
|
|
|
|
do {
|
|
if (cont == 1) {
|
|
Printf (PRINT_HIGH, "Key %s encountered out of context\n", Line1);
|
|
cont = 0;
|
|
} else if (cont == 2)
|
|
cont = HandleMode (Line1, atoi (Line2));
|
|
} while (cont);
|
|
|
|
free (PatchFile);
|
|
Printf (PRINT_HIGH, "Patch installed\n");
|
|
} |