Skip to content

Commit 237516c

Browse files
Goober5000claude
andcommitted
Convert CSG/PLR fixed ship/weapon arrays to SCP_vector
Ship_info is already an SCP_vector but MAX_SHIP_CLASSES (500) is held in place by fixed-size structures throughout the engine and editors. This converts the seven file-format-adjacent arrays whose persistence touches the .csg campaign save and .plr pilot file formats: campaign::ships_allowed [MAX_SHIP_CLASSES] -> SCP_vector<ubyte> campaign::weapons_allowed [MAX_WEAPON_TYPES] -> SCP_vector<ubyte> loadout_data::ship_pool [MAX_SHIP_CLASSES] -> SCP_vector<int> loadout_data::weapon_pool [MAX_WEAPON_TYPES] -> SCP_vector<int> scoring_struct::kills [MAX_SHIP_CLASSES] -> SCP_vector<int> scoring_struct::m_kills [MAX_SHIP_CLASSES] -> SCP_vector<int> scoring_struct::m_okKills [MAX_SHIP_CLASSES] -> SCP_vector<int> The CSG and PLR file formats already store explicit counts and key by ship/weapon names, so no on-disk format change is needed. Existing read/write loops already iterated by ship_info_size() / weapon_info_size() and just compile through against the new vector types. Vectors are sized to ship_info_size() / weapon_info_size() in scoring_struct::init(), mission_campaign_clear(), player_loadout_init(), and the campaign-pool reset paths in csg.cpp and multi_campaign.cpp. All of these run after weapon_init() / ship_init(), so the sizing invariant holds. scoring_struct globals constructed at static-init time start with empty vectors; first index access is always post-table-parse. Multiplayer stats sync (multimsgs.cpp) now derives offsets and counts from the vector size; added USHRT_MAX assertions on the sender side since the wire protocol packs offset/count as USHORT. The FSTracker sync (multi_fstracker.cpp) clamps vmt->num_ships down to the local mod's kills.size() before transmission and guards the receive-side write so a tracker payload with more ship slots than this mod has won't overrun the vector. The MAX_SHIP_CLASSES cap itself stays in place at ship.cpp:2327; this is one prerequisite toward lifting it. The other ~25 fixed-size ship-class-indexed structures (team_data, Ss_pool_teams, Wl_ships, FRED2 editor arrays, qtFRED Editor::_ship_usage, etc.) remain to be addressed in follow-up PRs. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent 74dad95 commit 237516c

16 files changed

Lines changed: 102 additions & 115 deletions

File tree

code/mission/missioncampaign.cpp

Lines changed: 22 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -392,35 +392,33 @@ void mission_campaign_build_list(bool desc, bool sort, bool multiplayer)
392392
*/
393393
void mission_campaign_get_sw_info()
394394
{
395-
int i, count, ship_list[MAX_SHIP_CLASSES], weapon_list[MAX_WEAPON_TYPES];
396-
397-
if (optional_string("+Starting Ships:")) {
398-
count = sz2i(stuff_int_list(ship_list, MAX_SHIP_CLASSES, ParseLookupType::SHIP_INFO_TYPE));
399-
400-
// now set the array elements stating which ships we are allowed
401-
for (i = 0; i < count; i++) {
402-
if (Ship_info[ship_list[i]].flags[Ship::Info_Flags::Player_ship])
403-
Campaign.ships_allowed[ship_list[i]] = 1;
404-
}
405-
}
406-
else {
395+
if (optional_string("+Starting Ships:")) {
396+
SCP_vector<int> ship_list;
397+
stuff_int_list(ship_list, ParseLookupType::SHIP_INFO_TYPE);
398+
399+
// now set the array elements stating which ships we are allowed
400+
for (int idx : ship_list) {
401+
if (Ship_info[idx].flags[Ship::Info_Flags::Player_ship])
402+
Campaign.ships_allowed[idx] = 1;
403+
}
404+
} else {
407405
// set allowable ships to the SIF_PLAYER_SHIPs
408406
for (auto it = Ship_info.cbegin(); it != Ship_info.cend(); ++it) {
409407
if (it->flags[Ship::Info_Flags::Player_ship])
410408
Campaign.ships_allowed[std::distance(Ship_info.cbegin(), it)] = 1;
411409
}
412410
}
413411

414-
if (optional_string("+Starting Weapons:")) {
415-
count = sz2i(stuff_int_list(weapon_list, MAX_WEAPON_TYPES, ParseLookupType::WEAPON_POOL_TYPE));
412+
if (optional_string("+Starting Weapons:")) {
413+
SCP_vector<int> weapon_list;
414+
stuff_int_list(weapon_list, ParseLookupType::WEAPON_POOL_TYPE);
416415

417-
// now set the array elements stating which ships we are allowed
418-
for (i = 0; i < count; i++) {
419-
if (Weapon_info[weapon_list[i]].wi_flags[Weapon::Info_Flags::Player_allowed])
420-
Campaign.weapons_allowed[weapon_list[i]] = 1;
416+
// now set the array elements stating which weapons we are allowed
417+
for (int idx : weapon_list) {
418+
if (Weapon_info[idx].wi_flags[Weapon::Info_Flags::Player_allowed])
419+
Campaign.weapons_allowed[idx] = 1;
421420
}
422-
}
423-
else {
421+
} else {
424422
// set allowable weapons to the player-allowed ones
425423
for (auto it = Weapon_info.cbegin(); it != Weapon_info.cend(); ++it) {
426424
if (it->wi_flags[Weapon::Info_Flags::Player_allowed])
@@ -732,13 +730,8 @@ void player_loadout_init()
732730
memset(Player_loadout.filename, 0, sizeof(Player_loadout.filename));
733731
memset(Player_loadout.last_modified, 0, sizeof(Player_loadout.last_modified));
734732

735-
for ( i = 0; i < MAX_SHIP_CLASSES; i++ ) {
736-
Player_loadout.ship_pool[i] = 0;
737-
}
738-
739-
for ( i = 0; i < MAX_WEAPON_TYPES; i++ ) {
740-
Player_loadout.weapon_pool[i] = 0;
741-
}
733+
Player_loadout.ship_pool.assign(ship_info_size(), 0);
734+
Player_loadout.weapon_pool.assign(weapon_info_size(), 0);
742735

743736
for ( i = 0; i < MAX_WSS_SLOTS; i++ ) {
744737
Player_loadout.unit_data[i].ship_class = -1;
@@ -1270,8 +1263,8 @@ void mission_campaign_clear()
12701263
Campaign.loop_reentry = 0;
12711264
Campaign.realign_required = 0;
12721265
Campaign.num_players = 0;
1273-
memset( Campaign.ships_allowed, 0, sizeof(Campaign.ships_allowed) );
1274-
memset( Campaign.weapons_allowed, 0, sizeof(Campaign.weapons_allowed) );
1266+
Campaign.ships_allowed.assign(ship_info_size(), 0);
1267+
Campaign.weapons_allowed.assign(weapon_info_size(), 0);
12751268
Campaign.persistent_variables.clear();
12761269
Campaign.red_alert_variables.clear();
12771270
Campaign.persistent_containers.clear();

code/mission/missioncampaign.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -129,8 +129,8 @@ class campaign
129129
int loop_reentry; // mission number to return to after loop is finished
130130
int realign_required; // are any missions missing alignment info? (Fred)
131131
int num_players; // valid in multiplayer campaigns -- number of players campaign supports.
132-
ubyte ships_allowed[MAX_SHIP_CLASSES]; // which ships the player can use
133-
ubyte weapons_allowed[MAX_WEAPON_TYPES]; // which weapons the player can use
132+
SCP_vector<ubyte> ships_allowed; // which ships the player can use
133+
SCP_vector<ubyte> weapons_allowed; // which weapons the player can use
134134
cmission missions[MAX_CAMPAIGN_MISSIONS]; // decription of the missions
135135
SCP_vector<sexp_variable> persistent_variables; // These variables will be saved at the end of a mission
136136
SCP_vector<sexp_variable> red_alert_variables; // state of the variables in the previous mission of a Red Alert scenario.

code/missionui/missionscreencommon.cpp

Lines changed: 9 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1095,13 +1095,13 @@ void wss_save_loadout()
10951095
Assert( (Ss_pool != NULL) && (Wl_pool != NULL) && (Wss_slots != NULL) );
10961096

10971097
// save the ship pool
1098-
for ( i = 0; i < MAX_SHIP_CLASSES; i++ ) {
1099-
Player_loadout.ship_pool[i] = Ss_pool[i];
1098+
for ( i = 0; i < ship_info_size(); i++ ) {
1099+
Player_loadout.ship_pool[i] = Ss_pool[i];
11001100
}
11011101

11021102
// save the weapons pool
1103-
for ( i = 0; i < MAX_WEAPON_TYPES; i++ ) {
1104-
Player_loadout.weapon_pool[i] = Wl_pool[i];
1103+
for ( i = 0; i < weapon_info_size(); i++ ) {
1104+
Player_loadout.weapon_pool[i] = Wl_pool[i];
11051105
}
11061106

11071107
// save the ship class / weapons for each slot
@@ -1132,23 +1132,13 @@ void wss_maybe_restore_loadout()
11321132
return;
11331133
}
11341134

1135-
// first we generate a pool of ships and weapons used the last time this mission was played. We also generate a pool of what is
1135+
// first we generate a pool of ships and weapons used the last time this mission was played. We also generate a pool of what is
11361136
// available in this mission.
1137-
int last_loadout_ships[MAX_SHIP_CLASSES];
1138-
int this_loadout_ships[MAX_SHIP_CLASSES];
1139-
1140-
int last_loadout_weapons[MAX_WEAPON_TYPES];
1141-
int this_loadout_weapons[MAX_WEAPON_TYPES];
1137+
SCP_vector<int> last_loadout_ships(ship_info_size(), 0);
1138+
SCP_vector<int> this_loadout_ships(ship_info_size(), 0);
11421139

1143-
// zero all pools
1144-
for (i = 0; i < MAX_SHIP_CLASSES; i++) {
1145-
last_loadout_ships[i] = 0;
1146-
this_loadout_ships[i] = 0;
1147-
}
1148-
for (i = 0; i < MAX_WEAPON_TYPES; i++) {
1149-
last_loadout_weapons[i] = 0;
1150-
this_loadout_weapons[i] = 0;
1151-
}
1140+
SCP_vector<int> last_loadout_weapons(weapon_info_size(), 0);
1141+
SCP_vector<int> this_loadout_weapons(weapon_info_size(), 0);
11521142

11531143
// record the ship classes / weapons used last time
11541144
for ( i = 0; i < MAX_WSS_SLOTS; i++ ) {

code/missionui/missionscreencommon.h

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -207,8 +207,8 @@ typedef struct loadout_data
207207
char filename[MAX_FILENAME_LEN]; // mission filename
208208
char last_modified[DATE_TIME_LENGTH]; // when mission was last modified
209209
wss_unit unit_data[MAX_WSS_SLOTS]; // ship and weapon data
210-
int weapon_pool[MAX_WEAPON_TYPES]; // available weapons
211-
int ship_pool[MAX_SHIP_CLASSES]; // available ships
210+
SCP_vector<int> weapon_pool; // available weapons
211+
SCP_vector<int> ship_pool; // available ships
212212
} loadout_data;
213213

214214
extern loadout_data Player_loadout;

code/network/multi_campaign.cpp

Lines changed: 7 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -279,29 +279,29 @@ void multi_campaign_process_update(ubyte *data, header *hinfo)
279279
}
280280
} else {
281281
// clear the ships and weapons allowed arrays
282-
memset(Campaign.ships_allowed,0,MAX_SHIP_CLASSES);
283-
memset(Campaign.weapons_allowed,0,MAX_WEAPON_TYPES);
282+
Campaign.ships_allowed.assign(ship_info_size(), 0);
283+
Campaign.weapons_allowed.assign(weapon_info_size(), 0);
284284

285285
// get all ship classes
286286
GET_USHORT(spool_size);
287287
for(idx=0;idx<spool_size;idx++){
288288
GET_USHORT(s_val);
289289

290-
if (s_val < MAX_SHIP_CLASSES) {
290+
if (Campaign.ships_allowed.in_bounds(s_val)) {
291291
Campaign.ships_allowed[s_val] = 1;
292292
}
293-
}
294-
293+
}
294+
295295
// get all weapon classes
296296
GET_USHORT(wpool_size);
297297
for(idx=0;idx<wpool_size;idx++){
298298
GET_USHORT(s_val);
299299

300-
if (s_val < MAX_WEAPON_TYPES) {
300+
if (Campaign.weapons_allowed.in_bounds(s_val)) {
301301
Campaign.weapons_allowed[s_val] = 1;
302302
}
303303
}
304-
}
304+
}
305305

306306
// ack the server
307307
Net_player->state = NETPLAYER_STATE_CPOOL_ACK;

code/network/multi_fstracker.cpp

Lines changed: 10 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -893,6 +893,11 @@ void multi_stats_fs_to_tracker(scoring_struct *fs, vmt_stats_struct *vmt, player
893893
// find only up to last in array with at least 1 kill
894894
vmt->num_ships = MAX_FS2OPEN_COUNTS - vmt->num_medals;
895895

896+
// can't transmit more ship classes than this mod actually has
897+
if (static_cast<size_t>(vmt->num_ships) > fs->kills.size()) {
898+
vmt->num_ships = static_cast<unsigned char>(fs->kills.size());
899+
}
900+
896901
for (int idx = vmt->num_ships-1; idx >= 0; --idx) {
897902
if (fs->kills[idx] > 0) {
898903
break;
@@ -958,7 +963,11 @@ void multi_stats_tracker_to_fs(vmt_stats_struct *vmt,scoring_struct *fs)
958963
fs->medal_counts[idx] = static_cast<int>(vmt->counts[idx]);
959964
}
960965
} else {
961-
fs->kills[idx2++] = static_cast<int>(vmt->counts[idx]);
966+
// tracker may have more ship slots than this mod's ship_info; drop excess
967+
if (fs->kills.in_bounds(idx2)) {
968+
fs->kills[idx2] = static_cast<int>(vmt->counts[idx]);
969+
}
970+
++idx2;
962971
}
963972
}
964973
}

code/network/multimsgs.cpp

Lines changed: 12 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -6564,11 +6564,13 @@ void send_player_stats_block_packet(net_player *pl, int stats_code, net_player *
65646564

65656565
// kill information - alltime
65666566
switch(stats_code){
6567-
case STATS_ALLTIME:
6567+
case STATS_ALLTIME:
65686568
// alltime kills
6569+
// Wire protocol packs offset/count as USHORT; bump to wider field if ship_info_size() ever exceeds USHRT_MAX.
6570+
Assertion(sc->kills.size() <= USHRT_MAX, "ship_info_size() exceeds STATS_ALLTIME_KILLS packet's USHORT field width");
65696571
idx = 0;
6570-
while (idx < MAX_SHIP_CLASSES) {
6571-
send_player_stats_block_packet(pl, STATS_ALLTIME_KILLS, target, idx, MAX_SHIP_CLASSES-idx);
6572+
while (idx < sz2i(sc->kills.size())) {
6573+
send_player_stats_block_packet(pl, STATS_ALLTIME_KILLS, target, idx, sz2i(sc->kills.size())-idx);
65726574
idx += MAX_SHIPS_PER_PACKET;
65736575
}
65746576

@@ -6600,11 +6602,12 @@ void send_player_stats_block_packet(net_player *pl, int stats_code, net_player *
66006602
ADD_INT(sc->last_backup); // should be 32-bit value - taylor
66016603
break;
66026604

6603-
case STATS_MISSION:
6604-
// mission OKkills
6605+
case STATS_MISSION:
6606+
// mission OKkills
6607+
Assertion(sc->m_okKills.size() <= USHRT_MAX, "ship_info_size() exceeds STATS_MISSION_CLASS_KILLS packet's USHORT field width");
66056608
idx = 0;
6606-
while (idx < MAX_SHIP_CLASSES) {
6607-
send_player_stats_block_packet(pl, STATS_MISSION_CLASS_KILLS, target, idx, MAX_SHIP_CLASSES-idx);
6609+
while (idx < sz2i(sc->m_okKills.size())) {
6610+
send_player_stats_block_packet(pl, STATS_MISSION_CLASS_KILLS, target, idx, sz2i(sc->m_okKills.size())-idx);
66086611
idx += MAX_SHIPS_PER_PACKET;
66096612
}
66106613

@@ -6720,7 +6723,7 @@ void process_player_stats_block_packet(ubyte *data, header *hinfo)
67206723
for (idx = si_offset; idx < si_offset+si_count; idx++) {
67216724
GET_INT(i_tmp);
67226725

6723-
if (idx < MAX_SHIP_CLASSES) {
6726+
if (sc->kills.in_bounds(idx)) {
67246727
sc->kills[idx] = i_tmp;
67256728
}
67266729
}
@@ -6733,7 +6736,7 @@ void process_player_stats_block_packet(ubyte *data, header *hinfo)
67336736
for (idx = si_offset; idx < si_offset+si_count; idx++) {
67346737
GET_INT(i_tmp);
67356738

6736-
if (idx < MAX_SHIP_CLASSES) {
6739+
if (sc->m_okKills.in_bounds(idx)) {
67376740
sc->m_okKills[idx] = i_tmp;
67386741
}
67396742
}

code/pilotfile/csg.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1610,8 +1610,8 @@ void pilotfile::csg_reset_data(bool reset_ships_and_weapons)
16101610

16111611
// zero out allowed ships/weapons
16121612
if (reset_ships_and_weapons) {
1613-
memset(Campaign.ships_allowed, 0, sizeof(Campaign.ships_allowed));
1614-
memset(Campaign.weapons_allowed, 0, sizeof(Campaign.weapons_allowed));
1613+
Campaign.ships_allowed.assign(ship_info_size(), 0);
1614+
Campaign.weapons_allowed.assign(weapon_info_size(), 0);
16151615
}
16161616

16171617
// reset campaign status

code/pilotfile/pilotfile.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -333,7 +333,7 @@ void pilotfile::set_multi_stats(const scoring_struct *stats)
333333
multi_stats.ship_kills.clear();
334334
multi_stats.ship_kills.shrink_to_fit();
335335

336-
auto kills_size = std::min(SDL_arraysize(stats->kills), Ship_info.size());
336+
auto kills_size = stats->kills.size();
337337

338338
for (idx = 0; idx < kills_size; ++idx) {
339339
if (stats->kills[idx] <= 0) {
@@ -446,7 +446,7 @@ bool pilotfile::export_stats(scoring_struct *stats)
446446

447447
// only export ships that this mod knows about (should already be index)
448448
for (auto &item : p_stats->ship_kills) {
449-
if ( (item.index >= 0) && (item.index < MAX_SHIP_CLASSES) ) {
449+
if ( stats->kills.in_bounds(item.index) ) {
450450
stats->kills[item.index] = item.val;
451451
}
452452
}

code/pilotfile/plr.cpp

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -568,7 +568,7 @@ void pilotfile::plr_read_stats()
568568
for (size_t idx = 0; idx < list_size; idx++) {
569569
auto j = all_time_stats.ship_kills[idx].index;
570570

571-
if (j >= 0) {
571+
if (p->stats.kills.in_bounds(j)) {
572572
p->stats.kills[j] = all_time_stats.ship_kills[idx].val;
573573
}
574574
}
@@ -665,7 +665,7 @@ void pilotfile::plr_read_stats_multi()
665665
for (size_t idx = 0; idx < list_size; idx++) {
666666
auto j = multi_stats.ship_kills[idx].index;
667667

668-
if (j >= 0) {
668+
if (p->stats.kills.in_bounds(j)) {
669669
p->stats.kills[j] = multi_stats.ship_kills[idx].val;
670670
}
671671
}

0 commit comments

Comments
 (0)