/* 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 "mission/missionbriefcommon.h" #include "network/psnet2.h" #include "pilotfile/pilotfile_convert.h" // this struct isn't well packed, and is written whole to the pilot file, so // we can't easily just get the RGBA that we need and instead must retain this typedef struct conv_color { //-V802 uint screen_sig; ubyte red; ubyte green; ubyte blue; ubyte alpha; ubyte ac_type; int is_alphacolor; ubyte raw8; int alphacolor; int magic; } conv_color; plr_data::plr_data() { // not carried over, just for reference during conversion process version = 0; is_multi = 0; lcl_get_language_name(language); // basic flags and settings tips = 0; rank = 0; skill_level = 1; save_flags = 0; readyroom_listing_mode = 0; voice_enabled = 1; auto_advance = 1; Use_mouse_to_fly = 0; Mouse_sensitivity = 4; Joy_sensitivity = 9; Dead_zone_size = 10; // multiplayer settings/options net_protocol = 1; multi_squad_set = 2; multi_endgame_set = 3; multi_flags = 3; multi_respawn = 2; multi_max_observers = 2; multi_skill_level = 2; multi_voice_qos = 10; multi_voice_token_wait = 2000; multi_voice_record_time = 5000; multi_time_limit = -65536; multi_kill_limit = 9999; multi_local_flags = 5; multi_local_update_level = 0; // pilot info stuff memset(image_filename, 0, sizeof(image_filename)); memset(squad_name, 0, sizeof(squad_name)); memset(squad_filename, 0, sizeof(squad_filename)); memset(current_campaign, 0, sizeof(current_campaign)); memset(last_ship_flown, 0, sizeof(last_ship_flown)); // HUD config hud_show_flags = -1; hud_show_flags2 = 31; hud_popup_flags = 0; hud_popup_flags2 = 0; hud_num_lines = 4; hud_rp_flags = 7; hud_rp_dist = 2; for (int idx = 0; idx < 39; idx++) { hud_colors[idx][0] = 0; hud_colors[idx][1] = 255; hud_colors[idx][2] = 0; hud_colors[idx][3] = 144; } // control setup joy_axis_map_to[0] = 0; joy_axis_map_to[1] = 1; joy_axis_map_to[2] = 3; joy_invert_axis[3] = -1; joy_invert_axis[4] = -1; memset(joy_invert_axis, 0, sizeof(joy_invert_axis)); // audio sound_volume = 1.0f; music_volume = 0.5f; voice_volume = 0.7f; // detail settings detail_setting = 3; detail_nebula = 3; detail_distance = 3; detail_hardware_textures = 4; detail_num_debris = 4; detail_num_particles = 3; detail_num_stars = 4; detail_shield_effects = 4; detail_lighting = 4; detail_targetview_model = 1; detail_planets_suns = 1; detail_weapon_extras = 1; } plr_data::~plr_data() { controls.clear(); } void pilotfile_convert::plr_import_controls() { int idx; CCI con; short buf; unsigned char num_controls = cfread_ubyte(cfp); if ( !num_controls ) { return; } // it may be less than 118, but it shouldn't be more than 118 // Don't touch this magic number! This is for old playerfiles. See banner at top of this file. if (num_controls > 118) { throw "Data check failure in controls!"; } plr->controls.reserve(num_controls); for (idx = 0; idx < num_controls; idx++) { buf = cfread_short(cfp); if (buf == 255) { buf = -1; } con.take(CC_bind(CID_KEYBOARD, buf), 0); buf = cfread_short(cfp); if (buf == 255) { buf = -1; } con.take(CC_bind(CID_JOY0, buf), 1); plr->controls.push_back( con ); } } void pilotfile_convert::plr_import_hud() { int idx; conv_color c; plr->hud_show_flags = cfread_int(cfp); plr->hud_show_flags2 = cfread_int(cfp); plr->hud_popup_flags = cfread_int(cfp); plr->hud_popup_flags2 = cfread_int(cfp); plr->hud_num_lines = cfread_ubyte(cfp); plr->hud_rp_flags = cfread_int(cfp); plr->hud_rp_dist = cfread_int(cfp); for (idx = 0; idx < 39; idx++) { cfread(&c, sizeof(conv_color), 1, cfp); if ( (c.alphacolor != -1) || (c.is_alphacolor != 1) ) { throw "Data check failure in hud!"; } plr->hud_colors[idx][0] = c.red; plr->hud_colors[idx][1] = c.green; plr->hud_colors[idx][2] = c.blue; plr->hud_colors[idx][3] = c.alpha; } } void pilotfile_convert::plr_import_detail() { bool data_failure = false; plr->detail_setting = cfread_int(cfp); plr->detail_nebula = cfread_int(cfp); plr->detail_distance = cfread_int(cfp); plr->detail_hardware_textures = cfread_int(cfp); plr->detail_num_debris = cfread_int(cfp); plr->detail_num_particles = cfread_int(cfp); plr->detail_num_stars = cfread_int(cfp); plr->detail_shield_effects = cfread_int(cfp); plr->detail_lighting = cfread_int(cfp); plr->detail_targetview_model = cfread_int(cfp); plr->detail_planets_suns = cfread_int(cfp); plr->detail_weapon_extras = cfread_int(cfp); if ( (plr->detail_setting < -1) || (plr->detail_setting > 4) ) { data_failure = true; } else if ( (plr->detail_nebula < 0) || (plr->detail_nebula > 4) ) { data_failure = true; } else if ( (plr->detail_distance < 0) || (plr->detail_distance > 4) ) { data_failure = true; } else if ( (plr->detail_hardware_textures < 0) || (plr->detail_hardware_textures > 4) ) { data_failure = true; } else if ( (plr->detail_num_debris < 0) || (plr->detail_num_debris > 4) ) { data_failure = true; } else if ( (plr->detail_num_particles < 0) || (plr->detail_num_particles > 4) ) { data_failure = true; } else if ( (plr->detail_num_stars < 0) || (plr->detail_num_stars > 4) ) { data_failure = true; } else if ( (plr->detail_shield_effects < 0) || (plr->detail_shield_effects > 4) ) { data_failure = true; } else if ( (plr->detail_lighting < 0) || (plr->detail_lighting > 4) ) { data_failure = true; } else if ( (plr->detail_targetview_model < 0) || (plr->detail_targetview_model > 1) ) { data_failure = true; } else if ( (plr->detail_planets_suns < 0) || (plr->detail_planets_suns > 1) ) { data_failure = true; } else if ( (plr->detail_weapon_extras < 0) || (plr->detail_weapon_extras > 1) ) { data_failure = true; } if (data_failure) { throw "Data check failure in details!"; } } void pilotfile_convert::plr_import_stats() { int idx; char name[35]; if (fver >= 242) { return; } // read everything, but we don't need any of it ... cfread_int(cfp); // score cfread_int(cfp); // rank cfread_int(cfp); // assists // medals for (idx = 0; idx < 18; idx++) { cfread_int(cfp); } // kills per ship int count = cfread_int(cfp); for (idx = 0; idx < count; idx++) { cfread_ushort(cfp); cfread_string_len(name, sizeof(name), cfp); } cfread_int(cfp); // kill_count cfread_int(cfp); // kill_count_ok cfread_uint(cfp); // p_shots_fired cfread_uint(cfp); // s_shots_fired cfread_uint(cfp); // p_shots_hit cfread_uint(cfp); // s_shots_hit cfread_uint(cfp); // p_bonehead_hits cfread_uint(cfp); // s_bonehead_hits cfread_uint(cfp); // bonehead_kills } void pilotfile_convert::plr_import_loadout() { int idx, j; int s_count, w_count; char name[52]; if (fver >= 242) { return; } // have to read it, but don't need any of it ... cfread_string_len(name, sizeof(name), cfp); // filename cfread_string_len(name, sizeof(name), cfp); // last_modified s_count = cfread_int(cfp); // num ships w_count = cfread_int(cfp); // num weapons // ships for (idx = 0; idx < s_count; idx++) { cfread_int(cfp); // count cfread_string_len(name, sizeof(name), cfp); // name } // weapons for (idx = 0; idx < w_count; idx++) { cfread_int(cfp); // count cfread_string_len(name, sizeof(name), cfp); // name } // loadout info for (idx = 0; idx < 12; idx++) { cfread_int(cfp); // ship class cfread_string_len(name, sizeof(name), cfp); // ship name for (j = 0; j < 12; j++) { cfread_int(cfp); // weapon type cfread_int(cfp); // weapon count cfread_string_len(name, sizeof(name), cfp); // weapon name } } } void pilotfile_convert::plr_import_multiplayer() { plr->multi_squad_set = cfread_ubyte(cfp); plr->multi_endgame_set = cfread_ubyte(cfp); plr->multi_flags = cfread_int(cfp); plr->multi_respawn = cfread_uint(cfp); plr->multi_max_observers = cfread_ubyte(cfp); plr->multi_skill_level = cfread_ubyte(cfp); plr->multi_voice_qos = cfread_ubyte(cfp); plr->multi_voice_token_wait = cfread_int(cfp); plr->multi_voice_record_time = cfread_int(cfp); plr->multi_time_limit = cfread_int(cfp); plr->multi_kill_limit = cfread_int(cfp); plr->multi_local_flags = cfread_int(cfp); plr->multi_local_update_level = cfread_int(cfp); } void pilotfile_convert::plr_import_red_alert() { int idx, j; char name[35]; if (fver >= 242) { return; } // have to read it, but don't need any of it ... int num_slots = cfread_int(cfp); if ( (num_slots < 0) || (num_slots >= 32) ) { throw "Data check failure in red-alert!"; } if ( !num_slots ) { return; } for (idx = 0; idx < num_slots; idx++) { cfread_string(name, sizeof(name) - 1, cfp); cfread_float(cfp); cfread_string_len(name, sizeof(name), cfp); // subsystem hits for (j = 0; j < 64; j++) { cfread_float(cfp); } // aggregate hits for (j = 0; j < 12; j++) { cfread_float(cfp); } // weapons for (j = 0; j < 12; j++) { cfread_string_len(name, sizeof(name), cfp); cfread_int(cfp); } } } void pilotfile_convert::plr_import_variables() { int idx; sexp_variable nvar; int num_variables = cfread_int(cfp); if ( (num_variables < 0) || (num_variables >= 100) ) { throw "Data check failure in variables!"; } plr->variables.reserve(num_variables); for (idx = 0; idx < num_variables; idx++) { nvar.type = cfread_int(cfp); cfread_string_len(nvar.text, sizeof(nvar.text), cfp); cfread_string_len(nvar.variable_name, sizeof(nvar.variable_name), cfp); plr->variables.push_back( nvar ); } } void pilotfile_convert::plr_import() { char name[35]; int idx; unsigned int plr_id = cfread_uint(cfp); if (plr_id != 0x46505346) { throw "Invalid file signature!"; } fver = cfread_uint(cfp); if ( (fver != 142) && (fver != 242) ) { throw "Unsupported file version!"; } // multi flag plr->is_multi = (int)cfread_ubyte(cfp); // rank plr->rank = cfread_int(cfp); // mainhall, don't need it if (fver < 242) { cfread_ubyte(cfp); } plr->tips = cfread_int(cfp); if ( (plr->tips < 0) || (plr->tips > 1) ) { throw "Data check failure!"; } cfread_string_len(plr->image_filename, sizeof(plr->image_filename), cfp); cfread_string_len(plr->squad_name, sizeof(plr->squad_name), cfp); cfread_string_len(plr->squad_filename, sizeof(plr->squad_filename), cfp); cfread_string_len(plr->current_campaign, sizeof(plr->current_campaign), cfp); cfread_string_len(plr->last_ship_flown, sizeof(plr->last_ship_flown), cfp); // controls plr_import_controls(); // hud config plr_import_hud(); // cutscenes, don't need it if (fver < 242) { cfread_int(cfp); } // volume stuff plr->sound_volume = cfread_float(cfp); plr->music_volume = cfread_float(cfp); plr->voice_volume = cfread_float(cfp); // detail settings plr_import_detail(); // recent missions, don't need it int num_missions = cfread_int(cfp); for (idx = 0; idx < num_missions; idx++) { cfread_string_len(name, sizeof(name), cfp); } // stats, will skip if fver < 242 plr_import_stats(); plr->skill_level = cfread_int(cfp); if ( (plr->skill_level < 0) || (plr->skill_level > 4) ) { throw "Data check failure!"; } // extra joystick stuff for (idx = 0; idx < 5; idx++) { plr->joy_axis_map_to[idx] = cfread_int(cfp); plr->joy_invert_axis[idx] = cfread_int(cfp); } // flags plr->save_flags = cfread_int(cfp); // loadout, will skip if fver < 242 plr_import_loadout(); // multiplayer plr_import_multiplayer(); // two briefing related values plr->readyroom_listing_mode = cfread_int(cfp); Briefing_voice_enabled = cfread_int(cfp) != 0; plr->net_protocol = cfread_int(cfp); // protocol must be set to something if (plr->net_protocol == NET_NONE) { plr->net_protocol = NET_TCP; } else if ( (plr->net_protocol < 0) || (plr->net_protocol > NET_VMT) ) { throw "Data check failure!"; } // red alert status, will skip if fver < 242 (and should be empty if multi) plr_import_red_alert(); // briefing auto-advance plr->auto_advance = cfread_int(cfp); if ( (plr->auto_advance < 0) || (plr->auto_advance > 1) ) { throw "Data check failure!"; } // some input options plr->Use_mouse_to_fly = cfread_int(cfp); plr->Mouse_sensitivity = cfread_int(cfp); plr->Joy_sensitivity = cfread_int(cfp); plr->Dead_zone_size = cfread_int(cfp); // variables plr_import_variables(); // and... we're done! :) } void pilotfile_convert::plr_export_flags() { startSection(Section::Flags); // tips cfwrite_ubyte((unsigned char)plr->tips, cfp); // saved flags cfwrite_int(plr->save_flags, cfp); // listing mode (single or campaign missions) cfwrite_int(plr->readyroom_listing_mode, cfp); // briefing auto-play cfwrite_int(plr->auto_advance, cfp); // special rank setting (to avoid having to read all stats on verify) cfwrite_int(plr->rank, cfp); // What game mode we were in last on this pilot cfwrite_int(plr->is_multi, cfp); // which language was this pilot created with cfwrite_string_len(plr->language, cfp); endSection(); } void pilotfile_convert::plr_export_info() { startSection(Section::Info); // pilot image cfwrite_string_len(plr->image_filename, cfp); // squad name cfwrite_string_len(plr->squad_name, cfp); // squad image cfwrite_string_len(plr->squad_filename, cfp); // active campaign cfwrite_string_len(plr->current_campaign, cfp); endSection(); } void pilotfile_convert::plr_export_stats() { int idx, list_size = 0; startSection(Section::Scoring); // global, all-time stats cfwrite_int(all_time_stats.score, cfp); cfwrite_int(all_time_stats.rank, cfp); cfwrite_int(all_time_stats.assists, cfp); cfwrite_int(all_time_stats.kill_count, cfp); cfwrite_int(all_time_stats.kill_count_ok, cfp); cfwrite_int(all_time_stats.bonehead_kills, cfp); cfwrite_uint(all_time_stats.p_shots_fired, cfp); cfwrite_uint(all_time_stats.p_shots_hit, cfp); cfwrite_uint(all_time_stats.p_bonehead_hits, cfp); cfwrite_uint(all_time_stats.s_shots_fired, cfp); cfwrite_uint(all_time_stats.s_shots_hit, cfp); cfwrite_uint(all_time_stats.s_bonehead_hits, cfp); cfwrite_uint(all_time_stats.flight_time, cfp); cfwrite_uint(all_time_stats.missions_flown, cfp); cfwrite_int((int)all_time_stats.last_flown, cfp); cfwrite_int((int)all_time_stats.last_backup, cfp); // ship kills (contains ships across all mods, not just current) list_size = (int)all_time_stats.ship_kills.size(); cfwrite_int(list_size, cfp); for (idx = 0; idx < list_size; idx++) { cfwrite_string_len(all_time_stats.ship_kills[idx].name.c_str(), cfp); cfwrite_int(all_time_stats.ship_kills[idx].val, cfp); } // medals earned (contains medals across all mods, not just current) list_size = (int)all_time_stats.medals_earned.size(); cfwrite_int(list_size, cfp); for (idx = 0; idx < list_size; idx++) { cfwrite_string_len(all_time_stats.medals_earned[idx].name.c_str(), cfp); cfwrite_int(all_time_stats.medals_earned[idx].val, cfp); } endSection(); } void pilotfile_convert::plr_export_stats_multi() { int idx, list_size = 0; startSection(Section::ScoringMulti); // global, all-time stats cfwrite_int(multi_stats.score, cfp); cfwrite_int(multi_stats.rank, cfp); cfwrite_int(multi_stats.assists, cfp); cfwrite_int(multi_stats.kill_count, cfp); cfwrite_int(multi_stats.kill_count_ok, cfp); cfwrite_int(multi_stats.bonehead_kills, cfp); cfwrite_uint(multi_stats.p_shots_fired, cfp); cfwrite_uint(multi_stats.p_shots_hit, cfp); cfwrite_uint(multi_stats.p_bonehead_hits, cfp); cfwrite_uint(multi_stats.s_shots_fired, cfp); cfwrite_uint(multi_stats.s_shots_hit, cfp); cfwrite_uint(multi_stats.s_bonehead_hits, cfp); cfwrite_uint(multi_stats.flight_time, cfp); cfwrite_uint(multi_stats.missions_flown, cfp); cfwrite_int((int)multi_stats.last_flown, cfp); cfwrite_int((int)multi_stats.last_backup, cfp); // ship kills (contains medals across all mods, not just current) list_size = (int)multi_stats.ship_kills.size(); cfwrite_int(list_size, cfp); for (idx = 0; idx < list_size; idx++) { cfwrite_string_len(multi_stats.ship_kills[idx].name.c_str(), cfp); cfwrite_int(multi_stats.ship_kills[idx].val, cfp); } // medals earned (contains medals across all mods, not just current) list_size = (int)multi_stats.medals_earned.size(); cfwrite_int(list_size, cfp); for (idx = 0; idx < list_size; idx++) { cfwrite_string_len(multi_stats.medals_earned[idx].name.c_str(), cfp); cfwrite_int(multi_stats.medals_earned[idx].val, cfp); } endSection(); } void pilotfile_convert::plr_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::plr_export_variables() { int list_size = 0; int idx; startSection(Section::Variables); list_size = (int)plr->variables.size(); cfwrite_int(list_size, cfp); for (idx = 0; idx < list_size; idx++) { cfwrite_int(plr->variables[idx].type, cfp); cfwrite_string_len(plr->variables[idx].text, cfp); cfwrite_string_len(plr->variables[idx].variable_name, cfp); } endSection(); } void pilotfile_convert::plr_export_multiplayer() { startSection(Section::Multiplayer); // netgame options cfwrite_ubyte(plr->multi_squad_set, cfp); cfwrite_ubyte(plr->multi_endgame_set, cfp); cfwrite_int(plr->multi_flags, cfp); cfwrite_uint(plr->multi_respawn, cfp); cfwrite_ubyte(plr->multi_max_observers, cfp); cfwrite_ubyte(plr->multi_skill_level, cfp); cfwrite_ubyte(plr->multi_voice_qos, cfp); cfwrite_int(plr->multi_voice_token_wait, cfp); cfwrite_int(plr->multi_voice_record_time, cfp); cfwrite_int(plr->multi_time_limit, cfp); cfwrite_int(plr->multi_kill_limit, cfp); // local options cfwrite_int(plr->multi_local_flags, cfp); cfwrite_int(plr->multi_local_update_level, cfp); // netgame protocol cfwrite_int(plr->net_protocol, cfp); endSection(); } void pilotfile_convert::plr_export_controls() { unsigned int idx; startSection(Section::Controls); cfwrite_ushort((unsigned short)plr->controls.size(), cfp); for (idx = 0; idx < plr->controls.size(); idx++) { cfwrite_short(plr->controls[idx].get_btn(CID_KEYBOARD), cfp); cfwrite_short(plr->controls[idx].get_btn(CID_JOY0), cfp); // placeholder? for future mouse_id? cfwrite_short(-1, cfp); } // extra joystick stuff cfwrite_int(MAX_JOY_AXES_CONV, cfp); for (idx = 0; idx < MAX_JOY_AXES_CONV; idx++) { cfwrite_int(plr->joy_axis_map_to[idx], cfp); cfwrite_int(plr->joy_invert_axis[idx], cfp); } endSection(); } void pilotfile_convert::plr_export_settings() { startSection(Section::Settings); // sound/voice/music cfwrite_float(plr->sound_volume, cfp); cfwrite_float(plr->music_volume, cfp); cfwrite_float(plr->voice_volume, cfp); cfwrite_int(plr->voice_enabled, cfp); // skill level cfwrite_int(plr->skill_level, cfp); // input options cfwrite_int(plr->Use_mouse_to_fly, cfp); cfwrite_int(plr->Mouse_sensitivity, cfp); cfwrite_int(plr->Joy_sensitivity, cfp); cfwrite_int(plr->Dead_zone_size, cfp); // detail cfwrite_int(plr->detail_setting, cfp); cfwrite_int(plr->detail_nebula, cfp); cfwrite_int(plr->detail_distance, cfp); cfwrite_int(plr->detail_hardware_textures, cfp); cfwrite_int(plr->detail_num_debris, cfp); cfwrite_int(plr->detail_num_particles, cfp); cfwrite_int(plr->detail_num_stars, cfp); cfwrite_int(plr->detail_shield_effects, cfp); cfwrite_int(plr->detail_lighting, cfp); cfwrite_int(plr->detail_targetview_model, cfp); cfwrite_int(plr->detail_planets_suns, cfp); cfwrite_int(plr->detail_weapon_extras, cfp); endSection(); } void pilotfile_convert::plr_export() { Assert( cfp != NULL ); // header and version cfwrite_int(PLR_FILE_ID, cfp); cfwrite_ubyte(PLR_VERSION, cfp); // flags and info sections go first plr_export_flags(); plr_export_info(); // everything else is next, not order specific plr_export_stats(); plr_export_stats_multi(); plr_export_hud(); plr_export_variables(); plr_export_multiplayer(); plr_export_controls(); plr_export_settings(); // and... we're done! :) } bool pilotfile_convert::plr_convert(const char *fname, bool inferno) { Assert( fname != NULL ); SCP_string filename; bool rval = true; if (plr == NULL) { plr = new(std::nothrow) plr_data; } if (plr == 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(".pl2"); mprintf((" PL2 => Converting '%s'...\n", filename.c_str())); cfp = cfopen(filename.c_str(), "rb"); if ( !cfp ) { mprintf((" PL2 => Unable to open '%s' for import!\n", fname)); return false; } try { plr_import(); } catch (const char *err) { mprintf(( " PL2 => Import ERROR: %s\n", err)); rval = false; } cfclose(cfp); cfp = NULL; if ( !rval ) { return false; } filename.assign(fname); filename.append(".plr"); cfp = cfopen(filename.c_str(), "wb", CF_TYPE_PLAYERS, false, CF_LOCATION_ROOT_USER | CF_LOCATION_ROOT_GAME | CF_LOCATION_TYPE_ROOT); if ( !cfp ) { mprintf((" PLR => Unable to open '%s' for export!\n", fname)); return false; } try { plr_export(); } catch (const char *err) { mprintf((" PLR => Export ERROR: %s\n", err)); rval = false; } cfclose(cfp); cfp = NULL; if (rval) { mprintf((" PLR => Conversion complete!\n")); } return rval; }