// // E_ProcessInventoryDeltas // // Does processing for inventorydelta sections, which allow cascading editing // of existing inventory items. The inventorydelta shares most of its fields // and processing code with the thingtype section. // void E_ProcessInventoryDeltas(cfg_t *cfg) { int i, numdeltas; E_EDFLogPuts("\t* Processing inventory deltas\n"); numdeltas = cfg_size(cfg, EDF_SEC_INVDELTA); E_EDFLogPrintf("\t\t%d inventorydelta(s) defined\n", numdeltas); for(i = 0; i < numdeltas; i++) { cfg_t *deltasec = cfg_getnsec(cfg, EDF_SEC_INVDELTA, i); inventory_t *inv; // get thingtype to edit if(!cfg_size(deltasec, ITEM_DELTA_NAME)) E_EDFLoggedErr(2, "E_ProcessInventoryDeltas: inventorydelta requires name field\n"); inv = E_GetInventoryForName(cfg_getstr(deltasec, ITEM_DELTA_NAME)); E_ProcessInventory(inv, deltasec, cfg, false); E_EDFLogPrintf("\t\tApplied inventorydelta #%d to %s(#%d)\n", i, inv->name, inv->numkey); } }
// // Process an individual switch // static void E_processSwitch(cfg_t *cfg) { const char *title = cfg_title(cfg); ESwitchDef *def = e_switch_namehash.objectForKey(title); const bool modified = !!def; if(!def) { def = new ESwitchDef; def->offpic = title; e_switch_namehash.addObject(def); eswitches.add(def); E_EDFLogPrintf("\t\tDefined switch %s\n", title); } else E_EDFLogPrintf("\t\tModified switch %s\n", title); auto isset = [modified, cfg](const char *field) { return !modified || cfg_size(cfg, field) > 0; }; if(isset(ITEM_SWITCH_ONPIC)) def->onpic = cfg_getstr(cfg, ITEM_SWITCH_ONPIC); if(isset(ITEM_SWITCH_ONSOUND)) def->onsound = cfg_getstr(cfg, ITEM_SWITCH_ONSOUND); if(isset(ITEM_SWITCH_OFFSOUND)) def->offsound = cfg_getstr(cfg, ITEM_SWITCH_OFFSOUND); if(isset(ITEM_SWITCH_GAMEINDEX)) def->episode = cfg_getint(cfg, ITEM_SWITCH_GAMEINDEX); }
// // E_CheckInclude // // Pass a pointer to some cached data and the size of that data. The SHA-1 // hash will be calculated and compared against the SHA-1 hashes of all other // data sources that have been sent into this function. Returns true if the // data should be included, and false otherwise (ie. there was a match). // bool E_CheckInclude(const char *data, size_t size) { size_t numincludes; char *digest; // calculate the SHA-1 hash of the data HashData newHash(HashData::SHA1, (const uint8_t *)data, (uint32_t)size); // output digest string digest = newHash.digestToString(); E_EDFLogPrintf("\t\t SHA-1 = %s\n", digest); efree(digest); numincludes = eincludes.getLength(); // compare against existing includes for(size_t i = 0; i < numincludes; i++) { // found a match? if(newHash == eincludes[i]) { E_EDFLogPuts("\t\t\tDeclined, SHA-1 match detected.\n"); return false; } } // this source has not been processed before, so add its hash to the list eincludes.add(newHash); return true; }
// // E_OpenAndCheckInclude // // Performs the following actions: // 1. Caches the indicated file or lump (lump only if lumpnum >= 0). // If the file cannot be opened, the libConfuse error code 1 is // returned. // 2. Calls E_CheckInclude on the cached data. // 3. If there is a hash collision, the data is freed. The libConfuse // "ok" code 0 will be returned. // 4. If there is not a hash collision, the data is included through a // call to cfg_lexer_include. The return value of that function will // be propagated. // static int E_OpenAndCheckInclude(cfg_t *cfg, const char *fn, int lumpnum) { size_t len; char *data; int code = 1; E_EDFLogPrintf("\t\t* Including %s\n", fn); // must open the data source if((data = cfg_lexer_mustopen(cfg, fn, lumpnum, &len))) { // see if we already parsed this data source if(E_CheckInclude(data, len)) code = cfg_lexer_include(cfg, data, fn, lumpnum); else { // we already parsed it, but this is not an error; // we ignore the include and return 0 to indicate success. efree(data); code = 0; } } return code; }
// // E_ProcessInventoryDefs // // Resolves and loads all information for the inventory structures. // void E_ProcessInventoryDefs(cfg_t *cfg) { unsigned int i, numInventory; E_EDFLogPuts("\t* Processing inventory data\n"); if(!(numInventory = cfg_size(cfg, EDF_SEC_INVENTORY))) return; // allocate inheritance stack and hitlist inv_pstack = ecalloc(inventory_t **, numInventoryDefs, sizeof(inventory_t *)); // TODO: any first-time-only processing? for(i = 0; i < numInventory; i++) { cfg_t *invsec = cfg_getnsec(cfg, EDF_SEC_INVENTORY, i); const char *name = cfg_title(invsec); inventory_t *inv = E_InventoryForName(name); // reset the inheritance stack E_ResetInventoryPStack(); // add this def to the stack E_AddInventoryToPStack(inv); E_ProcessInventory(inv, invsec, cfg, true); E_EDFLogPrintf("\t\tFinished inventory %s(#%d)\n", inv->name, inv->numkey); } // free tables efree(inv_pstack); }
// // E_ParseEDF // // Shared parsing phase code for E_ProcessEDF and E_ProcessNewEDF // static void E_ParseEDF(cfg_t *cfg, const char *filename) { E_EDFLogPuts("\n===================== Parsing Phase =====================\n"); // Parse the "root" EDF, which is either a chain of wad lumps named EDFROOT; // or a single file, which is determined by the -edf parameter, a GFS script, // the /base/game directory, or the default master EDF, which is root.edf in // /base. if(W_CheckNumForName("EDFROOT") != -1) { puts("E_ProcessEDF: Loading root lump.\n"); E_EDFLogPuts("\t* Parsing lump EDFROOT\n"); E_ParseEDFLump(cfg, "EDFROOT"); } else if(filename) { printf("E_ProcessEDF: Loading root file %s\n", filename); E_EDFLogPrintf("\t* Parsing EDF file %s\n", filename); E_ParseEDFFile(cfg, filename); } // Parse independent EDF lumps, which are not supposed to have been included // through any of the above (though if they have been, this is now accounted // for via the SHA-1 hashing mechanism which avoids reparses). E_ParseIndividualLumps(cfg); }
// // E_ProcessSpriteVars // // Sets the sprite numbers to be used for the player and blank // sprites by looking up a provided name in the sprite hash // table. // static void E_ProcessSpriteVars(cfg_t *cfg) { static bool firsttime = true; int sprnum; const char *str; E_EDFLogPuts("\t* Processing sprite variables\n"); // haleyjd 11/11/09: removed processing of obsolete playersprite variable // haleyjd 11/21/11: on subsequent runs, only replace if changed if(!firsttime && cfg_size(cfg, ITEM_BLANKSPRITE) == 0) return; firsttime = false; // load blank sprite number str = cfg_getstr(cfg, ITEM_BLANKSPRITE); sprnum = E_SpriteNumForName(str); if(sprnum == -1) { E_EDFLoggedErr(2, "E_ProcessSpriteVars: invalid blank sprite name: '%s'\n", str); } E_EDFLogPrintf("\t\tSet sprite %s(#%d) as blank sprite\n", str, sprnum); blankSpriteNum = sprnum; }
// // Processes "switch" blocks // void E_ProcessSwitches(cfg_t *cfg) { unsigned numswitches = cfg_size(cfg, EDF_SEC_SWITCH); E_EDFLogPrintf("\t* Processing switches\n" "\t\t%u switch(es) defined\n", numswitches); for(unsigned i = 0; i < numswitches; ++i) E_processSwitch(cfg_getnsec(cfg, EDF_SEC_SWITCH, i)); }
// // E_ProcessStrings // // 03/27/05: EDF can now define custom string objects. // These are now processed first. This is extremely simple. // void E_ProcessStrings(cfg_t *cfg) { unsigned int i, numstrings = cfg_size(cfg, EDF_SEC_STRING); E_EDFLogPrintf("\t* Processing strings\n" "\t\t%d string(s) defined\n", numstrings); for(i = 0; i < numstrings; ++i) { cfg_t *sec = cfg_getnsec(cfg, EDF_SEC_STRING, i); const char *mnemonic, *value, *bex, *bexsource; int number; dehstr_t *dehstr; mnemonic = cfg_title(sec); value = cfg_getstr(sec, ITEM_STRING_VAL); number = cfg_getint(sec, ITEM_STRING_NUM); // haleyjd 09/16/07: support also assigning the value of a BEX string, // and filling this string object with the value of a BEX string bex = cfg_getstr(sec, ITEM_STRING_BEXDST); bexsource = cfg_getstr(sec, ITEM_STRING_BEXSRC); // if bexsource is a valid BEX mnemonic, the value to use becomes the // value of that BEX string rather than any specified in this string object. if((dehstr = D_GetBEXStr(bexsource))) value = *(dehstr->ppstr); E_CreateString(value, mnemonic, number); E_EDFLogPrintf("\t\tDefined string '%s' (#%d)\n" "\t\t\tvalue = '%s'\n", mnemonic, number, value); if((dehstr = D_GetBEXStr(bex))) { *(dehstr->ppstr) = estrdup(value); E_EDFLogPrintf("\t\t\tCopied to BEX string '%s'\n", bex); } } }
// // E_CollectInventory // // Pre-creates and hashes by name the inventory definitions, for purpose // of mutual and forward references. // void E_CollectInventory(cfg_t *cfg) { static int currentID = 1; unsigned int i; unsigned int numInventory; // number of inventory defs defined by the cfg inventory_t *newInvDefs = NULL; // get number of inventory definitions defined by the cfg numInventory = cfg_size(cfg, EDF_SEC_INVENTORY); // echo counts E_EDFLogPrintf("\t\t%u inventory items defined\n", numInventory); if(numInventory) { // allocate inventory structures for the new thingtypes newInvDefs = estructalloc(inventory_t, numInventory); numInventoryDefs += numInventory; // create metatables for(i = 0; i < numInventory; i++) newInvDefs[i].meta = new MetaTable("inventory"); } // build hash tables E_EDFLogPuts("\t\tBuilding inventory hash tables\n"); // cycle through the thingtypes defined in the cfg for(i = 0; i < numInventory; i++) { cfg_t *invcfg = cfg_getnsec(cfg, EDF_SEC_INVENTORY, i); const char *name = cfg_title(invcfg); // This is a new inventory, whether or not one already exists by this name // in the hash table. For subsequent addition of EDF inventory defs at // runtime, the hash table semantics of "find newest first" take care of // overriding, while not breaking objects that depend on the original // definition of the inventory type for inheritance purposes. inventory_t *inv = &newInvDefs[i]; // initialize name inv->name = estrdup(name); // add to name hash inv_namehash.addObject(inv); // create ID number and add to hash table inv->numkey = currentID++; inv_numhash.addObject(inv); } }
static void E_EchoEnables() { E_Enable_t *enable = edf_enables; E_EDFLogPuts("\t* Final enable values:\n"); while(enable->name) { E_EDFLogPrintf("\t\t%s is %s\n", enable->name, enable->enabled ? "enabled" : "disabled"); ++enable; } }
// // E_ParseIndividualLumps // // haleyjd 03/21/10: consolidated function to handle the parsing of all // independent EDF lumps. // static void E_ParseIndividualLumps(cfg_t *cfg) { const char *lumpname; int i = 0; while((lumpname = edf_lumpnames[i++])) { E_EDFLogPrintf("\t* Parsing %s lump", lumpname); if(!E_ParseEDFLumpOptional(cfg, lumpname)) E_EDFLogPuts(" - not found, skipping.\n"); else E_EDFLogPuts("\n"); } }
// // Adds a switch defined externally // void E_AddSwitchDef(const ESwitchDef &extdef) { if(extdef.offpic.empty()) return; const char *title = extdef.offpic.constPtr(); ESwitchDef *def = e_switch_namehash.objectForKey(title); // NOTE: by external means, switches can't be modified. EDF takes priority. if(def) return; def = new ESwitchDef(extdef); e_switch_namehash.addObject(def); eswitches.add(def); E_EDFLogPrintf("\t\tDefined switch %s from ANIMDEFS\n", title); }
// // E_ProcessBossTypes // // Gets the thing type entries in the boss_spawner_types list, // for use by the SpawnFly codepointer. // // modified by schepe to remove 11-type limit // static void E_ProcessBossTypes(cfg_t *cfg) { int i, a = 0; int numTypes = cfg_size(cfg, SEC_BOSSTYPES); int numProbs = cfg_size(cfg, SEC_BOSSPROBS); bool useProbs = true; E_EDFLogPuts("\t* Processing boss spawn types\n"); if(!numTypes) { // haleyjd 05/31/06: allow zero boss types E_EDFLogPuts("\t\tNo boss types defined\n"); return; } // haleyjd 11/19/03: allow defaults for boss spawn probs if(!numProbs) useProbs = false; if(useProbs ? numTypes != numProbs : numTypes != 11) { E_EDFLoggedErr(2, "E_ProcessBossTypes: %d boss types, %d boss probs\n", numTypes, useProbs ? numProbs : 11); } // haleyjd 11/21/11: allow multiple runs if(BossSpawnTypes) { efree(BossSpawnTypes); BossSpawnTypes = NULL; } if(BossSpawnProbs) { efree(BossSpawnProbs); BossSpawnProbs = NULL; } NumBossTypes = numTypes; BossSpawnTypes = ecalloc(int *, numTypes, sizeof(int)); BossSpawnProbs = ecalloc(int *, numTypes, sizeof(int)); // load boss spawn probabilities for(i = 0; i < numTypes; ++i) { if(useProbs) { a += cfg_getnint(cfg, SEC_BOSSPROBS, i); BossSpawnProbs[i] = a; } else BossSpawnProbs[i] = BossDefaults[i]; } // check that the probabilities total 256 if(useProbs && a != 256) { E_EDFLoggedErr(2, "E_ProcessBossTypes: boss spawn probs do not total 256\n"); } for(i = 0; i < numTypes; ++i) { const char *typeName = cfg_getnstr(cfg, SEC_BOSSTYPES, i); int typeNum = E_ThingNumForName(typeName); if(typeNum == -1) { E_EDFLoggedWarning(2, "Warning: invalid boss type '%s'\n", typeName); typeNum = UnknownThingType; } BossSpawnTypes[i] = typeNum; E_EDFLogPrintf("\t\tAssigned type %s(#%d) to boss type %d\n", mobjinfo[typeNum]->name, typeNum, i); } }
// // E_ProcessCast // // Creates the DOOM II cast call information // static void E_ProcessCast(cfg_t *cfg) { static bool firsttime = true; int i, numcastorder = 0, numcastsections = 0; cfg_t **ci_order; E_EDFLogPuts("\t* Processing cast call\n"); // get number of cast sections numcastsections = cfg_size(cfg, SEC_CAST); if(firsttime && !numcastsections) // on main parse, at least one is required. E_EDFLoggedErr(2, "E_ProcessCast: no cast members defined.\n"); firsttime = false; E_EDFLogPrintf("\t\t%d cast member(s) defined\n", numcastsections); // haleyjd 11/21/11: allow multiple runs if(castorder) { int i; // free names for(i = 0; i < max_castorder; i++) { if(castorder[i].name) efree(castorder[i].name); } // free castorder efree(castorder); castorder = NULL; max_castorder = 0; } // check if the "castorder" array is defined for imposing an // order on the castinfo sections numcastorder = cfg_size(cfg, SEC_CASTORDER); E_EDFLogPrintf("\t\t%d cast member(s) in castorder\n", numcastorder); // determine size of castorder max_castorder = (numcastorder > 0) ? numcastorder : numcastsections; // allocate with size+1 for an end marker castorder = estructalloc(castinfo_t, max_castorder + 1); ci_order = ecalloc(cfg_t **, sizeof(cfg_t *), max_castorder); if(numcastorder > 0) { for(i = 0; i < numcastorder; ++i) { const char *title = cfg_getnstr(cfg, SEC_CASTORDER, i); cfg_t *section = cfg_gettsec(cfg, SEC_CAST, title); if(!section) { E_EDFLoggedErr(2, "E_ProcessCast: unknown cast member '%s' in castorder\n", title); } ci_order[i] = section; } } else { // no castorder array is defined, so use the cast members // in the order they are encountered (for backward compatibility) for(i = 0; i < numcastsections; ++i) ci_order[i] = cfg_getnsec(cfg, SEC_CAST, i); } for(i = 0; i < max_castorder; ++i) { int j; const char *tempstr; int tempint = 0; cfg_t *castsec = ci_order[i]; // resolve thing type tempstr = cfg_getstr(castsec, ITEM_CAST_TYPE); if(!tempstr || (tempint = E_ThingNumForName(tempstr)) == -1) { E_EDFLoggedWarning(2, "Warning: cast %d: unknown thing type %s\n", i, tempstr); tempint = UnknownThingType; } castorder[i].type = tempint; // get cast name, if any -- the first seventeen entries can // default to using the internal string editable via BEX strings tempstr = cfg_getstr(castsec, ITEM_CAST_NAME); if(cfg_size(castsec, ITEM_CAST_NAME) == 0 && i < 17) castorder[i].name = NULL; // set from DeHackEd else castorder[i].name = estrdup(tempstr); // store provided value // get stopattack flag (used by player) castorder[i].stopattack = cfg_getbool(castsec, ITEM_CAST_SA); // process sound blocks (up to four will be processed) tempint = cfg_size(castsec, ITEM_CAST_SOUND); for(j = 0; j < 4; ++j) { castorder[i].sounds[j].frame = 0; castorder[i].sounds[j].sound = 0; } for(j = 0; j < tempint && j < 4; ++j) { int num; sfxinfo_t *sfx; const char *name; cfg_t *soundsec = cfg_getnsec(castsec, ITEM_CAST_SOUND, j); // if these are invalid, just fill them with zero rather // than causing an error, as they're very unimportant // name of sound to play name = cfg_getstr(soundsec, ITEM_CAST_SOUNDNAME); // haleyjd 03/22/06: modified to support dehnum auto-allocation if((sfx = E_EDFSoundForName(name)) == NULL) { E_EDFLoggedWarning(2, "Warning: cast member references invalid sound %s\n", name); castorder[i].sounds[j].sound = 0; } else { if(sfx->dehackednum != -1 || E_AutoAllocSoundDEHNum(sfx)) castorder[i].sounds[j].sound = sfx->dehackednum; else { E_EDFLoggedWarning(2, "Warning: could not auto-allocate a DeHackEd number " "for sound %s\n", name); castorder[i].sounds[j].sound = 0; } } // name of frame that triggers sound event name = cfg_getstr(soundsec, ITEM_CAST_SOUNDFRAME); if((num = E_StateNumForName(name)) < 0) num = 0; castorder[i].sounds[j].frame = num; } } // initialize the end marker to all zeroes memset(&castorder[max_castorder], 0, sizeof(castinfo_t)); // free the ci_order table efree(ci_order); }