From ee4b554dd47f49919cfe1108ff9b98cc2f715bf8 Mon Sep 17 00:00:00 2001 From: Goober5000 Date: Sun, 23 Aug 2026 21:38:25 -0400 Subject: [PATCH 1/2] standardize and update the various _INDEX macros --- code/ai/ai_profiles.h | 2 +- code/ai/aiturret.cpp | 2 +- code/debris/debris.cpp | 2 +- code/mission/missionparse.h | 2 +- code/network/multi.h | 2 +- code/object/object.h | 2 +- code/ship/ship.h | 6 +++--- code/weapon/beam.h | 2 +- code/weapon/shockwave.cpp | 2 +- code/weapon/swarm.cpp | 2 +- code/weapon/weapon.h | 4 ++-- code/weapon/weapons.cpp | 15 ++------------- 12 files changed, 16 insertions(+), 27 deletions(-) diff --git a/code/ai/ai_profiles.h b/code/ai/ai_profiles.h index ecd6b22fe69..88f66d354bd 100644 --- a/code/ai/ai_profiles.h +++ b/code/ai/ai_profiles.h @@ -163,7 +163,7 @@ extern int Num_ai_profiles; extern int Default_ai_profile; extern ai_profile_t Ai_profiles[MAX_AI_PROFILES]; -#define AI_PROFILES_INDEX(ai_p) ((int)((ai_p) - Ai_profiles)) +#define AI_PROFILES_INDEX(ai_p) (static_cast((ai_p)-Ai_profiles)) void ai_profiles_init(); diff --git a/code/ai/aiturret.cpp b/code/ai/aiturret.cpp index bba3b6cef43..c2b9a537d81 100644 --- a/code/ai/aiturret.cpp +++ b/code/ai/aiturret.cpp @@ -1755,7 +1755,7 @@ bool turret_fire_weapon(int weapon_num, parent_aip = &Ai_info[Ships[Objects[parent_objnum].instance].ai_index]; parent_ship = &Ships[Objects[parent_objnum].instance]; - int turret_weapon_class = weapon_info_get_index(wip); + int turret_weapon_class = WEAPON_INFO_INDEX(wip); #ifndef NDEBUG // moved here from check_ok_to_fire diff --git a/code/debris/debris.cpp b/code/debris/debris.cpp index 7d45fa0ee79..1f960f949f2 100644 --- a/code/debris/debris.cpp +++ b/code/debris/debris.cpp @@ -56,7 +56,7 @@ int Debris_num_submodels = 0; particle::ParticleEffectHandle Debris_hit_particle; -#define DEBRIS_INDEX(dp) (int)(dp-Debris.data()) +#define DEBRIS_INDEX(dp) (static_cast((dp)-Debris.data())) // Find the first available arc slot. If none is available, and no_create is false, add one. debris_electrical_arc *debris_find_or_create_electrical_arc_slot(debris *db, bool no_create); diff --git a/code/mission/missionparse.h b/code/mission/missionparse.h index 74850a0e140..40dbf55614e 100644 --- a/code/mission/missionparse.h +++ b/code/mission/missionparse.h @@ -542,7 +542,7 @@ class p_object // Goober5000 - this is now dynamic extern SCP_vector Parse_objects; -#define POBJ_INDEX(pobjp) (int)(pobjp - &Parse_objects[0]) // yes, this arithmetic is valid :D +#define POBJ_INDEX(pobjp) (static_cast((pobjp)-Parse_objects.data())) extern p_object Support_ship_pobj, *Arriving_support_ship; extern p_object Ship_arrival_list; diff --git a/code/network/multi.h b/code/network/multi.h index 80e8608b4f5..721bf9fc4d2 100644 --- a/code/network/multi.h +++ b/code/network/multi.h @@ -135,7 +135,7 @@ class player; // // netplayer management -#define NET_PLAYER_INDEX(np) (static_cast(np-Net_players)) +#define NET_PLAYER_INDEX(np) (static_cast((np)-Net_players)) #define NET_PLAYER_NUM(np) (NET_PLAYER_INDEX(np)) #define MY_NET_PLAYER_NUM (NET_PLAYER_INDEX(Net_player)) diff --git a/code/object/object.h b/code/object/object.h index 24cda4bd068..984769d15fc 100644 --- a/code/object/object.h +++ b/code/object/object.h @@ -250,7 +250,7 @@ extern object *Player_obj; // Which object is the player. Has to be valid. // given it's pointer. This way, we can replace it with a macro // to check that the pointer is valid for debugging. // This code will break in 64 bit builds when we have more than 2^31 objects but that will probably never happen -#define OBJ_INDEX(objp) static_cast(objp-Objects) +#define OBJ_INDEX(objp) (static_cast((objp)-Objects)) /* * FUNCTIONS diff --git a/code/ship/ship.h b/code/ship/ship.h index 0aaf086a2f7..ab4d21952c0 100644 --- a/code/ship/ship.h +++ b/code/ship/ship.h @@ -1769,9 +1769,9 @@ extern SCP_vector Wing_formations; // Use the below macros when you want to find the index of an array element in the // Wings[] or Ships[] arrays. -#define WING_INDEX(wingp) (static_cast(wingp-Wings)) -#define SHIP_INDEX(shipp) (static_cast(shipp-Ships)) -#define SHIP_REGISTRY_INDEX(ship_entry) (static_cast(ship_entry-Ship_registry.data())) +#define WING_INDEX(wingp) (static_cast((wingp)-Wings)) +#define SHIP_INDEX(shipp) (static_cast((shipp)-Ships)) +#define SHIP_REGISTRY_INDEX(ship_entry) (static_cast((ship_entry)-Ship_registry.data())) extern void ship_init(); // called once at game start diff --git a/code/weapon/beam.h b/code/weapon/beam.h index c34181eaa0f..e182f492502 100644 --- a/code/weapon/beam.h +++ b/code/weapon/beam.h @@ -226,7 +226,7 @@ typedef struct beam { extern std::array Beams; // all beams extern int Beam_count; -#define BEAM_INDEX(beam) (int)((beam) - Beams.data()) +#define BEAM_INDEX(beam) (static_cast((beam)-Beams.data())) // ------------------------------------------------------------------------------------------------ // BEAM WEAPON FUNCTIONS diff --git a/code/weapon/shockwave.cpp b/code/weapon/shockwave.cpp index 74dbedd4ac0..651a7443fc7 100644 --- a/code/weapon/shockwave.cpp +++ b/code/weapon/shockwave.cpp @@ -42,7 +42,7 @@ int Shockwave_inited = 0; // ----------------------------------------------------------- // Function macros // ----------------------------------------------------------- -#define SW_INDEX(sw) (sw-Shockwaves) +#define SW_INDEX(sw) (static_cast((sw)-Shockwaves)) // ----------------------------------------------------------- // Externals diff --git a/code/weapon/swarm.cpp b/code/weapon/swarm.cpp index 2d510467de0..9dae9f96b57 100644 --- a/code/weapon/swarm.cpp +++ b/code/weapon/swarm.cpp @@ -387,7 +387,7 @@ void turret_swarm_set_up_info(int parent_objnum, ship_subsys *turret, const weap int bank_fired = swp->current_secondary_bank; // initialize tsi - tsi->weapon_class = weapon_info_get_index(wip); + tsi->weapon_class = WEAPON_INFO_INDEX(wip); if (wip->wi_flags[Weapon::Info_Flags::Swarm]) { tsi->num_to_launch = wip->swarm_count; } else { diff --git a/code/weapon/weapon.h b/code/weapon/weapon.h index 578f846ed90..afc561418c8 100644 --- a/code/weapon/weapon.h +++ b/code/weapon/weapon.h @@ -977,7 +977,8 @@ extern int Default_cmeasure_index; extern SCP_vector Player_weapon_precedence; // Vector of weapon types, precedence list for player weapon selection -#define WEAPON_INDEX(wp) (int)(wp-Weapons) +#define WEAPON_INDEX(wp) (static_cast((wp)-Weapons)) +#define WEAPON_INFO_INDEX(wip) (static_cast((wip)-Weapon_info.data())) typedef struct tracking_info { ship_subsys *subsys; @@ -988,7 +989,6 @@ typedef struct tracking_info { } tracking_info; int weapon_info_lookup(const char *name); -int weapon_info_get_index(const weapon_info *wip); inline int weapon_info_size() { diff --git a/code/weapon/weapons.cpp b/code/weapon/weapons.cpp index cb446621c0d..221fc65069f 100644 --- a/code/weapon/weapons.cpp +++ b/code/weapon/weapons.cpp @@ -381,17 +381,6 @@ int weapon_info_lookup(const char *name) return -1; } -/** - * Return the index of Weapon_info used by this pointer. Equivalent to the old WEAPON_INFO_INDEX macro: - * #define WEAPON_INFO_INDEX(wip) (int)(wip-Weapon_info) - */ -int weapon_info_get_index(const weapon_info *wip) -{ - Assertion(wip != nullptr, "NULL wip passed to weapon_info_get_index"); - const weapon_info *data = Weapon_info.data(); - return static_cast(std::distance(data, wip)); -} - // Parse the weapon flags. void parse_wi_flags(weapon_info *weaponp) { @@ -3885,7 +3874,7 @@ int parse_weapon(int subtype, bool replace, const char *filename) wip->particle_spewers[spew_index] = particle::util::parseEffect(wip->name); } else { // we have a valid index, now parse the spewer already - auto& pspew_buffer = pspew_legacy_parse_data_buffer[weapon_info_get_index(wip)][spew_index]; + auto& pspew_buffer = pspew_legacy_parse_data_buffer[WEAPON_INFO_INDEX(wip)][spew_index]; if (pspew_buffer.particle_spew_type == PSPEW_NONE) { //This must be an uninitialized effect, store defaults. pspew_buffer.particle_spew_count = 1; @@ -3988,7 +3977,7 @@ int parse_weapon(int subtype, bool replace, const char *filename) if (wip->particle_spewers.empty()) { wip->particle_spewers.emplace_back(particle::ParticleEffectHandle::invalid()); } - auto& pspew_buffer = pspew_legacy_parse_data_buffer[weapon_info_get_index(wip)][0]; + auto& pspew_buffer = pspew_legacy_parse_data_buffer[WEAPON_INFO_INDEX(wip)][0]; pspew_buffer.particle_spew_count = 1; pspew_buffer.particle_spew_time = 25; pspew_buffer.particle_spew_vel = 0.4f; From 41884cb1a38699f88929d8238c7e512f93a281af Mon Sep 17 00:00:00 2001 From: Goober5000 Date: Sat, 22 Aug 2026 00:22:58 -0400 Subject: [PATCH 2/2] wing name format compatibility and other cleanup Following PR #7429, ships belonging to wings were hashed the same way as every other ship, instead of the original hackish format where the hash was juggled around the number. This was a mostly transparent upgrade, and ship names were automatically converted from the legacy format to the new format. However, missions that referenced ships *in subsequent waves* broke; the converter only handled ships in the first wave. This PR adds compatibility checks to `eval_ship` and `ship_registry_get` to accommodate legacy names. --- code/mission/missionparse.cpp | 47 ++++++++++++--- code/parse/sexp.cpp | 10 +++- code/ship/ship.cpp | 105 ++++++++++++++++++++++++++++++---- code/ship/ship.h | 3 + 4 files changed, 144 insertions(+), 21 deletions(-) diff --git a/code/mission/missionparse.cpp b/code/mission/missionparse.cpp index 301a229abf9..94aa7836c6c 100644 --- a/code/mission/missionparse.cpp +++ b/code/mission/missionparse.cpp @@ -4801,8 +4801,26 @@ int parse_wing_create_ships( wing *wingp, int num_to_create, bool force_create, wingp->total_arrived_count++; if (wingp->num_waves > 1) { + char pre_bash_name[NAME_LENGTH]; + strcpy_s(pre_bash_name, p_objp->name); + wing_bash_ship_name(p_objp, wingp, wingp->total_arrived_count + wingp->red_alert_skipped_ships); + // if the bash renamed this parse object (which can happen if the ship was saved using the legacy + // hash format), re-key its not-yet-present registry entry so that the old name doesn't shadow + // lookups of the new name. (For subsequent waves, the pre-bash name belongs to a previous wave's + // ship, whose entry must keep its key; the status and parse object checks exclude that case.) + if (stricmp(pre_bash_name, p_objp->name) != 0) + { + auto ship_it = Ship_registry_map.find(pre_bash_name); + if (ship_it != Ship_registry_map.end() + && Ship_registry[ship_it->second].status == ShipStatus::NOT_YET_PRESENT + && Ship_registry[ship_it->second].pobj_num == POBJ_INDEX(p_objp)) + { + ship_registry_rename(ship_it->second, p_objp->name, true); + } + } + // subsequent waves of ships will not be in the ship registry, so add them if (!ship_registry_exists(p_objp->name)) { @@ -8026,6 +8044,17 @@ int mission_parse_get_multi_mission_info( const char *filename ) return The_mission.num_players; } +static p_object *mission_parse_get_arrival_ship_sub(const char *name) +{ + for (auto p_objp : list_range(&Ship_arrival_list)) + { + if (!stricmp(p_objp->name, name)) + return p_objp; // still on the arrival list + } + + return nullptr; +} + /** * @brief Returns the parse object on the ship arrival list associated with the given name. * @param[in] name The name of the object @@ -8036,18 +8065,18 @@ int mission_parse_get_multi_mission_info( const char *filename ) */ p_object *mission_parse_get_arrival_ship(const char *name) { - p_object *p_objp; - if (name == nullptr) return nullptr; - for (p_objp = GET_FIRST(&Ship_arrival_list); p_objp != END_OF_LIST(&Ship_arrival_list); p_objp = GET_NEXT(p_objp)) - { - if (!stricmp(p_objp->name, name)) - { - return p_objp; // still on the arrival list - } - } + // try the normal lookup + auto p_objp = mission_parse_get_arrival_ship_sub(name); + if (p_objp) + return p_objp; + + // also search for ship names hashed using the legacy format + SCP_string legacy_hashed; + if (wing_bash_legacy_hashed_ship_name(legacy_hashed, name)) + return mission_parse_get_arrival_ship_sub(legacy_hashed.c_str()); return nullptr; } diff --git a/code/parse/sexp.cpp b/code/parse/sexp.cpp index 90dcf10f95d..eea938359d5 100644 --- a/code/parse/sexp.cpp +++ b/code/parse/sexp.cpp @@ -5846,7 +5846,15 @@ const ship_registry_entry *eval_ship(int node) return eval_ship(arg_node); } - auto ship_it = Ship_registry_map.find(CTEXT(node)); + // look up the ship in the ship registry + auto ship_name = CTEXT(node); + auto ship_it = Ship_registry_map.find(ship_name); + if (ship_it == Ship_registry_map.end()) + { + SCP_string legacy_hashed; + if (wing_bash_legacy_hashed_ship_name(legacy_hashed, ship_name)) + ship_it = Ship_registry_map.find(legacy_hashed); + } if (ship_it != Ship_registry_map.end()) { // cache the value if it can't change later diff --git a/code/ship/ship.cpp b/code/ship/ship.cpp index 5f10428f44f..66984ad0232 100644 --- a/code/ship/ship.cpp +++ b/code/ship/ship.cpp @@ -238,6 +238,15 @@ int ship_registry_get_index(const char *name) if (ship_it != Ship_registry_map.end()) return ship_it->second; + // also search for ship names hashed using the legacy format + SCP_string legacy_hashed; + if (wing_bash_legacy_hashed_ship_name(legacy_hashed, name)) + { + ship_it = Ship_registry_map.find(legacy_hashed); + if (ship_it != Ship_registry_map.end()) + return ship_it->second; + } + return -1; } @@ -247,6 +256,15 @@ int ship_registry_get_index(const SCP_string &name) if (ship_it != Ship_registry_map.end()) return ship_it->second; + // also search for ship names hashed using the legacy format + SCP_string legacy_hashed; + if (wing_bash_legacy_hashed_ship_name(legacy_hashed, name.c_str())) + { + ship_it = Ship_registry_map.find(legacy_hashed); + if (ship_it != Ship_registry_map.end()) + return ship_it->second; + } + return -1; } @@ -267,18 +285,18 @@ bool ship_registry_exists(int index) const ship_registry_entry *ship_registry_get(const char *name) { - auto ship_it = Ship_registry_map.find(name); - if (ship_it != Ship_registry_map.end()) - return &Ship_registry[ship_it->second]; + auto idx = ship_registry_get_index(name); + if (idx >= 0) + return &Ship_registry[idx]; return nullptr; } const ship_registry_entry *ship_registry_get(const SCP_string &name) { - auto ship_it = Ship_registry_map.find(name); - if (ship_it != Ship_registry_map.end()) - return &Ship_registry[ship_it->second]; + auto idx = ship_registry_get_index(name); + if (idx >= 0) + return &Ship_registry[idx]; return nullptr; } @@ -297,6 +315,27 @@ const ship_registry_entry *ship_registry_get(anchor_t anchor) return ship_registry_get(anchor.value()); } +void ship_registry_rename(int entry_index, const char *new_name, bool erase_old_key) +{ + Assertion(Ship_registry.in_bounds(entry_index), "Invalid ship registry index %d passed to ship_registry_rename!", entry_index); + if (!Ship_registry.in_bounds(entry_index)) + return; + + auto entry = &Ship_registry[entry_index]; + + // the old key is sometimes useful for looking up the ship under its previous name; + // if not, remove it (provided it actually refers to this entry) + if (erase_old_key) + { + auto ship_it = Ship_registry_map.find(entry->name); + if (ship_it != Ship_registry_map.end() && ship_it->second == entry_index) + Ship_registry_map.erase(ship_it); + } + + strcpy_s(entry->name, new_name); + Ship_registry_map[entry->name] = entry_index; +} + int Num_engine_wash_types; int Num_ship_subobj_types; @@ -15028,6 +15067,34 @@ void wing_bash_ship_name(ship *shipp, const wing *wingp, int ordinal, bool reset } } +bool wing_bash_legacy_hashed_ship_name(SCP_string &dest, const char *src) +{ + // missions might have ships within wings that were saved using the legacy hash format, with the hash suffix at the end + auto hash = get_pointer_to_first_hash_symbol(src); + if (hash && *(hash + 1) != '\0') + { + // find the run of digits immediately preceding the hash + auto digits = hash; + while (digits > src && isdigit(static_cast(*(digits - 1)))) + digits--; + + // the ordinal must be at least one digit, preceded by a space, preceded by the wing name + if (digits < hash && digits > (src + 1) && *(digits - 1) == ' ') + { + // move the ordinal from before the hash to the end of the name + dest.assign(src, digits - 1); + dest += hash; + dest += ' '; + dest.append(digits, hash); + + // we changed it + return true; + } + } + + return false; +} + /** * Return the object index of the ship with name *name. */ @@ -15251,10 +15318,7 @@ int ship_info_lookup(const char *token) return ship_info_lookup_sub(name); } -/** - * Return the ship index of the ship with name *name. - */ -int ship_name_lookup(const char *name, int inc_players) +static int ship_name_lookup_sub(const char *name, int inc_players) { Assertion(name != nullptr, "NULL name passed to ship_name_lookup"); @@ -15272,7 +15336,26 @@ int ship_name_lookup(const char *name, int inc_players) return -1; } -int ship_type_name_lookup_sub(const char *name) +/** + * Return the ship index of the ship with name *name. + */ +int ship_name_lookup(const char *name, int inc_players) +{ + // try the normal lookup + auto idx = ship_name_lookup_sub(name, inc_players); + if (idx >= 0) + return idx; + + // also search for ship names hashed using the legacy format + SCP_string legacy_hashed; + if (wing_bash_legacy_hashed_ship_name(legacy_hashed, name)) + return ship_name_lookup_sub(legacy_hashed.c_str(), inc_players); + + // couldn't find it + return -1; +} + +static int ship_type_name_lookup_sub(const char *name) { Assertion(name != nullptr, "NULL name passed to ship_type_name_lookup"); diff --git a/code/ship/ship.h b/code/ship/ship.h index ab4d21952c0..407cc48b72c 100644 --- a/code/ship/ship.h +++ b/code/ship/ship.h @@ -1060,6 +1060,7 @@ extern const ship_registry_entry *ship_registry_get(const char *name); extern const ship_registry_entry *ship_registry_get(const SCP_string &name); extern const ship_registry_entry *ship_registry_get(int index); extern const ship_registry_entry *ship_registry_get(anchor_t anchor); +extern void ship_registry_rename(int entry_index, const char *new_name, bool erase_old_key); #define REGULAR_WEAPON (1<<0) #define DOGFIGHT_WEAPON (1<<1) @@ -1876,6 +1877,8 @@ extern void wing_bash_ship_name(SCP_string &ship_name, const char *wing_name, in extern void wing_bash_ship_name(char *ship_name, const char *wing_name, int ordinal); extern void wing_bash_ship_name(p_object *p_objp, const wing *wingp, int ordinal, bool reset_display_name_if_normal = false); extern void wing_bash_ship_name(ship *shipp, const wing *wingp, int ordinal, bool reset_display_name_if_normal = false); +extern bool wing_bash_legacy_hashed_ship_name(SCP_string &dest, const char *src); + extern int Player_ship_class; // Do the special effect for energy dissipating into the shield for a hit.