mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-09 04:49:39 -10:00
Clear red alert data when resetting campaign progress, changing campaigns and switching pilots In CSG read, move RA data clear from the read RA section to the data reset function (i.e. clear if CSG is valid, not if section contains data) git-svn-id: svn://svn.icculus.org/fs2open/trunk/fs2_open@11007 387891d4-d844-0410-90c0-e4c51a9137d3
1699 lines
41 KiB
C++
1699 lines
41 KiB
C++
|
|
#include "globalincs/pstypes.h"
|
|
#include "pilotfile/pilotfile.h"
|
|
#include "playerman/player.h"
|
|
#include "ship/ship.h"
|
|
#include "weapon/weapon.h"
|
|
#include "stats/medals.h"
|
|
#include "menuui/techmenu.h"
|
|
#include "hud/hudconfig.h"
|
|
#include "cutscene/cutscenes.h"
|
|
#include "missionui/missionscreencommon.h"
|
|
#include "mission/missioncampaign.h"
|
|
#include "missionui/missionshipchoice.h"
|
|
#include "mission/missionload.h"
|
|
#include "sound/audiostr.h"
|
|
#include "io/joy.h"
|
|
#include "io/mouse.h"
|
|
#include "gamesnd/eventmusic.h"
|
|
#include "freespace2/freespace.h"
|
|
|
|
#define REDALERT_INTERNAL
|
|
#include "missionui/redalert.h"
|
|
|
|
#include <iostream>
|
|
#include <sstream>
|
|
|
|
|
|
|
|
void pilotfile::csg_read_flags()
|
|
{
|
|
// tips?
|
|
p->tips = (int)cfread_ubyte(cfp);
|
|
|
|
// avoid having to read everything to get the rank
|
|
if (csg_ver >= 5) {
|
|
p->stats.rank = cfread_int(cfp);
|
|
}
|
|
}
|
|
|
|
void pilotfile::csg_write_flags()
|
|
{
|
|
startSection(Section::Flags);
|
|
|
|
// tips
|
|
cfwrite_ubyte((ubyte)p->tips, cfp);
|
|
|
|
// avoid having to read everything to get the rank
|
|
cfwrite_int(p->stats.rank, cfp);
|
|
|
|
endSection();
|
|
}
|
|
|
|
void pilotfile::csg_read_info()
|
|
{
|
|
char t_string[NAME_LENGTH+1] = { '\0' };
|
|
index_list_t ilist;
|
|
int idx, list_size = 0;
|
|
ubyte allowed = 0;
|
|
|
|
if ( !m_have_flags ) {
|
|
throw "Info before Flags!";
|
|
}
|
|
|
|
//
|
|
// NOTE: lists may contain missing/invalid entries for current data
|
|
// this is not necessarily fatal
|
|
//
|
|
|
|
// ship list (NOTE: may contain more than MAX_SHIP_CLASSES)
|
|
list_size = cfread_int(cfp);
|
|
|
|
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);
|
|
|
|
ship_list.push_back(ilist);
|
|
}
|
|
|
|
// weapon list (NOTE: may contain more than MAX_WEAPON_TYPES)
|
|
list_size = cfread_int(cfp);
|
|
|
|
for (idx = 0; idx < list_size; idx++) {
|
|
cfread_string_len(t_string, NAME_LENGTH, cfp);
|
|
|
|
ilist.name = t_string;
|
|
ilist.index = weapon_info_lookup(t_string);
|
|
|
|
weapon_list.push_back(ilist);
|
|
}
|
|
|
|
// intel list (NOTE: may contain more than MAX_INTEL_ENTRIES)
|
|
list_size = cfread_int(cfp);
|
|
|
|
for (idx = 0; idx < list_size; idx++) {
|
|
cfread_string_len(t_string, NAME_LENGTH, cfp);
|
|
|
|
ilist.name = t_string;
|
|
ilist.index = intel_info_lookup(t_string);
|
|
|
|
intel_list.push_back(ilist);
|
|
}
|
|
|
|
// medals list (NOTE: may contain more than Num_medals)
|
|
list_size = cfread_int(cfp);
|
|
|
|
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);
|
|
|
|
medals_list.push_back(ilist);
|
|
}
|
|
|
|
// last ship flown (index into ship_list)
|
|
idx = cfread_int(cfp);
|
|
|
|
// check the idx is within bounds
|
|
Assertion ((idx < (int)ship_list.size()), "Campaign file contains an incorrect value for the last flown ship class. No data in ship_list for ship number %d.", idx);
|
|
if (idx >= (int)ship_list.size())
|
|
idx = -1;
|
|
else if (idx != -1)
|
|
p->last_ship_flown_si_index = ship_list[idx].index;
|
|
else
|
|
p->last_ship_flown_si_index = -1;
|
|
|
|
// progression state
|
|
Campaign.prev_mission = cfread_int(cfp);
|
|
Campaign.next_mission = cfread_int(cfp);
|
|
|
|
// loop state
|
|
Campaign.loop_reentry = cfread_int(cfp);
|
|
Campaign.loop_enabled = cfread_int(cfp);
|
|
|
|
// missions completed
|
|
Campaign.num_missions_completed = cfread_int(cfp);
|
|
|
|
// allowed ships
|
|
list_size = (int)ship_list.size();
|
|
for (idx = 0; idx < list_size; idx++) {
|
|
allowed = cfread_ubyte(cfp);
|
|
|
|
if (allowed) {
|
|
if (ship_list[idx].index >= 0) {
|
|
Campaign.ships_allowed[ship_list[idx].index] = 1;
|
|
} else {
|
|
m_data_invalid = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// allowed weapons
|
|
list_size = (int)weapon_list.size();
|
|
for (idx = 0; idx < list_size; idx++) {
|
|
allowed = cfread_ubyte(cfp);
|
|
|
|
if (allowed) {
|
|
if (weapon_list[idx].index >= 0) {
|
|
Campaign.weapons_allowed[weapon_list[idx].index] = 1;
|
|
} else {
|
|
m_data_invalid = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (csg_ver >= 2) {
|
|
// single/campaign squad name & image
|
|
cfread_string_len(p->s_squad_name, NAME_LENGTH, cfp);
|
|
cfread_string_len(p->s_squad_filename, MAX_FILENAME_LEN, cfp);
|
|
}
|
|
|
|
// if anything we need/use was missing then it should be considered fatal
|
|
if (m_data_invalid) {
|
|
throw "Invalid data for CSG!";
|
|
}
|
|
}
|
|
|
|
void pilotfile::csg_write_info()
|
|
{
|
|
int idx;
|
|
|
|
startSection(Section::Info);
|
|
|
|
// ship list
|
|
cfwrite_int(Num_ship_classes, cfp);
|
|
|
|
for (idx = 0; idx < Num_ship_classes; idx++) {
|
|
cfwrite_string_len(Ship_info[idx].name, cfp);
|
|
}
|
|
|
|
// weapon list
|
|
cfwrite_int(Num_weapon_types, cfp);
|
|
|
|
for (idx = 0; idx < Num_weapon_types; idx++) {
|
|
cfwrite_string_len(Weapon_info[idx].name, cfp);
|
|
}
|
|
|
|
// intel list
|
|
cfwrite_int(Intel_info_size, cfp);
|
|
|
|
for (idx = 0; idx < Intel_info_size; idx++) {
|
|
cfwrite_string_len(Intel_info[idx].name, cfp);
|
|
}
|
|
|
|
// medals list
|
|
cfwrite_int(Num_medals, cfp);
|
|
|
|
for (idx = 0; idx < Num_medals; idx++) {
|
|
cfwrite_string_len(Medals[idx].name, cfp);
|
|
}
|
|
|
|
// last ship flown
|
|
cfwrite_int(p->last_ship_flown_si_index, cfp);
|
|
|
|
// progression state
|
|
cfwrite_int(Campaign.prev_mission, cfp);
|
|
cfwrite_int(Campaign.next_mission, cfp);
|
|
|
|
// loop state
|
|
cfwrite_int(Campaign.loop_reentry, cfp);
|
|
cfwrite_int(Campaign.loop_enabled, cfp);
|
|
|
|
// missions completed
|
|
cfwrite_int(Campaign.num_missions_completed, cfp);
|
|
|
|
// allowed ships
|
|
for (idx = 0; idx < Num_ship_classes; idx++) {
|
|
cfwrite_ubyte(Campaign.ships_allowed[idx], cfp);
|
|
}
|
|
|
|
// allowed weapons
|
|
for (idx = 0; idx < Num_weapon_types; idx++) {
|
|
cfwrite_ubyte(Campaign.weapons_allowed[idx], cfp);
|
|
}
|
|
|
|
// single/campaign squad name & image
|
|
cfwrite_string_len(p->s_squad_name, cfp);
|
|
cfwrite_string_len(p->s_squad_filename, cfp);
|
|
|
|
endSection();
|
|
}
|
|
|
|
void pilotfile::csg_read_missions()
|
|
{
|
|
int i, j, idx, list_size;
|
|
cmission *missionp;
|
|
|
|
if ( !m_have_info ) {
|
|
throw "Missions before Info!";
|
|
}
|
|
|
|
for (i = 0; i < Campaign.num_missions_completed; i++) {
|
|
idx = cfread_int(cfp);
|
|
missionp = &Campaign.missions[idx];
|
|
|
|
missionp->completed = 1;
|
|
|
|
// flags
|
|
missionp->flags = cfread_int(cfp);
|
|
|
|
// goals
|
|
missionp->num_goals = cfread_int(cfp);
|
|
|
|
if (missionp->num_goals > 0) {
|
|
missionp->goals = (mgoal *) vm_malloc( missionp->num_goals * sizeof(mgoal) );
|
|
Verify( missionp->goals != NULL );
|
|
|
|
memset( missionp->goals, 0, missionp->num_goals * sizeof(mgoal) );
|
|
|
|
for (j = 0; j < missionp->num_goals; j++) {
|
|
cfread_string_len(missionp->goals[j].name, NAME_LENGTH, cfp);
|
|
missionp->goals[j].status = cfread_char(cfp);
|
|
}
|
|
}
|
|
|
|
// events
|
|
missionp->num_events = cfread_int(cfp);
|
|
|
|
if (missionp->num_events > 0) {
|
|
missionp->events = (mevent *) vm_malloc( missionp->num_events * sizeof(mevent) );
|
|
Verify( missionp->events != NULL );
|
|
|
|
memset( missionp->events, 0, missionp->num_events * sizeof(mevent) );
|
|
|
|
for (j = 0; j < missionp->num_events; j++) {
|
|
cfread_string_len(missionp->events[j].name, NAME_LENGTH, cfp);
|
|
missionp->events[j].status = cfread_char(cfp);
|
|
}
|
|
}
|
|
|
|
// variables
|
|
missionp->num_variables = cfread_int(cfp);
|
|
|
|
if (missionp->num_variables > 0) {
|
|
missionp->variables = (sexp_variable *) vm_malloc( missionp->num_variables * sizeof(sexp_variable) );
|
|
Verify( missionp->variables != NULL );
|
|
|
|
memset( missionp->variables, 0, missionp->num_variables * sizeof(sexp_variable) );
|
|
|
|
for (j = 0; j < missionp->num_variables; j++) {
|
|
missionp->variables[j].type = cfread_int(cfp);
|
|
cfread_string_len(missionp->variables[j].text, TOKEN_LENGTH, cfp);
|
|
cfread_string_len(missionp->variables[j].variable_name, TOKEN_LENGTH, cfp);
|
|
}
|
|
}
|
|
|
|
// scoring stats
|
|
missionp->stats.score = cfread_int(cfp);
|
|
missionp->stats.rank = cfread_int(cfp);
|
|
missionp->stats.assists = cfread_int(cfp);
|
|
missionp->stats.kill_count = cfread_int(cfp);
|
|
missionp->stats.kill_count_ok = cfread_int(cfp);
|
|
missionp->stats.bonehead_kills = cfread_int(cfp);
|
|
|
|
missionp->stats.p_shots_fired = cfread_uint(cfp);
|
|
missionp->stats.p_shots_hit = cfread_uint(cfp);
|
|
missionp->stats.p_bonehead_hits = cfread_uint(cfp);
|
|
|
|
missionp->stats.s_shots_fired = cfread_uint(cfp);
|
|
missionp->stats.s_shots_hit = cfread_uint(cfp);
|
|
missionp->stats.s_bonehead_hits = cfread_uint(cfp);
|
|
|
|
// ship kills (scoring)
|
|
list_size = (int)ship_list.size();
|
|
for (j = 0; j < list_size; j++) {
|
|
idx = cfread_int(cfp);
|
|
|
|
if (ship_list[j].index >= 0) {
|
|
missionp->stats.kills[ship_list[j].index] = idx;
|
|
}
|
|
}
|
|
|
|
// medals (scoring)
|
|
list_size = (int)medals_list.size();
|
|
for (j = 0; j < list_size; j++) {
|
|
idx = cfread_int(cfp);
|
|
|
|
if (medals_list[j].index >= 0) {
|
|
missionp->stats.medal_counts[medals_list[j].index] = idx;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void pilotfile::csg_write_missions()
|
|
{
|
|
int idx, j;
|
|
cmission *missionp;
|
|
|
|
startSection(Section::Missions);
|
|
|
|
for (idx = 0; idx < MAX_CAMPAIGN_MISSIONS; idx++) {
|
|
if (Campaign.missions[idx].completed) {
|
|
missionp = &Campaign.missions[idx];
|
|
|
|
cfwrite_int(idx, cfp);
|
|
|
|
// flags
|
|
cfwrite_int(missionp->flags, cfp);
|
|
|
|
// goals
|
|
cfwrite_int(missionp->num_goals, cfp);
|
|
|
|
for (j = 0; j < missionp->num_goals; j++) {
|
|
cfwrite_string_len(missionp->goals[j].name, cfp);
|
|
cfwrite_char(missionp->goals[j].status, cfp);
|
|
}
|
|
|
|
// events
|
|
cfwrite_int(missionp->num_events, cfp);
|
|
|
|
for (j = 0; j < missionp->num_events; j++) {
|
|
cfwrite_string_len(missionp->events[j].name, cfp);
|
|
cfwrite_char(missionp->events[j].status, cfp);
|
|
}
|
|
|
|
// variables
|
|
cfwrite_int(missionp->num_variables, cfp);
|
|
|
|
for (j = 0; j < missionp->num_variables; j++) {
|
|
cfwrite_int(missionp->variables[j].type, cfp);
|
|
cfwrite_string_len(missionp->variables[j].text, cfp);
|
|
cfwrite_string_len(missionp->variables[j].variable_name, cfp);
|
|
}
|
|
|
|
// scoring stats
|
|
cfwrite_int(missionp->stats.score, cfp);
|
|
cfwrite_int(missionp->stats.rank, cfp);
|
|
cfwrite_int(missionp->stats.assists, cfp);
|
|
cfwrite_int(missionp->stats.kill_count, cfp);
|
|
cfwrite_int(missionp->stats.kill_count_ok, cfp);
|
|
cfwrite_int(missionp->stats.bonehead_kills, cfp);
|
|
|
|
cfwrite_uint(missionp->stats.p_shots_fired, cfp);
|
|
cfwrite_uint(missionp->stats.p_shots_hit, cfp);
|
|
cfwrite_uint(missionp->stats.p_bonehead_hits, cfp);
|
|
|
|
cfwrite_uint(missionp->stats.s_shots_fired, cfp);
|
|
cfwrite_uint(missionp->stats.s_shots_hit, cfp);
|
|
cfwrite_uint(missionp->stats.s_bonehead_hits, cfp);
|
|
|
|
// ship kills (scoring)
|
|
for (j = 0; j < Num_ship_classes; j++) {
|
|
cfwrite_int(missionp->stats.kills[j], cfp);
|
|
}
|
|
|
|
// medals earned (scoring)
|
|
for (j = 0; j < Num_medals; j++) {
|
|
cfwrite_int(missionp->stats.medal_counts[j], cfp);
|
|
}
|
|
}
|
|
}
|
|
|
|
endSection();
|
|
}
|
|
|
|
void pilotfile::csg_read_techroom()
|
|
{
|
|
int idx, list_size = 0;
|
|
ubyte visible;
|
|
|
|
if ( !m_have_info ) {
|
|
throw "Techroom before Info!";
|
|
}
|
|
|
|
// visible ships
|
|
list_size = (int)ship_list.size();
|
|
for (idx = 0; idx < list_size; idx++) {
|
|
visible = cfread_ubyte(cfp);
|
|
|
|
if (visible) {
|
|
if (ship_list[idx].index >= 0) {
|
|
Ship_info[ship_list[idx].index].flags |= SIF_IN_TECH_DATABASE;
|
|
} else {
|
|
m_data_invalid = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// visible weapons
|
|
list_size = (int)weapon_list.size();
|
|
for (idx = 0; idx < list_size; idx++) {
|
|
visible = cfread_ubyte(cfp);
|
|
|
|
if (visible) {
|
|
if (weapon_list[idx].index >= 0) {
|
|
Weapon_info[weapon_list[idx].index].wi_flags |= WIF_IN_TECH_DATABASE;
|
|
} else {
|
|
m_data_invalid = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// visible intel entries
|
|
list_size = (int)intel_list.size();
|
|
for (idx = 0; idx < list_size; idx++) {
|
|
visible = cfread_ubyte(cfp);
|
|
|
|
if (visible) {
|
|
if (intel_list[idx].index >= 0) {
|
|
Intel_info[intel_list[idx].index].flags |= IIF_IN_TECH_DATABASE;
|
|
} else {
|
|
m_data_invalid = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
// if anything we need/use was missing then it should be considered fatal
|
|
if (m_data_invalid) {
|
|
throw "Invalid data for CSG!";
|
|
}
|
|
}
|
|
|
|
void pilotfile::csg_write_techroom()
|
|
{
|
|
int idx;
|
|
ubyte visible;
|
|
|
|
startSection(Section::Techroom);
|
|
|
|
// visible ships
|
|
for (idx = 0; idx < Num_ship_classes; idx++) {
|
|
// only visible if not in techroom by default
|
|
if ( (Ship_info[idx].flags & SIF_IN_TECH_DATABASE) && !(Ship_info[idx].flags2 & SIF2_DEFAULT_IN_TECH_DATABASE) ) {
|
|
visible = 1;
|
|
} else {
|
|
visible = 0;
|
|
}
|
|
|
|
cfwrite_ubyte(visible, cfp);
|
|
}
|
|
|
|
// visible weapons
|
|
for (idx = 0; idx < Num_weapon_types; idx++) {
|
|
// only visible if not in techroom by default
|
|
if ( (Weapon_info[idx].wi_flags & WIF_IN_TECH_DATABASE) && !(Weapon_info[idx].wi_flags2 & WIF2_DEFAULT_IN_TECH_DATABASE) ) {
|
|
visible = 1;
|
|
} else {
|
|
visible = 0;
|
|
}
|
|
|
|
cfwrite_ubyte(visible, cfp);
|
|
}
|
|
|
|
// visible intel entries
|
|
for (idx = 0; idx < Intel_info_size; idx++) {
|
|
// only visible if not in techroom by default
|
|
if ( (Intel_info[idx].flags & IIF_IN_TECH_DATABASE) && !(Intel_info[idx].flags & IIF_DEFAULT_IN_TECH_DATABASE) ) {
|
|
visible = 1;
|
|
} else {
|
|
visible = 0;
|
|
}
|
|
|
|
cfwrite_ubyte(visible, cfp);
|
|
}
|
|
|
|
endSection();
|
|
}
|
|
|
|
void pilotfile::csg_read_loadout()
|
|
{
|
|
int j, count, ship_idx = -1, wep_idx = -1;
|
|
uint idx, list_size = 0;
|
|
|
|
if ( !m_have_info ) {
|
|
throw "Loadout before Info!";
|
|
}
|
|
|
|
// base info
|
|
cfread_string_len(Player_loadout.filename, MAX_FILENAME_LEN, cfp);
|
|
cfread_string_len(Player_loadout.last_modified, DATE_TIME_LENGTH, cfp);
|
|
|
|
// ship pool
|
|
list_size = ship_list.size();
|
|
for (idx = 0; idx < list_size; idx++) {
|
|
count = cfread_int(cfp);
|
|
|
|
if (ship_list[idx].index >= 0) {
|
|
Player_loadout.ship_pool[ship_list[idx].index] = count;
|
|
}
|
|
}
|
|
|
|
// weapon pool
|
|
list_size = weapon_list.size();
|
|
for (idx = 0; idx < list_size; idx++) {
|
|
count = cfread_int(cfp);
|
|
|
|
if (weapon_list[idx].index >= 0) {
|
|
Player_loadout.weapon_pool[weapon_list[idx].index] = count;
|
|
}
|
|
}
|
|
|
|
// player ship loadout
|
|
list_size = (uint)cfread_ushort(cfp);
|
|
for (uint i = 0; i < list_size; i++) {
|
|
wss_unit *slot = NULL;
|
|
|
|
if (i < MAX_WSS_SLOTS) {
|
|
slot = &Player_loadout.unit_data[i];
|
|
}
|
|
|
|
// ship
|
|
ship_idx = cfread_int(cfp);
|
|
|
|
if ( (ship_idx >= (int)ship_list.size()) || (ship_idx < -1) ) { // on the casts, assume that ship & weapon lists will never exceed ~2 billion
|
|
mprintf(("CSG => Parse Warning: Invalid value for ship index (%d), emptying slot.\n", ship_idx));
|
|
ship_idx = -1;
|
|
}
|
|
|
|
if (slot) {
|
|
if (ship_idx == -1) { // -1 means no ship in this slot
|
|
slot->ship_class = -1;
|
|
} else {
|
|
slot->ship_class = ship_list[ship_idx].index;
|
|
}
|
|
}
|
|
|
|
// primary weapons
|
|
count = cfread_int(cfp);
|
|
|
|
for (j = 0; j < count; j++) {
|
|
wep_idx = cfread_int(cfp);
|
|
|
|
if ( (wep_idx >= (int)weapon_list.size()) || (wep_idx < -1) ) {
|
|
mprintf(("CSG => Parse Warning: Invalid value for primary weapon index (%d), emptying slot.\n", wep_idx));
|
|
wep_idx = -1;
|
|
}
|
|
|
|
|
|
if ( slot && (j < MAX_SHIP_PRIMARY_BANKS) ) {
|
|
if (wep_idx == -1) { // -1 means no weapon in this slot
|
|
slot->wep[j] = -1;
|
|
} else {
|
|
slot->wep[j] = weapon_list[wep_idx].index;
|
|
}
|
|
}
|
|
|
|
idx = cfread_int(cfp);
|
|
|
|
if ( slot && (j < MAX_SHIP_PRIMARY_BANKS) ) {
|
|
slot->wep_count[j] = idx;
|
|
}
|
|
}
|
|
|
|
// secondary weapons
|
|
count = cfread_int(cfp);
|
|
|
|
for (j = 0; j < count; j++) {
|
|
wep_idx = cfread_int(cfp);
|
|
|
|
if ( (wep_idx >= (int)weapon_list.size()) || (wep_idx < -1) ) {
|
|
mprintf(("CSG => Parse Warning: Invalid value for secondary weapon index (%d), emptying slot.\n", wep_idx));
|
|
wep_idx = -1;
|
|
}
|
|
|
|
if ( slot && (j < MAX_SHIP_SECONDARY_BANKS) ) {
|
|
if (wep_idx == -1) { // -1 means no weapon in this slot
|
|
slot->wep[j+MAX_SHIP_PRIMARY_BANKS] = -1;
|
|
} else {
|
|
slot->wep[j+MAX_SHIP_PRIMARY_BANKS] = weapon_list[wep_idx].index;
|
|
}
|
|
}
|
|
|
|
idx = cfread_int(cfp);
|
|
|
|
if ( slot && (j < MAX_SHIP_SECONDARY_BANKS) ) {
|
|
slot->wep_count[j+MAX_SHIP_PRIMARY_BANKS] = idx;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void pilotfile::csg_write_loadout()
|
|
{
|
|
int idx, j;
|
|
|
|
startSection(Section::Loadout);
|
|
|
|
// base info
|
|
cfwrite_string_len(Player_loadout.filename, cfp);
|
|
cfwrite_string_len(Player_loadout.last_modified, cfp);
|
|
|
|
// ship pool
|
|
for (idx = 0; idx < Num_ship_classes; idx++) {
|
|
cfwrite_int(Player_loadout.ship_pool[idx], cfp);
|
|
}
|
|
|
|
// weapon pool
|
|
for (idx = 0; idx < Num_weapon_types; idx++) {
|
|
cfwrite_int(Player_loadout.weapon_pool[idx], cfp);
|
|
}
|
|
|
|
// play ship loadout
|
|
cfwrite_ushort(MAX_WSS_SLOTS, cfp);
|
|
|
|
for (idx = 0; idx < MAX_WSS_SLOTS; idx++) {
|
|
wss_unit *slot = &Player_loadout.unit_data[idx];
|
|
|
|
// ship
|
|
cfwrite_int(slot->ship_class, cfp);
|
|
|
|
// primary weapons
|
|
cfwrite_int(MAX_SHIP_PRIMARY_BANKS, cfp);
|
|
|
|
for (j = 0; j < MAX_SHIP_PRIMARY_BANKS; j++) {
|
|
cfwrite_int(slot->wep[j], cfp);
|
|
cfwrite_int(slot->wep_count[j], cfp);
|
|
}
|
|
|
|
// secondary weapons
|
|
cfwrite_int(MAX_SHIP_SECONDARY_BANKS, cfp);
|
|
|
|
for (j = 0; j < MAX_SHIP_SECONDARY_BANKS; j++) {
|
|
cfwrite_int(slot->wep[j+MAX_SHIP_PRIMARY_BANKS], cfp);
|
|
cfwrite_int(slot->wep_count[j+MAX_SHIP_PRIMARY_BANKS], cfp);
|
|
}
|
|
}
|
|
|
|
endSection();
|
|
}
|
|
|
|
void pilotfile::csg_read_stats()
|
|
{
|
|
int idx, list_size = 0;
|
|
int count;
|
|
|
|
if ( !m_have_info ) {
|
|
throw "Stats before Info!";
|
|
}
|
|
|
|
// scoring stats
|
|
p->stats.score = cfread_int(cfp);
|
|
p->stats.rank = cfread_int(cfp);
|
|
p->stats.assists = cfread_int(cfp);
|
|
p->stats.kill_count = cfread_int(cfp);
|
|
p->stats.kill_count_ok = cfread_int(cfp);
|
|
p->stats.bonehead_kills = cfread_int(cfp);
|
|
|
|
p->stats.p_shots_fired = cfread_uint(cfp);
|
|
p->stats.p_shots_hit = cfread_uint(cfp);
|
|
p->stats.p_bonehead_hits = cfread_uint(cfp);
|
|
|
|
p->stats.s_shots_fired = cfread_uint(cfp);
|
|
p->stats.s_shots_hit = cfread_uint(cfp);
|
|
p->stats.s_bonehead_hits = cfread_uint(cfp);
|
|
|
|
p->stats.flight_time = cfread_uint(cfp);
|
|
p->stats.missions_flown = cfread_uint(cfp);
|
|
p->stats.last_flown = (_fs_time_t)cfread_int(cfp);
|
|
p->stats.last_backup = (_fs_time_t)cfread_int(cfp);
|
|
|
|
// ship kills (scoring)
|
|
list_size = (int)ship_list.size();
|
|
for (idx = 0; idx < list_size; idx++) {
|
|
count = cfread_int(cfp);
|
|
|
|
if (ship_list[idx].index >= 0) {
|
|
p->stats.kills[ship_list[idx].index] = count;
|
|
}
|
|
}
|
|
|
|
// medals earned (scoring)
|
|
list_size = (int)medals_list.size();
|
|
for (idx = 0; idx < list_size; idx++) {
|
|
count = cfread_int(cfp);
|
|
|
|
if (medals_list[idx].index >= 0) {
|
|
p->stats.medal_counts[medals_list[idx].index] = count;
|
|
}
|
|
}
|
|
}
|
|
|
|
void pilotfile::csg_write_stats()
|
|
{
|
|
int idx;
|
|
|
|
startSection(Section::Scoring);
|
|
|
|
// scoring stats
|
|
cfwrite_int(p->stats.score, cfp);
|
|
cfwrite_int(p->stats.rank, cfp);
|
|
cfwrite_int(p->stats.assists, cfp);
|
|
cfwrite_int(p->stats.kill_count, cfp);
|
|
cfwrite_int(p->stats.kill_count_ok, cfp);
|
|
cfwrite_int(p->stats.bonehead_kills, cfp);
|
|
|
|
cfwrite_uint(p->stats.p_shots_fired, cfp);
|
|
cfwrite_uint(p->stats.p_shots_hit, cfp);
|
|
cfwrite_uint(p->stats.p_bonehead_hits, cfp);
|
|
|
|
cfwrite_uint(p->stats.s_shots_fired, cfp);
|
|
cfwrite_uint(p->stats.s_shots_hit, cfp);
|
|
cfwrite_uint(p->stats.s_bonehead_hits, cfp);
|
|
|
|
cfwrite_uint(p->stats.flight_time, cfp);
|
|
cfwrite_uint(p->stats.missions_flown, cfp);
|
|
cfwrite_int((int)p->stats.last_flown, cfp);
|
|
cfwrite_int((int)p->stats.last_backup, cfp);
|
|
|
|
// ship kills (scoring)
|
|
for (idx = 0; idx < Num_ship_classes; idx++) {
|
|
cfwrite_int(p->stats.kills[idx], cfp);
|
|
}
|
|
|
|
// medals earned (scoring)
|
|
for (idx = 0; idx < Num_medals; idx++) {
|
|
cfwrite_int(p->stats.medal_counts[idx], cfp);
|
|
}
|
|
|
|
endSection();
|
|
}
|
|
|
|
void pilotfile::csg_read_redalert()
|
|
{
|
|
int idx, i, j, list_size = 0;
|
|
int count;
|
|
char t_string[MAX_FILENAME_LEN+NAME_LENGTH+1] = { '\0' };
|
|
float hit;
|
|
wep_t weapons;
|
|
|
|
if ( !m_have_info ) {
|
|
throw "RedAlert before Info!";
|
|
}
|
|
|
|
list_size = cfread_int(cfp);
|
|
|
|
if (list_size <= 0) {
|
|
return;
|
|
}
|
|
|
|
cfread_string_len(t_string, MAX_FILENAME_LEN, cfp);
|
|
|
|
Red_alert_precursor_mission = t_string;
|
|
|
|
for (idx = 0; idx < list_size; idx++) {
|
|
red_alert_ship_status ras;
|
|
|
|
cfread_string_len(t_string, NAME_LENGTH, cfp);
|
|
ras.name = t_string;
|
|
|
|
ras.hull = cfread_float(cfp);
|
|
|
|
// ship class, index into ship_list[]
|
|
i = cfread_int(cfp);
|
|
if ( (i >= (int)ship_list.size()) || (i < RED_ALERT_LOWEST_VALID_SHIP_CLASS) ) {
|
|
mprintf(("CSG => Parse Warning: Invalid value for red alert ship index (%d), emptying slot.\n", i));
|
|
ras.ship_class = RED_ALERT_DESTROYED_SHIP_CLASS;
|
|
} else if ( (i < 0 ) && (i >= RED_ALERT_LOWEST_VALID_SHIP_CLASS) ) { // ship destroyed/exited
|
|
ras.ship_class = i;
|
|
} else {
|
|
ras.ship_class = ship_list[i].index;
|
|
}
|
|
|
|
// subsystem hits
|
|
count = cfread_int(cfp);
|
|
|
|
for (j = 0; j < count; j++) {
|
|
hit = cfread_float(cfp);
|
|
ras.subsys_current_hits.push_back( hit );
|
|
}
|
|
|
|
// subsystem aggregate hits
|
|
count = cfread_int(cfp);
|
|
|
|
for (j = 0; j < count; j++) {
|
|
hit = cfread_float(cfp);
|
|
ras.subsys_aggregate_current_hits.push_back( hit );
|
|
}
|
|
|
|
// primary weapon loadout and status
|
|
count = cfread_int(cfp);
|
|
|
|
for (j = 0; j < count; j++) {
|
|
i = cfread_int(cfp);
|
|
weapons.index = weapon_list[i].index;
|
|
weapons.count = cfread_int(cfp);
|
|
|
|
// triggering this means something is really fubar
|
|
if (weapons.index < 0) {
|
|
continue;
|
|
}
|
|
|
|
ras.primary_weapons.push_back( weapons );
|
|
}
|
|
|
|
// secondary weapon loadout and status
|
|
count = cfread_int(cfp);
|
|
|
|
for (j = 0; j < count; j++) {
|
|
i = cfread_int(cfp);
|
|
weapons.index = weapon_list[i].index;
|
|
weapons.count = cfread_int(cfp);
|
|
|
|
// triggering this means something is really fubar
|
|
if (weapons.index < 0) {
|
|
continue;
|
|
}
|
|
|
|
ras.secondary_weapons.push_back( weapons );
|
|
}
|
|
|
|
// this is quite likely a *bad* thing if it doesn't happen
|
|
if (ras.ship_class >= RED_ALERT_LOWEST_VALID_SHIP_CLASS) {
|
|
Red_alert_wingman_status.push_back( ras );
|
|
}
|
|
}
|
|
}
|
|
|
|
void pilotfile::csg_write_redalert()
|
|
{
|
|
int idx, j, list_size = 0;
|
|
int count;
|
|
red_alert_ship_status *ras;
|
|
|
|
startSection(Section::RedAlert);
|
|
|
|
list_size = (int)Red_alert_wingman_status.size();
|
|
|
|
cfwrite_int(list_size, cfp);
|
|
|
|
if (list_size) {
|
|
cfwrite_string_len(Red_alert_precursor_mission.c_str(), cfp);
|
|
|
|
for (idx = 0; idx < list_size; idx++) {
|
|
ras = &Red_alert_wingman_status[idx];
|
|
|
|
cfwrite_string_len(ras->name.c_str(), cfp);
|
|
|
|
cfwrite_float(ras->hull, cfp);
|
|
|
|
// ship class, should be index into ship_list[] on load
|
|
cfwrite_int(ras->ship_class, cfp);
|
|
|
|
// subsystem hits
|
|
count = (int)ras->subsys_current_hits.size();
|
|
cfwrite_int(count, cfp);
|
|
|
|
for (j = 0; j < count; j++) {
|
|
cfwrite_float(ras->subsys_current_hits[j], cfp);
|
|
}
|
|
|
|
// subsystem aggregate hits
|
|
count = (int)ras->subsys_aggregate_current_hits.size();
|
|
cfwrite_int(count, cfp);
|
|
|
|
for (j = 0; j < count; j++) {
|
|
cfwrite_float(ras->subsys_aggregate_current_hits[j], cfp);
|
|
}
|
|
|
|
// primary weapon loadout and status
|
|
count = (int)ras->primary_weapons.size();
|
|
cfwrite_int(count, cfp);
|
|
|
|
for (j = 0; j < count; j++) {
|
|
cfwrite_int(ras->primary_weapons[j].index, cfp);
|
|
cfwrite_int(ras->primary_weapons[j].count, cfp);
|
|
}
|
|
|
|
// secondary weapon loadout and status
|
|
count = (int)ras->secondary_weapons.size();
|
|
cfwrite_int(count, cfp);
|
|
|
|
for (j = 0; j < count; j++) {
|
|
cfwrite_int(ras->secondary_weapons[j].index, cfp);
|
|
cfwrite_int(ras->secondary_weapons[j].count, cfp);
|
|
}
|
|
}
|
|
}
|
|
|
|
endSection();
|
|
}
|
|
|
|
void pilotfile::csg_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_record_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::csg_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::csg_read_variables()
|
|
{
|
|
int idx;
|
|
|
|
Campaign.num_variables = cfread_int(cfp);
|
|
|
|
if (Campaign.num_variables > 0) {
|
|
Campaign.variables = (sexp_variable *) vm_malloc( Campaign.num_variables * sizeof(sexp_variable) );
|
|
Verify( Campaign.variables != NULL );
|
|
|
|
memset( Campaign.variables, 0, Campaign.num_variables * sizeof(sexp_variable) );
|
|
|
|
for (idx = 0; idx < Campaign.num_variables; idx++) {
|
|
Campaign.variables[idx].type = cfread_int(cfp);
|
|
cfread_string_len(Campaign.variables[idx].text, TOKEN_LENGTH, cfp);
|
|
cfread_string_len(Campaign.variables[idx].variable_name, TOKEN_LENGTH, cfp);
|
|
}
|
|
}
|
|
|
|
if (csg_ver < 4) { // CSG files before version 4 don't have a Red Alert set of CPVs to load, so just copy the regular set.
|
|
Campaign.redalert_num_variables = Campaign.num_variables;
|
|
Campaign.redalert_variables = (sexp_variable *) vm_malloc( Campaign.redalert_num_variables * sizeof(sexp_variable) );
|
|
Verify( Campaign.redalert_variables != NULL);
|
|
|
|
memcpy( Campaign.redalert_variables, Campaign.variables, Campaign.num_variables * sizeof(sexp_variable));
|
|
} else {
|
|
Campaign.redalert_num_variables = cfread_int(cfp);
|
|
|
|
if (Campaign.redalert_num_variables > 0) {
|
|
Campaign.redalert_variables = (sexp_variable *) vm_malloc( Campaign.redalert_num_variables * sizeof(sexp_variable) );
|
|
Verify( Campaign.redalert_variables != NULL );
|
|
|
|
memset( Campaign.redalert_variables, 0, Campaign.redalert_num_variables * sizeof(sexp_variable) );
|
|
|
|
for (idx = 0; idx < Campaign.redalert_num_variables; idx++) {
|
|
Campaign.redalert_variables[idx].type = cfread_int(cfp);
|
|
cfread_string_len(Campaign.redalert_variables[idx].text, TOKEN_LENGTH, cfp);
|
|
cfread_string_len(Campaign.redalert_variables[idx].variable_name, TOKEN_LENGTH, cfp);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void pilotfile::csg_write_variables()
|
|
{
|
|
int idx;
|
|
|
|
startSection(Section::Variables);
|
|
|
|
cfwrite_int(Campaign.num_variables, cfp);
|
|
|
|
for (idx = 0; idx < Campaign.num_variables; idx++) {
|
|
cfwrite_int(Campaign.variables[idx].type, cfp);
|
|
cfwrite_string_len(Campaign.variables[idx].text, cfp);
|
|
cfwrite_string_len(Campaign.variables[idx].variable_name, cfp);
|
|
}
|
|
|
|
cfwrite_int(Campaign.redalert_num_variables, cfp);
|
|
|
|
for (idx = 0; idx < Campaign.redalert_num_variables; idx++) {
|
|
cfwrite_int(Campaign.redalert_variables[idx].type, cfp);
|
|
cfwrite_string_len(Campaign.redalert_variables[idx].text, cfp);
|
|
cfwrite_string_len(Campaign.redalert_variables[idx].variable_name, cfp);
|
|
}
|
|
|
|
endSection();
|
|
}
|
|
|
|
void pilotfile::csg_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);
|
|
|
|
if (csg_ver < 3) {
|
|
// detail
|
|
int dummy __attribute__((__unused__)) = cfread_int(cfp);
|
|
dummy = cfread_int(cfp);
|
|
dummy = cfread_int(cfp);
|
|
dummy = cfread_int(cfp);
|
|
dummy = cfread_int(cfp);
|
|
dummy = cfread_int(cfp);
|
|
dummy = cfread_int(cfp);
|
|
dummy = cfread_int(cfp);
|
|
dummy = cfread_int(cfp);
|
|
dummy = cfread_int(cfp);
|
|
dummy = cfread_int(cfp);
|
|
dummy = cfread_int(cfp);
|
|
}
|
|
}
|
|
|
|
void pilotfile::csg_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);
|
|
|
|
endSection();
|
|
}
|
|
|
|
void pilotfile::csg_read_controls()
|
|
{
|
|
int idx, list_size;
|
|
short id1, id2, id3 __attribute__((__unused__));
|
|
|
|
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;
|
|
}
|
|
}
|
|
}
|
|
|
|
void pilotfile::csg_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);
|
|
}
|
|
|
|
endSection();
|
|
}
|
|
|
|
void pilotfile::csg_read_cutscenes() {
|
|
size_t list_size = cfread_uint(cfp);
|
|
|
|
for(size_t i = 0; i < list_size; i++) {
|
|
char tempFilename[MAX_FILENAME_LEN];
|
|
|
|
cfread_string_len(tempFilename, MAX_FILENAME_LEN, cfp);
|
|
cutscene_mark_viewable(tempFilename);
|
|
}
|
|
}
|
|
|
|
void pilotfile::csg_write_cutscenes() {
|
|
SCP_vector<cutscene_info>::iterator cut;
|
|
|
|
startSection(Section::Cutscenes);
|
|
|
|
size_t viewableScenes = 0;
|
|
for(cut = Cutscenes.begin(); cut != Cutscenes.end(); ++cut) {
|
|
if(cut->viewable)
|
|
viewableScenes ++;
|
|
}
|
|
cfwrite_uint(viewableScenes, cfp);
|
|
|
|
for(cut = Cutscenes.begin(); cut != Cutscenes.end(); ++cut) {
|
|
if(cut->viewable)
|
|
cfwrite_string_len(cut->filename, cfp);
|
|
}
|
|
|
|
endSection();
|
|
}
|
|
|
|
/*
|
|
* Only used for quick start missions
|
|
*/
|
|
void pilotfile::csg_read_lastmissions()
|
|
{
|
|
int i;
|
|
|
|
// restore list of most recently played missions
|
|
Num_recent_missions = cfread_int( cfp );
|
|
Assert(Num_recent_missions <= MAX_RECENT_MISSIONS);
|
|
for ( i = 0; i < Num_recent_missions; i++ ) {
|
|
char *cp;
|
|
|
|
cfread_string_len( Recent_missions[i], MAX_FILENAME_LEN, cfp);
|
|
// Remove the extension (safety check: shouldn't exist anyway)
|
|
cp = strchr(Recent_missions[i], '.');
|
|
if (cp)
|
|
*cp = 0;
|
|
}
|
|
}
|
|
|
|
/*
|
|
* Only used for quick start missions
|
|
*/
|
|
|
|
void pilotfile::csg_write_lastmissions()
|
|
{
|
|
int i;
|
|
|
|
startSection(Section::LastMissions);
|
|
|
|
// store list of most recently played missions
|
|
cfwrite_int(Num_recent_missions, cfp);
|
|
for (i=0; i<Num_recent_missions; i++) {
|
|
cfwrite_string_len(Recent_missions[i], cfp);
|
|
}
|
|
|
|
endSection();
|
|
}
|
|
|
|
void pilotfile::csg_reset_data()
|
|
{
|
|
int idx;
|
|
cmission *missionp;
|
|
|
|
// internals
|
|
m_have_flags = false;
|
|
m_have_info = false;
|
|
|
|
m_data_invalid = false;
|
|
|
|
// init stats
|
|
p->stats.init();
|
|
|
|
// zero out allowed ships/weapons
|
|
memset(Campaign.ships_allowed, 0, sizeof(Campaign.ships_allowed));
|
|
memset(Campaign.weapons_allowed, 0, sizeof(Campaign.weapons_allowed));
|
|
|
|
// reset campaign status
|
|
Campaign.prev_mission = -1;
|
|
Campaign.next_mission = -1;
|
|
Campaign.num_missions_completed = 0;
|
|
|
|
// techroom reset
|
|
tech_reset_to_default();
|
|
|
|
// clear out variables
|
|
if (Campaign.variables) {
|
|
Campaign.num_variables = 0;
|
|
vm_free(Campaign.variables);
|
|
Campaign.variables = NULL;
|
|
}
|
|
|
|
if (Campaign.redalert_variables) {
|
|
Campaign.redalert_num_variables = 0;
|
|
vm_free(Campaign.redalert_variables);
|
|
Campaign.redalert_variables = NULL;
|
|
}
|
|
|
|
// clear red alert data
|
|
Red_alert_wingman_status.clear();
|
|
|
|
// clear out mission stuff
|
|
for (idx = 0; idx < MAX_CAMPAIGN_MISSIONS; idx++) {
|
|
missionp = &Campaign.missions[idx];
|
|
|
|
if (missionp->goals) {
|
|
missionp->num_goals = 0;
|
|
vm_free(missionp->goals);
|
|
missionp->goals = NULL;
|
|
}
|
|
|
|
if (missionp->events) {
|
|
missionp->num_events = 0;
|
|
vm_free(missionp->events);
|
|
missionp->events = NULL;
|
|
}
|
|
|
|
if (missionp->variables) {
|
|
missionp->num_variables = 0;
|
|
vm_free(missionp->variables);
|
|
missionp->variables = NULL;
|
|
}
|
|
|
|
missionp->stats.init();
|
|
}
|
|
}
|
|
|
|
void pilotfile::csg_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_savefile(const char *campaign)
|
|
{
|
|
char base[_MAX_FNAME] = { '\0' };
|
|
std::ostringstream buf;
|
|
|
|
if (Game_mode & GM_MULTIPLAYER) {
|
|
return false;
|
|
}
|
|
|
|
if ( (campaign == NULL) || !strlen(campaign) ) {
|
|
return false;
|
|
}
|
|
|
|
// set player ptr first thing
|
|
Assert( (Player_num >= 0) && (Player_num < MAX_PLAYERS) );
|
|
p = &Players[Player_num];
|
|
|
|
// build up filename for the savefile...
|
|
_splitpath((char*)campaign, NULL, NULL, base, NULL);
|
|
|
|
buf << p->callsign << "." << base << ".csg";
|
|
|
|
filename = buf.str().c_str();
|
|
|
|
// if campaign file doesn't exist, abort so we don't load irrelevant data
|
|
buf.str(std::string());
|
|
buf << base << FS_CAMPAIGN_FILE_EXT;
|
|
if ( !cf_exists_full((char*)buf.str().c_str(), CF_TYPE_MISSIONS) ) {
|
|
mprintf(("CSG => Unable to find campaign file '%s'!\n", buf.str().c_str()));
|
|
return false;
|
|
}
|
|
|
|
// we need to reset this early, in case open fails and we need to create
|
|
m_data_invalid = false;
|
|
|
|
// open it, hopefully...
|
|
cfp = cfopen((char*)filename.c_str(), "rb", CFILE_NORMAL, CF_TYPE_PLAYERS);
|
|
|
|
if ( !cfp ) {
|
|
mprintf(("CSG => Unable to open '%s' for reading!\n", filename.c_str()));
|
|
return false;
|
|
}
|
|
|
|
unsigned int csg_id = cfread_uint(cfp);
|
|
|
|
if (csg_id != CSG_FILE_ID) {
|
|
mprintf(("CSG => Invalid header id for '%s'!\n", filename.c_str()));
|
|
csg_close();
|
|
return false;
|
|
}
|
|
|
|
// version, now used
|
|
csg_ver = cfread_ubyte(cfp);
|
|
|
|
mprintf(("CSG => Loading '%s' with version %d...\n", filename.c_str(), (int)csg_ver));
|
|
|
|
csg_reset_data();
|
|
|
|
// the point of all this: read in the CSG 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(("CSG => Parsing: Flags...\n"));
|
|
m_have_flags = true;
|
|
csg_read_flags();
|
|
break;
|
|
|
|
case Section::Info:
|
|
mprintf(("CSG => Parsing: Info...\n"));
|
|
m_have_info = true;
|
|
csg_read_info();
|
|
break;
|
|
|
|
case Section::Variables:
|
|
mprintf(("CSG => Parsing: Variables...\n"));
|
|
csg_read_variables();
|
|
break;
|
|
|
|
case Section::HUD:
|
|
mprintf(("CSG => Parsing: HUD...\n"));
|
|
csg_read_hud();
|
|
break;
|
|
|
|
case Section::RedAlert:
|
|
mprintf(("CSG => Parsing: RedAlert...\n"));
|
|
csg_read_redalert();
|
|
break;
|
|
|
|
case Section::Scoring:
|
|
mprintf(("CSG => Parsing: Scoring...\n"));
|
|
csg_read_stats();
|
|
break;
|
|
|
|
case Section::Loadout:
|
|
mprintf(("CSG => Parsing: Loadout...\n"));
|
|
csg_read_loadout();
|
|
break;
|
|
|
|
case Section::Techroom:
|
|
mprintf(("CSG => Parsing: Techroom...\n"));
|
|
csg_read_techroom();
|
|
break;
|
|
|
|
case Section::Missions:
|
|
mprintf(("CSG => Parsing: Missions...\n"));
|
|
csg_read_missions();
|
|
break;
|
|
|
|
case Section::Settings:
|
|
mprintf(("CSG => Parsing: Settings...\n"));
|
|
csg_read_settings();
|
|
break;
|
|
|
|
case Section::Controls:
|
|
mprintf(("CSG => Parsing: Controls...\n"));
|
|
csg_read_controls();
|
|
break;
|
|
|
|
case Section::Cutscenes:
|
|
mprintf(("CSG => Parsing: Cutscenes...\n"));
|
|
csg_read_cutscenes();
|
|
break;
|
|
|
|
case Section::LastMissions:
|
|
mprintf(("CSG => Parsing: Last Missions...\n"));
|
|
csg_read_lastmissions();
|
|
break;
|
|
|
|
default:
|
|
mprintf(("CSG => 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(("CSG => Warning: (0x%04x) %s\n", section_id, msg.what()));
|
|
} catch (const char *err) {
|
|
mprintf(("CSG => ERROR: %s\n", err));
|
|
csg_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(("CSG => Warning: (0x%04x) Short read, information may have been lost!\n", section_id));
|
|
cfseek(cfp, offset_pos, CF_SEEK_CUR);
|
|
}
|
|
}
|
|
|
|
// if the campaign (for whatever reason) doesn't have a squad image, use the multi one
|
|
if (p->s_squad_filename[0] == '\0') {
|
|
strcpy_s(p->s_squad_filename, p->m_squad_filename);
|
|
}
|
|
player_set_squad_bitmap(p, p->s_squad_filename, false);
|
|
|
|
mprintf(("CSG => Loading complete!\n"));
|
|
|
|
// cleanup and return
|
|
csg_close();
|
|
|
|
return true;
|
|
}
|
|
|
|
bool pilotfile::save_savefile()
|
|
{
|
|
char base[_MAX_FNAME] = { '\0' };
|
|
std::ostringstream buf;
|
|
|
|
if (Game_mode & GM_MULTIPLAYER) {
|
|
return false;
|
|
}
|
|
|
|
// set player ptr first thing
|
|
Assert( (Player_num >= 0) && (Player_num < MAX_PLAYERS) );
|
|
p = &Players[Player_num];
|
|
|
|
if ( !strlen(Campaign.filename) ) {
|
|
return false;
|
|
}
|
|
|
|
// build up filename for the savefile...
|
|
_splitpath(Campaign.filename, NULL, NULL, base, NULL);
|
|
|
|
buf << p->callsign << "." << base << ".csg";
|
|
|
|
filename = buf.str().c_str();
|
|
|
|
// make sure that we can actually save this safely
|
|
if (m_data_invalid) {
|
|
mprintf(("CSG => Skipping save of '%s' due to invalid data check!\n", filename.c_str()));
|
|
return false;
|
|
}
|
|
|
|
// validate the number of red alert entries
|
|
// assertion before writing so that we don't corrupt the .csg by asserting halfway through writing
|
|
// assertion should also prevent loss of major campaign progress
|
|
// i.e. lose one mission, not several missions worth (in theory)
|
|
Assertion(Red_alert_wingman_status.size() <= MAX_SHIPS, "Invalid number of Red_alert_wingman_status entries: %u\n", Red_alert_wingman_status.size());
|
|
|
|
// open it, hopefully...
|
|
cfp = cfopen((char*)filename.c_str(), "wb", CFILE_NORMAL, CF_TYPE_PLAYERS);
|
|
|
|
if ( !cfp ) {
|
|
mprintf(("CSG => Unable to open '%s' for saving!\n", filename.c_str()));
|
|
return false;
|
|
}
|
|
|
|
// header and version
|
|
cfwrite_int(CSG_FILE_ID, cfp);
|
|
cfwrite_ubyte(CSG_VERSION, cfp);
|
|
|
|
mprintf(("CSG => Saving '%s' with version %d...\n", filename.c_str(), (int)CSG_VERSION));
|
|
|
|
// flags and info sections go first
|
|
mprintf(("CSG => Saving: Flags...\n"));
|
|
csg_write_flags();
|
|
mprintf(("CSG => Saving: Info...\n"));
|
|
csg_write_info();
|
|
|
|
// everything else is next, not order specific
|
|
mprintf(("CSG => Saving: Missions...\n"));
|
|
csg_write_missions();
|
|
mprintf(("CSG => Saving: Techroom...\n"));
|
|
csg_write_techroom();
|
|
mprintf(("CSG => Saving: Loadout...\n"));
|
|
csg_write_loadout();
|
|
mprintf(("CSG => Saving: Scoring...\n"));
|
|
csg_write_stats();
|
|
mprintf(("CSG => Saving: RedAlert...\n"));
|
|
csg_write_redalert();
|
|
mprintf(("CSG => Saving: HUD...\n"));
|
|
csg_write_hud();
|
|
mprintf(("CSG => Saving: Variables...\n"));
|
|
csg_write_variables();
|
|
mprintf(("CSG => Saving: Settings...\n"));
|
|
csg_write_settings();
|
|
mprintf(("CSG => Saving: Controls...\n"));
|
|
csg_write_controls();
|
|
mprintf(("CSG => Saving: Cutscenes...\n"));
|
|
csg_write_cutscenes();
|
|
mprintf(("CSG => Saving: Last Missions...\n"));
|
|
csg_write_lastmissions();
|
|
|
|
// Done!
|
|
mprintf(("CSG => Saving complete!\n"));
|
|
|
|
csg_close();
|
|
|
|
return true;
|
|
}
|
|
|
|
/*
|
|
* get_csg_rank: this function is called from plr.cpp & is
|
|
* tightly linked with pilotfile::verify()
|
|
*/
|
|
bool pilotfile::get_csg_rank(int *rank)
|
|
{
|
|
player t_csg;
|
|
|
|
// set player ptr first thing
|
|
p = &t_csg;
|
|
|
|
// filename has already been set
|
|
cfp = cfopen((char*)filename.c_str(), "rb", CFILE_NORMAL, CF_TYPE_PLAYERS);
|
|
|
|
if ( !cfp ) {
|
|
mprintf(("CSG => Unable to open '%s'!\n", filename.c_str()));
|
|
return false;
|
|
}
|
|
|
|
unsigned int csg_id = cfread_uint(cfp);
|
|
|
|
if (csg_id != CSG_FILE_ID) {
|
|
mprintf(("CSG => Invalid header id for '%s'!\n", filename.c_str()));
|
|
csg_close();
|
|
return false;
|
|
}
|
|
|
|
// version, now used
|
|
csg_ver = cfread_ubyte(cfp);
|
|
|
|
mprintf(("CSG => Get Rank from '%s' with version %d...\n", filename.c_str(), (int)csg_ver));
|
|
|
|
// the point of all this: read in the CSG 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);
|
|
size_t offset_pos;
|
|
|
|
// safety, to help protect against long reads
|
|
cf_set_max_read_len(cfp, section_size);
|
|
|
|
try {
|
|
switch (section_id) {
|
|
case Section::Flags:
|
|
mprintf(("CSG => Parsing: Flags...\n"));
|
|
m_have_flags = true;
|
|
csg_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(("CSG => (0x%04x) %s\n", section_id, msg.what()));
|
|
} catch (const char *err) {
|
|
mprintf(("CSG => ERROR: %s\n", err));
|
|
csg_close();
|
|
return false;
|
|
}
|
|
|
|
// reset safety catch
|
|
cf_set_max_read_len(cfp, 0);
|
|
|
|
// skip to next section (if not already there)
|
|
offset_pos = (start_pos + section_size) - cftell(cfp);
|
|
|
|
if (offset_pos) {
|
|
mprintf(("CSG => Warning: (0x%04x) Short read, information may have been lost!\n", section_id));
|
|
cfseek(cfp, offset_pos, CF_SEEK_CUR);
|
|
}
|
|
}
|
|
|
|
// this is what we came for...
|
|
*rank = p->stats.rank;
|
|
|
|
mprintf(("CSG => Get Rank complete!\n"));
|
|
|
|
// cleanup & return
|
|
csg_close();
|
|
|
|
return true;
|
|
}
|