Files
fs2open.github.com/code/pilotfile/plr.cpp
T
niffiwan 603a4086f5 Fix mantis 2920: prevent pilots switching languages
In other words, a pilot can only be used in the same language it was
created with. Checks both the initial selection screen and in the
barracks.
Also fixes issue with PLR version being ignored in pilotfile::verify()

git-svn-id: svn://svn.icculus.org/fs2open/trunk/fs2_open@10817 387891d4-d844-0410-90c0-e4c51a9137d3
2014-06-14 20:29:36 +10:00

1098 lines
28 KiB
C++

#include "globalincs/pstypes.h"
#include "pilotfile/pilotfile.h"
#include "playerman/player.h"
#include "ship/ship.h"
#include "menuui/techmenu.h"
#include "weapon/weapon.h"
#include "hud/hudconfig.h"
#include "stats/medals.h"
#include "hud/hudsquadmsg.h"
#include "gamesnd/eventmusic.h"
#include "osapi/osregistry.h"
#include "sound/audiostr.h"
#include "io/joy.h"
#include "io/mouse.h"
#include "network/multi.h"
#include "freespace2/freespace.h"
#include "playerman/managepilot.h"
#include "localization/localize.h"
void pilotfile::plr_read_flags()
{
// tips?
p->tips = (int)cfread_ubyte(cfp);
// saved flags
p->save_flags = cfread_int(cfp);
// listing mode (single or campaign missions
p->readyroom_listing_mode = cfread_int(cfp);
// briefing auto-play
p->auto_advance = cfread_int(cfp);
// special rank setting (to avoid having to read all stats on verify)
p->stats.rank = cfread_int(cfp);
if (version > 0)
{
p->player_was_multi = cfread_int(cfp);
} else
{
p->player_was_multi = 0; // Default to single player
}
// which language was this pilot created with
if (version > 1) {
cfread_string_len(p->language, sizeof(p->language), cfp);
} else {
// if we don't know, default to the current language setting
lcl_get_language_name(p->language);
}
}
void pilotfile::plr_write_flags()
{
startSection(Section::Flags);
// tips
cfwrite_ubyte((unsigned char)p->tips, cfp);
// saved flags
cfwrite_int(p->save_flags, cfp);
// listing mode (single or campaign missions)
cfwrite_int(p->readyroom_listing_mode, cfp);
// briefing auto-play
cfwrite_int(p->auto_advance, cfp);
// special rank setting (to avoid having to read all stats on verify)
cfwrite_int(p->stats.rank, cfp);
// What game mode we were in last on this pilot
cfwrite_int(p->player_was_multi, cfp);
// which language was this pilot created with
cfwrite_string_len(p->language, cfp);
endSection();
}
void pilotfile::plr_read_info()
{
if ( !m_have_flags ) {
throw "Info before Flags!";
}
// pilot image
cfread_string_len(p->image_filename, MAX_FILENAME_LEN, cfp);
// multi squad name
cfread_string_len(p->m_squad_name, NAME_LENGTH, cfp);
// squad image
cfread_string_len(p->m_squad_filename, MAX_FILENAME_LEN, cfp);
// active campaign
cfread_string_len(p->current_campaign, MAX_FILENAME_LEN, cfp);
}
void pilotfile::plr_write_info()
{
startSection(Section::Info);
// pilot image
cfwrite_string_len(p->image_filename, cfp);
// multi squad name
cfwrite_string_len(p->m_squad_name, cfp);
// squad image
cfwrite_string_len(p->m_squad_filename, cfp);
// active campaign
cfwrite_string_len(p->current_campaign, cfp);
endSection();
}
void pilotfile::plr_read_hud()
{
int idx;
// flags
HUD_config.show_flags = cfread_int(cfp);
HUD_config.show_flags2 = cfread_int(cfp);
HUD_config.popup_flags = cfread_int(cfp);
HUD_config.popup_flags2 = cfread_int(cfp);
// settings
HUD_config.num_msg_window_lines = cfread_ubyte(cfp);
HUD_config.rp_flags = cfread_int(cfp);
HUD_config.rp_dist = cfread_int(cfp);
// basic colors
HUD_config.main_color = cfread_int(cfp);
HUD_color_alpha = cfread_int(cfp);
if (HUD_color_alpha < HUD_COLOR_ALPHA_USER_MIN) {
HUD_color_alpha = HUD_COLOR_ALPHA_DEFAULT;
}
hud_config_set_color(HUD_config.main_color);
// gauge-specific colors
int num_gauges = cfread_int(cfp);
for (idx = 0; idx < num_gauges; idx++) {
ubyte red = cfread_ubyte(cfp);
ubyte green = cfread_ubyte(cfp);
ubyte blue = cfread_ubyte(cfp);
ubyte alpha = cfread_ubyte(cfp);
if (idx >= NUM_HUD_GAUGES) {
continue;
}
HUD_config.clr[idx].red = red;
HUD_config.clr[idx].green = green;
HUD_config.clr[idx].blue = blue;
HUD_config.clr[idx].alpha = alpha;
}
}
void pilotfile::plr_write_hud()
{
int idx;
startSection(Section::HUD);
// flags
cfwrite_int(HUD_config.show_flags, cfp);
cfwrite_int(HUD_config.show_flags2, cfp);
cfwrite_int(HUD_config.popup_flags, cfp);
cfwrite_int(HUD_config.popup_flags2, cfp);
// settings
cfwrite_ubyte(HUD_config.num_msg_window_lines, cfp);
cfwrite_int(HUD_config.rp_flags, cfp);
cfwrite_int(HUD_config.rp_dist, cfp);
// basic colors
cfwrite_int(HUD_config.main_color, cfp);
cfwrite_int(HUD_color_alpha, cfp);
// gauge-specific colors
cfwrite_int(NUM_HUD_GAUGES, cfp);
for (idx = 0; idx < NUM_HUD_GAUGES; idx++) {
cfwrite_ubyte(HUD_config.clr[idx].red, cfp);
cfwrite_ubyte(HUD_config.clr[idx].green, cfp);
cfwrite_ubyte(HUD_config.clr[idx].blue, cfp);
cfwrite_ubyte(HUD_config.clr[idx].alpha, cfp);
}
endSection();
}
void pilotfile::plr_read_variables()
{
int list_size = 0;
int idx;
sexp_variable n_var;
list_size = cfread_int(cfp);
if (list_size <= 0) {
return;
}
p->variables.reserve(list_size);
for (idx = 0; idx < list_size; idx++) {
n_var.type = cfread_int(cfp);
cfread_string_len(n_var.text, TOKEN_LENGTH, cfp);
cfread_string_len(n_var.variable_name, TOKEN_LENGTH, cfp);
p->variables.push_back( n_var );
}
}
void pilotfile::plr_write_variables()
{
int list_size = 0;
int idx;
startSection(Section::Variables);
list_size = (int)p->variables.size();
cfwrite_int(list_size, cfp);
for (idx = 0; idx < list_size; idx++) {
cfwrite_int(p->variables[idx].type, cfp);
cfwrite_string_len(p->variables[idx].text, cfp);
cfwrite_string_len(p->variables[idx].variable_name, cfp);
}
endSection();
}
void pilotfile::plr_read_multiplayer()
{
// netgame options
p->m_server_options.squad_set = cfread_ubyte(cfp);
p->m_server_options.endgame_set = cfread_ubyte(cfp);
p->m_server_options.flags = cfread_int(cfp);
p->m_server_options.respawn = cfread_uint(cfp);
p->m_server_options.max_observers = cfread_ubyte(cfp);
p->m_server_options.skill_level = cfread_ubyte(cfp);
p->m_server_options.voice_qos = cfread_ubyte(cfp);
p->m_server_options.voice_token_wait = cfread_int(cfp);
p->m_server_options.voice_record_time = cfread_int(cfp);
p->m_server_options.mission_time_limit = (fix)cfread_int(cfp);
p->m_server_options.kill_limit = cfread_int(cfp);
// local options
p->m_local_options.flags = cfread_int(cfp);
p->m_local_options.obj_update_level = cfread_int(cfp);
// netgame protocol
Multi_options_g.protocol = cfread_int(cfp);
if (Multi_options_g.protocol == NET_VMT) {
Multi_options_g.protocol = NET_TCP;
}
Assert( (Multi_options_g.protocol == NET_IPX) || (Multi_options_g.protocol == NET_TCP) );
}
void pilotfile::plr_write_multiplayer()
{
startSection(Section::Multiplayer);
// netgame options
cfwrite_ubyte(p->m_server_options.squad_set, cfp);
cfwrite_ubyte(p->m_server_options.endgame_set, cfp);
cfwrite_int(p->m_server_options.flags, cfp);
cfwrite_uint(p->m_server_options.respawn, cfp);
cfwrite_ubyte(p->m_server_options.max_observers, cfp);
cfwrite_ubyte(p->m_server_options.skill_level, cfp);
cfwrite_ubyte(p->m_server_options.voice_qos, cfp);
cfwrite_int(p->m_server_options.voice_token_wait, cfp);
cfwrite_int(p->m_server_options.voice_record_time, cfp);
cfwrite_int((int)p->m_server_options.mission_time_limit, cfp);
cfwrite_int(p->m_server_options.kill_limit, cfp);
// local options
cfwrite_int(p->m_local_options.flags, cfp);
cfwrite_int(p->m_local_options.obj_update_level, cfp);
// netgame protocol
cfwrite_int(Multi_options_g.protocol, cfp);
endSection();
}
void pilotfile::plr_read_stats()
{
int idx, j, list_size = 0;
index_list_t ilist;
char t_string[NAME_LENGTH+1];
// global, all-time stats (used only until campaign stats are loaded)
all_time_stats.score = cfread_int(cfp);
all_time_stats.rank = cfread_int(cfp);
all_time_stats.assists = cfread_int(cfp);
all_time_stats.kill_count = cfread_int(cfp);
all_time_stats.kill_count_ok = cfread_int(cfp);
all_time_stats.bonehead_kills = cfread_int(cfp);
all_time_stats.p_shots_fired = cfread_uint(cfp);
all_time_stats.p_shots_hit = cfread_uint(cfp);
all_time_stats.p_bonehead_hits = cfread_uint(cfp);
all_time_stats.s_shots_fired = cfread_uint(cfp);
all_time_stats.s_shots_hit = cfread_uint(cfp);
all_time_stats.s_bonehead_hits = cfread_uint(cfp);
all_time_stats.flight_time = cfread_uint(cfp);
all_time_stats.missions_flown = cfread_uint(cfp);
all_time_stats.last_flown = (_fs_time_t)cfread_int(cfp);
all_time_stats.last_backup = (_fs_time_t)cfread_int(cfp);
// ship kills (contains ships across all mods, not just current)
list_size = cfread_int(cfp);
all_time_stats.ship_kills.reserve(list_size);
for (idx = 0; idx < list_size; idx++) {
cfread_string_len(t_string, NAME_LENGTH, cfp);
ilist.name = t_string;
ilist.index = ship_info_lookup(t_string);
ilist.val = cfread_int(cfp);
all_time_stats.ship_kills.push_back(ilist);
}
// medals earned (contains medals across all mods, not just current)
list_size = cfread_int(cfp);
all_time_stats.medals_earned.reserve(list_size);
for (idx = 0; idx < list_size; idx++) {
cfread_string_len(t_string, NAME_LENGTH,cfp);
ilist.name = t_string;
ilist.index = medals_info_lookup(t_string);
ilist.val = cfread_int(cfp);
all_time_stats.medals_earned.push_back(ilist);
}
// if not in multiplayer mode then set these stats as the player stats
if ( !(Game_mode & GM_MULTIPLAYER) ) {
p->stats.score = all_time_stats.score;
p->stats.rank = all_time_stats.rank;
p->stats.assists = all_time_stats.assists;
p->stats.kill_count = all_time_stats.kill_count;
p->stats.kill_count_ok = all_time_stats.kill_count_ok;
p->stats.bonehead_kills = all_time_stats.bonehead_kills;
p->stats.p_shots_fired = all_time_stats.p_shots_fired;
p->stats.p_shots_hit = all_time_stats.p_shots_hit;
p->stats.p_bonehead_hits = all_time_stats.p_bonehead_hits;
p->stats.s_shots_fired = all_time_stats.s_shots_fired;
p->stats.s_shots_hit = all_time_stats.s_shots_hit;
p->stats.s_bonehead_hits = all_time_stats.s_bonehead_hits;
p->stats.flight_time = all_time_stats.flight_time;
p->stats.missions_flown = all_time_stats.missions_flown;
p->stats.last_flown = all_time_stats.last_flown;
p->stats.last_backup = all_time_stats.last_backup;
// ship kills (have to find ones that match content)
list_size = (int)all_time_stats.ship_kills.size();
for (idx = 0; idx < list_size; idx++) {
j = all_time_stats.ship_kills[idx].index;
if (j >= 0) {
p->stats.kills[j] = all_time_stats.ship_kills[idx].val;
}
}
// medals earned (have to find ones that match content)
list_size = (int)all_time_stats.medals_earned.size();
for (idx = 0; idx < list_size; idx++) {
j = all_time_stats.medals_earned[idx].index;
if (j >= 0) {
p->stats.medal_counts[j] = all_time_stats.medals_earned[idx].val;
}
}
}
}
void pilotfile::plr_write_stats()
{
int idx, list_size = 0;
startSection(Section::Scoring);
// global, all-time stats
cfwrite_int(all_time_stats.score, cfp);
cfwrite_int(all_time_stats.rank, cfp);
cfwrite_int(all_time_stats.assists, cfp);
cfwrite_int(all_time_stats.kill_count, cfp);
cfwrite_int(all_time_stats.kill_count_ok, cfp);
cfwrite_int(all_time_stats.bonehead_kills, cfp);
cfwrite_uint(all_time_stats.p_shots_fired, cfp);
cfwrite_uint(all_time_stats.p_shots_hit, cfp);
cfwrite_uint(all_time_stats.p_bonehead_hits, cfp);
cfwrite_uint(all_time_stats.s_shots_fired, cfp);
cfwrite_uint(all_time_stats.s_shots_hit, cfp);
cfwrite_uint(all_time_stats.s_bonehead_hits, cfp);
cfwrite_uint(all_time_stats.flight_time, cfp);
cfwrite_uint(all_time_stats.missions_flown, cfp);
cfwrite_int((int)all_time_stats.last_flown, cfp);
cfwrite_int((int)all_time_stats.last_backup, cfp);
// ship kills (contains ships across all mods, not just current)
list_size = (int)all_time_stats.ship_kills.size();
cfwrite_int(list_size, cfp);
for (idx = 0; idx < list_size; idx++) {
cfwrite_string_len(all_time_stats.ship_kills[idx].name.c_str(), cfp);
cfwrite_int(all_time_stats.ship_kills[idx].val, cfp);
}
// medals earned (contains medals across all mods, not just current)
list_size = (int)all_time_stats.medals_earned.size();
cfwrite_int(list_size, cfp);
for (idx = 0; idx < list_size; idx++) {
cfwrite_string_len(all_time_stats.medals_earned[idx].name.c_str(), cfp);
cfwrite_int(all_time_stats.medals_earned[idx].val, cfp);
}
endSection();
}
void pilotfile::plr_read_stats_multi()
{
int idx, j, list_size = 0;
index_list_t ilist;
char t_string[NAME_LENGTH+1];
// global, all-time stats (used only until campaign stats are loaded)
multi_stats.score = cfread_int(cfp);
multi_stats.rank = cfread_int(cfp);
multi_stats.assists = cfread_int(cfp);
multi_stats.kill_count = cfread_int(cfp);
multi_stats.kill_count_ok = cfread_int(cfp);
multi_stats.bonehead_kills = cfread_int(cfp);
multi_stats.p_shots_fired = cfread_uint(cfp);
multi_stats.p_shots_hit = cfread_uint(cfp);
multi_stats.p_bonehead_hits = cfread_uint(cfp);
multi_stats.s_shots_fired = cfread_uint(cfp);
multi_stats.s_shots_hit = cfread_uint(cfp);
multi_stats.s_bonehead_hits = cfread_uint(cfp);
multi_stats.flight_time = cfread_uint(cfp);
multi_stats.missions_flown = cfread_uint(cfp);
multi_stats.last_flown = (_fs_time_t)cfread_int(cfp);
multi_stats.last_backup = (_fs_time_t)cfread_int(cfp);
// ship kills (contains ships across all mods, not just current)
list_size = cfread_int(cfp);
multi_stats.ship_kills.reserve(list_size);
for (idx = 0; idx < list_size; idx++) {
cfread_string_len(t_string, NAME_LENGTH, cfp);
ilist.name = t_string;
ilist.index = ship_info_lookup(t_string);
ilist.val = cfread_int(cfp);
multi_stats.ship_kills.push_back(ilist);
}
// medals earned (contains medals across all mods, not just current)
list_size = cfread_int(cfp);
multi_stats.medals_earned.reserve(list_size);
for (idx = 0; idx < list_size; idx++) {
cfread_string_len(t_string, NAME_LENGTH,cfp);
ilist.name = t_string;
ilist.index = medals_info_lookup(t_string);
ilist.val = cfread_int(cfp);
multi_stats.medals_earned.push_back(ilist);
}
// if in multiplayer mode then set these stats as the player stats
if (Game_mode & GM_MULTIPLAYER) {
p->stats.score = multi_stats.score;
p->stats.rank = multi_stats.rank;
p->stats.assists = multi_stats.assists;
p->stats.kill_count = multi_stats.kill_count;
p->stats.kill_count_ok = multi_stats.kill_count_ok;
p->stats.bonehead_kills = multi_stats.bonehead_kills;
p->stats.p_shots_fired = multi_stats.p_shots_fired;
p->stats.p_shots_hit = multi_stats.p_shots_hit;
p->stats.p_bonehead_hits = multi_stats.p_bonehead_hits;
p->stats.s_shots_fired = multi_stats.s_shots_fired;
p->stats.s_shots_hit = multi_stats.s_shots_hit;
p->stats.s_bonehead_hits = multi_stats.s_bonehead_hits;
p->stats.flight_time = multi_stats.flight_time;
p->stats.missions_flown = multi_stats.missions_flown;
p->stats.last_flown = multi_stats.last_flown;
p->stats.last_backup = multi_stats.last_backup;
// ship kills (have to find ones that match content)
list_size = (int)multi_stats.ship_kills.size();
for (idx = 0; idx < list_size; idx++) {
j = multi_stats.ship_kills[idx].index;
if (j >= 0) {
p->stats.kills[j] = multi_stats.ship_kills[idx].val;
}
}
// medals earned (have to find ones that match content)
list_size = (int)multi_stats.medals_earned.size();
for (idx = 0; idx < list_size; idx++) {
j = multi_stats.medals_earned[idx].index;
if (j >= 0) {
p->stats.medal_counts[j] = multi_stats.medals_earned[idx].val;
}
}
}
}
void pilotfile::plr_write_stats_multi()
{
int idx, list_size = 0;
startSection(Section::ScoringMulti);
// global, all-time stats
cfwrite_int(multi_stats.score, cfp);
cfwrite_int(multi_stats.rank, cfp);
cfwrite_int(multi_stats.assists, cfp);
cfwrite_int(multi_stats.kill_count, cfp);
cfwrite_int(multi_stats.kill_count_ok, cfp);
cfwrite_int(multi_stats.bonehead_kills, cfp);
cfwrite_uint(multi_stats.p_shots_fired, cfp);
cfwrite_uint(multi_stats.p_shots_hit, cfp);
cfwrite_uint(multi_stats.p_bonehead_hits, cfp);
cfwrite_uint(multi_stats.s_shots_fired, cfp);
cfwrite_uint(multi_stats.s_shots_hit, cfp);
cfwrite_uint(multi_stats.s_bonehead_hits, cfp);
cfwrite_uint(multi_stats.flight_time, cfp);
cfwrite_uint(multi_stats.missions_flown, cfp);
cfwrite_int((int)multi_stats.last_flown, cfp);
cfwrite_int((int)multi_stats.last_backup, cfp);
// ship kills (contains medals across all mods, not just current)
list_size = (int)multi_stats.ship_kills.size();
cfwrite_int(list_size, cfp);
for (idx = 0; idx < list_size; idx++) {
cfwrite_string_len(multi_stats.ship_kills[idx].name.c_str(), cfp);
cfwrite_int(multi_stats.ship_kills[idx].val, cfp);
}
// medals earned (contains medals across all mods, not just current)
list_size = (int)multi_stats.medals_earned.size();
cfwrite_int(list_size, cfp);
for (idx = 0; idx < list_size; idx++) {
cfwrite_string_len(multi_stats.medals_earned[idx].name.c_str(), cfp);
cfwrite_int(multi_stats.medals_earned[idx].val, cfp);
}
endSection();
}
void pilotfile::plr_read_controls()
{
int idx, list_size, list_axis;
short id1, id2, id3 __attribute__((__unused__));
int axi, inv;
list_size = (int)cfread_ushort(cfp);
for (idx = 0; idx < list_size; idx++) {
id1 = cfread_short(cfp);
id2 = cfread_short(cfp);
id3 = cfread_short(cfp); // unused, at the moment
if (idx < CCFG_MAX) {
Control_config[idx].key_id = id1;
Control_config[idx].joy_id = id2;
}
}
list_axis = cfread_int(cfp);
for (idx = 0; idx < list_axis; idx++) {
axi = cfread_int(cfp);
inv = cfread_int(cfp);
if (idx < NUM_JOY_AXIS_ACTIONS) {
Axis_map_to[idx] = axi;
Invert_axis[idx] = inv;
}
}
}
void pilotfile::plr_write_controls()
{
int idx;
startSection(Section::Controls);
cfwrite_ushort(CCFG_MAX, cfp);
for (idx = 0; idx < CCFG_MAX; idx++) {
cfwrite_short(Control_config[idx].key_id, cfp);
cfwrite_short(Control_config[idx].joy_id, cfp);
// placeholder? for future mouse_id?
cfwrite_short(-1, cfp);
}
cfwrite_int(NUM_JOY_AXIS_ACTIONS, cfp);
for (idx = 0; idx < NUM_JOY_AXIS_ACTIONS; idx++) {
cfwrite_int(Axis_map_to[idx], cfp);
cfwrite_int(Invert_axis[idx], cfp);
}
endSection();
}
void pilotfile::plr_read_settings()
{
// sound/voice/music
Master_sound_volume = cfread_float(cfp);
Master_event_music_volume = cfread_float(cfp);
Master_voice_volume = cfread_float(cfp);
audiostream_set_volume_all(Master_voice_volume, ASF_VOICE);
audiostream_set_volume_all(Master_event_music_volume, ASF_EVENTMUSIC);
if (Master_event_music_volume > 0.0f) {
Event_music_enabled = 1;
} else {
Event_music_enabled = 0;
}
Briefing_voice_enabled = cfread_int(cfp);
// skill level
Game_skill_level = cfread_int(cfp);
// input options
Use_mouse_to_fly = cfread_int(cfp);
Mouse_sensitivity = cfread_int(cfp);
Joy_sensitivity = cfread_int(cfp);
Dead_zone_size = cfread_int(cfp);
// detail
Detail.setting = cfread_int(cfp);
Detail.nebula_detail = cfread_int(cfp);
Detail.detail_distance = cfread_int(cfp);
Detail.hardware_textures = cfread_int(cfp);
Detail.num_small_debris = cfread_int(cfp);
Detail.num_particles = cfread_int(cfp);
Detail.num_stars = cfread_int(cfp);
Detail.shield_effects = cfread_int(cfp);
Detail.lighting = cfread_int(cfp);
Detail.targetview_model = cfread_int(cfp);
Detail.planets_suns = cfread_int(cfp);
Detail.weapon_extras = cfread_int(cfp);
}
void pilotfile::plr_write_settings()
{
startSection(Section::Settings);
// sound/voice/music
cfwrite_float(Master_sound_volume, cfp);
cfwrite_float(Master_event_music_volume, cfp);
cfwrite_float(Master_voice_volume, cfp);
cfwrite_int(Briefing_voice_enabled, cfp);
// skill level
cfwrite_int(Game_skill_level, cfp);
// input options
cfwrite_int(Use_mouse_to_fly, cfp);
cfwrite_int(Mouse_sensitivity, cfp);
cfwrite_int(Joy_sensitivity, cfp);
cfwrite_int(Dead_zone_size, cfp);
// detail
cfwrite_int(Detail.setting, cfp);
cfwrite_int(Detail.nebula_detail, cfp);
cfwrite_int(Detail.detail_distance, cfp);
cfwrite_int(Detail.hardware_textures, cfp);
cfwrite_int(Detail.num_small_debris, cfp);
cfwrite_int(Detail.num_particles, cfp);
cfwrite_int(Detail.num_stars, cfp);
cfwrite_int(Detail.shield_effects, cfp);
cfwrite_int(Detail.lighting, cfp);
cfwrite_int(Detail.targetview_model, cfp);
cfwrite_int(Detail.planets_suns, cfp);
cfwrite_int(Detail.weapon_extras, cfp);
endSection();
}
void pilotfile::plr_reset_data()
{
// internals
m_have_flags = false;
m_have_info = false;
m_data_invalid = false;
// set all the entries in the control config arrays to -1 (undefined)
control_config_clear();
// init stats
p->stats.init();
// reset scoring lists
all_time_stats.ship_kills.clear();
all_time_stats.medals_earned.clear();
multi_stats.ship_kills.clear();
multi_stats.medals_earned.clear();
// clear variables
p->variables.clear();
// reset techroom to defaults (CSG will override this, multi will use defaults)
tech_reset_to_default();
}
void pilotfile::plr_close()
{
if (cfp) {
cfclose(cfp);
cfp = NULL;
}
p = NULL;
filename = "";
ship_list.clear();
weapon_list.clear();
intel_list.clear();
medals_list.clear();
m_have_flags = false;
m_have_info = false;
}
bool pilotfile::load_player(const char *callsign, player *_p)
{
// if we're a standalone server in multiplayer, just fill in some bogus values
// since we don't have a pilot file
if ( (Game_mode & GM_MULTIPLAYER) && (Game_mode & GM_STANDALONE_SERVER) ) {
Player->insignia_texture = -1;
strcpy_s(Player->callsign, NOX("Standalone"));
strcpy_s(Player->short_callsign, NOX("Standalone"));
return true;
}
// set player ptr first thing
p = _p;
if ( !p ) {
Assert( (Player_num >= 0) && (Player_num < MAX_PLAYERS) );
p = &Players[Player_num];
}
filename = callsign;
filename += ".plr";
if ( filename.size() == 4 ) {
mprintf(("PLR => Invalid filename '%s'!\n", filename.c_str()));
return false;
}
cfp = cfopen((char*)filename.c_str(), "rb", CFILE_NORMAL, CF_TYPE_PLAYERS);
if ( !cfp ) {
mprintf(("PLR => Unable to open '%s' for reading!\n", filename.c_str()));
return false;
}
unsigned int plr_id = cfread_uint(cfp);
if (plr_id != PLR_FILE_ID) {
mprintf(("PLR => Invalid header id for '%s'!\n", filename.c_str()));
plr_close();
return false;
}
// version, now used
version = cfread_ubyte(cfp);
mprintf(("PLR => Loading '%s' with version %d...\n", filename.c_str(), version));
plr_reset_data();
// the point of all this: read in the PLR contents
while ( !cfeof(cfp) ) {
ushort section_id = cfread_ushort(cfp);
uint section_size = cfread_uint(cfp);
size_t start_pos = cftell(cfp);
// safety, to help protect against long reads
cf_set_max_read_len(cfp, section_size);
try {
switch (section_id) {
case Section::Flags:
mprintf(("PLR => Parsing: Flags...\n"));
m_have_flags = true;
plr_read_flags();
break;
case Section::Info:
mprintf(("PLR => Parsing: Info...\n"));
m_have_info = true;
plr_read_info();
break;
case Section::Variables:
mprintf(("PLR => Parsing: Variables...\n"));
plr_read_variables();
break;
case Section::HUD:
mprintf(("PLR => Parsing: HUD...\n"));
plr_read_hud();
break;
case Section::Scoring:
mprintf(("PLR => Parsing: Scoring...\n"));
plr_read_stats();
break;
case Section::ScoringMulti:
mprintf(("PLR => Parsing: ScoringMulti...\n"));
plr_read_stats_multi();
break;
case Section::Multiplayer:
mprintf(("PLR => Parsing: Multiplayer...\n"));
plr_read_multiplayer();
break;
case Section::Controls:
mprintf(("PLR => Parsing: Controls...\n"));
plr_read_controls();
break;
case Section::Settings:
mprintf(("PLR => Parsing: Settings...\n"));
plr_read_settings();
break;
default:
mprintf(("PLR => Skipping unknown section 0x%04x!\n", section_id));
break;
}
} catch (cfile::max_read_length &msg) {
// read to max section size, move to next section, discarding
// extra/unknown data
mprintf(("PLR => (0x%04x) %s\n", section_id, msg.what()));
} catch (const char *err) {
mprintf(("PLR => ERROR: %s\n", err));
plr_close();
return false;
}
// reset safety catch
cf_set_max_read_len(cfp, 0);
// skip to next section (if not already there)
size_t offset_pos = (start_pos + section_size) - cftell(cfp);
if (offset_pos) {
cfseek(cfp, offset_pos, CF_SEEK_CUR);
mprintf(("PLR => WARNING: Advancing to the next section. %i bytes were skipped!\n", offset_pos));
}
}
// restore the callsign into the Player structure
strcpy_s(p->callsign, callsign);
// restore the truncated callsign into Player structure
pilot_set_short_callsign(p, SHORT_CALLSIGN_PIXEL_W);
player_set_squad_bitmap(p, p->m_squad_filename, true);
hud_squadmsg_save_keys();
// set last pilot
os_config_write_string(NULL, "LastPlayer", (char*)callsign);
mprintf(("PLR => Loading complete!\n"));
// cleanup and return
plr_close();
return true;
}
bool pilotfile::save_player(player *_p)
{
// never save a pilot file for the standalone server in multiplayer
if ( (Game_mode & GM_MULTIPLAYER) && (Game_mode & GM_STANDALONE_SERVER) ) {
return false;
}
// set player ptr first thing
p = _p;
if ( !p ) {
Assert( (Player_num >= 0) && (Player_num < MAX_PLAYERS) );
p = &Players[Player_num];
}
if ( !strlen(p->callsign) ) {
return false;
}
filename = p->callsign;
filename += ".plr";
if ( filename.size() == 4 ) {
mprintf(("PLR => Invalid filename '%s'!\n", filename.c_str()));
return false;
}
// open it, hopefully...
cfp = cfopen((char*)filename.c_str(), "wb", CFILE_NORMAL, CF_TYPE_PLAYERS);
if ( !cfp ) {
mprintf(("PLR => Unable to open '%s' for saving!\n", filename.c_str()));
return false;
}
// header and version
cfwrite_int(PLR_FILE_ID, cfp);
cfwrite_ubyte(PLR_VERSION, cfp);
mprintf(("PLR => Saving '%s' with version %d...\n", filename.c_str(), (int)PLR_VERSION));
// flags and info sections go first
mprintf(("PLR => Saving: Flags...\n"));
plr_write_flags();
mprintf(("PLR => Saving: Info...\n"));
plr_write_info();
// everything else is next, not order specific
mprintf(("PLR => Saving: Scoring...\n"));
plr_write_stats();
mprintf(("PLR => Saving: ScoringMulti...\n"));
plr_write_stats_multi();
mprintf(("PLR => Saving: HUD...\n"));
plr_write_hud();
mprintf(("PLR => Saving: Variables...\n"));
plr_write_variables();
mprintf(("PLR => Saving: Multiplayer...\n"));
plr_write_multiplayer();
mprintf(("PLR => Saving: Controls...\n"));
plr_write_controls();
mprintf(("PLR => Saving: Settings...\n"));
plr_write_settings();
// Done!
mprintf(("PLR => Saving complete!\n"));
plr_close();
return true;
}
bool pilotfile::verify(const char *fname, int *rank, char *valid_language)
{
player t_plr;
// set player ptr first thing
p = &t_plr;
filename = fname;
if ( filename.size() == 4 ) {
mprintf(("PLR => Invalid filename '%s'!\n", filename.c_str()));
return false;
}
cfp = cfopen((char*)filename.c_str(), "rb", CFILE_NORMAL, CF_TYPE_PLAYERS);
if ( !cfp ) {
mprintf(("PLR => Unable to open '%s'!\n", filename.c_str()));
return false;
}
unsigned int plr_id = cfread_uint(cfp);
if (plr_id != PLR_FILE_ID) {
mprintf(("PLR => Invalid header id for '%s'!\n", filename.c_str()));
plr_close();
return false;
}
// version, now used
version = cfread_ubyte(cfp);
mprintf(("PLR => Verifying '%s' with version %d...\n", filename.c_str(), (int)version));
// the point of all this: read in the PLR contents
while ( !m_have_flags && !cfeof(cfp) ) {
ushort section_id = cfread_ushort(cfp);
uint section_size = cfread_uint(cfp);
size_t start_pos = cftell(cfp);
// safety, to help protect against long reads
cf_set_max_read_len(cfp, section_size);
try {
switch (section_id) {
case Section::Flags:
mprintf(("PLR => Parsing: Flags...\n"));
m_have_flags = true;
plr_read_flags();
break;
default:
break;
}
} catch (cfile::max_read_length &msg) {
// read to max section size, move to next section, discarding
// extra/unknown data
mprintf(("PLR => (0x%04x) %s\n", section_id, msg.what()));
} catch (const char *err) {
mprintf(("PLR => ERROR: %s\n", err));
plr_close();
return false;
}
// reset safety catch
cf_set_max_read_len(cfp, 0);
// skip to next section (if not already there)
size_t offset_pos = (start_pos + section_size) - cftell(cfp);
if (offset_pos) {
mprintf(("PLR => Warning: (0x%04x) Short read, information may have been lost!\n", section_id));
cfseek(cfp, offset_pos, CF_SEEK_CUR);
}
}
if (rank) {
*rank = p->stats.rank;
}
if (valid_language) {
strncpy(valid_language, p->language, sizeof(p->language));
}
mprintf(("PLR => Verifying complete!\n"));
// cleanup and return
plr_close();
return true;
}