Skip to content

Commit 9807eb8

Browse files
committed
fix some bugs and issues
1 parent babd885 commit 9807eb8

7 files changed

Lines changed: 162 additions & 53 deletions

File tree

code/missioneditor/sexp_tree_actions.cpp

Lines changed: 17 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -73,7 +73,7 @@ void SexpTreeActions::replace_variable_data(int var_idx, int type)
7373
clear_node_children(node_idx);
7474

7575
// Assemble name
76-
sprintf(buf, "%s(%s)", Sexp_variables[var_idx].variable_name, Sexp_variables[var_idx].text);
76+
snprintf(buf, sizeof(buf), "%s(%s)", Sexp_variables[var_idx].variable_name, Sexp_variables[var_idx].text);
7777

7878
_model.set_node(node_idx, type, buf);
7979
void* h = _model.tree_nodes[node_idx].handle;
@@ -435,7 +435,7 @@ int SexpTreeActions::add_default_operator(int op_index, int argnum)
435435
}
436436

437437
char node_text[2 * TOKEN_LENGTH + 2];
438-
sprintf(node_text, "%s(%s)", item.text.c_str(), Sexp_variables[sexp_var_index].text);
438+
snprintf(node_text, sizeof(node_text), "%s(%s)", item.text.c_str(), Sexp_variables[sexp_var_index].text);
439439
add_variable_data(node_text, type);
440440
}
441441
// special case for sexps that take containers
@@ -489,18 +489,17 @@ int SexpTreeActions::add_default_operator(int op_index, int argnum)
489489
void SexpTreeActions::verify_and_fix_arguments(int node)
490490
{
491491
int op_index, arg_num, type, tmp;
492-
static int here_count = 0;
493492
sexp_list_item* list;
494493
sexp_list_item* ptr;
495494
bool is_variable_arg = false;
496495

497-
if (here_count)
496+
if (_verify_arguments_reentry_guard)
498497
return;
499498

500-
here_count++;
499+
_verify_arguments_reentry_guard++;
501500
op_index = get_operator_index(_model.tree_nodes[node].text);
502501
if (op_index < 0) {
503-
here_count--;
502+
_verify_arguments_reentry_guard--;
504503
return;
505504
}
506505

@@ -509,6 +508,7 @@ void SexpTreeActions::verify_and_fix_arguments(int node)
509508
arg_num = 0;
510509
_model.item_index = _model.tree_nodes[node].child;
511510
while (_model.item_index >= 0) {
511+
is_variable_arg = false;
512512
// get listing of valid argument values for node item_index
513513
type = query_operator_argument_type(op_index, arg_num);
514514
// special case for modify-variable
@@ -526,7 +526,7 @@ void SexpTreeActions::verify_and_fix_arguments(int node)
526526
if (!list && (arg_num >= Operators[op_index].min)) {
527527
_model.free_node(_model.item_index, 1);
528528
_model.item_index = tmp;
529-
here_count--;
529+
_verify_arguments_reentry_guard--;
530530
return;
531531
}
532532

@@ -558,6 +558,8 @@ void SexpTreeActions::verify_and_fix_arguments(int node)
558558
}
559559

560560
if (types_match) {
561+
list->destroy();
562+
list = nullptr;
561563
_model.item_index = _model.tree_nodes[_model.item_index].next;
562564
arg_num++;
563565
continue;
@@ -605,6 +607,11 @@ void SexpTreeActions::verify_and_fix_arguments(int node)
605607

606608
if (_model.tree_nodes[_model.item_index].type & SEXPT_OPERATOR)
607609
verify_and_fix_arguments(_model.item_index);
610+
611+
if (list) {
612+
list->destroy();
613+
list = nullptr;
614+
}
608615
}
609616

610617
// fix the node if it is the argument for modify-variable
@@ -630,7 +637,7 @@ void SexpTreeActions::verify_and_fix_arguments(int node)
630637
}
631638

632639
_model.item_index = tmp;
633-
here_count--;
640+
_verify_arguments_reentry_guard--;
634641
}
635642

636643
// -----------------------------------------------------------------------
@@ -646,7 +653,7 @@ void SexpTreeActions::delete_sexp_tree_variable(const char* var_name)
646653
char search_str[64];
647654
char replace_text[TOKEN_LENGTH];
648655

649-
sprintf(search_str, "%s(", var_name);
656+
snprintf(search_str, sizeof(search_str), "%s(", var_name);
650657

651658
int old_item_index = _model.item_index;
652659

@@ -693,7 +700,7 @@ void SexpTreeActions::modify_sexp_tree_variable(const char* old_name, int sexp_v
693700

694701
int old_item_index = _model.item_index;
695702

696-
sprintf(search_str, "%s(", old_name);
703+
snprintf(search_str, sizeof(search_str), "%s(", old_name);
697704

698705
for (int idx = 0; idx < static_cast<int>(_model.tree_nodes.size()); idx++) {
699706
if (_model.tree_nodes[idx].type & SEXPT_VARIABLE) {

code/missioneditor/sexp_tree_actions.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -115,4 +115,5 @@ class SexpTreeActions {
115115

116116
SexpTreeModel& _model; // shared tree node data and logic
117117
ISexpTreeUI& _ui; // UI callback interface for widget updates
118+
int _verify_arguments_reentry_guard = 0;
118119
};

code/missioneditor/sexp_tree_model.cpp

Lines changed: 23 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -36,15 +36,26 @@ constexpr int TREE_NODE_INCREMENT = 100;
3636
// If op_num is an op value (>= FIRST_OP), it is converted to an operator index first.
3737
void sexp_list_item::set_op(int op_num)
3838
{
39-
int i;
39+
int op_index = op_num;
4040

4141
if (op_num >= FIRST_OP) { // do we have an op value instead of an op number (index)?
42-
for (i = 0; i < static_cast<int>(Operators.size()); i++)
43-
if (op_num == Operators[i].value)
44-
op_num = i; // convert op value to op number
42+
op_index = -1;
43+
for (int i = 0; i < static_cast<int>(Operators.size()); i++) {
44+
if (op_num == Operators[i].value) {
45+
op_index = i; // convert op value to op number
46+
break;
47+
}
48+
}
4549
}
4650

47-
op = op_num;
51+
if (!SCP_vector_inbounds(Operators, op_index)) {
52+
op = -1;
53+
text = "<invalid operator>";
54+
type = SEXPT_UNINIT;
55+
return;
56+
}
57+
58+
op = op_index;
4859
text = Operators[op].text;
4960
type = (SEXPT_OPERATOR | SEXPT_VALID);
5061
}
@@ -403,9 +414,9 @@ void get_combined_variable_name(char* combined_name, const char* sexp_var_name)
403414
int sexp_var_index = get_index_sexp_variable_name(sexp_var_name);
404415

405416
if (sexp_var_index >= 0)
406-
sprintf(combined_name, "%s(%s)", Sexp_variables[sexp_var_index].variable_name, Sexp_variables[sexp_var_index].text);
417+
snprintf(combined_name, 2 * TOKEN_LENGTH + 2, "%s(%s)", Sexp_variables[sexp_var_index].variable_name, Sexp_variables[sexp_var_index].text);
407418
else
408-
sprintf(combined_name, "%s(undefined)", sexp_var_name);
419+
snprintf(combined_name, 2 * TOKEN_LENGTH + 2, "%s(undefined)", sexp_var_name);
409420
}
410421

411422
// Clear all tree nodes and reset counters. If 'op' is provided and non-empty,
@@ -848,6 +859,9 @@ int SexpTreeModel::query_restricted_opf_range(int opf)
848859
// Returns -1 if the node has no valid parent.
849860
int SexpTreeModel::get_sibling_place(int node) const
850861
{
862+
if (!SCP_vector_inbounds(tree_nodes, node))
863+
return -1;
864+
851865
if (!SCP_vector_inbounds(tree_nodes, tree_nodes[node].parent))
852866
return -1;
853867

@@ -890,7 +904,7 @@ NodeImage SexpTreeModel::get_data_image(int node) const
890904
int idx = (count % 100) / 5;
891905

892906
// There are 20 numbered data icons (DATA_00 through DATA_95)
893-
if (idx > 20) {
907+
if (idx >= 20) {
894908
return NodeImage::DATA;
895909
}
896910

@@ -1032,7 +1046,7 @@ void get_variable_default_text_from_variable_text(char* text, char* default_text
10321046
int SexpTreeModel::get_item_index_to_var_index() const
10331047
{
10341048
// check valid item index and node is a variable
1035-
if ((item_index > 0) && (tree_nodes[item_index].type & SEXPT_VARIABLE)) {
1049+
if ((item_index >= 0) && SCP_vector_inbounds(tree_nodes, item_index) && (tree_nodes[item_index].type & SEXPT_VARIABLE)) {
10361050
return get_tree_name_to_sexp_variable_index(tree_nodes[item_index].text);
10371051
} else {
10381052
return -1;

code/missioneditor/sexp_tree_model.h

Lines changed: 62 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -146,8 +146,6 @@ struct sexp_list_item {
146146
void add_data(const char* str, int t = (SEXPT_STRING | SEXPT_VALID));
147147
// Append another linked list to the end of this one
148148
void add_list(sexp_list_item* list);
149-
// Copy fields from src without transferring ownership of the next pointer
150-
void shallow_copy(const sexp_list_item* src);
151149
// Delete this item and all subsequent items in the linked list
152150
void destroy();
153151
};
@@ -307,7 +305,7 @@ struct SexpContextMenuState {
307305
// Campaign mode filtering
308306
bool campaign_mode = false;
309307

310-
// Data lists for add/replace data submenus (caller must call destroy())
308+
// Data lists for add/replace data submenus
311309
// Entries with op >= 0 are operators to enable; op < 0 are data items for the submenu
312310
sexp_list_item* add_data_list = nullptr;
313311
int add_data_opf_type = 0; // OPF_* type, needed for OPF_VARIABLE_NAME display
@@ -340,7 +338,66 @@ struct SexpContextMenuState {
340338
bool show_container_data = false;
341339
SCP_vector<ContainerEntry> replace_container_data;
342340

343-
// Free the dynamically allocated data lists (must be called when done with this state)
341+
SexpContextMenuState() = default;
342+
~SexpContextMenuState()
343+
{
344+
cleanup();
345+
}
346+
SexpContextMenuState(const SexpContextMenuState&) = delete;
347+
SexpContextMenuState& operator=(const SexpContextMenuState&) = delete;
348+
SexpContextMenuState(SexpContextMenuState&& other) noexcept
349+
{
350+
*this = std::move(other);
351+
}
352+
SexpContextMenuState& operator=(SexpContextMenuState&& other) noexcept
353+
{
354+
if (this != &other) {
355+
cleanup();
356+
357+
is_labeled_root = other.is_labeled_root;
358+
is_root_editable = other.is_root_editable;
359+
can_edit_text = other.can_edit_text;
360+
can_edit_comment = other.can_edit_comment;
361+
can_edit_bg_color = other.can_edit_bg_color;
362+
can_add_variable = other.can_add_variable;
363+
can_modify_variable = other.can_modify_variable;
364+
can_copy = other.can_copy;
365+
can_cut = other.can_cut;
366+
can_paste = other.can_paste;
367+
can_paste_add = other.can_paste_add;
368+
can_delete = other.can_delete;
369+
can_replace_number = other.can_replace_number;
370+
can_replace_string = other.can_replace_string;
371+
can_add_number = other.can_add_number;
372+
can_add_string = other.can_add_string;
373+
add_type = other.add_type;
374+
replace_type = other.replace_type;
375+
insert_opf_type = other.insert_opf_type;
376+
add_count = other.add_count;
377+
replace_count = other.replace_count;
378+
modify_variable = other.modify_variable;
379+
campaign_mode = other.campaign_mode;
380+
add_data_list = other.add_data_list;
381+
add_data_opf_type = other.add_data_opf_type;
382+
replace_data_list = other.replace_data_list;
383+
add_enabled_op_indices = std::move(other.add_enabled_op_indices);
384+
replace_enabled_op_indices = std::move(other.replace_enabled_op_indices);
385+
op_add_enabled = std::move(other.op_add_enabled);
386+
op_replace_enabled = std::move(other.op_replace_enabled);
387+
op_insert_enabled = std::move(other.op_insert_enabled);
388+
replace_variables = std::move(other.replace_variables);
389+
show_container_names = other.show_container_names;
390+
replace_container_names = std::move(other.replace_container_names);
391+
show_container_data = other.show_container_data;
392+
replace_container_data = std::move(other.replace_container_data);
393+
394+
other.add_data_list = nullptr;
395+
other.replace_data_list = nullptr;
396+
}
397+
return *this;
398+
}
399+
400+
// Free the dynamically allocated data lists (safe to call multiple times)
344401
void cleanup() {
345402
if (add_data_list) { add_data_list->destroy(); add_data_list = nullptr; }
346403
if (replace_data_list) { replace_data_list->destroy(); replace_data_list = nullptr; }
@@ -506,7 +563,7 @@ class SexpTreeModel {
506563

507564
// Analyze the current selection and compute which context menu actions are available.
508565
// The returned state includes operator enablement, variable/container menus, and
509-
// clipboard paste validation. Caller must call cleanup() on the result.
566+
// clipboard paste validation. Returned state owns temporary lists and cleans them up automatically.
510567
SexpContextMenuState compute_context_menu_state();
511568
// Returns true if the given operator value should be hidden from menus (deprecated/hidden ops)
512569
static bool is_operator_hidden(int op_value);

code/missioneditor/sexp_tree_opf.cpp

Lines changed: 28 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -199,7 +199,10 @@ sexp_list_item *SexpTreeOPF::get_listing_opf_prop()
199199
ptr = GET_FIRST(&obj_used_list);
200200
while (ptr != END_OF_LIST(&obj_used_list)) {
201201
if (ptr->type == OBJ_PROP) {
202-
head.add_data(prop_id_lookup(ptr->instance)->prop_name);
202+
auto prop = prop_id_lookup(ptr->instance);
203+
if (prop != nullptr) {
204+
head.add_data(prop->prop_name);
205+
}
203206
}
204207

205208
ptr = GET_NEXT(ptr);
@@ -477,9 +480,15 @@ sexp_list_item *SexpTreeOPF::get_listing_opf_docker_point(int parent_node, int a
477480

478481
if (sh >= 0)
479482
{
480-
polymodel *pm = model_get(Ship_info[Ships[sh].ship_info_index].model_num);
481-
for (int i = 0; i < pm->n_docks; i++)
482-
head.add_data(pm->docking_bays[i].name);
483+
auto model_num = Ship_info[Ships[sh].ship_info_index].model_num;
484+
if (model_num >= 0) {
485+
polymodel* pm = model_get(model_num);
486+
if (pm != nullptr) {
487+
for (int i = 0; i < pm->n_docks; i++) {
488+
head.add_data(pm->docking_bays[i].name);
489+
}
490+
}
491+
}
483492
}
484493

485494
return head.next;
@@ -517,9 +526,15 @@ sexp_list_item *SexpTreeOPF::get_listing_opf_dockee_point(int parent_node)
517526

518527
if (sh >= 0)
519528
{
520-
polymodel *pm = model_get(Ship_info[Ships[sh].ship_info_index].model_num);
521-
for (int i = 0; i < pm->n_docks; i++)
522-
head.add_data(pm->docking_bays[i].name);
529+
auto model_num = Ship_info[Ships[sh].ship_info_index].model_num;
530+
if (model_num >= 0) {
531+
polymodel* pm = model_get(model_num);
532+
if (pm != nullptr) {
533+
for (int i = 0; i < pm->n_docks; i++) {
534+
head.add_data(pm->docking_bays[i].name);
535+
}
536+
}
537+
}
523538
}
524539

525540
return head.next;
@@ -1429,11 +1444,17 @@ sexp_list_item *SexpTreeOPF::get_listing_opf_animation_name(int parent_node)
14291444
if (child < 0)
14301445
return nullptr;
14311446
sh = ship_name_lookup(_model.tree_nodes[child].text, 1);
1447+
if (sh < 0) {
1448+
return nullptr;
1449+
}
14321450

14331451
switch(op) {
14341452
case OP_TRIGGER_ANIMATION_NEW:
14351453
case OP_STOP_LOOPING_ANIMATION: {
14361454
child = _model.tree_nodes[child].next;
1455+
if (child < 0) {
1456+
return head.next;
1457+
}
14371458
auto triggerType = animation::anim_match_type(_model.tree_nodes[child].text);
14381459

14391460
for (const auto& anim_ref : Ship_info[Ships[sh].ship_info_index].animations.getRegisteredTriggers()) {

0 commit comments

Comments
 (0)