mirror of
https://github.com/scp-fs2open/fs2open.github.com.git
synced 2026-11-10 04:31:34 -10:00
* add subfolder support to cfile * sort files in VP archives before indexing them * fix indexing of sub-subfolders * remove unused 8B22K & 16B11K sound path types * add more items to skip subfolder scanning * fix windows build issues * fix unit tests by allowing 0-byte files to be indexed * fix file indexing on windows * add test for cfile subfolders * remove specific 'markup' and 'css' interface subfolders They aren't directly referenced anywhere by pathtype and the new subfolder code will continue to index and load files from them * add player binds to list of always-root locations * remove file-based localization support An alternative is left in place for old fonts, as that was the only place it appeared to be used. This method of file localization conflicts with subfolder support however so if it's needed in the future then changes to the subfolder code will be necessary. * add subfolder support to cf_find_file_location_ext() * add subfolder tests for cf_find_file_location*() * add shlwapi to needed win32 libs * small bit of cleanup * add subfolder support to cf_get_file_list() * add tests for cfile subfolder lists * report files that might be shadowed * fix issue with lists when a filter is used Also fixes discrepancy in older cf_get_file_list() when a sub path is specified (should be used in all cases, not just glob). * address review suggestions
1238 lines
29 KiB
C++
1238 lines
29 KiB
C++
|
|
/* WARNING:
|
|
* This is magic-number central, but these numbers are set specifically
|
|
* to the acceptable defaults or range values that were used when the
|
|
* pl2/plr files were created. Standard game defines should not be used in
|
|
* place of these major numbers for /any/ reason, *ever*!
|
|
*/
|
|
|
|
#include "cfile/cfilesystem.h"
|
|
#include "pilotfile/pilotfile_convert.h"
|
|
#include "cutscene/cutscenes.h"
|
|
#include "menuui/techmenu.h"
|
|
#include "ship/ship.h"
|
|
#include "stats/medals.h"
|
|
#include "weapon/weapon.h"
|
|
|
|
#include <iostream>
|
|
#include <sstream>
|
|
#include <limits>
|
|
|
|
// copy of old scoring struct * NORMAL PILOTS *
|
|
typedef struct scoring_conv_t {
|
|
int flags;
|
|
|
|
int score;
|
|
int rank;
|
|
int medals[18];
|
|
|
|
int kills[130];
|
|
int assists;
|
|
int kill_count;
|
|
int kill_count_ok;
|
|
unsigned int p_shots_fired;
|
|
unsigned int s_shots_fired;
|
|
|
|
unsigned int p_shots_hit;
|
|
unsigned int s_shots_hit;
|
|
|
|
unsigned int p_bonehead_hits;
|
|
unsigned int s_bonehead_hits;
|
|
int bonehead_kills;
|
|
|
|
unsigned int missions_flown;
|
|
unsigned int flight_time;
|
|
_fs_time_t last_flown;
|
|
_fs_time_t last_backup;
|
|
|
|
// Mission total
|
|
int m_medal_earned;
|
|
int m_badge_earned;
|
|
int m_promotion_earned;
|
|
|
|
int m_score;
|
|
int m_kills[130];
|
|
int m_okKills[130];
|
|
int m_kill_count;
|
|
int m_kill_count_ok;
|
|
int m_assists;
|
|
unsigned int mp_shots_fired;
|
|
unsigned int ms_shots_fired;
|
|
unsigned int mp_shots_hit;
|
|
unsigned int ms_shots_hit;
|
|
unsigned int mp_bonehead_hits;
|
|
unsigned int ms_bonehead_hits;
|
|
int m_bonehead_kills;
|
|
int m_player_deaths;
|
|
|
|
int m_dogfight_kills[12];
|
|
} scoring_conv_t;
|
|
|
|
static const size_t SCORING_SIZE = 1808;
|
|
|
|
// copy of old scoring struct * INFERNO PILOTS *
|
|
typedef struct scoring_conv_INF_t {
|
|
int flags;
|
|
|
|
int score;
|
|
int rank;
|
|
int medals[18];
|
|
|
|
int kills[250];
|
|
int assists;
|
|
int kill_count;
|
|
int kill_count_ok;
|
|
unsigned int p_shots_fired;
|
|
unsigned int s_shots_fired;
|
|
|
|
unsigned int p_shots_hit;
|
|
unsigned int s_shots_hit;
|
|
|
|
unsigned int p_bonehead_hits;
|
|
unsigned int s_bonehead_hits;
|
|
int bonehead_kills;
|
|
|
|
unsigned int missions_flown;
|
|
unsigned int flight_time;
|
|
_fs_time_t last_flown;
|
|
_fs_time_t last_backup;
|
|
|
|
// Mission total
|
|
int m_medal_earned;
|
|
int m_badge_earned;
|
|
int m_promotion_earned;
|
|
|
|
int m_score;
|
|
int m_kills[250];
|
|
int m_okKills[250];
|
|
int m_kill_count;
|
|
int m_kill_count_ok;
|
|
int m_assists;
|
|
unsigned int mp_shots_fired;
|
|
unsigned int ms_shots_fired;
|
|
unsigned int mp_shots_hit;
|
|
unsigned int ms_shots_hit;
|
|
unsigned int mp_bonehead_hits;
|
|
unsigned int ms_bonehead_hits;
|
|
int m_bonehead_kills;
|
|
int m_player_deaths;
|
|
|
|
int m_dogfight_kills[12];
|
|
} scoring_conv_INF_t;
|
|
|
|
static const size_t SCORING_INF_SIZE = 3248;
|
|
|
|
|
|
csg_data::csg_data()
|
|
{
|
|
sig = 0;
|
|
cutscenes = 0;
|
|
|
|
main_hall = "";
|
|
prev_mission = -1;
|
|
next_mission = -1;
|
|
loop_reentry = 0;
|
|
loop_enabled = 0;
|
|
num_completed = 0;
|
|
|
|
last_ship_flown_index = -1;
|
|
}
|
|
|
|
csg_data::~csg_data()
|
|
{
|
|
|
|
}
|
|
|
|
void pilotfile_convert::csg_import_ships_weapons()
|
|
{
|
|
index_list_t ilist;
|
|
char name[35];
|
|
int idx;
|
|
int list_size = 0;
|
|
|
|
int ship_count = cfread_int(cfp);
|
|
int weap_count = cfread_int(cfp);
|
|
|
|
for (idx = 0; idx < ship_count; idx++) {
|
|
ubyte allowed = cfread_ubyte(cfp);
|
|
csg->ships_allowed.push_back( (allowed != 0) );
|
|
|
|
cfread_string_len(name, sizeof(name), cfp);
|
|
|
|
ilist.name = name;
|
|
ilist.index = ship_info_lookup(name);
|
|
|
|
if (ilist.index < 0) {
|
|
std::ostringstream error_msg;
|
|
error_msg << "Data mismatch (ship lookup: " << ilist.name << ")!";
|
|
throw std::runtime_error(error_msg.str().c_str());
|
|
}
|
|
|
|
csg->ship_list.push_back( ilist );
|
|
}
|
|
|
|
for (idx = 0; idx < weap_count; idx++) {
|
|
ubyte allowed = cfread_ubyte(cfp);
|
|
csg->weapons_allowed.push_back( allowed != 0 );
|
|
|
|
cfread_string_len(name, sizeof(name), cfp);
|
|
|
|
ilist.name = name;
|
|
ilist.index = weapon_info_lookup(name);
|
|
|
|
if (ilist.index < 0) {
|
|
std::ostringstream error_msg;
|
|
error_msg << "Data mismatch (weapon lookup: " << ilist.name << ")!";
|
|
throw std::runtime_error(error_msg.str().c_str());
|
|
}
|
|
|
|
csg->weapon_list.push_back( ilist );
|
|
}
|
|
|
|
// get last ship flown index
|
|
for (idx = 0; idx < ship_count; idx++) {
|
|
if ( csg->ship_list[idx].name == plr->last_ship_flown ) {
|
|
csg->last_ship_flown_index = idx;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (csg->last_ship_flown_index < 0) {
|
|
std::ostringstream error_msg;
|
|
error_msg << "Data mismatch (player ship: " << csg->last_ship_flown_index << ")!";
|
|
throw std::runtime_error(error_msg.str().c_str());
|
|
}
|
|
|
|
// create list of medals (since it's missing from the old files)
|
|
list_size = (int)Medals.size();
|
|
|
|
for (idx = 0; idx < list_size; idx++) {
|
|
ilist.name = Medals[idx].name;
|
|
ilist.index = idx;
|
|
|
|
csg->medals_list.push_back( ilist );
|
|
}
|
|
|
|
// stuff intel list as well (present but buried in old files)
|
|
list_size = intel_info_size();
|
|
|
|
for (idx = 0; idx < list_size; idx++) {
|
|
ilist.name = Intel_info[idx].name;
|
|
ilist.index = idx;
|
|
|
|
csg->intel_list.push_back( ilist );
|
|
}
|
|
}
|
|
|
|
void pilotfile_convert::csg_import_missions(bool inferno)
|
|
{
|
|
cmission_conv_t miss;
|
|
int idx, j;
|
|
int count;
|
|
int list_size = 0, k;
|
|
scoring_conv_t t_score;
|
|
scoring_conv_INF_t t_inf_score;
|
|
|
|
Assert( sizeof(scoring_conv_t) == SCORING_SIZE );
|
|
Assert( sizeof(scoring_conv_INF_t) == SCORING_INF_SIZE );
|
|
|
|
int ship_list_size = (int)csg->ship_list.size();
|
|
|
|
int num_missions = cfread_int(cfp);
|
|
|
|
csg->missions.reserve(num_missions);
|
|
|
|
for (idx = 0; idx < num_missions; idx++) {
|
|
miss.index = cfread_int(cfp);
|
|
|
|
// goals
|
|
count = cfread_int(cfp);
|
|
|
|
if (count > 0) {
|
|
mgoal n_goal;
|
|
|
|
miss.goals.reserve(count);
|
|
|
|
for (j = 0; j < count; j++) {
|
|
memset(&n_goal, 0, sizeof(mgoal));
|
|
|
|
cfread_string_len(n_goal.name, sizeof(n_goal.name), cfp);
|
|
n_goal.status = cfread_char(cfp);
|
|
|
|
miss.goals.push_back( n_goal );
|
|
}
|
|
}
|
|
|
|
// events
|
|
count = cfread_int(cfp);
|
|
|
|
if (count > 0) {
|
|
mevent n_event;
|
|
|
|
miss.events.reserve(count);
|
|
|
|
for (j = 0; j < count; j++) {
|
|
memset(&n_event, 0, sizeof(mevent));
|
|
|
|
cfread_string_len(n_event.name, sizeof(n_event.name), cfp);
|
|
n_event.status = cfread_char(cfp);
|
|
|
|
miss.events.push_back( n_event );
|
|
}
|
|
}
|
|
|
|
// variables
|
|
count = cfread_int(cfp);
|
|
|
|
if (count > 0) {
|
|
sexp_variable n_variable;
|
|
|
|
miss.variables.reserve(count);
|
|
|
|
for (j = 0; j < count; j++) {
|
|
memset(&n_variable, 0, sizeof(sexp_variable));
|
|
|
|
n_variable.type = cfread_int(cfp);
|
|
cfread_string_len(n_variable.text, sizeof(n_variable.text), cfp);
|
|
cfread_string_len(n_variable.variable_name, sizeof(n_variable.variable_name), cfp);
|
|
|
|
miss.variables.push_back( n_variable );
|
|
}
|
|
}
|
|
|
|
// stats
|
|
miss.stats.ship_kills = csg->ship_list;
|
|
miss.stats.medals_earned = csg->medals_list;
|
|
|
|
if (inferno) {
|
|
cfread(&t_inf_score, sizeof(scoring_conv_INF_t), 1, cfp);
|
|
|
|
for (j = 0; j < ship_list_size; j++) {
|
|
miss.stats.ship_kills[j].val = INTEL_INT(t_inf_score.kills[j]);
|
|
}
|
|
|
|
miss.stats.score = INTEL_INT(t_inf_score.score);
|
|
miss.stats.rank = INTEL_INT(t_inf_score.rank);
|
|
miss.stats.assists = INTEL_INT(t_inf_score.assists);
|
|
miss.stats.kill_count = INTEL_INT(t_inf_score.kill_count);
|
|
miss.stats.kill_count_ok = INTEL_INT(t_inf_score.kill_count_ok);
|
|
miss.stats.p_shots_fired = INTEL_INT(t_inf_score.p_shots_fired);
|
|
miss.stats.s_shots_fired = INTEL_INT(t_inf_score.s_shots_fired);
|
|
miss.stats.p_shots_hit = INTEL_INT(t_inf_score.p_shots_hit);
|
|
miss.stats.s_shots_hit = INTEL_INT(t_inf_score.s_shots_hit);
|
|
miss.stats.p_bonehead_hits = INTEL_INT(t_inf_score.p_bonehead_hits);
|
|
miss.stats.s_bonehead_hits = INTEL_INT(t_inf_score.s_bonehead_hits);
|
|
miss.stats.bonehead_kills = INTEL_INT(t_inf_score.bonehead_kills);
|
|
|
|
for (j = 0; j < 18; j++) {
|
|
miss.stats.medals_earned[j].val = INTEL_INT(t_inf_score.medals[j]);
|
|
}
|
|
} else {
|
|
cfread(&t_score, sizeof(scoring_conv_t), 1, cfp);
|
|
|
|
for (j = 0; j < ship_list_size; j++) {
|
|
miss.stats.ship_kills[j].val = INTEL_INT(t_score.kills[j]);
|
|
}
|
|
|
|
miss.stats.score = INTEL_INT(t_score.score);
|
|
miss.stats.rank = INTEL_INT(t_score.rank);
|
|
miss.stats.assists = INTEL_INT(t_score.assists);
|
|
miss.stats.kill_count = INTEL_INT(t_score.kill_count);
|
|
miss.stats.kill_count_ok = INTEL_INT(t_score.kill_count_ok);
|
|
miss.stats.p_shots_fired = INTEL_INT(t_score.p_shots_fired);
|
|
miss.stats.s_shots_fired = INTEL_INT(t_score.s_shots_fired);
|
|
miss.stats.p_shots_hit = INTEL_INT(t_score.p_shots_hit);
|
|
miss.stats.s_shots_hit = INTEL_INT(t_score.s_shots_hit);
|
|
miss.stats.p_bonehead_hits = INTEL_INT(t_score.p_bonehead_hits);
|
|
miss.stats.s_bonehead_hits = INTEL_INT(t_score.s_bonehead_hits);
|
|
miss.stats.bonehead_kills = INTEL_INT(t_score.bonehead_kills);
|
|
|
|
for (j = 0; j < 18; j++) {
|
|
miss.stats.medals_earned[j].val = INTEL_INT(t_score.medals[j]);
|
|
}
|
|
}
|
|
|
|
// flags
|
|
miss.flags = cfread_int(cfp);
|
|
|
|
|
|
// now add to list
|
|
csg->missions.push_back( miss );
|
|
}
|
|
|
|
// finally, convert old mission variables to proper campaign variables
|
|
for (idx = 0; idx < num_missions; idx++) {
|
|
count = (int)csg->missions[idx].variables.size();
|
|
|
|
for (j = 0; j < count; j++) {
|
|
bool add_it = true;
|
|
|
|
list_size = (int)csg->variables.size();
|
|
|
|
for (k = 0; k < list_size; k++) {
|
|
if ( !stricmp(csg->variables[k].variable_name, csg->missions[idx].variables[j].variable_name) ) {
|
|
csg->variables[k] = csg->missions[idx].variables[j];
|
|
add_it = false;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (add_it) {
|
|
csg->variables.push_back( csg->missions[idx].variables[j] );
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void pilotfile_convert::csg_import_red_alert()
|
|
{
|
|
int idx, i, j;
|
|
int count;
|
|
char t_string[35] = { '\0' };
|
|
float val;
|
|
wep_t weapons;
|
|
|
|
count = cfread_int(cfp);
|
|
|
|
if (count <= 0) {
|
|
return;
|
|
}
|
|
|
|
csg->wingman_status.reserve( count );
|
|
|
|
cfread_string(t_string, sizeof(t_string)-1, cfp);
|
|
csg->precursor_mission = t_string;
|
|
|
|
int ship_list_size = (int)csg->ship_list.size();
|
|
int weapon_list_size = (int)csg->weapon_list.size();
|
|
|
|
for (idx = 0; idx < count; idx++) {
|
|
red_alert_ship_status ras;
|
|
|
|
// ship name
|
|
cfread_string(t_string, sizeof(t_string)-1, cfp);
|
|
ras.name = t_string;
|
|
|
|
ras.hull = cfread_float(cfp);
|
|
ras.ship_class = cfread_int(cfp);
|
|
|
|
if (ras.ship_class >= ship_list_size) {
|
|
throw std::runtime_error("Data failure (RedAlert-ship)!");
|
|
}
|
|
|
|
// system status
|
|
ras.subsys_current_hits.reserve(64);
|
|
|
|
for (j = 0; j < 64; j++) {
|
|
val = cfread_float(cfp);
|
|
ras.subsys_current_hits.push_back( val );
|
|
}
|
|
|
|
ras.subsys_aggregate_current_hits.reserve(12);
|
|
|
|
for (j = 0; j < 12; j++) {
|
|
val = cfread_float(cfp);
|
|
ras.subsys_aggregate_current_hits.push_back( val );
|
|
}
|
|
|
|
// loadout
|
|
ras.primary_weapons.reserve(3);
|
|
|
|
for (j = 0; j < 3; j++) {
|
|
i = cfread_int(cfp);
|
|
|
|
if (i >= weapon_list_size || i < -1) {
|
|
throw std::runtime_error("Data check failure (RedAlert-weapon)!");
|
|
} else if (i >= 0) {
|
|
weapons.index = csg->weapon_list[i].index;
|
|
} else {
|
|
weapons.index = -1;
|
|
}
|
|
|
|
weapons.count = cfread_int(cfp);
|
|
|
|
if (weapons.index >= 0) {
|
|
ras.primary_weapons.push_back( weapons );
|
|
}
|
|
}
|
|
|
|
ras.secondary_weapons.reserve(4);
|
|
|
|
for (j = 0; j < 4; j++) {
|
|
i = cfread_int(cfp);
|
|
|
|
if (i >= weapon_list_size || i < -1) {
|
|
throw std::runtime_error("Data check failure (RedAlert-weapon)!");
|
|
} else if (i >= 0) {
|
|
weapons.index = csg->weapon_list[i].index;
|
|
} else {
|
|
weapons.index = -1;
|
|
}
|
|
|
|
weapons.count = cfread_int(cfp);
|
|
|
|
if (weapons.index >= 0) {
|
|
ras.secondary_weapons.push_back( weapons );
|
|
}
|
|
}
|
|
|
|
// add to list
|
|
csg->wingman_status.push_back( ras );
|
|
}
|
|
}
|
|
|
|
void pilotfile_convert::csg_import_techroom()
|
|
{
|
|
int idx, list_size = 0;
|
|
bool visible;
|
|
unsigned char in = 0;
|
|
|
|
// intel entry count
|
|
int intel_count = cfread_int(cfp);
|
|
|
|
csg->ships_techroom.reserve( csg->ship_list.size() );
|
|
csg->weapons_techroom.reserve( csg->weapon_list.size() );
|
|
csg->intel_techroom.reserve( intel_count );
|
|
|
|
// ships
|
|
list_size = (int)csg->ship_list.size();
|
|
|
|
for (idx = 0; idx < list_size; idx++) {
|
|
in = cfread_ubyte(cfp);
|
|
|
|
if (in > 1) {
|
|
throw std::runtime_error("Data check failure (techroom-ship)!");
|
|
}
|
|
|
|
visible = (in == 1) ? true : false;
|
|
|
|
csg->ships_techroom.push_back( visible );
|
|
}
|
|
|
|
// weapons
|
|
list_size = (int)csg->weapon_list.size();
|
|
|
|
for (idx = 0; idx < list_size; idx++) {
|
|
in = cfread_ubyte(cfp);
|
|
|
|
if (in > 1) {
|
|
throw std::runtime_error("Data check failure (techroom-weapon)!");
|
|
}
|
|
|
|
visible = (in == 1) ? true : false;
|
|
|
|
csg->weapons_techroom.push_back( visible );
|
|
}
|
|
|
|
// intel
|
|
list_size = intel_count;
|
|
|
|
for (idx = 0; idx < list_size; idx++) {
|
|
in = cfread_ubyte(cfp);
|
|
|
|
if (in > 1) {
|
|
throw std::runtime_error("Data check failure (techroom-intel)!");
|
|
}
|
|
|
|
visible = (in == 1) ? true : false;
|
|
|
|
csg->intel_techroom.push_back( visible );
|
|
}
|
|
}
|
|
|
|
void pilotfile_convert::csg_import_loadout()
|
|
{
|
|
char t_string[50] = { '\0' };
|
|
|
|
// mission name/status
|
|
cfread_string_len(t_string, sizeof(t_string), cfp);
|
|
csg->loadout.filename = t_string;
|
|
|
|
cfread_string_len(t_string, sizeof(t_string), cfp);
|
|
csg->loadout.last_modified = t_string;
|
|
|
|
// ship pool
|
|
size_t list_size = csg->ship_list.size();
|
|
csg->loadout.ship_pool.reserve(list_size);
|
|
|
|
for (size_t idx = 0; idx < list_size; idx++) {
|
|
int count = cfread_int(cfp);
|
|
csg->loadout.ship_pool.push_back( count );
|
|
}
|
|
|
|
// weapon pool
|
|
list_size = csg->weapon_list.size();
|
|
csg->loadout.weapon_pool.reserve(list_size);
|
|
|
|
for (size_t idx = 0; idx < list_size; idx++) {
|
|
int count = cfread_int(cfp);
|
|
csg->loadout.weapon_pool.push_back( count );
|
|
}
|
|
|
|
// loadout info
|
|
for (size_t idx = 0; idx < MAX_WSS_SLOTS_CONV; idx++) {
|
|
csg->loadout.slot[idx].ship_index = cfread_int(cfp);
|
|
|
|
for (size_t j = 0; j < MAX_SHIP_WEAPONS_CONV; j++) {
|
|
csg->loadout.slot[idx].wep[j] = cfread_int(cfp);
|
|
csg->loadout.slot[idx].wep_count[j] = cfread_int(cfp);
|
|
}
|
|
}
|
|
}
|
|
|
|
void pilotfile_convert::csg_import_stats()
|
|
{
|
|
int list_size = 0;
|
|
int idx;
|
|
|
|
csg->stats.score = cfread_int(cfp);
|
|
csg->stats.rank = cfread_int(cfp);
|
|
csg->stats.assists = cfread_int(cfp);
|
|
|
|
csg->stats.medals_earned = csg->medals_list;
|
|
|
|
list_size = (int)csg->stats.medals_earned.size();
|
|
|
|
for (idx = 0; idx < list_size; idx++) {
|
|
csg->stats.medals_earned[idx].val = cfread_int(cfp);
|
|
}
|
|
|
|
csg->stats.ship_kills = csg->ship_list;
|
|
|
|
list_size = cfread_int(cfp);
|
|
|
|
// NOTE: could be less, but never greater than
|
|
if ( list_size > (int)csg->stats.ship_kills.size() ) {
|
|
throw std::runtime_error("Data check failure (kills size)!");
|
|
}
|
|
|
|
for (idx = 0; idx < list_size; idx++) {
|
|
csg->stats.ship_kills[idx].val = (int)cfread_ushort(cfp);
|
|
}
|
|
|
|
csg->stats.kill_count = cfread_int(cfp);
|
|
csg->stats.kill_count_ok = cfread_int(cfp);
|
|
|
|
csg->stats.p_shots_fired = cfread_uint(cfp);
|
|
csg->stats.s_shots_fired = cfread_uint(cfp);
|
|
csg->stats.p_shots_hit = cfread_uint(cfp);
|
|
csg->stats.s_shots_hit = cfread_uint(cfp);
|
|
|
|
csg->stats.p_bonehead_hits = cfread_uint(cfp);
|
|
csg->stats.s_bonehead_hits = cfread_uint(cfp);
|
|
csg->stats.bonehead_kills = cfread_uint(cfp);
|
|
}
|
|
|
|
void pilotfile_convert::csg_import(bool inferno)
|
|
{
|
|
Assert( cfp != NULL );
|
|
|
|
char name[35];
|
|
|
|
unsigned int csg_id = cfread_uint(cfp);
|
|
|
|
if (csg_id != 0xbeefcafe) {
|
|
throw std::runtime_error("Invalid file signature!");
|
|
}
|
|
|
|
fver = cfread_uint(cfp);
|
|
|
|
if (fver != 15) {
|
|
throw std::runtime_error("Unsupported file version!");
|
|
}
|
|
|
|
// campaign type (single/multi)
|
|
csg->sig = cfread_int(cfp);
|
|
|
|
// trash
|
|
cfread_string_len(name, sizeof(name), cfp);
|
|
|
|
csg->prev_mission = cfread_int(cfp);
|
|
csg->next_mission = cfread_int(cfp);
|
|
csg->loop_reentry = cfread_int(cfp);
|
|
csg->loop_enabled = cfread_int(cfp);
|
|
|
|
csg_import_ships_weapons();
|
|
|
|
csg_import_missions(inferno);
|
|
|
|
csg->main_hall = cfread_ubyte(cfp);
|
|
|
|
csg_import_red_alert();
|
|
|
|
csg_import_techroom();
|
|
|
|
csg_import_loadout();
|
|
|
|
csg_import_stats();
|
|
|
|
csg->cutscenes = cfread_int(cfp);
|
|
|
|
// final data checks
|
|
if ( csg->ship_list.size() != csg->ships_allowed.size() ) {
|
|
throw std::runtime_error("Data check failure (ship size)!");
|
|
} else if ( csg->ship_list.size() != csg->ships_techroom.size() ) {
|
|
throw std::runtime_error("Data check failure (ship size)!");
|
|
} else if ( csg->weapon_list.size() != csg->weapons_allowed.size() ) {
|
|
throw std::runtime_error("Data check failure (weapon size)!");
|
|
} else if ( csg->weapon_list.size() != csg->weapons_techroom.size() ) {
|
|
throw std::runtime_error("Data check failure (weapon size)!");
|
|
} else if ( csg->intel_list.size() != csg->intel_techroom.size() ) {
|
|
throw std::runtime_error("Data check failure (intel size)!");
|
|
}
|
|
|
|
|
|
// and... we're done!
|
|
}
|
|
|
|
void pilotfile_convert::csg_export_flags()
|
|
{
|
|
startSection(Section::Flags);
|
|
|
|
// tips
|
|
cfwrite_ubyte((ubyte)plr->tips, cfp);
|
|
|
|
// special rank
|
|
cfwrite_int(csg->stats.rank, cfp);
|
|
|
|
endSection();
|
|
}
|
|
|
|
void pilotfile_convert::csg_export_info()
|
|
{
|
|
int list_size = 0;
|
|
int idx;
|
|
ubyte visible;
|
|
|
|
startSection(Section::Info);
|
|
|
|
// ship list
|
|
list_size = (int)csg->ship_list.size();
|
|
cfwrite_int(list_size, cfp);
|
|
|
|
for (idx = 0; idx < list_size; idx++) {
|
|
cfwrite_string_len(csg->ship_list[idx].name.c_str(), cfp);
|
|
}
|
|
|
|
// weapon list
|
|
list_size = (int)csg->weapon_list.size();
|
|
cfwrite_int(list_size, cfp);
|
|
|
|
for (idx = 0; idx < list_size; idx++) {
|
|
cfwrite_string_len(csg->weapon_list[idx].name.c_str(), cfp);
|
|
}
|
|
|
|
// intel list
|
|
list_size = (int)csg->intel_list.size();
|
|
cfwrite_int(list_size, cfp);
|
|
|
|
for (idx = 0; idx < list_size; idx++) {
|
|
cfwrite_string_len(csg->intel_list[idx].name.c_str(), cfp);
|
|
}
|
|
|
|
// medals list
|
|
list_size = (int)csg->stats.medals_earned.size();
|
|
cfwrite_int(list_size, cfp);
|
|
|
|
for (idx = 0; idx < list_size; idx++) {
|
|
cfwrite_string_len(csg->stats.medals_earned[idx].name.c_str(), cfp);
|
|
}
|
|
|
|
// last ship flown
|
|
cfwrite_int(csg->last_ship_flown_index, cfp);
|
|
|
|
// progression state
|
|
cfwrite_int(csg->prev_mission, cfp);
|
|
cfwrite_int(csg->next_mission, cfp);
|
|
|
|
// loop state
|
|
cfwrite_int(csg->loop_enabled, cfp);
|
|
cfwrite_int(csg->loop_reentry, cfp);
|
|
|
|
// missions completed
|
|
list_size = (int)csg->missions.size();
|
|
cfwrite_int(list_size, cfp);
|
|
|
|
// allowed ships
|
|
list_size = (int)csg->ships_allowed.size();
|
|
for (idx = 0; idx < list_size; idx++) {
|
|
visible = csg->ships_allowed[idx] ? 1 : 0;
|
|
cfwrite_ubyte(visible, cfp);
|
|
}
|
|
|
|
// allowed weapons
|
|
list_size = (int)csg->weapons_allowed.size();
|
|
for (idx = 0; idx < list_size; idx++) {
|
|
visible = csg->weapons_allowed[idx] ? 1 : 0;
|
|
cfwrite_ubyte(visible, cfp);
|
|
}
|
|
|
|
// single/campaign squad name & image, make it the same as multi
|
|
cfwrite_string_len(plr->squad_name, cfp);
|
|
cfwrite_string_len(plr->squad_filename, cfp);
|
|
|
|
endSection();
|
|
}
|
|
|
|
void pilotfile_convert::csg_export_missions()
|
|
{
|
|
startSection(Section::Missions);
|
|
|
|
size_t list_size = csg->missions.size();
|
|
|
|
for (size_t idx = 0; idx < list_size; idx++) {
|
|
cfwrite_int(csg->missions[idx].index, cfp);
|
|
|
|
// flags
|
|
cfwrite_int(csg->missions[idx].flags, cfp);
|
|
|
|
// goals
|
|
size_t count = csg->missions[idx].goals.size();
|
|
cfwrite_int((int)count, cfp);
|
|
|
|
for (size_t j = 0; j < count; j++) {
|
|
cfwrite_string_len(csg->missions[idx].goals[j].name, cfp);
|
|
cfwrite_char(csg->missions[idx].goals[j].status, cfp);
|
|
}
|
|
|
|
// events
|
|
count = csg->missions[idx].events.size();
|
|
cfwrite_int((int)count, cfp);
|
|
|
|
for (size_t j = 0; j < count; j++) {
|
|
cfwrite_string_len(csg->missions[idx].events[j].name, cfp);
|
|
cfwrite_char(csg->missions[idx].events[j].status, cfp);
|
|
}
|
|
|
|
// variables
|
|
count = csg->missions[idx].variables.size();
|
|
cfwrite_int((int)count, cfp);
|
|
|
|
for (size_t j = 0; j < count; j++) {
|
|
cfwrite_int(csg->missions[idx].variables[j].type, cfp);
|
|
cfwrite_string_len(csg->missions[idx].variables[j].text, cfp);
|
|
cfwrite_string_len(csg->missions[idx].variables[j].variable_name, cfp);
|
|
}
|
|
|
|
// scoring stats
|
|
cfwrite_int(csg->missions[idx].stats.score, cfp);
|
|
cfwrite_int(csg->missions[idx].stats.rank, cfp);
|
|
cfwrite_int(csg->missions[idx].stats.assists, cfp);
|
|
cfwrite_int(csg->missions[idx].stats.kill_count, cfp);
|
|
cfwrite_int(csg->missions[idx].stats.kill_count_ok, cfp);
|
|
cfwrite_int(csg->missions[idx].stats.bonehead_kills, cfp);
|
|
|
|
cfwrite_uint(csg->missions[idx].stats.p_shots_fired, cfp);
|
|
cfwrite_uint(csg->missions[idx].stats.p_shots_hit, cfp);
|
|
cfwrite_uint(csg->missions[idx].stats.p_bonehead_hits, cfp);
|
|
|
|
cfwrite_uint(csg->missions[idx].stats.s_shots_fired, cfp);
|
|
cfwrite_uint(csg->missions[idx].stats.s_shots_hit, cfp);
|
|
cfwrite_uint(csg->missions[idx].stats.s_bonehead_hits, cfp);
|
|
|
|
// ship kills (scoring)
|
|
count = csg->missions[idx].stats.ship_kills.size();
|
|
|
|
for (size_t j = 0; j < count; j++) {
|
|
cfwrite_int(csg->missions[idx].stats.ship_kills[j].val, cfp);
|
|
}
|
|
|
|
// medals earned (scoring)
|
|
count = csg->missions[idx].stats.medals_earned.size();
|
|
|
|
for (size_t j = 0; j < count; j++) {
|
|
cfwrite_int(csg->missions[idx].stats.medals_earned[j].val, cfp);
|
|
}
|
|
}
|
|
|
|
endSection();
|
|
}
|
|
|
|
void pilotfile_convert::csg_export_techroom()
|
|
{
|
|
int list_size = 0;
|
|
int idx;
|
|
ubyte visible;
|
|
|
|
startSection(Section::Techroom);
|
|
|
|
// visible ships
|
|
list_size = (int)csg->ships_techroom.size();
|
|
|
|
for (idx = 0; idx < list_size; idx++) {
|
|
visible = csg->ships_techroom[idx] ? 1 : 0;
|
|
cfwrite_ubyte(visible, cfp);
|
|
}
|
|
|
|
// visible weapons
|
|
list_size = (int)csg->weapons_techroom.size();
|
|
|
|
for (idx = 0; idx < list_size; idx++) {
|
|
visible = csg->weapons_techroom[idx] ? 1 : 0;
|
|
cfwrite_ubyte(visible, cfp);
|
|
}
|
|
|
|
// visible intel entries
|
|
list_size = (int)csg->intel_techroom.size();
|
|
|
|
for (idx = 0; idx < list_size; idx++) {
|
|
visible = csg->intel_techroom[idx] ? 1 : 0;
|
|
cfwrite_ubyte(visible, cfp);
|
|
}
|
|
|
|
endSection();
|
|
}
|
|
|
|
void pilotfile_convert::csg_export_loadout()
|
|
{
|
|
startSection(Section::Loadout);
|
|
|
|
// base info
|
|
cfwrite_string_len(csg->loadout.filename.c_str(), cfp);
|
|
cfwrite_string_len(csg->loadout.last_modified.c_str(), cfp);
|
|
|
|
// ship pool
|
|
size_t list_size = csg->loadout.ship_pool.size();
|
|
|
|
for (size_t idx = 0; idx < list_size; idx++) {
|
|
cfwrite_int(csg->loadout.ship_pool[idx], cfp);
|
|
}
|
|
|
|
// weapon pool
|
|
list_size = csg->loadout.weapon_pool.size();
|
|
|
|
for (size_t idx = 0; idx < list_size; idx++) {
|
|
cfwrite_int(csg->loadout.weapon_pool[idx], cfp);
|
|
}
|
|
|
|
// play ship loadout
|
|
cfwrite_ushort(12, cfp);
|
|
|
|
for (size_t idx = 0; idx < 12; idx++) {
|
|
// ship
|
|
cfwrite_int(csg->loadout.slot[idx].ship_index, cfp);
|
|
|
|
// primary weapons
|
|
cfwrite_int(3, cfp);
|
|
|
|
for (size_t j = 0; j < 3; j++) {
|
|
cfwrite_int(csg->loadout.slot[idx].wep[j], cfp);
|
|
cfwrite_int(csg->loadout.slot[idx].wep_count[j], cfp);
|
|
}
|
|
|
|
// secondary weapons
|
|
cfwrite_int(4, cfp);
|
|
|
|
for (size_t j = 0; j < 4; j++) {
|
|
cfwrite_int(csg->loadout.slot[idx].wep[j+3], cfp);
|
|
cfwrite_int(csg->loadout.slot[idx].wep_count[j+3], cfp);
|
|
}
|
|
}
|
|
|
|
endSection();
|
|
}
|
|
|
|
void pilotfile_convert::csg_export_stats()
|
|
{
|
|
startSection(Section::Scoring);
|
|
|
|
// scoring stats
|
|
cfwrite_int(csg->stats.score, cfp);
|
|
cfwrite_int(csg->stats.rank, cfp);
|
|
cfwrite_int(csg->stats.assists, cfp);
|
|
cfwrite_int(csg->stats.kill_count, cfp);
|
|
cfwrite_int(csg->stats.kill_count_ok, cfp);
|
|
cfwrite_int(csg->stats.bonehead_kills, cfp);
|
|
|
|
cfwrite_uint(csg->stats.p_shots_fired, cfp);
|
|
cfwrite_uint(csg->stats.p_shots_hit, cfp);
|
|
cfwrite_uint(csg->stats.p_bonehead_hits, cfp);
|
|
|
|
cfwrite_uint(csg->stats.s_shots_fired, cfp);
|
|
cfwrite_uint(csg->stats.s_shots_hit, cfp);
|
|
cfwrite_uint(csg->stats.s_bonehead_hits, cfp);
|
|
|
|
cfwrite_uint(csg->stats.flight_time, cfp);
|
|
cfwrite_uint(csg->stats.missions_flown, cfp);
|
|
cfwrite_int((int)csg->stats.last_flown, cfp);
|
|
cfwrite_int((int)csg->stats.last_backup, cfp);
|
|
|
|
// ship kills (scoring)
|
|
size_t list_size = csg->stats.ship_kills.size();
|
|
for (size_t idx = 0; idx < list_size; idx++) {
|
|
cfwrite_int(csg->stats.ship_kills[idx].val, cfp);
|
|
}
|
|
|
|
// medals earned (scoring)
|
|
list_size = csg->stats.medals_earned.size();
|
|
for (size_t idx = 0; idx < list_size; idx++) {
|
|
cfwrite_int(csg->stats.medals_earned[idx].val, cfp);
|
|
}
|
|
|
|
endSection();
|
|
}
|
|
|
|
void pilotfile_convert::csg_export_redalert()
|
|
{
|
|
int idx, j, list_size = 0;
|
|
int count;
|
|
red_alert_ship_status *ras;
|
|
|
|
startSection(Section::RedAlert);
|
|
|
|
list_size = (int)csg->wingman_status.size();
|
|
|
|
cfwrite_int(list_size, cfp);
|
|
|
|
if (list_size) {
|
|
cfwrite_string_len(csg->precursor_mission.c_str(), cfp);
|
|
|
|
for (idx = 0; idx < list_size; idx++) {
|
|
ras = &csg->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_convert::csg_export_hud()
|
|
{
|
|
int idx;
|
|
|
|
startSection(Section::HUD);
|
|
|
|
// flags
|
|
cfwrite_int(plr->hud_show_flags, cfp);
|
|
cfwrite_int(plr->hud_show_flags2, cfp);
|
|
|
|
cfwrite_int(plr->hud_popup_flags, cfp);
|
|
cfwrite_int(plr->hud_popup_flags2, cfp);
|
|
|
|
// settings
|
|
cfwrite_ubyte(plr->hud_num_lines, cfp);
|
|
|
|
cfwrite_int(plr->hud_rp_flags, cfp);
|
|
cfwrite_int(plr->hud_rp_dist, cfp);
|
|
|
|
// basic colors
|
|
cfwrite_int(0, cfp); // color
|
|
cfwrite_int(8, cfp); // alpha
|
|
|
|
// gauge-specific colors
|
|
cfwrite_int(39, cfp);
|
|
|
|
for (idx = 0; idx < 39; idx++) {
|
|
cfwrite_ubyte(plr->hud_colors[idx][0], cfp);
|
|
cfwrite_ubyte(plr->hud_colors[idx][1], cfp);
|
|
cfwrite_ubyte(plr->hud_colors[idx][2], cfp);
|
|
cfwrite_ubyte(plr->hud_colors[idx][3], cfp);
|
|
}
|
|
|
|
endSection();
|
|
}
|
|
|
|
void pilotfile_convert::csg_export_variables()
|
|
{
|
|
int list_size = 0;
|
|
int idx;
|
|
|
|
startSection(Section::Variables);
|
|
|
|
list_size = (int)csg->variables.size();
|
|
|
|
cfwrite_int(list_size, cfp);
|
|
|
|
for (idx = 0; idx < list_size; idx++) {
|
|
cfwrite_int(csg->variables[idx].type, cfp);
|
|
cfwrite_string_len(csg->variables[idx].text, cfp);
|
|
cfwrite_string_len(csg->variables[idx].variable_name, cfp);
|
|
}
|
|
|
|
endSection();
|
|
}
|
|
|
|
void pilotfile_convert::csg_export_cutscenes() {
|
|
SCP_vector<cutscene_info>::iterator cut;
|
|
|
|
startSection(Section::Cutscenes);
|
|
|
|
// convert the old int bitfield to the new vector
|
|
// the 32 is the size-in-bits of the old int on all platforms
|
|
// supported by FSO prior to 3.7.0
|
|
size_t size = Cutscenes.size();
|
|
size_t viewableScenes = 0;
|
|
for (size_t j=0; j<size && j<32; ++j) {
|
|
if ( (j < Cutscenes.size()) && (csg->cutscenes & (1<<j)) ) {
|
|
Cutscenes.at(j).flags.set(Cutscene::Cutscene_Flags::Viewable);
|
|
viewableScenes++;
|
|
}
|
|
}
|
|
|
|
// Check for possible overflow because we can only write 32 bit integers
|
|
Assertion(viewableScenes <= std::numeric_limits<uint>::max(), "Too many viewable cutscenes! Maximum is %ud!", std::numeric_limits<uint>::max());
|
|
|
|
// output cutscene data in new format
|
|
cfwrite_uint((uint)viewableScenes, cfp);
|
|
|
|
for (cut = Cutscenes.begin(); cut != Cutscenes.end(); ++cut) {
|
|
if (cut->flags[Cutscene::Cutscene_Flags::Viewable]) {
|
|
cfwrite_string_len(cut->filename, cfp);
|
|
}
|
|
}
|
|
|
|
endSection();
|
|
}
|
|
|
|
void pilotfile_convert::csg_export()
|
|
{
|
|
Assert( cfp != NULL );
|
|
|
|
// header and version
|
|
cfwrite_int(CSG_FILE_ID, cfp);
|
|
cfwrite_ubyte(CSG_VERSION, cfp);
|
|
|
|
// flags and info sections go first
|
|
csg_export_flags();
|
|
csg_export_info();
|
|
|
|
// everything else is next, not order specific
|
|
csg_export_missions();
|
|
csg_export_techroom();
|
|
csg_export_loadout();
|
|
csg_export_stats();
|
|
csg_export_redalert();
|
|
csg_export_hud();
|
|
csg_export_variables();
|
|
csg_export_cutscenes();
|
|
|
|
// and... we're done! :)
|
|
}
|
|
|
|
bool pilotfile_convert::csg_convert(const char *fname, bool inferno)
|
|
{
|
|
Assert( fname != NULL );
|
|
Assert( plr != NULL);
|
|
Assert( csg == NULL );
|
|
|
|
SCP_string filename;
|
|
bool rval = true;
|
|
|
|
csg = new(std::nothrow) csg_data;
|
|
|
|
if (csg == NULL) {
|
|
return false;
|
|
}
|
|
|
|
filename.reserve(200);
|
|
|
|
cf_create_default_path_string(filename, CF_TYPE_SINGLE_PLAYERS, (inferno) ? "inferno" : nullptr,
|
|
CF_LOCATION_ROOT_USER | CF_LOCATION_ROOT_GAME | CF_LOCATION_TYPE_ROOT);
|
|
|
|
if (inferno) {
|
|
filename.append(DIR_SEPARATOR_STR);
|
|
}
|
|
|
|
filename.append(fname);
|
|
filename.append(".cs2");
|
|
|
|
mprintf((" CS2 => Converting '%s'...\n", filename.c_str()));
|
|
|
|
cfp = cfopen(filename.c_str(), "rb", CFILE_NORMAL);
|
|
|
|
if ( !cfp ) {
|
|
mprintf((" CS2 => Unable to open '%s' for import!\n", fname));
|
|
delete csg;
|
|
csg = NULL;
|
|
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
csg_import(inferno);
|
|
} catch (const std::exception& err) {
|
|
mprintf((" CS2 => Import ERROR: %s\n", err.what()));
|
|
rval = false;
|
|
}
|
|
|
|
cfclose(cfp);
|
|
cfp = NULL;
|
|
|
|
if ( !rval ) {
|
|
delete csg;
|
|
csg = NULL;
|
|
|
|
return false;
|
|
}
|
|
|
|
filename.assign(fname);
|
|
filename.append(".csg");
|
|
|
|
cfp = cfopen(filename.c_str(), "wb", CFILE_NORMAL, CF_TYPE_PLAYERS, false,
|
|
CF_LOCATION_ROOT_USER | CF_LOCATION_ROOT_GAME | CF_LOCATION_TYPE_ROOT);
|
|
|
|
if ( !cfp ) {
|
|
mprintf((" CSG => Unable to open '%s' for export!\n", fname));
|
|
return false;
|
|
}
|
|
|
|
try {
|
|
csg_export();
|
|
} catch (const char *err) {
|
|
mprintf((" CSG => Export ERROR: %s\n", err));
|
|
rval = false;
|
|
}
|
|
|
|
cfclose(cfp);
|
|
cfp = NULL;
|
|
|
|
delete csg;
|
|
csg = NULL;
|
|
|
|
if (rval) {
|
|
mprintf((" CSG => Conversion complete!\n"));
|
|
}
|
|
|
|
return rval;
|
|
}
|