mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-10 04:31:34 -10:00
"Cloning" failure was actually being caused by the pilot verification function resetting all pilot info and then not loading controls from the file to replace them. The Control_Config array would all be set to -1 and then would get written into the file the next time the player was saved, making it seem that the controls were not being copied. The original and current code would reset controls to defaults when a pilot was created. And it would just let the old controls remain in case of a clone. The above problem would occur just as you hit accept on the bottom of the baracks screen.
1099 lines
33 KiB
C++
1099 lines
33 KiB
C++
|
|
#include "freespace.h"
|
|
#include "gamesnd/eventmusic.h"
|
|
#include "hud/hudconfig.h"
|
|
#include "hud/hudsquadmsg.h"
|
|
#include "io/joy.h"
|
|
#include "io/mouse.h"
|
|
#include "localization/localize.h"
|
|
#include "menuui/techmenu.h"
|
|
#include "network/multi.h"
|
|
#include "osapi/osregistry.h"
|
|
#include "pilotfile/pilotfile.h"
|
|
#include "pilotfile/BinaryFileHandler.h"
|
|
#include "pilotfile/JSONFileHandler.h"
|
|
#include "playerman/managepilot.h"
|
|
#include "playerman/player.h"
|
|
#include "ship/ship.h"
|
|
#include "sound/audiostr.h"
|
|
#include "stats/medals.h"
|
|
#include "weapon/weapon.h"
|
|
|
|
namespace {
|
|
void read_multi_stats(pilot::FileHandler* handler, scoring_special_t* scoring) {
|
|
scoring->score = handler->readInt("score");
|
|
scoring->rank = handler->readInt("rank");
|
|
scoring->assists = handler->readInt("assists");
|
|
scoring->kill_count = handler->readInt("kill_count");
|
|
scoring->kill_count_ok = handler->readInt("kill_count_ok");
|
|
scoring->bonehead_kills = handler->readInt("bonehead_kills");
|
|
|
|
scoring->p_shots_fired = handler->readUInt("p_shots_fired");
|
|
scoring->p_shots_hit = handler->readUInt("p_shots_hit");
|
|
scoring->p_bonehead_hits = handler->readUInt("p_bonehead_hits");
|
|
|
|
scoring->s_shots_fired = handler->readUInt("s_shots_fired");
|
|
scoring->s_shots_hit = handler->readUInt("s_shots_hit");
|
|
scoring->s_bonehead_hits = handler->readUInt("s_bonehead_hits");
|
|
|
|
scoring->flight_time = handler->readUInt("flight_time");
|
|
scoring->missions_flown = handler->readUInt("missions_flown");
|
|
scoring->last_flown = (_fs_time_t)handler->readInt("last_flown");
|
|
scoring->last_backup = (_fs_time_t)handler->readInt("last_backup");
|
|
|
|
// ship kills (contains ships across all mods, not just current)
|
|
auto list_size = handler->startArrayRead("kills");
|
|
scoring->ship_kills.reserve(list_size);
|
|
|
|
for (size_t idx = 0; idx < list_size; idx++, handler->nextArraySection()) {
|
|
index_list_t ilist;
|
|
ilist.name = handler->readString("name");
|
|
ilist.index = ship_info_lookup(ilist.name.c_str());
|
|
ilist.val = handler->readInt("val");
|
|
|
|
scoring->ship_kills.push_back(ilist);
|
|
}
|
|
handler->endArrayRead();
|
|
|
|
// medals earned (contains medals across all mods, not just current)
|
|
list_size = handler->startArrayRead("medals");
|
|
scoring->medals_earned.reserve(list_size);
|
|
|
|
for (size_t idx = 0; idx < list_size; idx++, handler->nextArraySection()) {
|
|
index_list_t ilist;
|
|
ilist.name = handler->readString("name");
|
|
ilist.index = medals_info_lookup(ilist.name.c_str());
|
|
ilist.val = handler->readInt("val");
|
|
|
|
scoring->medals_earned.push_back(ilist);
|
|
}
|
|
handler->endArrayRead();
|
|
}
|
|
}
|
|
|
|
void pilotfile::plr_read_flags()
|
|
{
|
|
// tips?
|
|
p->tips = (int)handler->readUByte("tips");
|
|
|
|
// saved flags
|
|
p->save_flags = handler->readInt("save_flags");
|
|
|
|
// listing mode (single or campaign missions
|
|
p->readyroom_listing_mode = handler->readInt("readyroom_listing_mode");
|
|
|
|
// briefing auto-play
|
|
p->auto_advance = handler->readInt("auto_advance");
|
|
|
|
// special rank setting (to avoid having to read all stats on verify)
|
|
// will be the multi rank
|
|
// if there's a valid CSG, this will be overwritten
|
|
p->stats.rank = handler->readInt("multi_rank");
|
|
|
|
if (version > 0)
|
|
{
|
|
p->player_was_multi = handler->readInt("was_multi");
|
|
} else
|
|
{
|
|
p->player_was_multi = 0; // Default to single player
|
|
}
|
|
|
|
// which language was this pilot created with
|
|
if (version > 1) {
|
|
handler->readString("language", p->language, sizeof(p->language));
|
|
} else {
|
|
// if we don't know, default to the current language setting
|
|
lcl_get_language_name(p->language);
|
|
}
|
|
}
|
|
|
|
void pilotfile::plr_write_flags()
|
|
{
|
|
handler->startSectionWrite(Section::Flags);
|
|
|
|
// tips
|
|
handler->writeUByte("tips", (unsigned char)p->tips);
|
|
|
|
// saved flags
|
|
handler->writeInt("save_flags", p->save_flags);
|
|
|
|
// listing mode (single or campaign missions)
|
|
handler->writeInt("readyroom_listing_mode", p->readyroom_listing_mode);
|
|
|
|
// briefing auto-play
|
|
handler->writeInt("auto_advance", p->auto_advance);
|
|
|
|
// special rank setting (to avoid having to read all stats on verify)
|
|
// should be multi only from now on
|
|
handler->writeInt("multi_rank", multi_stats.rank);
|
|
|
|
// What game mode we were in last on this pilot
|
|
handler->writeInt("was_multi", p->player_was_multi);
|
|
|
|
// which language was this pilot created with
|
|
handler->writeString("language", p->language);
|
|
|
|
handler->endSectionWrite();
|
|
}
|
|
|
|
void pilotfile::plr_read_info()
|
|
{
|
|
// pilot image
|
|
handler->readString("image_filename", p->image_filename, MAX_FILENAME_LEN);
|
|
|
|
// multi squad name
|
|
handler->readString("squad_name", p->m_squad_name, NAME_LENGTH);
|
|
|
|
// squad image
|
|
handler->readString("squad_filename", p->m_squad_filename, MAX_FILENAME_LEN);
|
|
|
|
// active campaign
|
|
handler->readString("current_campaign", p->current_campaign, MAX_FILENAME_LEN);
|
|
}
|
|
|
|
void pilotfile::plr_write_info()
|
|
{
|
|
handler->startSectionWrite(Section::Info);
|
|
|
|
// pilot image
|
|
handler->writeString("image_filename", p->image_filename);
|
|
|
|
// multi squad name
|
|
handler->writeString("squad_name", p->m_squad_name);
|
|
|
|
// squad image
|
|
handler->writeString("squad_filename", p->m_squad_filename);
|
|
|
|
// active campaign
|
|
handler->writeString("current_campaign", p->current_campaign);
|
|
|
|
handler->endSectionWrite();
|
|
}
|
|
|
|
void pilotfile::plr_read_hud()
|
|
{
|
|
// flags
|
|
HUD_config.show_flags = handler->readInt("show_flags");
|
|
HUD_config.show_flags2 = handler->readInt("show_flags2");
|
|
|
|
HUD_config.popup_flags = handler->readInt("popup_flags");
|
|
HUD_config.popup_flags2 = handler->readInt("popup_flags2");
|
|
|
|
// settings
|
|
HUD_config.num_msg_window_lines = handler->readUByte("num_msg_window_lines");
|
|
|
|
HUD_config.rp_flags = handler->readInt("rp_flags");
|
|
HUD_config.rp_dist = handler->readInt("rp_dist");
|
|
if (HUD_config.rp_dist < 0 || HUD_config.rp_dist >= RR_MAX_RANGES) {
|
|
Warning(LOCATION, "Player file has invalid radar range %d, setting to default.\n", HUD_config.rp_dist);
|
|
HUD_config.rp_dist = RR_INFINITY;
|
|
}
|
|
// basic colors
|
|
HUD_config.main_color = handler->readInt("main_color");
|
|
HUD_color_alpha = handler->readInt("color_alpha");
|
|
|
|
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
|
|
auto num_gauges = handler->startArrayRead("hud_gauges");
|
|
for (size_t idx = 0; idx < num_gauges; idx++, handler->nextArraySection()) {
|
|
ubyte red = handler->readUByte("red");
|
|
ubyte green = handler->readUByte("green");
|
|
ubyte blue = handler->readUByte("blue");
|
|
ubyte alpha = handler->readUByte("alpha");
|
|
|
|
|
|
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;
|
|
}
|
|
handler->endArrayRead();
|
|
}
|
|
|
|
void pilotfile::plr_write_hud()
|
|
{
|
|
int idx;
|
|
|
|
handler->startSectionWrite(Section::HUD);
|
|
|
|
// flags
|
|
handler->writeInt("show_flags", HUD_config.show_flags);
|
|
handler->writeInt("show_flags2", HUD_config.show_flags2);
|
|
|
|
handler->writeInt("popup_flags", HUD_config.popup_flags);
|
|
handler->writeInt("popup_flags2", HUD_config.popup_flags2);
|
|
|
|
// settings
|
|
handler->writeUByte("num_msg_window_lines", HUD_config.num_msg_window_lines);
|
|
|
|
handler->writeInt("rp_flags", HUD_config.rp_flags);
|
|
handler->writeInt("rp_dist", HUD_config.rp_dist);
|
|
|
|
// basic colors
|
|
handler->writeInt("main_color", HUD_config.main_color);
|
|
handler->writeInt("color_alpha", HUD_color_alpha);
|
|
|
|
// gauge-specific colors
|
|
handler->startArrayWrite("hud_gauges", NUM_HUD_GAUGES);
|
|
for (idx = 0; idx < NUM_HUD_GAUGES; idx++) {
|
|
handler->startSectionWrite(Section::Unnamed);
|
|
|
|
handler->writeUByte("red", HUD_config.clr[idx].red);
|
|
handler->writeUByte("green", HUD_config.clr[idx].green);
|
|
handler->writeUByte("blue", HUD_config.clr[idx].blue);
|
|
handler->writeUByte("alpha", HUD_config.clr[idx].alpha);
|
|
|
|
handler->endSectionWrite();
|
|
}
|
|
handler->endArrayWrite();
|
|
|
|
handler->endSectionWrite();
|
|
}
|
|
|
|
void pilotfile::plr_read_variables()
|
|
{
|
|
auto list_size = handler->startArrayRead("variables");
|
|
|
|
p->variables.reserve(list_size);
|
|
for (size_t idx = 0; idx < list_size; idx++, handler->nextArraySection()) {
|
|
sexp_variable n_var;
|
|
n_var.type = handler->readInt("type");
|
|
handler->readString("text", n_var.text, TOKEN_LENGTH);
|
|
handler->readString("variable_name", n_var.variable_name, TOKEN_LENGTH);
|
|
|
|
p->variables.push_back( n_var );
|
|
}
|
|
handler->endArrayRead();
|
|
}
|
|
|
|
void pilotfile::plr_write_variables()
|
|
{
|
|
handler->startSectionWrite(Section::Variables);
|
|
|
|
handler->startArrayWrite("variables", p->variables.size());
|
|
for (auto& var : p->variables) {
|
|
handler->startSectionWrite(Section::Unnamed);
|
|
|
|
handler->writeInt("type", var.type);
|
|
handler->writeString("text", var.text);
|
|
handler->writeString("variable_name", var.variable_name);
|
|
|
|
handler->endSectionWrite();
|
|
}
|
|
handler->endArrayWrite();
|
|
|
|
handler->endSectionWrite();
|
|
}
|
|
|
|
void pilotfile::plr_read_multiplayer()
|
|
{
|
|
// netgame options
|
|
p->m_server_options.squad_set = handler->readUByte("squad_set");
|
|
p->m_server_options.endgame_set = handler->readUByte("endgame_set");
|
|
p->m_server_options.flags = handler->readInt("server_flags");
|
|
p->m_server_options.respawn = handler->readUInt("respawn");
|
|
p->m_server_options.max_observers = handler->readUByte("max_observers");
|
|
p->m_server_options.skill_level = handler->readUByte("skill_level");
|
|
p->m_server_options.voice_qos = handler->readUByte("voice_qos");
|
|
p->m_server_options.voice_token_wait = handler->readInt("voice_token_wait");
|
|
p->m_server_options.voice_record_time = handler->readInt("voice_record_time");
|
|
p->m_server_options.mission_time_limit = (fix)handler->readInt("mission_time_limit");
|
|
p->m_server_options.kill_limit = handler->readInt("kill_limit");
|
|
|
|
// local options
|
|
p->m_local_options.flags = handler->readInt("local_flags");
|
|
p->m_local_options.obj_update_level = handler->readInt("obj_update_level");
|
|
|
|
// netgame protocol
|
|
Multi_options_g.protocol = handler->readInt("protocol");
|
|
|
|
if (Multi_options_g.protocol != NET_TCP) {
|
|
Multi_options_g.protocol = NET_TCP;
|
|
}
|
|
}
|
|
|
|
void pilotfile::plr_write_multiplayer()
|
|
{
|
|
handler->startSectionWrite(Section::Multiplayer);
|
|
|
|
// netgame options
|
|
handler->writeUByte("squad_set", p->m_server_options.squad_set);
|
|
handler->writeUByte("endgame_set", p->m_server_options.endgame_set);
|
|
handler->writeInt("server_flags", p->m_server_options.flags);
|
|
handler->writeUInt("respawn", p->m_server_options.respawn);
|
|
handler->writeUByte("max_observers", p->m_server_options.max_observers);
|
|
handler->writeUByte("skill_level", p->m_server_options.skill_level);
|
|
handler->writeUByte("voice_qos", p->m_server_options.voice_qos);
|
|
handler->writeInt("voice_token_wait", p->m_server_options.voice_token_wait);
|
|
handler->writeInt("voice_record_time", p->m_server_options.voice_record_time);
|
|
handler->writeInt("mission_time_limit", (int)p->m_server_options.mission_time_limit);
|
|
handler->writeInt("kill_limit", p->m_server_options.kill_limit);
|
|
|
|
// local options
|
|
handler->writeInt("local_flags", p->m_local_options.flags);
|
|
handler->writeInt("obj_update_level", p->m_local_options.obj_update_level);
|
|
|
|
// netgame protocol
|
|
handler->writeInt("protocol", Multi_options_g.protocol);
|
|
|
|
handler->endSectionWrite();
|
|
}
|
|
|
|
void pilotfile::plr_read_stats()
|
|
{
|
|
// global, all-time stats (used only until campaign stats are loaded)
|
|
read_multi_stats(handler.get(), &all_time_stats);
|
|
|
|
// 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)
|
|
auto list_size = all_time_stats.ship_kills.size();
|
|
for (size_t idx = 0; idx < list_size; idx++) {
|
|
auto 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 = all_time_stats.medals_earned.size();
|
|
for (size_t idx = 0; idx < list_size; idx++) {
|
|
auto 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()
|
|
{
|
|
handler->startSectionWrite(Section::Scoring);
|
|
|
|
// global, all-time stats
|
|
handler->writeInt("score", all_time_stats.score);
|
|
handler->writeInt("rank", all_time_stats.rank);
|
|
handler->writeInt("assists", all_time_stats.assists);
|
|
handler->writeInt("kill_count", all_time_stats.kill_count);
|
|
handler->writeInt("kill_count_ok", all_time_stats.kill_count_ok);
|
|
handler->writeInt("bonehead_kills", all_time_stats.bonehead_kills);
|
|
|
|
handler->writeUInt("p_shots_fired", all_time_stats.p_shots_fired);
|
|
handler->writeUInt("p_shots_hit", all_time_stats.p_shots_hit);
|
|
handler->writeUInt("p_bonehead_hits", all_time_stats.p_bonehead_hits);
|
|
|
|
handler->writeUInt("s_shots_fired", all_time_stats.s_shots_fired);
|
|
handler->writeUInt("s_shots_hit", all_time_stats.s_shots_hit);
|
|
handler->writeUInt("s_bonehead_hits", all_time_stats.s_bonehead_hits);
|
|
|
|
handler->writeUInt("flight_time", all_time_stats.flight_time);
|
|
handler->writeUInt("missions_flown", all_time_stats.missions_flown);
|
|
handler->writeInt("last_flown", (int)all_time_stats.last_flown);
|
|
handler->writeInt("last_backup", (int)all_time_stats.last_backup);
|
|
|
|
// ship kills (contains ships across all mods, not just current)
|
|
handler->startArrayWrite("kills", all_time_stats.ship_kills.size());
|
|
for (auto& kill : all_time_stats.ship_kills) {
|
|
handler->startSectionWrite(Section::Unnamed);
|
|
handler->writeString("name", kill.name.c_str());
|
|
handler->writeInt("val", kill.val);
|
|
handler->endSectionWrite();
|
|
}
|
|
handler->endArrayWrite();
|
|
|
|
// medals earned (contains medals across all mods, not just current)
|
|
handler->startArrayWrite("medals", all_time_stats.medals_earned.size());
|
|
for (auto& medal : all_time_stats.medals_earned) {
|
|
handler->startSectionWrite(Section::Unnamed);
|
|
handler->writeString("name", medal.name.c_str());
|
|
handler->writeInt("val", medal.val);
|
|
handler->endSectionWrite();
|
|
}
|
|
handler->endArrayWrite();
|
|
|
|
handler->endSectionWrite();
|
|
}
|
|
|
|
void pilotfile::plr_read_stats_multi()
|
|
{
|
|
// global, all-time stats (used only until campaign stats are loaded)
|
|
read_multi_stats(handler.get(), &multi_stats);
|
|
|
|
// 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)
|
|
auto list_size = multi_stats.ship_kills.size();
|
|
for (size_t idx = 0; idx < list_size; idx++) {
|
|
auto 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 (size_t idx = 0; idx < list_size; idx++) {
|
|
auto 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()
|
|
{
|
|
handler->startSectionWrite(Section::ScoringMulti);
|
|
|
|
// global, all-time stats
|
|
handler->writeInt("score", multi_stats.score);
|
|
handler->writeInt("rank", multi_stats.rank);
|
|
handler->writeInt("assists", multi_stats.assists);
|
|
handler->writeInt("kill_count", multi_stats.kill_count);
|
|
handler->writeInt("kill_count_ok", multi_stats.kill_count_ok);
|
|
handler->writeInt("bonehead_kills", multi_stats.bonehead_kills);
|
|
|
|
handler->writeUInt("p_shots_fired", multi_stats.p_shots_fired);
|
|
handler->writeUInt("p_shots_hit", multi_stats.p_shots_hit);
|
|
handler->writeUInt("p_bonehead_hits", multi_stats.p_bonehead_hits);
|
|
|
|
handler->writeUInt("s_shots_fired", multi_stats.s_shots_fired);
|
|
handler->writeUInt("s_shots_hit", multi_stats.s_shots_hit);
|
|
handler->writeUInt("s_bonehead_hits", multi_stats.s_bonehead_hits);
|
|
|
|
handler->writeUInt("flight_time", multi_stats.flight_time);
|
|
handler->writeUInt("missions_flown", multi_stats.missions_flown);
|
|
handler->writeInt("last_flown", (int)multi_stats.last_flown);
|
|
handler->writeInt("last_backup", (int)multi_stats.last_backup);
|
|
|
|
// ship kills (contains medals across all mods, not just current)
|
|
handler->startArrayWrite("kills", multi_stats.ship_kills.size());
|
|
for (auto& kill : multi_stats.ship_kills) {
|
|
handler->startSectionWrite(Section::Unnamed);
|
|
handler->writeString("name", kill.name.c_str());
|
|
handler->writeInt("val", kill.val);
|
|
handler->endSectionWrite();
|
|
}
|
|
handler->endArrayWrite();
|
|
|
|
// medals earned (contains medals across all mods, not just current)
|
|
handler->startArrayWrite("medals", multi_stats.medals_earned.size());
|
|
for (auto& medal : multi_stats.medals_earned) {
|
|
handler->writeString("name", medal.name.c_str());
|
|
handler->writeInt("val", medal.val);
|
|
}
|
|
handler->endArrayWrite();
|
|
|
|
handler->endSectionWrite();
|
|
}
|
|
|
|
void pilotfile::plr_read_controls()
|
|
{
|
|
short id1, id2;
|
|
int axi, inv;
|
|
|
|
auto list_size = handler->startArrayRead("controls", true);
|
|
for (size_t idx = 0; idx < list_size; idx++, handler->nextArraySection()) {
|
|
id1 = handler->readShort("key");
|
|
id2 = handler->readShort("joystick");
|
|
handler->readShort("mouse"); // unused, at the moment
|
|
|
|
if (idx < CCFG_MAX) {
|
|
Control_config[idx].key_id = id1;
|
|
Control_config[idx].joy_id = id2;
|
|
}
|
|
}
|
|
handler->endArrayRead();
|
|
|
|
auto list_axis = handler->startArrayRead("axes");
|
|
for (size_t idx = 0; idx < list_axis; idx++, handler->nextArraySection()) {
|
|
axi = handler->readInt("axis_map");
|
|
inv = handler->readInt("invert_axis");
|
|
|
|
if (idx < NUM_JOY_AXIS_ACTIONS) {
|
|
Axis_map_to[idx] = axi;
|
|
Invert_axis[idx] = inv;
|
|
}
|
|
}
|
|
handler->endArrayRead();
|
|
}
|
|
|
|
void pilotfile::plr_write_controls()
|
|
{
|
|
handler->startSectionWrite(Section::Controls);
|
|
|
|
// For some unknown reason, the old code used a short for the array length here...
|
|
handler->startArrayWrite("controls", CCFG_MAX, true);
|
|
for (size_t idx = 0; idx < CCFG_MAX; idx++) {
|
|
handler->startSectionWrite(Section::Unnamed);
|
|
|
|
handler->writeShort("key", Control_config[idx].key_id);
|
|
handler->writeShort("joystick", Control_config[idx].joy_id);
|
|
// placeholder? for future mouse_id?
|
|
handler->writeShort("mouse", -1);
|
|
|
|
handler->endSectionWrite();
|
|
}
|
|
handler->endArrayWrite();
|
|
|
|
handler->startArrayWrite("axes", NUM_JOY_AXIS_ACTIONS);
|
|
for (size_t idx = 0; idx < NUM_JOY_AXIS_ACTIONS; idx++) {
|
|
handler->startSectionWrite(Section::Unnamed);
|
|
|
|
handler->writeInt("axis_map", Axis_map_to[idx]);
|
|
handler->writeInt("invert_axis", Invert_axis[idx]);
|
|
|
|
handler->endSectionWrite();
|
|
}
|
|
handler->endArrayWrite();
|
|
|
|
handler->endSectionWrite();
|
|
}
|
|
|
|
void pilotfile::plr_read_settings()
|
|
{
|
|
// sound/voice/music
|
|
Master_sound_volume = handler->readFloat("master_sound_volume");
|
|
Master_event_music_volume = handler->readFloat("master_event_music_volume");
|
|
Master_voice_volume = handler->readFloat("aster_voice_volume");
|
|
|
|
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 = handler->readInt("briefing_voice_enabled");
|
|
|
|
// skill level
|
|
Game_skill_level = handler->readInt("game_skill_level");
|
|
|
|
// input options
|
|
Use_mouse_to_fly = handler->readInt("use_mouse_to_fly");
|
|
Mouse_sensitivity = handler->readInt("mouse_sensitivity");
|
|
Joy_sensitivity = handler->readInt("joy_sensitivity");
|
|
Joy_dead_zone_size = handler->readInt("joy_dead_zone_size");
|
|
|
|
// detail
|
|
Detail.setting = handler->readInt("setting");
|
|
Detail.nebula_detail = handler->readInt("nebula_detail");
|
|
Detail.detail_distance = handler->readInt("detail_distance");
|
|
Detail.hardware_textures = handler->readInt("hardware_textures");
|
|
Detail.num_small_debris = handler->readInt("num_small_debris");
|
|
Detail.num_particles = handler->readInt("num_particles");
|
|
Detail.num_stars = handler->readInt("num_stars");
|
|
Detail.shield_effects = handler->readInt("shield_effects");
|
|
Detail.lighting = handler->readInt("lighting");
|
|
Detail.targetview_model = handler->readInt("targetview_model");
|
|
Detail.planets_suns = handler->readInt("planets_suns");
|
|
Detail.weapon_extras = handler->readInt("weapon_extras");
|
|
}
|
|
|
|
void pilotfile::plr_write_settings()
|
|
{
|
|
handler->startSectionWrite(Section::Settings);
|
|
|
|
// sound/voice/music
|
|
handler->writeFloat("master_sound_volume", Master_sound_volume);
|
|
handler->writeFloat("master_event_music_volume", Master_event_music_volume);
|
|
handler->writeFloat("aster_voice_volume", Master_voice_volume);
|
|
|
|
handler->writeInt("briefing_voice_enabled", Briefing_voice_enabled);
|
|
|
|
// skill level
|
|
handler->writeInt("game_skill_level", Game_skill_level);
|
|
|
|
// input options
|
|
handler->writeInt("use_mouse_to_fly", Use_mouse_to_fly);
|
|
handler->writeInt("mouse_sensitivity", Mouse_sensitivity);
|
|
handler->writeInt("joy_sensitivity", Joy_sensitivity);
|
|
handler->writeInt("joy_dead_zone_size", Joy_dead_zone_size);
|
|
|
|
// detail
|
|
handler->writeInt("setting", Detail.setting);
|
|
handler->writeInt("nebula_detail", Detail.nebula_detail);
|
|
handler->writeInt("detail_distance", Detail.detail_distance);
|
|
handler->writeInt("hardware_textures", Detail.hardware_textures);
|
|
handler->writeInt("num_small_debris", Detail.num_small_debris);
|
|
handler->writeInt("num_particles", Detail.num_particles);
|
|
handler->writeInt("num_stars", Detail.num_stars);
|
|
handler->writeInt("shield_effects", Detail.shield_effects);
|
|
handler->writeInt("lighting", Detail.lighting);
|
|
handler->writeInt("targetview_model", Detail.targetview_model);
|
|
handler->writeInt("planets_suns", Detail.planets_suns);
|
|
handler->writeInt("weapon_extras", Detail.weapon_extras);
|
|
|
|
handler->endSectionWrite();
|
|
}
|
|
|
|
void pilotfile::plr_reset_data(bool reset_all)
|
|
{
|
|
// 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)
|
|
// Cyborg17 - clear_all is false when we are just trying to verify the file, but true when we load the whole thing.
|
|
// Otherwise, we can end up getting rid of a pilot's controls by accident.
|
|
if (reset_all) {
|
|
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;
|
|
}
|
|
if (handler) {
|
|
handler.reset();
|
|
}
|
|
|
|
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, bool force_binary)
|
|
{
|
|
// 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;
|
|
if (force_binary) {
|
|
// Caller want to read the binary file
|
|
filename += ".plr";
|
|
} else {
|
|
// The default is the JSON file
|
|
filename += ".json";
|
|
}
|
|
|
|
if ( filename.size() == 4 ) {
|
|
mprintf(("PLR => Invalid filename '%s'!\n", filename.c_str()));
|
|
return false;
|
|
}
|
|
|
|
auto fp = cfopen(filename.c_str(), "rb", CFILE_NORMAL, CF_TYPE_PLAYERS, false,
|
|
CF_LOCATION_ROOT_USER | CF_LOCATION_ROOT_GAME | CF_LOCATION_TYPE_ROOT);
|
|
if ( !fp ) {
|
|
mprintf(("PLR => Unable to open '%s' for reading!\n", filename.c_str()));
|
|
return false;
|
|
}
|
|
|
|
if (force_binary) {
|
|
handler.reset(new pilot::BinaryFileHandler(fp));
|
|
} else {
|
|
try {
|
|
handler.reset(new pilot::JSONFileHandler(fp, true));
|
|
} catch (const std::exception& e) {
|
|
mprintf(("PLR => Failed to parse JSON: %s\n", e.what()));
|
|
return false;
|
|
}
|
|
}
|
|
|
|
unsigned int plr_id = handler->readUInt("signature");
|
|
|
|
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 = handler->readUByte("version");
|
|
|
|
mprintf(("PLR => Loading '%s' with version %d...\n", filename.c_str(), version));
|
|
|
|
//true resets everything, false sets up file verify.
|
|
plr_reset_data(true);
|
|
|
|
// the point of all this: read in the PLR contents
|
|
handler->beginSectionRead();
|
|
while (handler->hasMoreSections()) {
|
|
auto section_id = handler->nextSection();
|
|
try {
|
|
switch (section_id) {
|
|
case Section::Flags:
|
|
mprintf(("PLR => Parsing: Flags...\n"));
|
|
plr_read_flags();
|
|
break;
|
|
|
|
case Section::Info:
|
|
mprintf(("PLR => Parsing: Info...\n"));
|
|
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;
|
|
|
|
case Section::Invalid:
|
|
plr_close();
|
|
return false;
|
|
|
|
default:
|
|
mprintf(("PLR => Skipping unknown section 0x%04x!\n", (uint32_t)section_id));
|
|
break;
|
|
}
|
|
} catch (const cfile::max_read_length &msg) {
|
|
// read to max section size, move to next section, discarding
|
|
// extra/unknown data
|
|
mprintf(("PLR => (0x%04x) %s\n", (uint32_t)section_id, msg.what()));
|
|
} catch (const char *err) {
|
|
mprintf(("PLR => ERROR: %s\n", err));
|
|
plr_close();
|
|
return false;
|
|
}
|
|
}
|
|
handler->endSectionRead();
|
|
|
|
// 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();
|
|
|
|
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;
|
|
if ( filename.empty() ) {
|
|
mprintf(("PLR => Invalid filename '%s'!\n", filename.c_str()));
|
|
return false;
|
|
}
|
|
|
|
// We always write the player file as JSON now
|
|
filename += ".json";
|
|
|
|
// open it, hopefully...
|
|
auto fp = cfopen(filename.c_str(), "wb", CFILE_NORMAL, CF_TYPE_PLAYERS, false,
|
|
CF_LOCATION_ROOT_USER | CF_LOCATION_ROOT_GAME | CF_LOCATION_TYPE_ROOT);
|
|
|
|
if ( !fp ) {
|
|
mprintf(("PLR => Unable to open '%s' for saving!\n", filename.c_str()));
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
handler.reset(new pilot::JSONFileHandler(fp, false));
|
|
} catch (const std::exception& e) {
|
|
mprintf(("PLR => Failed to parse JSON: %s\n", e.what()));
|
|
return false;
|
|
}
|
|
|
|
// header and version
|
|
handler->writeInt("signature", PLR_FILE_ID);
|
|
handler->writeUByte("version", PLR_VERSION);
|
|
|
|
mprintf(("PLR => Saving '%s' with version %d...\n", filename.c_str(), (int)PLR_VERSION));
|
|
|
|
handler->beginWritingSections();
|
|
|
|
// 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();
|
|
|
|
handler->endWritingSections();
|
|
|
|
handler->flush();
|
|
|
|
// 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;
|
|
}
|
|
|
|
auto fp = cfopen(filename.c_str(), "rb", CFILE_NORMAL, CF_TYPE_PLAYERS, false,
|
|
CF_LOCATION_ROOT_USER | CF_LOCATION_ROOT_GAME | CF_LOCATION_TYPE_ROOT);
|
|
|
|
if ( !fp ) {
|
|
mprintf(("PLR => Unable to open '%s'!\n", filename.c_str()));
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
handler.reset(new pilot::JSONFileHandler(fp, true));
|
|
} catch (const std::exception& e) {
|
|
mprintf(("PLR => Failed to parse JSON: %s\n", e.what()));
|
|
return false;
|
|
}
|
|
|
|
unsigned int plr_id = handler->readUInt("signature");
|
|
|
|
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 = handler->readUByte("version");
|
|
|
|
mprintf(("PLR => Verifying '%s' with version %d...\n", filename.c_str(), version));
|
|
|
|
// true resets everything, false sets up file verify.
|
|
plr_reset_data(false);
|
|
|
|
bool have_flags = false;
|
|
bool have_info = false;
|
|
// the point of all this: read in the PLR contents
|
|
handler->beginSectionRead();
|
|
while (!(have_flags && have_info) && handler->hasMoreSections()) {
|
|
auto section_id = handler->nextSection();
|
|
try {
|
|
switch (section_id) {
|
|
case Section::Flags:
|
|
mprintf(("PLR => Parsing: Flags...\n"));
|
|
have_flags = true;
|
|
plr_read_flags();
|
|
break;
|
|
|
|
// now reading the Info section to get the campaign
|
|
// and be able to lookup the campaign rank
|
|
case Section::Info:
|
|
mprintf(("PLR => Parsing: Info...\n"));
|
|
have_info = true;
|
|
plr_read_info();
|
|
break;
|
|
|
|
case Section::Invalid:
|
|
plr_close();
|
|
return false;
|
|
|
|
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", (uint32_t)section_id, msg.what()));
|
|
} catch (const char *err) {
|
|
mprintf(("PLR => ERROR: %s\n", err));
|
|
plr_close();
|
|
return false;
|
|
}
|
|
}
|
|
handler->endSectionRead();
|
|
|
|
if (valid_language) {
|
|
strcpy(valid_language, p->language);
|
|
}
|
|
|
|
// need to cleanup early to ensure everything is OK for use in the CSG next
|
|
// also means we can't use *p from now on, use t_plr instead for a few vars
|
|
plr_close();
|
|
|
|
if (rank) {
|
|
// maybe get the rank from the CSG
|
|
if ( !(Game_mode & GM_MULTIPLAYER) ) {
|
|
// build the csg filename
|
|
// since filename/fname was validated above, perform less safety checks here
|
|
filename = fname;
|
|
filename = filename.replace(filename.find_last_of('.')+1,filename.npos, t_plr.current_campaign);
|
|
filename.append(".csg");
|
|
|
|
if (!this->get_csg_rank(rank)) {
|
|
// if we failed to get the csg rank, default to multi rank
|
|
*rank = t_plr.stats.rank;
|
|
}
|
|
} else {
|
|
// if the CSG isn't valid, or for multi, use this rank
|
|
*rank = t_plr.stats.rank;
|
|
}
|
|
}
|
|
|
|
mprintf(("PLR => Verifying complete!\n"));
|
|
|
|
return true;
|
|
}
|