void parse_rank_tbl() { atexit(scoreing_close); char buf[MULTITEXT_LENGTH]; int rval, idx, persona; if ((rval = setjmp(parse_abort)) != 0) { mprintf(("TABLES: Unable to parse '%s'! Error code = %i.\n", "rank.tbl", rval)); return; } read_file_text("rank.tbl", CF_TYPE_TABLES); reset_parse(); // parse in all the rank names idx = 0; skip_to_string("[RANK NAMES]"); ignore_white_space(); while ( required_string_either("#End", "$Name:") ) { Assert ( idx < NUM_RANKS ); required_string("$Name:"); stuff_string( Ranks[idx].name, F_NAME, NAME_LENGTH ); required_string("$Points:"); stuff_int( &Ranks[idx].points ); required_string("$Bitmap:"); stuff_string( Ranks[idx].bitmap, F_NAME, MAX_FILENAME_LEN ); required_string("$Promotion Voice Base:"); stuff_string( Ranks[idx].promotion_voice_base, F_NAME, MAX_FILENAME_LEN ); while (check_for_string("$Promotion Text:")) { required_string("$Promotion Text:"); stuff_string(buf, F_MULTITEXT, sizeof(buf)); drop_white_space(buf); compact_multitext_string(buf); persona = -1; if (optional_string("+Persona:")) { stuff_int(&persona); if (persona < 0) { Warning(LOCATION, "Debriefing text for %s rank is assigned to an invalid persona: %i (must be 0 or greater).\n", Ranks[idx].name, persona); continue; } } Ranks[idx].promotion_text[persona] = vm_strdup(buf); } if (Ranks[idx].promotion_text.find(-1) == Ranks[idx].promotion_text.end()) { Warning(LOCATION, "%s rank is missing default debriefing text.\n", Ranks[idx].name); Ranks[idx].promotion_text[-1] = ""; } idx++; } required_string("#End"); // be sure that all rank points are in order #ifndef NDEBUG for ( idx = 0; idx < NUM_RANKS-1; idx++ ) { if ( Ranks[idx].points >= Ranks[idx+1].points ) Int3(); } #endif }
// game init void ssm_init() { int rval; ssm_info bogus, *s; char weapon_name[NAME_LENGTH]; if ((rval = setjmp(parse_abort)) != 0) { mprintf(("TABLES: Unable to parse '%s'! Error code = %i.\n", "ssm.tbl", rval)); return; } read_file_text("ssm.tbl", CF_TYPE_TABLES); reset_parse(); // parse the table Ssm_info_count = 0; while (!optional_string("#end")) { // another ssm definition if (optional_string("$SSM:")) { // pointer to info struct if (Ssm_info_count >= MAX_SSM_TYPES) { s = &bogus; } else { s = &Ssm_info[Ssm_info_count]; } // name stuff_string(s->name, F_NAME, NAME_LENGTH); // stuff data required_string("+Weapon:"); stuff_string(weapon_name, F_NAME, NAME_LENGTH); required_string("+Count:"); stuff_int(&s->count); required_string("+WarpRadius:"); stuff_float(&s->warp_radius); required_string("+WarpTime:"); stuff_float(&s->warp_time); required_string("+Radius:"); stuff_float(&s->radius); required_string("+Offset:"); stuff_float(&s->offset); // see if we have a valid weapon s->weapon_info_index = -1; s->weapon_info_index = weapon_name_lookup(weapon_name); if (s->weapon_info_index >= 0) { // valid Ssm_info_count++; } } } }
// initialization stuff for cutscenes void cutscene_init() { atexit(cutscene_close); char buf[MULTITEXT_LENGTH]; int rval; cutscene_info cutinfo; if ((rval = setjmp(parse_abort)) != 0) { mprintf(("TABLES: Unable to parse '%s'! Error code = %i.\n", "cutscenes.tbl", rval)); return; } read_file_text("cutscenes.tbl", CF_TYPE_TABLES); reset_parse(); // parse in all the cutscenes Cutscenes.clear(); skip_to_string("#Cutscenes"); ignore_white_space(); bool isFirstCutscene = true; while ( required_string_either("#End", "$Filename:") ) { required_string("$Filename:"); stuff_string( cutinfo.filename, F_PATHNAME, MAX_FILENAME_LEN ); required_string("$Name:"); stuff_string( cutinfo.name, F_NAME, NAME_LENGTH ); required_string("$Description:"); stuff_string(buf, F_MULTITEXT, sizeof(buf)); drop_white_space(buf); compact_multitext_string(buf); cutinfo.description = vm_strdup(buf); if (optional_string("$cd:")) stuff_int( &cutinfo.cd ); else cutinfo.cd = 0; cutinfo.viewable = false; if (isFirstCutscene) { isFirstCutscene = false; // The original code assumes the first movie is the intro, so always viewable cutinfo.viewable = true; } if (optional_string("$Always Viewable:")) { stuff_boolean(&cutinfo.viewable); } Cutscenes.push_back(cutinfo); } required_string("#End"); }
void techroom_intel_init() { int rval, temp; static int inited = 0; if (inited) return; // open localization lcl_ext_open(); if ((rval = setjmp(parse_abort)) != 0) { mprintf(("TABLES: Unable to parse '%s'! Error code = %i.\n", "species.tbl", rval)); lcl_ext_close(); return; } read_file_text("species.tbl", CF_TYPE_TABLES); reset_parse(); Intel_info_size = 0; while (optional_string("$Entry:")) { Assert(Intel_info_size < MAX_INTEL_ENTRIES); if (Intel_info_size >= MAX_INTEL_ENTRIES) { mprintf(("TECHMENU: Too many intel entries!\n")); break; } Intel_info[Intel_info_size].flags = IIF_DEFAULT_VALUE; required_string("$Name:"); stuff_string(Intel_info[Intel_info_size].name, F_NAME, NAME_LENGTH); required_string("$Anim:"); stuff_string(Intel_info[Intel_info_size].anim_filename, F_NAME, NAME_LENGTH); required_string("$AlwaysInTechRoom:"); stuff_int(&temp); if (temp) { // set default to align with what we read - Goober5000 Intel_info[Intel_info_size].flags |= IIF_IN_TECH_DATABASE; Intel_info[Intel_info_size].flags |= IIF_DEFAULT_IN_TECH_DATABASE; } required_string("$Description:"); stuff_string(Intel_info[Intel_info_size].desc, F_MULTITEXT, TECH_INTEL_DESC_LEN); Intel_info_size++; } inited = 1; // close localization lcl_ext_close(); }
void techroom_intel_init() { int temp; static int inited = 0; if (inited) return; try { read_file_text("species.tbl", CF_TYPE_TABLES); reset_parse(); Intel_info_size = 0; while (optional_string("$Entry:")) { Assert(Intel_info_size < MAX_INTEL_ENTRIES); if (Intel_info_size >= MAX_INTEL_ENTRIES) { mprintf(("TECHMENU: Too many intel entries!\n")); break; } Intel_info[Intel_info_size].flags = IIF_DEFAULT_VALUE; required_string("$Name:"); stuff_string(Intel_info[Intel_info_size].name, F_NAME, NAME_LENGTH); required_string("$Anim:"); stuff_string(Intel_info[Intel_info_size].anim_filename, F_NAME, NAME_LENGTH); required_string("$AlwaysInTechRoom:"); stuff_int(&temp); if (temp) { // set default to align with what we read - Goober5000 Intel_info[Intel_info_size].flags |= IIF_IN_TECH_DATABASE; Intel_info[Intel_info_size].flags |= IIF_DEFAULT_IN_TECH_DATABASE; } required_string("$Description:"); stuff_string(Intel_info[Intel_info_size].desc, F_MULTITEXT, TECH_INTEL_DESC_LEN); Intel_info_size++; } inited = 1; } catch (const parse::ParseException& e) { mprintf(("TABLES: Unable to parse '%s'! Error message = %s.\n", "species.tbl", e.what())); return; } }
// initialize neb2 stuff at game startup void neb2_init() { char name[MAX_FILENAME_LEN]; try { // read in the nebula.tbl read_file_text("nebula.tbl", CF_TYPE_TABLES); reset_parse(); // background bitmaps Neb2_bitmap_count = 0; while (!optional_string("#end")) { // nebula required_string("+Nebula:"); stuff_string(name, F_NAME, MAX_FILENAME_LEN); if (Neb2_bitmap_count < MAX_NEB2_BITMAPS) { strcpy_s(Neb2_bitmap_filenames[Neb2_bitmap_count++], name); } else { WarningEx(LOCATION, "nebula.tbl\nExceeded maximum number of nebulas (%d)!\nSkipping %s.", MAX_NEB2_BITMAPS, name); } } // poofs Neb2_poof_count = 0; while (!optional_string("#end")) { // nebula required_string("+Poof:"); stuff_string(name, F_NAME, MAX_FILENAME_LEN); if (Neb2_poof_count < MAX_NEB2_POOFS) { strcpy_s(Neb2_poof_filenames[Neb2_poof_count++], name); } else { WarningEx(LOCATION, "nebula.tbl\nExceeded maximum number of nebula poofs (%d)!\nSkipping %s.", MAX_NEB2_POOFS, name); } } // should always have 6 neb poofs Assert(Neb2_poof_count == 6); } catch (const parse::ParseException& e) { mprintf(("TABLES: Unable to parse '%s'! Error message = %s.\n", "nebula.tbl", e.what())); return; } }
char *TCLinkGetEntireResponse(TCLinkHandle handle, char *buf, int size) { param *p; int len = 0; TCLinkCon *c = (TCLinkCon *)handle; for (p = c->recv_param_list; p; p = p->next) { stuff_string(buf, &len, size, p->name); stuff_string(buf, &len, size, "="); stuff_string(buf, &len, size, p->value); stuff_string(buf, &len, size, "\n"); } return buf; }
ParticleEffectIndex parseEffectElement(EffectType forcedType, const SCP_string& name) { if (!optional_string("$New Effect")) { SCP_string newName; stuff_string(newName, F_NAME); auto index = ParticleManager::get()->getEffectByName(newName); if (index < 0) { error_display(0, "Unknown particle effect name '%s' encountered!", newName.c_str()); } if (forcedType != EffectType::Invalid) { // Validate the effect type auto effect = ParticleManager::get()->getEffect(index); if (effect->getType() != forcedType) { error_display(0, "Particle effect '%s' has the wrong effect type! Expected %s but was %s!", getEffectTypeName(forcedType), getEffectTypeName(effect->getType())); } } return index; } if (forcedType == EffectType::Invalid) { forcedType = parseEffectType(); } auto effect = constructEffect(name, forcedType); return ParticleManager::get()->addEffect(effect); }
// ------------------------------------------------------------------------------------------------- // gamesnd_parse_line() // // Parse a sound effect line // void gamesnd_parse_line(game_snd* gs, char* tag) { int is_3d; required_string(tag); stuff_int(&gs->sig); stuff_string(gs->filename, F_NAME, MAX_FILENAME_LEN, ","); if (!stricmp(gs->filename, NOX("empty"))) { gs->filename[0] = 0; advance_to_eoln(NULL); return; } Mp++; stuff_int(&gs->preload); stuff_float(&gs->default_volume); stuff_int(&is_3d); if (is_3d) { gs->flags |= GAME_SND_USE_DS3D; stuff_int(&gs->min); stuff_int(&gs->max); } else { gs->min = 0; gs->max = 0; } advance_to_eoln(NULL); }
// game init void ssm_init() { ssm_info bogus, *s; char weapon_name[NAME_LENGTH+1] = ""; read_file_text("ssm.tbl"); reset_parse(); // parse the table Ssm_info_count = 0; while(!optional_string("#end")){ // another ssm definition if(optional_string("$SSM:")){ // pointer to info struct if(Ssm_info_count >= MAX_SSM_TYPES){ s = &bogus; } else { s = &Ssm_info[Ssm_info_count]; } // name stuff_string(s->name, F_NAME, NULL); // stuff data required_string("+Weapon:"); stuff_string(weapon_name, F_NAME, NULL); required_string("+Count:"); stuff_int(&s->count); required_string("+WarpRadius:"); stuff_float(&s->warp_radius); required_string("+WarpTime:"); stuff_float(&s->warp_time); required_string("+Radius:"); stuff_float(&s->radius); required_string("+Offset:"); stuff_float(&s->offset); // see if we have a valid weapon s->weapon_info_index = -1; s->weapon_info_index = weapon_name_lookup(weapon_name); if(s->weapon_info_index >= 0){ // valid Ssm_info_count++; } } } }
void parse_fonts_tbl(char *only_parse_first_font, size_t only_parse_first_font_size) { int rval; char *filename; // choose file name // (this can be done within the function, as opposed to being passed as a parameter, // because fonts.tbl doesn't have a modular counterpart) if ( cf_exists_full("fonts.tbl", CF_TYPE_TABLES) ) { filename = "fonts.tbl"; } else { filename = NULL; } if ((rval = setjmp(parse_abort)) != 0) { mprintf(("TABLES: Unable to parse '%s'! Error code = %i.\n", (filename) ? filename : NOX("<default fonts.tbl>"), rval)); return; } if (filename != NULL) { read_file_text(filename, CF_TYPE_TABLES); } else { read_file_text_from_array(defaults_get_file("fonts.tbl")); } reset_parse(); // start parsing required_string("#Fonts"); // read fonts while (required_string_either("#End","$Font:")) { char font_filename[MAX_FILENAME_LEN]; // grab font required_string("$Font:"); stuff_string(font_filename, F_NAME, MAX_FILENAME_LEN); // if we only need the first font, copy it and bail if (only_parse_first_font != NULL) { strcpy_s(only_parse_first_font, only_parse_first_font_size, font_filename); return; } // create font int font_id = gr_create_font(font_filename); if (font_id < 0) { Warning(LOCATION, "Could not create font from typeface '%s'!", font_filename); } } // done parsing required_string("#End"); // double check if (Num_fonts < 3) { Error(LOCATION, "There must be at least three fonts in %s!", (filename) ? filename : NOX("<default fonts.tbl>")); } }
// parses the string.tbl to see which languages are supported. Doesn't read in any strings. void parse_stringstbl_quick(const char *filename) { lang_info language; int lang_idx; int i; read_file_text(filename, CF_TYPE_TABLES); reset_parse(); if (optional_string("#Supported Languages")) { while (required_string_either("#End","$Language:")) { required_string("$Language:"); stuff_string(language.lang_name, F_RAW, LCL_LANG_NAME_LEN + 1); required_string("+Extension:"); stuff_string(language.lang_ext, F_RAW, LCL_LANG_NAME_LEN + 1); required_string("+Special Character Index:"); stuff_ubyte(&language.special_char_indexes[0]); for (i = 1; i < LCL_MAX_FONTS; ++i) { // default to "none"/0 except for font03 which defaults to 176 // NOTE: fonts.tbl may override these values if (i == font::FONT3) { language.special_char_indexes[i] = 176; } else { language.special_char_indexes[i] = 0; } } lang_idx = -1; // see if we already have this language for (i = 0; i < (int)Lcl_languages.size(); i++) { if (!strcmp(Lcl_languages[i].lang_name, language.lang_name)) { strcpy_s(Lcl_languages[i].lang_ext, language.lang_ext); Lcl_languages[i].special_char_indexes[0] = language.special_char_indexes[0]; lang_idx = i; break; } } // if we have a new language, add it. if (lang_idx == -1) { Lcl_languages.push_back(language); } } } }
void parse_thrust_glows(species_info *species, bool no_create) { if (!no_create) { required_string("$ThrustGlows:"); generic_anim_init(&species->thruster_info.glow.normal, NULL); generic_anim_init(&species->thruster_info.glow.afterburn, NULL); } else if (!optional_string("$ThrustGlows:")) { return; } if (no_create) { if (optional_string("+Normal:")) stuff_string(species->thruster_info.glow.normal.filename, F_NAME, MAX_FILENAME_LEN); } else { required_string("+Normal:"); stuff_string(species->thruster_info.glow.normal.filename, F_NAME, MAX_FILENAME_LEN); } // if no glow is wanted then clear it if ( !VALID_FNAME(species->thruster_info.glow.normal.filename) ) generic_anim_init(&species->thruster_info.glow.normal, NULL); if (no_create) { if (optional_string("+Afterburn:")) stuff_string(species->thruster_info.glow.afterburn.filename, F_NAME, MAX_FILENAME_LEN); } else { required_string("+Afterburn:"); stuff_string(species->thruster_info.glow.afterburn.filename, F_NAME, MAX_FILENAME_LEN); } // if no glow is wanted then clear it if ( !VALID_FNAME(species->thruster_info.glow.afterburn.filename) ) generic_anim_init(&species->thruster_info.glow.afterburn, NULL); }
// initialization stuff for cutscenes void cutscene_init() { char buf[MULTITEXT_LENGTH]; int rval; if ((rval = setjmp(parse_abort)) != 0) { Error(LOCATION, "Error parsing 'rank.tbl'\r\nError code = %i.\r\n", rval); } // open localization lcl_ext_open(); read_file_text("cutscenes.tbl"); reset_parse(); // parse in all the rank names Num_cutscenes = 0; skip_to_string("#Cutscenes"); ignore_white_space(); while ( required_string_either("#End", "$Filename:") ) { Assert ( Num_cutscenes < MAX_CUTSCENES ); required_string("$Filename:"); stuff_string( Cutscenes[Num_cutscenes].filename, F_PATHNAME, NULL ); required_string("$Name:"); stuff_string( Cutscenes[Num_cutscenes].name, F_NAME, NULL ); required_string("$Description:"); stuff_string(buf, F_MULTITEXT, NULL); drop_white_space(buf); compact_multitext_string(buf); Cutscenes[Num_cutscenes].description = strdup(buf); required_string("$cd:"); stuff_int( &Cutscenes[Num_cutscenes].cd ); Num_cutscenes++; } required_string("#End"); Cutscenes_viewable = INTRO_CUTSCENE_FLAG; // close localization lcl_ext_close(); }
/** * Parse a sound. When using this function for a table entry, * required_string and optional_string aren't needed, as this function deals with * that as its tag parameter, just make sure that the destination sound index can * handle -1 if things don't work out. * * @param tag Tag * @param idx_dest Sound index destination * @param object_name Object name being parsed * @param flags See the parse_sound_flags enum * */ void parse_sound(const char* tag, int *idx_dest, const char* object_name, parse_sound_flags flags) { if(optional_string(tag)) { char buf[MAX_FILENAME_LEN]; stuff_string(buf, F_NAME, MAX_FILENAME_LEN); parse_sound_core(tag, idx_dest, object_name, buf, flags); } }
int parseAnimation(bool critical) { SCP_string name; stuff_string(name, F_FILESPEC); auto handle = bm_load_animation(name.c_str()); if (handle < 0) { int level = critical ? 1 : 0; error_display(level, "Failed to load effect %s!", name.c_str()); } return handle; }
ParticleEffectIndex parseEffect(const SCP_string& objectName) { SCP_string name; stuff_string(name, F_NAME); auto idx = ParticleManager::get()->getEffectByName(name); if (idx < 0) { if (objectName.empty()) { error_display(0, "Unknown particle effect name '%s' encountered!", name.c_str()); } else { error_display(0, "Unknown particle effect name '%s' encountered while parsing '%s'!", name.c_str(), objectName.c_str()); } } return idx; }
void gamesnd_parse_entry(game_snd *gs, bool no_create, SCP_vector<game_snd> *lookupVector) { SCP_string name; stuff_string(name, F_NAME, "\t \n"); if (!no_create) { if (lookupVector != NULL) { if (gamesnd_lookup_name(name.c_str(), *lookupVector) >= 0) { Warning(LOCATION, "Duplicate sound name \"%s\" found!", name.c_str()); } } gs->name = name; } else { int vectorIndex = gamesnd_lookup_name(name.c_str(), *lookupVector); if (vectorIndex < 0) { Warning(LOCATION, "No existing sound entry with name \"%s\" found!", name.c_str()); no_create = false; gs->name = name; } else { gs = &lookupVector->at(vectorIndex); } } if (optional_string("+Filename:")) { parse_gamesnd_new(gs, no_create); } else { parse_gamesnd_old(gs); } }
int parse_font() { int font_idx; SCP_string input; stuff_string(input, F_NAME); SCP_stringstream ss(input); int fontNum; ss >> fontNum; if (ss.fail()) { fontNum = FontManager::getFontIndex(input); if (fontNum < 0) { error_display(0, "Invalid font name \"%s\"!", input.c_str()); font_idx = -1; } else { font_idx = fontNum; } } else { if (fontNum < 0 || fontNum >= FontManager::numberOfFonts()) { error_display(0, "Invalid font number %d! must be greater or equal to zero and smaller than %d.", fontNum, FontManager::numberOfFonts()); font_idx = -1; } else { font_idx = fontNum; } } return font_idx; }
void hud_shield_game_init() { char name[MAX_FILENAME_LEN+1] = ""; // read in hud.tbl read_file_text("hud.tbl"); reset_parse(); Hud_shield_filename_count = 0; required_string("#Shield Icons Begin"); while(!optional_string("#End")){ required_string("$Shield:"); stuff_string(name, F_NAME, NULL); // maybe store Assert(Hud_shield_filename_count < MAX_SHIELD_ICONS); if(Hud_shield_filename_count < MAX_SHIELD_ICONS){ strcpy(Hud_shield_filenames[Hud_shield_filename_count++], name); } } }
//Takes a tag, a sound index destination, and the object name being parsed //tag and object_name are mostly so that we can debug stuff //This also means you shouldn't use optional_string or required_string, //just make sure the destination sound index can handle -1 if things //don't work out. void parse_sound(char* tag, int* idx_dest, char* object_name) { char buf[MAX_FILENAME_LEN]; int idx; if (optional_string(tag)) { stuff_string(buf, F_NAME, MAX_FILENAME_LEN); idx = gamesnd_get_by_name(buf); if (idx != -1) (*idx_dest) = idx; else (*idx_dest) = atoi(buf); //Ensure sound is in range if ((*idx_dest) < -1 || (*idx_dest) >= Num_game_sounds) { Warning(LOCATION, "%s sound index out of range on '%s'. Must be between 0 and %d. Forcing to -1 (Nonexistant sound).\n", tag, object_name, Num_game_sounds); } } }
// parses help.tbl and populates help_overlaylist[] void parse_helptbl(const char *filename) { int overlay_id, currcount, vtxcount; char name[HELP_MAX_NAME_LENGTH]; char buf[HELP_MAX_STRING_LENGTH + 1]; int i, j, rval; SCP_vector<help_pline> pline_temp; help_pline pline_temp2; SCP_vector<help_text> text_temp; help_text text_temp2; SCP_vector<help_right_bracket> rbracket_temp; help_right_bracket rbracket_temp2; SCP_vector<help_left_bracket> lbracket_temp; help_left_bracket lbracket_temp2; vec3d vec3d_temp; if ((rval = setjmp(parse_abort)) != 0) { mprintf(("TABLES: Unable to parse '%s'! Error code = %i.\n", filename, rval)); return; } read_file_text(filename, CF_TYPE_TABLES); reset_parse(); // for each overlay... while (optional_string("$")) { stuff_string(name, F_NAME, HELP_MAX_NAME_LENGTH); overlay_id = help_overlay_get_index(name); if (overlay_id < 0) { if (num_help_overlays >= MAX_HELP_OVERLAYS) { Warning(LOCATION, "Could not load help overlay after '%s' as maximum number of help overlays was reached (Max is %d)", help_overlaylist[overlay_id - 1].name, MAX_HELP_OVERLAYS); if (!skip_to_string("$end")) { Error(LOCATION, "Couldn't find $end. Help.tbl or -hlp.tbm is invalid.\n"); } continue; } else { overlay_id = num_help_overlays; strcpy_s(help_overlaylist[overlay_id].name, name); num_help_overlays++; } } // clear out counters in the overlay struct help_overlaylist[overlay_id].plinecount = 0; help_overlaylist[overlay_id].textcount = 0; help_overlaylist[overlay_id].rbracketcount = 0; help_overlaylist[overlay_id].lbracketcount = 0; help_overlaylist[overlay_id].fontlist.clear(); help_overlaylist[overlay_id].plinelist.clear(); help_overlaylist[overlay_id].textlist.clear(); help_overlaylist[overlay_id].rbracketlist.clear(); help_overlaylist[overlay_id].lbracketlist.clear(); if (optional_string("+resolutions")) { stuff_int(&help_overlaylist[overlay_id].num_resolutions); } else { help_overlaylist[overlay_id].num_resolutions = 2; } if (help_overlaylist[overlay_id].num_resolutions < 1) { Error(LOCATION, "+resolutions in %s is %d. (Must be 1 or greater)", filename, help_overlaylist[overlay_id].num_resolutions); } if (optional_string("+font")) { int font; for (i=0; i<help_overlaylist[overlay_id].num_resolutions; i++) { stuff_int(&font); help_overlaylist[overlay_id].fontlist.push_back(font); } } else { for (i=0; i<help_overlaylist[overlay_id].num_resolutions; i++) { help_overlaylist[overlay_id].fontlist.push_back(FONT1); } } for (i=0; i<help_overlaylist[overlay_id].num_resolutions; i++) { help_overlaylist[overlay_id].plinelist.push_back(pline_temp); help_overlaylist[overlay_id].textlist.push_back(text_temp); help_overlaylist[overlay_id].rbracketlist.push_back(rbracket_temp); help_overlaylist[overlay_id].lbracketlist.push_back(lbracket_temp); } int type; // read in all elements for this overlay while ((type = required_string_one_of(5, "+pline", "+text", "+right_bracket", "+left_bracket", "$end")) != 4) { // Doing it this way means an error lists "$end" at the end, which seems appropriate. -MageKing17 switch (type) { case 0: // +pline required_string("+pline"); currcount = help_overlaylist[overlay_id].plinecount; int a, b; // temp vars to read in int before cast to float; // read number of pline vertices stuff_int(&vtxcount); // get vertex coordinates for each resolution for (i = 0; i < help_overlaylist[overlay_id].num_resolutions; i++) { help_overlaylist[overlay_id].plinelist.at(i).push_back(pline_temp2); for (j = 0; j < vtxcount; j++) { help_overlaylist[overlay_id].plinelist.at(i).at(currcount).vtx.push_back(vec3d_temp); help_overlaylist[overlay_id].plinelist.at(i).at(currcount).vtxcount = vtxcount; stuff_int(&a); stuff_int(&b); help_overlaylist[overlay_id].plinelist.at(i).at(currcount).vtx.at(j).xyz.x = (float)a; help_overlaylist[overlay_id].plinelist.at(i).at(currcount).vtx.at(j).xyz.y = (float)b; help_overlaylist[overlay_id].plinelist.at(i).at(currcount).vtx.at(j).xyz.z = 0.0f; } } help_overlaylist[overlay_id].plinecount++; break; case 1: // +text required_string("+text"); currcount = help_overlaylist[overlay_id].textcount; // get coordinates for each resolution for (i = 0; i < help_overlaylist[overlay_id].num_resolutions; i++) { help_overlaylist[overlay_id].textlist.at(i).push_back(text_temp2); stuff_int(&(help_overlaylist[overlay_id].textlist.at(i).at(currcount).x_coord)); stuff_int(&(help_overlaylist[overlay_id].textlist.at(i).at(currcount).y_coord)); } // get string (always use the first resolution) stuff_string(buf, F_MESSAGE, sizeof(buf)); help_overlaylist[overlay_id].textlist.at(0).at(currcount).string = vm_strdup(buf); help_overlaylist[overlay_id].textcount++; break; case 2: // +right_bracket required_string("+right_bracket"); currcount = help_overlaylist[overlay_id].rbracketcount; // get coordinates for each resolution for (i = 0; i < help_overlaylist[overlay_id].num_resolutions; i++) { help_overlaylist[overlay_id].rbracketlist.at(i).push_back(rbracket_temp2); stuff_int(&(help_overlaylist[overlay_id].rbracketlist.at(i).at(currcount).x_coord)); stuff_int(&(help_overlaylist[overlay_id].rbracketlist.at(i).at(currcount).y_coord)); } help_overlaylist[overlay_id].rbracketcount++; break; case 3: // +left_bracket required_string("+left_bracket"); currcount = help_overlaylist[overlay_id].lbracketcount; // get coordinates for each resolution for (i = 0; i < help_overlaylist[overlay_id].num_resolutions; i++) { help_overlaylist[overlay_id].lbracketlist.at(i).push_back(lbracket_temp2); stuff_int(&(help_overlaylist[overlay_id].lbracketlist.at(i).at(currcount).x_coord)); stuff_int(&(help_overlaylist[overlay_id].lbracketlist.at(i).at(currcount).y_coord)); } help_overlaylist[overlay_id].lbracketcount++; break; case -1: // -noparseerrors is set break; case 4: // $end default: Assertion(false, "This should never happen.\n"); break; } } // end while required_string("$end"); } // end while }
void parse_everything_else(const char *filename) { Assertion(filename != NULL, "parse_everything_else() called on NULL; get a coder!\n"); read_file_text(filename, CF_TYPE_TABLES); int err_code; if ((err_code = setjmp(parse_abort)) != 0) { mprintf(("TABLES: Unable to parse '%s'! Error code = %d.\n", filename, err_code)); return; } reset_parse(); int rgba[4] = {0,0,0,0}; // reusable temp vars int i, j; SCP_string temp; if (optional_string("#Start Colors")) { // Skip this section; we already parsed it in every file. skip_to_string("#End", NULL); } //Team coloring if (optional_string("#Team Colors")) { while (required_string_either("#End", "$Team Name:")) { required_string("$Team Name:"); // required to move the parse pointer forward team_color temp_color; char temp2[NAME_LENGTH]; stuff_string(temp2, F_NAME, NAME_LENGTH); temp = temp2; if (!stricmp(temp2, "none")) { Warning(LOCATION, "Team color in '%s' defined with a name of '%s'; this won't be usable due to 'None' being used for a lack of a team color by the engine.\n", filename, temp2); } if (required_string("$Team Stripe Color:")) { int rgb[3]; stuff_int_list(rgb, 3, RAW_INTEGER_TYPE); for (i = 0; i < 3; i++) { CLAMP(rgb[i], 0, 255); } temp_color.stripe.r = rgb[0] / 255.0f; temp_color.stripe.g = rgb[1] / 255.0f; temp_color.stripe.b = rgb[2] / 255.0f; } if (required_string("$Team Base Color:")) { int rgb[3]; stuff_int_list(rgb, 3, RAW_INTEGER_TYPE); for (i = 0; i < 3; i++) { CLAMP(rgb[i], 0, 255); } temp_color.base.r = rgb[0] / 255.0f; temp_color.base.g = rgb[1] / 255.0f; temp_color.base.b = rgb[2] / 255.0f; } if (Team_Colors.find(temp) == Team_Colors.end()) { // Only push to the vector if the team isn't already defined. Team_Names.push_back(temp); } Team_Colors[temp] = temp_color; } required_string("#End"); } // Previously-hardcoded interface colors if (optional_string("#Interface Colors")) { char *color_names[INTERFACE_COLORS] = { "$Text Normal:", "$Text Subselected:", "$Text Selected:", "$Text Error:", "$Text Error Highlighted:", "$Text Active:", "$Text Active Highlighted:", "$Text Heading:", "$More Indicator:", "$Bright More Indicator:", "$Bright:", "$Normal:", }; // now for each color, check if its corresponding string is there for (i = 0; i < INTERFACE_COLORS; i++) { if (optional_string(color_names[i])) { // if so, get its rgba values and initialise it using them mprintf(("'%s' has been redefined.\n", color_names[i])); if ( check_for_string("(") ) { // If we have a list of integers, use them. stuff_int_list(rgba, 4, RAW_INTEGER_TYPE); for (j = 0; j < 4; j++) { if (rgba[j] < 0) { Warning(LOCATION, "RGBA value for '%s' in %s too low (%d), capping to 0.\n", color_names[i], filename, rgba[j]); rgba[j] = 0; } else if (rgba[j] > 255) { Warning(LOCATION, "RGBA value for '%s' in %s too high (%d), capping to 255.\n", color_names[i], filename, rgba[j]); rgba[j] = 255; } } gr_init_alphacolor(interface_colors[i], rgba[0], rgba[1], rgba[2], rgba[3]); //} else if (check_for_string("#")) { // stuff_hex_list(rgba, 4); // gr_init_alphacolor(interface_colors[i], rgba[0], rgba[1], rgba[2], rgba[3]); } else { // We have a string; it should be the name of a color to use. stuff_string(temp, F_NAME); for (j = 0; j < TOTAL_COLORS; j++) { if ( !temp.compare(COLOR_NAMES[j]) ) { break; } } if ( j == TOTAL_COLORS ) { Warning(LOCATION, "Unknown color '%s' in %s, for definition of '%s'; using default ('%s').\n", temp.c_str(), filename, color_names[i], COLOR_NAMES[interface_defaults[i]]); } else { Assertion(j >= 0 && j < TOTAL_COLORS, "Attempting to copy nonexistant color (%d out of 0-%d)!\n", j, TOTAL_COLORS-1); memcpy(interface_colors[i], COLOR_LIST[j], sizeof(color)); } } } } required_string("#End"); } // Text color tags; for briefings, command briefings, debriefings, and the fiction viewer if (optional_string("#Color Tags")) { while (required_string_either("$Tag:", "#End") < 1) { required_string("$Tag:"); color temp_color; char tag; stuff_string(temp, F_RAW); if (temp[0] == '$') { if (temp[1] == '\0') { Error(LOCATION, "%s - found a '$Tag:' entry with a solitary '$'.\n", filename); } tag = temp[1]; if (temp[2] != '\0') { Warning(LOCATION, "%s - tag '$%c' has extra text in its definition.\n", filename, tag); } } else if (temp[0] == '\0') { Error(LOCATION, "%s - found a '$Tag:' entry with no tag.\n", filename); } else { tag = temp[0]; if (temp[1] != '\0') { Warning(LOCATION, "%s - tag '$%c' has extra text in its definition.\n", filename, tag); } } if (Tagged_Colors.find(tag) == Tagged_Colors.end()) { // Only push the tag to our list of tags if it's actually new, not just a redefinition. Color_Tags.push_back(tag); } switch(required_string_one_of(4, "+Color:", "+Friendly", "+Hostile", "+Neutral")) { case 0: // +Color required_string("+Color:"); rgba[0] = rgba[1] = rgba[2] = 0; rgba[3] = 255; // Odds are pretty high you want it to have full alpha... if ( check_for_string("(") ) { stuff_int_list(rgba, 4, RAW_INTEGER_TYPE); for (j = 0; j < 4; j++) { if (rgba[j] < 0) { Warning(LOCATION, "RGBA value for '$%c' in %s too low (%d), capping to 0.\n", tag, filename, rgba[j]); rgba[j] = 0; } else if (rgba[j] > 255) { Warning(LOCATION, "RGBA value for '$%c' in %s too high (%d), capping to 255.\n", tag, filename, rgba[j]); rgba[j] = 255; } } gr_init_alphacolor(&temp_color, rgba[0], rgba[1], rgba[2], rgba[3]); Custom_Colors[tag] = temp_color; Tagged_Colors[tag] = &Custom_Colors[tag]; //} else if ( check_for_string ("#") ) { // stuff_hex_list(rgba, 4); // gr_init_alphacolor(&temp_color, rgba[0], rgba[1], rgba[2], rgba[3]); // Custom_Colors[tag] = temp_color; // Tagged_Colors[tag] = &Custom_Colors[tag]; } else { // We have a string; it should be the name of a color to use. stuff_string(temp, F_NAME); for (j = 0; j < TOTAL_COLORS; j++) { if ( !temp.compare(COLOR_NAMES[j]) ) { break; } } if ( j == TOTAL_COLORS ) { Error(LOCATION, "Unknown color '%s' in %s, for definition of tag '$%c'.\n", temp.c_str(), filename, tag); } Tagged_Colors[tag] = COLOR_LIST[j]; } break; case 1: // +Friendly required_string("+Friendly"); Tagged_Colors[tag] = &Brief_color_green; break; case 2: // +Hostile required_string("+Hostile"); Tagged_Colors[tag] = &Brief_color_red; break; case 3: // +Neutral required_string("+Neutral"); Tagged_Colors[tag] = &Brief_color_legacy_neutral; break; case -1: // -noparseerrors is set if (Tagged_Colors.find(tag) == Tagged_Colors.end()) { // It was a new color, but since we haven't actually defined it... Color_Tags.pop_back(); } break; default: Assertion(false, "MageKing17 made a coding error somewhere, and you should laugh at him (and report this error).\n"); break; } } required_string("#End"); } Assertion(Color_Tags.size() == Tagged_Colors.size(), "Color_Tags and Tagged_Colors size mismatch; get a coder!\n"); if (optional_string("#Default Text Colors")) { char* color_names[MAX_DEFAULT_TEXT_COLORS] = { "$Fiction Viewer:", "$Command Briefing:", "$Briefing:", "$Redalert Briefing:", "$Debriefing:", "$Recommendation:", "$Loop Briefing:", }; char *color_value[MAX_DEFAULT_TEXT_COLORS] = { &default_fiction_viewer_color, &default_command_briefing_color, &default_briefing_color, &default_redalert_briefing_color, &default_debriefing_color, &default_recommendation_color, &default_loop_briefing_color, }; for (i = 0; i < MAX_DEFAULT_TEXT_COLORS; i++) { if ( optional_string(color_names[i]) ) { stuff_string(temp, F_RAW); if (temp[0] == '$') { if (temp[1] == '\0') { Error(LOCATION, "%s - default text color '%s' entry with a solitary '$'.\n", filename, color_names[i]); } *color_value[i] = temp[1]; if (temp[2] != '\0') { Warning(LOCATION, "%s - default text color '%s' has extra text after the tag '$%c'.\n", filename, color_names[i], *color_value[i]); } } else if (temp[0] == '\0') { Error(LOCATION, "%s - default text color '%s' entry with no tag.\n", filename, color_names[i]); } else { *color_value[i] = temp[0]; if (temp[1] != '\0') { Warning(LOCATION, "%s - default text color '%s' has extra text after the tag '$%c'.\n", filename, color_names[i], *color_value[i]); } } if (Tagged_Colors.find(*color_value[i]) == Tagged_Colors.end()) { // Just mprintf() this information instead of complaining with a Warning(); the tag might be defined in a later-loading TBM, and if it isn't, nothing too terrible will happen. mprintf(("%s - default text color '%s' set to non-existant tag '$%c'.\n", filename, color_names[i], *color_value[i])); } } } required_string("#End"); } }
void parse_mod_table(const char *filename) { int rval; // SCP_vector<SCP_string> lines; // open localization lcl_ext_open(); if ((rval = setjmp(parse_abort)) != 0) { mprintf(("TABLES: Unable to parse '%s'! Error code = %i.\n", (filename) ? filename : "<default game_settings.tbl>", rval)); lcl_ext_close(); return; } if (filename == NULL) read_file_text_from_array(defaults_get_file("game_settings.tbl")); else read_file_text(filename, CF_TYPE_TABLES); reset_parse(); // start parsing optional_string("#CAMPAIGN SETTINGS"); if (optional_string("$Default Campaign File Name:")) { char temp[MAX_FILENAME_LEN]; stuff_string(temp, F_NAME, MAX_FILENAME_LEN); // remove extension? if (drop_extension(temp)) { mprintf(("Game Settings Table: Removed extension on default campaign file name %s\n", temp)); } // check length int maxlen = (MAX_FILENAME_LEN - 4); int len = strlen(temp); if (len > maxlen) { Warning(LOCATION, "Token too long: [%s]. Length = %i. Max is %i.\n", temp, len, maxlen); temp[maxlen] = 0; } strcpy_s(Default_campaign_file_name, temp); } if (optional_string("#Ignored Campaign File Names")) { SCP_string campaign_name; while (optional_string("$Campaign File Name:")) { stuff_string(campaign_name, F_NAME); // remove extension? if (drop_extension(campaign_name)) { mprintf(("Game Settings Table: Removed extension on ignored campaign file name %s\n", campaign_name.c_str())); } Ignored_campaigns.push_back(campaign_name); } } optional_string("#HUD SETTINGS"); // how long should the game wait before displaying a directive? if (optional_string("$Directive Wait Time:")) { stuff_int(&Directive_wait_time); } if (optional_string("$Cutscene camera displays HUD:")) { stuff_boolean(&Cutscene_camera_displays_hud); } // compatibility if (optional_string("$Cutscene camera disables HUD:")) { mprintf(("Game Settings Table: \"$$Cutscene camera disables HUD\" is deprecated in favor of \"$Cutscene camera displays HUD\"\n")); bool temp; stuff_boolean(&temp); Cutscene_camera_displays_hud = !temp; } if (optional_string("$Full color head animations:")) { stuff_boolean(&Full_color_head_anis); } // compatibility if (optional_string("$Color head animations with hud colors:")) { mprintf(("Game Settings Table: \"$Color head animations with hud colors\" is deprecated in favor of \"$Full color head animations\"\n")); bool temp; stuff_boolean(&temp); Full_color_head_anis = !temp; } optional_string("#SEXP SETTINGS"); if (optional_string("$Loop SEXPs Then Arguments:")) { stuff_boolean(&True_loop_argument_sexps); if (True_loop_argument_sexps) { mprintf(("Game Settings Table: Using Reversed Loops For SEXP Arguments\n")); } else { mprintf(("Game Settings Table: Using Standard Loops For SEXP Arguments\n")); } } if (optional_string("$Use Alternate Chaining Behavior:")) { stuff_boolean(&Alternate_chaining_behavior); if (Alternate_chaining_behavior) { mprintf(("Game Settings Table: Using alternate event chaining behavior\n")); } else { mprintf(("Game Settings Table: Using standard event chaining behavior\n")); } } optional_string("#GRAPHICS SETTINGS"); if (optional_string("$Enable External Shaders:")) { stuff_boolean(&Enable_external_shaders); if (Enable_external_shaders) mprintf(("Game Settings Table: External shaders are enabled\n")); else mprintf(("Game Settings Table: External shaders are DISABLED\n")); } if (optional_string("$Default Detail Level:")) { int detail_level; stuff_int(&detail_level); mprintf(("Game Settings Table: Setting default detail level to %i of %i-%i\n", detail_level, 0, NUM_DEFAULT_DETAIL_LEVELS-1)); if (detail_level < 0 || detail_level > NUM_DEFAULT_DETAIL_LEVELS-1) { Warning(LOCATION, "Invalid detail level: %i, setting to %i\n", detail_level, Default_detail_level); } else { Default_detail_level = detail_level; } } optional_string("#NETWORK SETTINGS"); if (optional_string("$FS2NetD port:")) { stuff_int(&FS2NetD_port); if (FS2NetD_port) mprintf(("Game Settings Table: FS2NetD connecting to port %i\n", FS2NetD_port)); } optional_string("#OTHER SETTINGS"); if (optional_string("$Fixed Turret Collisions:")) { stuff_boolean(&Fixed_turret_collisions); } if (optional_string("$Damage Impacted Subsystem First:")) { stuff_boolean(&Damage_impacted_subsystem_first); } if (optional_string("$Default ship select effect:")) { char effect[NAME_LENGTH]; stuff_string(effect, F_NAME, NAME_LENGTH); if (!stricmp(effect, "FS2")) Default_ship_select_effect = 2; else if (!stricmp(effect, "FS1")) Default_ship_select_effect = 1; else if (!stricmp(effect, "off")) Default_ship_select_effect = 0; } if (optional_string("$Default weapon select effect:")) { char effect[NAME_LENGTH]; stuff_string(effect, F_NAME, NAME_LENGTH); if (!stricmp(effect, "FS2")) Default_weapon_select_effect = 2; else if (!stricmp(effect, "FS1")) Default_weapon_select_effect = 1; else if (!stricmp(effect, "off")) Default_weapon_select_effect = 0; } if (optional_string("$Weapons inherit parent collision group:")) { stuff_boolean(&Weapons_inherit_parent_collision_group); if (Weapons_inherit_parent_collision_group) mprintf(("Game Settings Table: Weapons inherit parent collision group\n")); } if (optional_string("$Flight controls follow eyepoint orientation:")) { stuff_boolean(&Flight_controls_follow_eyepoint_orientation); if (Flight_controls_follow_eyepoint_orientation) mprintf(("Game Settings Table: Flight controls follow eyepoint orientation\n")); } required_string("#END"); // close localization lcl_ext_close(); }
void credits_parse_table(const char* filename) { try { read_file_text(filename, CF_TYPE_TABLES); reset_parse(); // any metadata? if (optional_string("$Music:")) { stuff_string(Credits_music_name, F_NAME, NAME_LENGTH); } if (optional_string("$Number of Images:")) { int temp; stuff_int(&temp); if (temp > 0) Credits_num_images = temp; } if (optional_string("$Start Image Index:")) { stuff_int(&Credits_artwork_index); // bounds check if (Credits_artwork_index < 0) { Credits_artwork_index = 0; } else if (Credits_artwork_index >= Credits_num_images) { Credits_artwork_index = Credits_num_images - 1; } } if (optional_string("$Text scroll rate:")) { stuff_float(&Credits_scroll_rate); if (Credits_scroll_rate < 0.01f) Credits_scroll_rate = 0.01f; } if (optional_string("$Artworks display time:")) { stuff_float(&Credits_artwork_display_time); if (Credits_artwork_display_time < 0.01f) Credits_artwork_display_time = 0.01f; } if (optional_string("$Artworks fade time:")) { stuff_float(&Credits_artwork_fade_time); if (Credits_artwork_fade_time < 0.01f) Credits_artwork_fade_time = 0.01f; } if (optional_string("$SCP Credits position:")) { char mode[NAME_LENGTH]; stuff_string(mode, F_NAME, NAME_LENGTH); if (!stricmp(mode, "Start")) SCP_credits_position = START; else if (!stricmp(mode, "End")) SCP_credits_position = END; else Warning(LOCATION, "Unknown credits position mode \"%s\".", mode); } ignore_white_space(); SCP_string credits_text; SCP_string line; SCP_vector<int> charNum; SCP_vector<const char*> lines; int numLines = -1; bool first_run = true; while (!check_for_string_raw("#end")) { // Read in a line of text stuff_string_line(line); // This is a bit odd but it means if a total conversion uses different credits the // Volition credit won't happen // Also don't append the default credits anymore when there was already a parsed table if (first_run && !Credits_parsed && !line.compare(mod_check)) { credits_text.append(unmodified_credits); } first_run = false; if (line.empty()) { // If the line is empty then just append a newline, don't bother with splitting it first credits_text.append("\n"); } else { // split_str doesn't take care of this. charNum.clear(); // Split the string into multiple lines if it's too long numLines = split_str(line.c_str(), Credits_text_coords[gr_screen.res][2], charNum, lines, -1); // Make sure that we have valid data Assertion(lines.size() == (size_t)numLines, "split_str reported %d lines but vector contains " SIZE_T_ARG " entries!", numLines, lines.size()); Assertion(lines.size() <= charNum.size(), "Something has gone wrong while splitting strings. Got " SIZE_T_ARG " lines but only " SIZE_T_ARG " chacter lengths.", lines.size(), charNum.size()); // Now add all splitted lines to the credit text and append a newline to the end for (int i = 0; i < numLines; i++) { credits_text.append(SCP_string(lines[i], charNum[i])); credits_text.append("\n"); } } } Credit_text_parts.push_back(credits_text); Credits_parsed = true; } catch (const parse::ParseException& e) { mprintf(("TABLES: Unable to parse '%s'! Error message = %s.\n", filename, e.what())); return; } }
void parse_ai_profiles_tbl(const char *filename) { int i; char profile_name[NAME_LENGTH]; ai_profile_t dummy_profile; char *saved_Mp = NULL; char buf[NAME_LENGTH]; try { if (filename == NULL) read_file_text_from_default(defaults_get_file("ai_profiles.tbl")); else read_file_text(filename, CF_TYPE_TABLES); reset_parse(); // start parsing required_string("#AI Profiles"); // new default? if (optional_string("$Default Profile:")) stuff_string(Default_profile_name, F_NAME, NAME_LENGTH); // begin reading data while (required_string_either("#End", "$Profile Name:")) { ai_profile_t *profile = &dummy_profile; ai_profile_t *previous_profile = NULL; bool no_create = false; // get the name required_string("$Profile Name:"); stuff_string(profile_name, F_NAME, NAME_LENGTH); // see if it exists for (i = 0; i < Num_ai_profiles; i++) { if (!stricmp(Ai_profiles[i].profile_name, profile_name)) { previous_profile = &Ai_profiles[i]; break; } } // modular table stuff if (optional_string("+nocreate")) { no_create = true; // use the previous one if possible, // otherwise continue to use the dummy one if (previous_profile != NULL) profile = previous_profile; } else { // don't create multiple profiles with the same name if (previous_profile != NULL) { Warning(LOCATION, "An ai profile named '%s' already exists! The new one will not be created.\n", profile_name); } else { // make sure we're under the limit if (Num_ai_profiles >= MAX_AI_PROFILES) { Warning(LOCATION, "Too many profiles in ai_profiles.tbl! Max is %d.\n", MAX_AI_PROFILES - 1); // -1 because one is built-in skip_to_string("#End", NULL); break; } profile = &Ai_profiles[Num_ai_profiles]; Num_ai_profiles++; } } // initialize profile if we're not building from a previously parsed one if (!no_create) { // base profile, so zero it out if (profile == &Ai_profiles[0]) { profile->reset(); } // brand new profile, so set it to the base defaults else { *profile = Ai_profiles[0]; } } // set the name strcpy_s(profile->profile_name, profile_name); // fill in any and all settings; they're all optional and can be in any order while (!check_for_string("$Profile Name:") && !check_for_string("#End")) { if (optional_string("$Player Afterburner Recharge Scale:")) parse_float_list(profile->afterburner_recharge_scale, NUM_SKILL_LEVELS); if (optional_string("$Max Beam Friendly Fire Damage:")) parse_float_list(profile->beam_friendly_damage_cap, NUM_SKILL_LEVELS); if (optional_string("$Player Countermeasure Life Scale:")) parse_float_list(profile->cmeasure_life_scale, NUM_SKILL_LEVELS); if (optional_string("$AI Countermeasure Firing Chance:")) parse_float_list(profile->cmeasure_fire_chance, NUM_SKILL_LEVELS); if (optional_string("$AI In Range Time:")) parse_float_list(profile->in_range_time, NUM_SKILL_LEVELS); if (optional_string("$AI Always Links Ammo Weapons:")) parse_float_list(profile->link_ammo_levels_always, NUM_SKILL_LEVELS); if (optional_string("$AI Maybe Links Ammo Weapons:")) parse_float_list(profile->link_ammo_levels_maybe, NUM_SKILL_LEVELS); if (optional_string("$Primary Ammo Burst Multiplier:")) parse_float_list(profile->primary_ammo_burst_mult, NUM_SKILL_LEVELS); if (optional_string("$AI Always Links Energy Weapons:")) parse_float_list(profile->link_energy_levels_always, NUM_SKILL_LEVELS); if (optional_string("$AI Maybe Links Energy Weapons:")) parse_float_list(profile->link_energy_levels_maybe, NUM_SKILL_LEVELS); if (optional_string("$Max Missles Locked on Player:") || optional_string("$Max Missiles Locked on Player:")) parse_int_list(profile->max_allowed_player_homers, NUM_SKILL_LEVELS); if (optional_string("$Max Player Attackers:")) parse_int_list(profile->max_attackers, NUM_SKILL_LEVELS); if (optional_string("$Max Incoming Asteroids:")) parse_int_list(profile->max_incoming_asteroids, NUM_SKILL_LEVELS); if (optional_string("$Player Damage Factor:") || optional_string("$AI Damage Reduction to Player Hull:")) parse_float_list(profile->player_damage_scale, NUM_SKILL_LEVELS); if (optional_string("$Player Subsys Damage Factor:") || optional_string("$AI Damage Reduction to Player Subsys:")) parse_float_list(profile->subsys_damage_scale, NUM_SKILL_LEVELS); // represented in fractions of F1_0 if (optional_string("$Predict Position Delay:")) { int iLoop; float temp_list[NUM_SKILL_LEVELS]; parse_float_list(temp_list, NUM_SKILL_LEVELS); for (iLoop = 0; iLoop < NUM_SKILL_LEVELS; iLoop++) profile->predict_position_delay[iLoop] = fl2f(temp_list[iLoop]); } if (optional_string("$AI Shield Manage Delay:") || optional_string("$AI Shield Manage Delays:")) parse_float_list(profile->shield_manage_delay, NUM_SKILL_LEVELS); if (optional_string("$Friendly AI Fire Delay Scale:")) parse_float_list(profile->ship_fire_delay_scale_friendly, NUM_SKILL_LEVELS); if (optional_string("$Hostile AI Fire Delay Scale:")) parse_float_list(profile->ship_fire_delay_scale_hostile, NUM_SKILL_LEVELS); if (optional_string("$Friendly AI Secondary Fire Delay Scale:")) parse_float_list(profile->ship_fire_secondary_delay_scale_friendly, NUM_SKILL_LEVELS); if (optional_string("$Hostile AI Secondary Fire Delay Scale:")) parse_float_list(profile->ship_fire_secondary_delay_scale_hostile, NUM_SKILL_LEVELS); if (optional_string("$AI Turn Time Scale:")) parse_float_list(profile->turn_time_scale, NUM_SKILL_LEVELS); if (optional_string("$Glide Attack Percent:")) { parse_float_list(profile->glide_attack_percent, NUM_SKILL_LEVELS); //Percent is nice for modders, but here in the code we want it betwwen 0 and 1.0 //While we're at it, verify the range for (i = 0; i < NUM_SKILL_LEVELS; i++) { if (profile->glide_attack_percent[i] < 0.0f || profile->glide_attack_percent[i] > 100.0f) { Warning(LOCATION, "$Glide Attack Percent should be between 0 and 100.0 (read %f). Setting to 0.", profile->glide_attack_percent[i]); profile->glide_attack_percent[i] = 0.0f; } profile->glide_attack_percent[i] /= 100.0; } } if (optional_string("$Circle Strafe Percent:")) { parse_float_list(profile->circle_strafe_percent, NUM_SKILL_LEVELS); //Percent is nice for modders, but here in the code we want it betwwen 0 and 1.0 //While we're at it, verify the range for (i = 0; i < NUM_SKILL_LEVELS; i++) { if (profile->circle_strafe_percent[i] < 0.0f || profile->circle_strafe_percent[i] > 100.0f) { Warning(LOCATION, "$Circle Strafe Percent should be between 0 and 100.0 (read %f). Setting to 0.", profile->circle_strafe_percent[i]); profile->circle_strafe_percent[i] = 0.0f; } profile->circle_strafe_percent[i] /= 100.0; } } if (optional_string("$Glide Strafe Percent:")) { parse_float_list(profile->glide_strafe_percent, NUM_SKILL_LEVELS); //Percent is nice for modders, but here in the code we want it betwwen 0 and 1.0 //While we're at it, verify the range for (i = 0; i < NUM_SKILL_LEVELS; i++) { if (profile->glide_strafe_percent[i] < 0.0f || profile->glide_strafe_percent[i] > 100.0f) { Warning(LOCATION, "$Glide Strafe Percent should be between 0 and 100.0 (read %f). Setting to 0.", profile->glide_strafe_percent[i]); profile->glide_strafe_percent[i] = 0.0f; } profile->glide_strafe_percent[i] /= 100.0; } } if (optional_string("$Random Sidethrust Percent:")) { parse_float_list(profile->random_sidethrust_percent, NUM_SKILL_LEVELS); //Percent is nice for modders, but here in the code we want it betwwen 0 and 1.0 //While we're at it, verify the range for (i = 0; i < NUM_SKILL_LEVELS; i++) { if (profile->random_sidethrust_percent[i] < 0.0f || profile->random_sidethrust_percent[i] > 100.0f) { Warning(LOCATION, "$Random Sidethrust Percent should be between 0 and 100.0 (read %f). Setting to 0.", profile->random_sidethrust_percent[i]); profile->random_sidethrust_percent[i] = 0.0f; } profile->random_sidethrust_percent[i] /= 100.0; } } if (optional_string("$Stalemate Time Threshold:")) parse_float_list(profile->stalemate_time_thresh, NUM_SKILL_LEVELS); if (optional_string("$Stalemate Distance Threshold:")) parse_float_list(profile->stalemate_dist_thresh, NUM_SKILL_LEVELS); if (optional_string("$Player Shield Recharge Scale:")) parse_float_list(profile->shield_energy_scale, NUM_SKILL_LEVELS); if (optional_string("$Player Weapon Recharge Scale:")) parse_float_list(profile->weapon_energy_scale, NUM_SKILL_LEVELS); if (optional_string("$Max Turret Target Ownage:")) parse_int_list(profile->max_turret_ownage_target, NUM_SKILL_LEVELS); if (optional_string("$Max Turret Player Ownage:")) parse_int_list(profile->max_turret_ownage_player, NUM_SKILL_LEVELS); if (optional_string("$Percentage Required For Kill Scale:")) parse_float_list(profile->kill_percentage_scale, NUM_SKILL_LEVELS); if (optional_string("$Percentage Required For Assist Scale:")) parse_float_list(profile->assist_percentage_scale, NUM_SKILL_LEVELS); if (optional_string("$Percentage Awarded For Capship Assist:")) parse_float_list(profile->assist_award_percentage_scale, NUM_SKILL_LEVELS); if (optional_string("$Repair Penalty:")) parse_int_list(profile->repair_penalty, NUM_SKILL_LEVELS); if (optional_string("$Delay Before Allowing Bombs to Be Shot Down:")) parse_float_list(profile->delay_bomb_arm_timer, NUM_SKILL_LEVELS); if (optional_string("$Chance AI Has to Fire Missiles at Player:")) parse_int_list(profile->chance_to_use_missiles_on_plr, NUM_SKILL_LEVELS); if (optional_string("$Max Aim Update Delay:")) parse_float_list(profile->max_aim_update_delay, NUM_SKILL_LEVELS); if (optional_string("$Turret Max Aim Update Delay:")) parse_float_list(profile->turret_max_aim_update_delay, NUM_SKILL_LEVELS); if (optional_string("$Player Autoaim FOV:")) { float fov_list[NUM_SKILL_LEVELS]; parse_float_list(fov_list, NUM_SKILL_LEVELS); for (i = 0; i < NUM_SKILL_LEVELS; i++) { //Enforce range if (fov_list[i] < 0.0f || fov_list[i] >= 360.0f) { Warning(LOCATION, "$Player Autoaim FOV should be >= 0 and < 360.0 (read %f). Setting to 0.", fov_list[i]); fov_list[i] = 0.0f; } //Convert units profile->player_autoaim_fov[i] = fov_list[i] * PI / 180.0f; } } if (optional_string("$Detail Distance Multiplier:")) parse_float_list(profile->detail_distance_mult, MAX_DETAIL_LEVEL + 1); set_flag(profile, "$big ships can attack beam turrets on untargeted ships:", AI::Profile_Flags::Big_ships_can_attack_beam_turrets_on_untargeted_ships); set_flag(profile, "$smart primary weapon selection:", AI::Profile_Flags::Smart_primary_weapon_selection); set_flag(profile, "$smart secondary weapon selection:", AI::Profile_Flags::Smart_secondary_weapon_selection); set_flag(profile, "$smart shield management:", AI::Profile_Flags::Smart_shield_management); set_flag(profile, "$smart afterburner management:", AI::Profile_Flags::Smart_afterburner_management); set_flag(profile, "$free afterburner use:", AI::Profile_Flags::Free_afterburner_use); set_flag(profile, "$allow rapid secondary dumbfire:", AI::Profile_Flags::Allow_rapid_secondary_dumbfire); set_flag(profile, "$huge turret weapons ignore bombs:", AI::Profile_Flags::Huge_turret_weapons_ignore_bombs); set_flag(profile, "$don't insert random turret fire delay:", AI::Profile_Flags::Dont_insert_random_turret_fire_delay); set_flag(profile, "$hack improve non-homing swarm turret fire accuracy:", AI::Profile_Flags::Hack_improve_non_homing_swarm_turret_fire_accuracy); set_flag(profile, "$shockwaves damage small ship subsystems:", AI::Profile_Flags::Shockwaves_damage_small_ship_subsystems); set_flag(profile, "$navigation subsystem governs warpout capability:", AI::Profile_Flags::Navigation_subsys_governs_warp); set_flag(profile, "$ignore lower bound for minimum speed of docked ship:", AI::Profile_Flags::No_min_dock_speed_cap); set_flag(profile, "$disable linked fire penalty:", AI::Profile_Flags::Disable_linked_fire_penalty); set_flag(profile, "$disable weapon damage scaling:", AI::Profile_Flags::Disable_weapon_damage_scaling); set_flag(profile, "$use additive weapon velocity:", AI::Profile_Flags::Use_additive_weapon_velocity); set_flag(profile, "$use newtonian dampening:", AI::Profile_Flags::Use_newtonian_dampening); set_flag(profile, "$include beams for kills and assists:", AI::Profile_Flags::Include_beams_in_stat_calcs); set_flag(profile, "$score kills based on damage caused:", AI::Profile_Flags::Kill_scoring_scales_with_damage); set_flag(profile, "$score assists based on damage caused:", AI::Profile_Flags::Assist_scoring_scales_with_damage); set_flag(profile, "$allow event and goal scoring in multiplayer:", AI::Profile_Flags::Allow_multi_event_scoring); set_flag(profile, "$fix linked primary weapon decision bug:", AI::Profile_Flags::Fix_linked_primary_bug); set_flag(profile, "$prevent turrets targeting too distant bombs:", AI::Profile_Flags::Prevent_targeting_bombs_beyond_range); set_flag(profile, "$smart subsystem targeting for turrets:", AI::Profile_Flags::Smart_subsystem_targeting_for_turrets); set_flag(profile, "$fix heat seekers homing on stealth ships bug:", AI::Profile_Flags::Fix_heat_seeker_stealth_bug); set_flag(profile, "$multi allow empty primaries:", AI::Profile_Flags::Multi_allow_empty_primaries); set_flag(profile, "$multi allow empty secondaries:", AI::Profile_Flags::Multi_allow_empty_secondaries); set_flag(profile, "$allow turrets target weapons freely:", AI::Profile_Flags::Allow_turrets_target_weapons_freely); set_flag(profile, "$use only single fov for turrets:", AI::Profile_Flags::Use_only_single_fov_for_turrets); set_flag(profile, "$allow vertical dodge:", AI::Profile_Flags::Allow_vertical_dodge); set_flag(profile, "$force beam turrets to use normal fov:", AI::Profile_Flags::Force_beam_turret_fov); set_flag(profile, "$fix ai class bug:", AI::Profile_Flags::Fix_ai_class_bug); set_flag(profile, "$turrets ignore targets radius in range checks:", AI::Profile_Flags::Turrets_ignore_target_radius); set_flag(profile, "$no extra collision avoidance vs player:", AI::Profile_Flags::No_special_player_avoid); set_flag(profile, "$perform fewer checks for death screams:", AI::Profile_Flags::Perform_fewer_scream_checks); set_flag(profile, "$advanced turret fov edge checks:", AI::Profile_Flags::Advanced_turret_fov_edge_checks); set_flag(profile, "$require turrets to have target in fov:", AI::Profile_Flags::Require_turret_to_have_target_in_fov); set_flag(profile, "$all ships manage shields:", AI::Profile_Flags::All_ships_manage_shields); set_flag(profile, "$ai aims from ship center:", AI::Profile_Flags::Ai_aims_from_ship_center); set_flag(profile, "$allow primary link at mission start:", AI::Profile_Flags::Allow_primary_link_at_start); set_flag(profile, "$allow beams to damage bombs:", AI::Profile_Flags::Beams_damage_weapons); set_flag(profile, "$disable weapon damage scaling for player:", AI::Profile_Flags::Player_weapon_scale_fix); set_flag(profile, "$countermeasures affect aspect seekers:", AI::Profile_Flags::Aspect_lock_countermeasure); set_flag(profile, "$ai guards specific ship in wing:", AI::Profile_Flags::Ai_guards_specific_ship_in_wing); profile->ai_path_mode = AI_PATH_MODE_NORMAL; if (optional_string("$ai path mode:")) { stuff_string(buf, F_NAME, NAME_LENGTH); int j = ai_path_type_match(buf); if (j >= 0) { profile->ai_path_mode = j; } else { Warning(LOCATION, "Invalid ai path mode '%s' specified", buf); } } set_flag(profile, "$no warp camera:", AI::Profile_Flags::No_warp_camera); set_flag(profile, "$fix ai path order bug:", AI::Profile_Flags::Fix_ai_path_order_bug); set_flag(profile, "$strict turret-tagged-only targeting:", AI::Profile_Flags::Strict_turred_tagged_only_targeting); set_flag(profile, "$aspect bomb invulnerability fix:", AI::Profile_Flags::Aspect_invulnerability_fix); set_flag(profile, "$glide decay requires thrust:", AI::Profile_Flags::Glide_decay_requires_thrust); set_flag(profile, "$ai can slow down when attacking big ships:", AI::Profile_Flags::Ai_can_slow_down_attacking_big_ships); set_flag(profile, "$use actual primary range:", AI::Profile_Flags::Use_actual_primary_range); profile->bay_arrive_speed_mult = 1.0f; profile->bay_depart_speed_mult = 1.0f; if (optional_string("$bay arrive speed multiplier:")) { stuff_float(&profile->bay_arrive_speed_mult); } if (optional_string("$bay depart speed multiplier:")) { stuff_float(&profile->bay_depart_speed_mult); } // ---------- // compatibility if (optional_string("$perform less checks for death screams:")) { mprintf(("Warning: \"$perform less checks for death screams\" flag is deprecated in favor of \"$perform fewer checks for death screams\"\n")); bool temp; stuff_boolean(&temp); profile->flags.set(AI::Profile_Flags::Perform_fewer_scream_checks, temp); } if (optional_string("$allow primary link delay:")) { mprintf(("Warning: \"$allow primary link delay\" flag is deprecated in favor of \"$allow primary link at mission start\"\n")); bool temp; stuff_boolean(&temp); profile->flags.set(AI::Profile_Flags::Allow_primary_link_at_start, !temp); } // if we've been through once already and are at the same place, force a move if (saved_Mp && (saved_Mp == Mp)) { char tmp[60]; memset(tmp, 0, 60); strncpy(tmp, Mp, 59); mprintf(("WARNING: Unrecognized parameter in ai_profiles: %s\n", tmp)); Mp++; } // find next valid option if (!skip_to_start_of_string_either("$", "#")) break; saved_Mp = Mp; } } required_string("#End"); } catch (const parse::ParseException& e) { mprintf(("TABLES: Unable to parse '%s'! Error message = %s.\n", (filename) ? filename : "<default ai_profiles.tbl>", e.what())); return; } // add tbl/tbm to multiplayer validation list extern void fs2netd_add_table_validation(const char *tblname); fs2netd_add_table_validation(filename); }
/** * @brief Parses controlconfigdefault.tbl, and overrides the default control configuration for each valid entry in the .tbl */ void control_config_common_load_overrides() { LoadEnumsIntoMaps(); try { if (cf_exists_full("controlconfigdefaults.tbl", CF_TYPE_TABLES)) { read_file_text("controlconfigdefaults.tbl", CF_TYPE_TABLES); } else { read_file_text_from_default(defaults_get_file("controlconfigdefaults.tbl")); } reset_parse(); // start parsing // TODO: Split this out into more helps. Too many tabs! while(optional_string("#ControlConfigOverride")) { config_item *cfg_preset = new config_item[CCFG_MAX + 1]; std::copy(Control_config, Control_config + CCFG_MAX + 1, cfg_preset); Control_config_presets.push_back(cfg_preset); SCP_string preset_name; if (optional_string("$Name:")) { stuff_string_line(preset_name); } else { preset_name = "<unnamed preset>"; } Control_config_preset_names.push_back(preset_name); while (required_string_either("#End","$Bind Name:")) { const int iBufferLength = 64; char szTempBuffer[iBufferLength]; required_string("$Bind Name:"); stuff_string(szTempBuffer, F_NAME, iBufferLength); const size_t cCntrlAryLength = sizeof(Control_config) / sizeof(Control_config[0]); for (size_t i = 0; i < cCntrlAryLength; ++i) { config_item& r_ccConfig = cfg_preset[i]; if (!strcmp(szTempBuffer, r_ccConfig.text)) { /** * short key_default; * short joy_default; * char tab; * bool hasXSTR; * char type; */ int iTemp; if (optional_string("$Key Default:")) { if (optional_string("NONE")) { r_ccConfig.key_default = (short)-1; } else { stuff_string(szTempBuffer, F_NAME, iBufferLength); r_ccConfig.key_default = (short)mKeyNameToVal[szTempBuffer]; } } if (optional_string("$Joy Default:")) { stuff_int(&iTemp); r_ccConfig.joy_default = (short)iTemp; } if (optional_string("$Key Mod Shift:")) { stuff_int(&iTemp); r_ccConfig.key_default |= (iTemp == 1) ? KEY_SHIFTED : 0; } if (optional_string("$Key Mod Alt:")) { stuff_int(&iTemp); r_ccConfig.key_default |= (iTemp == 1) ? KEY_ALTED : 0; } if (optional_string("$Key Mod Ctrl:")) { stuff_int(&iTemp); r_ccConfig.key_default |= (iTemp == 1) ? KEY_CTRLED : 0; } if (optional_string("$Category:")) { stuff_string(szTempBuffer, F_NAME, iBufferLength); r_ccConfig.tab = (char)mCCTabNameToVal[szTempBuffer]; } if (optional_string("$Has XStr:")) { stuff_int(&iTemp); r_ccConfig.hasXSTR = (iTemp == 1); } if (optional_string("$Type:")) { stuff_string(szTempBuffer, F_NAME, iBufferLength); r_ccConfig.type = (char)mCCTypeNameToVal[szTempBuffer]; } if (optional_string("+Disable")) { r_ccConfig.disabled = true; } if (optional_string("$Disable:")) { stuff_boolean(&r_ccConfig.disabled); } // Nerf the buffer now. szTempBuffer[0] = '\0'; } else if ((i + 1) == cCntrlAryLength) { error_display(1, "Bind Name not found: %s\n", szTempBuffer); advance_to_eoln(NULL); ignore_white_space(); return; } } } required_string("#End"); } } catch (const parse::ParseException& e) { mprintf(("TABLES: Unable to parse 'controlconfigdefaults.tbl'! Error message = %s.\n", e.what())); return; } // Overwrite the control config with the first preset that was found if (!Control_config_presets.empty()) { std::copy(Control_config_presets[0], Control_config_presets[0] + CCFG_MAX + 1, Control_config); } }
void parse_medal_tbl() { int i; try { read_file_text("medals.tbl", CF_TYPE_TABLES); reset_parse(); required_string("#Medals"); // special background information if (optional_string("+Background Bitmap:")) { stuff_string(Medals_background_filename, F_NAME, NAME_LENGTH); } else { strcpy_s(Medals_background_filename, Default_medals_background_filename); } // special mask information if (optional_string("+Mask Bitmap:")) { stuff_string(Medals_mask_filename, F_NAME, NAME_LENGTH); } else { strcpy_s(Medals_mask_filename, Default_medals_mask_filename); } // configurable hotspot for the exit button if (optional_string("+Exit Button Hotspot Index:")) { stuff_int(&Exit_button_hotspot_override); } // special positioning for player callsign if (optional_string("+Callsign Position 640:")) { stuff_int(&Medals_callsign_coords[GR_640].x); stuff_int(&Medals_callsign_coords[GR_640].y); } else { Medals_callsign_coords[GR_640].x = Default_medals_callsign_coords[GR_640][0]; Medals_callsign_coords[GR_640].y = Default_medals_callsign_coords[GR_640][1]; } if (optional_string("+Callsign Position 1024:")) { stuff_int(&Medals_callsign_coords[GR_1024].x); stuff_int(&Medals_callsign_coords[GR_1024].y); } else { Medals_callsign_coords[GR_1024].x = Default_medals_callsign_coords[GR_1024][0]; Medals_callsign_coords[GR_1024].y = Default_medals_callsign_coords[GR_1024][1]; } // special positioning for medal label if (optional_string("+Label Position 640:")) { stuff_int(&Medals_label_coords[GR_640].x); stuff_int(&Medals_label_coords[GR_640].y); stuff_int(&Medals_label_coords[GR_640].w); } else { Medals_label_coords[GR_640].x = Default_medals_label_coords[GR_640][0]; Medals_label_coords[GR_640].y = Default_medals_label_coords[GR_640][1]; Medals_label_coords[GR_640].w = Default_medals_label_coords[GR_640][2]; } if (optional_string("+Label Position 1024:")) { stuff_int(&Medals_label_coords[GR_1024].x); stuff_int(&Medals_label_coords[GR_1024].y); stuff_int(&Medals_label_coords[GR_1024].w); } else { Medals_label_coords[GR_1024].x = Default_medals_label_coords[GR_1024][0]; Medals_label_coords[GR_1024].y = Default_medals_label_coords[GR_1024][1]; Medals_label_coords[GR_1024].w = Default_medals_label_coords[GR_1024][2]; } // parse in all the medal names Num_medals = 0; while (required_string_either("#End", "$Name:")) { medal_stuff temp_medal; medal_display_info temp_display; required_string("$Name:"); stuff_string(temp_medal.name, F_NAME, NAME_LENGTH); // is this rank? if so, save it if (!stricmp(temp_medal.name, "Rank")) Rank_medal_index = Num_medals; required_string("$Bitmap:"); stuff_string(temp_medal.bitmap, F_NAME, MAX_FILENAME_LEN); if (optional_string("+Position 640:")) { stuff_int(&temp_display.coords[GR_640].x); stuff_int(&temp_display.coords[GR_640].y); } else if (Num_medals < NUM_MEDALS_FS2) { temp_display.coords[GR_640].x = Default_medal_coords[GR_640][Num_medals][0]; temp_display.coords[GR_640].y = Default_medal_coords[GR_640][Num_medals][1]; } else { Warning(LOCATION, "No default GR_640 position for medal '%s'!", temp_medal.name); temp_display.coords[GR_640].x = 0; temp_display.coords[GR_640].y = 0; } if (optional_string("+Position 1024:")) { stuff_int(&temp_display.coords[GR_1024].x); stuff_int(&temp_display.coords[GR_1024].y); } else if (Num_medals < NUM_MEDALS_FS2) { temp_display.coords[GR_1024].x = Default_medal_coords[GR_1024][Num_medals][0]; temp_display.coords[GR_1024].y = Default_medal_coords[GR_1024][Num_medals][1]; } else { Warning(LOCATION, "No default GR_1024 position for medal '%s'!", temp_medal.name); temp_display.coords[GR_1024].x = 0; temp_display.coords[GR_1024].y = 0; } if (optional_string("+Debriefing Bitmap:")) { stuff_string(temp_medal.debrief_bitmap, F_NAME, MAX_FILENAME_LEN); } else if (Num_medals < NUM_MEDALS_FS2) { strcpy_s(temp_medal.debrief_bitmap, Default_debriefing_bitmaps[Num_medals]); } else { Warning(LOCATION, "No default debriefing bitmap for medal '%s'!", temp_medal.name); strcpy_s(temp_medal.debrief_bitmap, ""); } required_string("$Num mods:"); stuff_int(&temp_medal.num_versions); // this is dumb temp_medal.version_starts_at_1 = (Num_medals == Rank_medal_index); if (optional_string("+Version starts at 1:")) { stuff_boolean(&temp_medal.version_starts_at_1); } if (optional_string("+Available From Start:")) { stuff_boolean(&temp_medal.available_from_start); } // some medals are based on kill counts. When string +Num Kills: is present, we know that // this medal is a badge and should be treated specially if (optional_string("+Num Kills:")) { char buf[MULTITEXT_LENGTH]; int persona; stuff_int(&temp_medal.kills_needed); if (optional_string("$Wavefile 1:")) stuff_string(temp_medal.voice_base, F_NAME, MAX_FILENAME_LEN); if (optional_string("$Wavefile 2:")) stuff_string(temp_medal.voice_base, F_NAME, MAX_FILENAME_LEN); if (optional_string("$Wavefile Base:")) stuff_string(temp_medal.voice_base, F_NAME, MAX_FILENAME_LEN); while (check_for_string("$Promotion Text:")) { required_string("$Promotion Text:"); stuff_string(buf, F_MULTITEXT, sizeof(buf)); persona = -1; if (optional_string("+Persona:")) { stuff_int(&persona); if (persona < 0) { Warning(LOCATION, "Debriefing text for %s is assigned to an invalid persona: %i (must be 0 or greater).\n", temp_medal.name, persona); continue; } } temp_medal.promotion_text[persona] = SCP_string(buf); } if (temp_medal.promotion_text.find(-1) == temp_medal.promotion_text.end()) { Warning(LOCATION, "%s medal is missing default debriefing text.\n", temp_medal.name); temp_medal.promotion_text[-1] = ""; } } Medals.push_back(temp_medal); Medal_display_info.push_back(temp_display); Num_medals++; } required_string("#End"); // be sure that we know where the rank is if (Rank_medal_index < 0) { Warning(LOCATION, "Could not find the 'Rank' medal!"); Rank_medal_index = 0; } // be sure that the badges kill numbers show up in order //WMC - I don't think this is needed anymore due to my changes to post-mission functions //but I'm keeping it here to be sure. int prev_badge_kills = 0; for (i = 0; i < Num_medals; i++) { if (Medals[i].kills_needed < prev_badge_kills && Medals[i].kills_needed != 0) Error(LOCATION, "Badges must appear sorted by lowest kill # first in medals.tbl\nFind Allender for most information."); if (Medals[i].kills_needed > 0) prev_badge_kills = Medals[i].kills_needed; } } catch (const parse::ParseException& e) { mprintf(("TABLES: Unable to parse '%s'! Error message = %s.\n", "medals.tbl", e.what())); return; } }
// parses help.tbl and populates help_overlaylist[] void parse_helptbl() { int overlay_id, currcount; char buf[HELP_MAX_STRING_LENGTH + 1]; int i, rval; // open localization lcl_ext_open(); if ((rval = setjmp(parse_abort)) != 0) { mprintf(("TABLES: Unable to parse '%s'! Error code = %i.\n", HELP_OVERLAY_FILENAME, rval)); lcl_ext_close(); return; } read_file_text(HELP_OVERLAY_FILENAME, CF_TYPE_TABLES); // for each overlay... for (overlay_id = 0; overlay_id < MAX_HELP_OVERLAYS; overlay_id++) { reset_parse(); skip_to_string(help_overlay_section_names[overlay_id]); // clear out counters in the overlay struct help_overlaylist[overlay_id].plinecount = 0; help_overlaylist[overlay_id].textcount = 0; help_overlaylist[overlay_id].rbracketcount = 0; help_overlaylist[overlay_id].lbracketcount = 0; // read in all elements for this overlay while (!(check_for_string("$end"))) { if (optional_string("+pline")) { currcount = help_overlaylist[overlay_id].plinecount; int a, b; // temp vars to read in int before cast to float; if (currcount < HELP_MAX_ITEM) { // read number of pline vertices stuff_int(&help_overlaylist[overlay_id].plinelist[GR_640][currcount].vtxcount); // note that it is read into GR_640 // help_overlaylist[overlay_id].plinelist[GR_1024][currcount].vtxcount = help_overlaylist[overlay_id].plinelist[GR_640][currcount].vtxcount; // set equal to 1024 version vertex count to prevent bugs Assert(help_overlaylist[overlay_id].plinelist[GR_640][currcount].vtxcount <= HELP_MAX_PLINE_VERTICES); // get 640x480 vertex coordinates for (i = 0; i < help_overlaylist[overlay_id].plinelist[GR_640][currcount].vtxcount; i++) { stuff_int(&a); stuff_int(&b); help_overlaylist[overlay_id].plinelist[GR_640][currcount].vtx[i].xyz.x = (float)a; help_overlaylist[overlay_id].plinelist[GR_640][currcount].vtx[i].xyz.y = (float)b; help_overlaylist[overlay_id].plinelist[GR_640][currcount].vtx[i].xyz.z = 0.0f; help_overlaylist[overlay_id].plinelist[GR_640][currcount].pvtx[i] = &help_overlaylist[ overlay_id].plinelist[GR_640][currcount].vtx[i]; } // get 1024x768 vertex coordinates for (i = 0; i < help_overlaylist[overlay_id].plinelist[GR_640][currcount].vtxcount; i++) { stuff_int(&a); stuff_int(&b); help_overlaylist[overlay_id].plinelist[GR_1024][currcount].vtx[i].xyz.x = (float)a; help_overlaylist[overlay_id].plinelist[GR_1024][currcount].vtx[i].xyz.y = (float)b; help_overlaylist[overlay_id].plinelist[GR_1024][currcount].vtx[i].xyz.z = 0.0f; help_overlaylist[overlay_id].plinelist[GR_1024][currcount].pvtx[i] = &help_overlaylist[ overlay_id].plinelist[GR_1024][currcount].vtx[i]; } } //mprintf(("Found pline - start location (%f,%f), end location (%f,%f)\n", help_overlaylist[overlay_id].plinelist[GR_640][currcount].vtx[0].xyz.x, help_overlaylist[overlay_id].plinelist[GR_640][currcount].vtx[0].xyz.y, help_overlaylist[overlay_id].plinelist[GR_640][currcount].vtx[2].xyz.x, help_overlaylist[overlay_id].plinelist[GR_640][currcount].vtx[2].xyz.y)); help_overlaylist[overlay_id].plinecount++; } else if (optional_string("+text")) { currcount = help_overlaylist[overlay_id].textcount; if (currcount < HELP_MAX_ITEM) { // get 640x480 coordinates stuff_int(&(help_overlaylist[overlay_id].textlist[GR_640][currcount].x_coord)); stuff_int(&(help_overlaylist[overlay_id].textlist[GR_640][currcount].y_coord)); // get 1024x768 coordinates stuff_int(&(help_overlaylist[overlay_id].textlist[GR_1024][currcount].x_coord)); stuff_int(&(help_overlaylist[overlay_id].textlist[GR_1024][currcount].y_coord)); // get string (always use the GR_640 one) stuff_string(buf, F_MESSAGE, sizeof(buf)); help_overlaylist[overlay_id].textlist[GR_640][currcount].string = vm_strdup(buf); //mprintf(("Found text %d on overlay %d - location (%d,%d) @ 640x480 :: location (%d,%d) @ 1024x768\n", currcount, overlay_id, help_overlaylist[overlay_id].textlist[GR_640][currcount].x_coord, help_overlaylist[overlay_id].textlist[GR_640][currcount].y_coord, help_overlaylist[overlay_id].textlist[GR_1024][currcount].x_coord, help_overlaylist[overlay_id].textlist[GR_1024][currcount].x_coord)); help_overlaylist[overlay_id].textcount++; } } else if (optional_string("+right_bracket")) { currcount = help_overlaylist[overlay_id].rbracketcount; if (currcount < HELP_MAX_ITEM) { // get 640x480 coordinates stuff_int(&(help_overlaylist[overlay_id].rbracketlist[GR_640][currcount].x_coord)); stuff_int(&(help_overlaylist[overlay_id].rbracketlist[GR_640][currcount].y_coord)); // get 1024x768 coordinates stuff_int(&(help_overlaylist[overlay_id].rbracketlist[GR_1024][currcount].x_coord)); stuff_int(&(help_overlaylist[overlay_id].rbracketlist[GR_1024][currcount].y_coord)); //mprintf(("Found rbracket %d on overlay %d - location (%d,%d) @ 640x480 :: location (%d,%d) @ 1024x768\n", currcount, overlay_id, help_overlaylist[overlay_id].rbracketlist[GR_640][currcount].x_coord, help_overlaylist[overlay_id].rbracketlist[GR_640][currcount].y_coord, help_overlaylist[overlay_id].rbracketlist[GR_1024][currcount].x_coord, help_overlaylist[overlay_id].rbracketlist[GR_1024][currcount].y_coord)); help_overlaylist[overlay_id].rbracketcount++; } } else if (optional_string("+left_bracket")) { currcount = help_overlaylist[overlay_id].lbracketcount; if (currcount < HELP_MAX_ITEM) { // get 640x480 coordinates stuff_int(&(help_overlaylist[overlay_id].lbracketlist[GR_640][currcount].x_coord)); stuff_int(&(help_overlaylist[overlay_id].lbracketlist[GR_640][currcount].y_coord)); // get 1024x768 coordinates stuff_int(&(help_overlaylist[overlay_id].lbracketlist[GR_1024][currcount].x_coord)); stuff_int(&(help_overlaylist[overlay_id].lbracketlist[GR_1024][currcount].y_coord)); //mprintf(("Found lbracket %d on overlay %d - location (%d,%d) @ 640x480 :: location (%d,%d) @ 1024x768\n", currcount, overlay_id, help_overlaylist[overlay_id].lbracketlist[GR_640][currcount].x_coord, help_overlaylist[overlay_id].lbracketlist[GR_640][currcount].y_coord, help_overlaylist[overlay_id].lbracketlist[GR_1024][currcount].x_coord, help_overlaylist[overlay_id].lbracketlist[GR_1024][currcount].y_coord)); help_overlaylist[overlay_id].lbracketcount++; } } else { // help.tbl is corrupt Assert(0); } // end if } // end while } // end for // close localization lcl_ext_close(); }
// Unified function for loading strings.tbl and tstrings.tbl (and their modular versions). // The "external" parameter controls which format to load: true for tstrings.tbl, false for strings.tbl void parse_stringstbl_common(const char *filename, const bool external) { char chr, buf[4096]; char language_tag[512]; int z, index; char *p_offset = NULL; int offset_lo = 0, offset_hi = 0; read_file_text(filename, CF_TYPE_TABLES); reset_parse(); // move down to the proper section memset(language_tag, 0, sizeof(language_tag)); strcpy_s(language_tag, "#"); if (external && Lcl_current_lang == FS2_OPEN_DEFAULT_LANGUAGE){ strcat_s(language_tag, "default"); } else { strcat_s(language_tag, Lcl_languages[Lcl_current_lang].lang_name); } if ( skip_to_string(language_tag) != 1 ) { mprintf(("Current language not found in %s\n", filename)); return; } // parse all the strings in this section of the table while ( !check_for_string("#") ) { int num_offsets_on_this_line = 0; stuff_int(&index); if (external) { ignore_white_space(); get_string(buf, sizeof(buf)); drop_trailing_white_space(buf); } else { stuff_string(buf, F_RAW, sizeof(buf)); } if (external && (index < 0 || index >= LCL_MAX_STRINGS)) { error_display(0, "Invalid tstrings table index specified (%i). Please increment LCL_MAX_STRINGS in localize.cpp.", index); return; } else if (!external && (index < 0 || index >= XSTR_SIZE)) { Error(LOCATION, "Invalid strings table index specified (%i)", index); } if (!external) { size_t i = strlen(buf); while (i--) { if ( !isspace(buf[i]) ) break; } // trim unnecessary end of string // Assert(buf[i] == '"'); if (buf[i] != '"') { // probably an offset on this entry // drop down a null terminator (prolly unnecessary) buf[i+1] = 0; // back up over the potential offset while ( !is_white_space(buf[i]) ) i--; // now back up over intervening spaces while ( is_white_space(buf[i]) ) i--; num_offsets_on_this_line = 1; if (buf[i] != '"') { // could have a 2nd offset value (one for 640, one for 1024) // so back up again while ( !is_white_space(buf[i]) ) i--; // now back up over intervening spaces while ( is_white_space(buf[i]) ) i--; num_offsets_on_this_line = 2; } p_offset = &buf[i+1]; // get ptr to string section with offset in it if (buf[i] != '"') Error(LOCATION, "%s is corrupt", filename); // now its an error } buf[i] = 0; // copy string into buf z = 0; for (i = 1; buf[i]; i++) { chr = buf[i]; if (chr == '\\') { chr = buf[++i]; if (chr == 'n') chr = '\n'; else if (chr == 'r') chr = '\r'; } buf[z++] = chr; } // null terminator on buf buf[z] = 0; } // write into Xstr_table (for strings.tbl) or Lcl_ext_str (for tstrings.tbl) if (Parsing_modular_table) { if ( external && (Lcl_ext_str[index] != NULL) ) { vm_free((void *) Lcl_ext_str[index]); Lcl_ext_str[index] = NULL; } else if ( !external && (Xstr_table[index].str != NULL) ) { vm_free((void *) Xstr_table[index].str); Xstr_table[index].str = NULL; } } if (external && (Lcl_ext_str[index] != NULL)) { Warning(LOCATION, "Tstrings table index %d used more than once", index); } else if (!external && (Xstr_table[index].str != NULL)) { Warning(LOCATION, "Strings table index %d used more than once", index); } if (external) { Lcl_ext_str[index] = vm_strdup(buf); } else { Xstr_table[index].str = vm_strdup(buf); } // the rest of this loop applies only to strings.tbl, // so we can move on to the next line if we're reading from tstrings.tbl if (external) { continue; } // read offset information, assume 0 if nonexistant if (p_offset != NULL) { if (sscanf(p_offset, "%d%d", &offset_lo, &offset_hi) < num_offsets_on_this_line) { // whatever is in the file ain't a proper offset Error(LOCATION, "%s is corrupt", filename); } } Xstr_table[index].offset_x = offset_lo; if (num_offsets_on_this_line == 2) Xstr_table[index].offset_x_hi = offset_hi; else Xstr_table[index].offset_x_hi = offset_lo; // clear out our vars p_offset = NULL; offset_lo = 0; offset_hi = 0; } }