Ejemplo n.º 1
0
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
}
Ejemplo n.º 2
0
// 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++;
			}
		}
	}
}
Ejemplo n.º 3
0
// 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");
}
Ejemplo n.º 4
0
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();
}
Ejemplo n.º 5
0
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;
	}
}
Ejemplo n.º 6
0
// 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;
	}
}
Ejemplo n.º 7
0
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;
}
Ejemplo n.º 8
0
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);
}
Ejemplo n.º 9
0
// -------------------------------------------------------------------------------------------------
// 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);
}
Ejemplo n.º 10
0
// 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++;
			}
		}
	}
}
Ejemplo n.º 11
0
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>"));
	}
}
Ejemplo n.º 12
0
// 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);
}
Ejemplo n.º 14
0
// 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();
}
Ejemplo n.º 15
0
/**
 * 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);
	}
}
Ejemplo n.º 16
0
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;
}
Ejemplo n.º 17
0
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;
}
Ejemplo n.º 18
0
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);
	}
}
Ejemplo n.º 19
0
	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;
	}
Ejemplo n.º 20
0
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);
		}
	}
}
Ejemplo n.º 21
0
//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);
		}
	}
}
Ejemplo n.º 22
0
// 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
}
Ejemplo n.º 23
0
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");
	}
}
Ejemplo n.º 24
0
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();
}
Ejemplo n.º 25
0
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;
	}
}
Ejemplo n.º 26
0
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);
	}
}
Ejemplo n.º 28
0
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;
	}
}
Ejemplo n.º 29
0
// 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();
}
Ejemplo n.º 30
0
// 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;
	}
}