diff --git a/src/saveload/tracerestrict_sl.cpp b/src/saveload/tracerestrict_sl.cpp index 52dae2a4fc..ed773121ae 100644 --- a/src/saveload/tracerestrict_sl.cpp +++ b/src/saveload/tracerestrict_sl.cpp @@ -77,12 +77,12 @@ static void Load_TRRP() char str[4096]; char *strend = str + seprintf(str, lastof(str), "Trace restrict program %d: %s\nProgram dump:", index, GetStringPtr(validation_result.GetErrorMessage())); - for (unsigned int i = 0; i < prog->items.size(); i++) { - if (i % 3) { - strend += seprintf(strend, lastof(str), " %08X", prog->items[i]); - } else { - strend += seprintf(strend, lastof(str), "\n%4u: %08X", i, prog->items[i]); + uint fail_offset = validation_result.GetResultData() ^ (1 << 31); + for (uint i = 0; i < (uint)prog->items.size(); i++) { + if ((i % 3) == 0) { + strend += seprintf(strend, lastof(str), "\n%4u:", i); } + strend += seprintf(strend, lastof(str), (i == fail_offset) ? " [%08X]" : " %08X", prog->items[i]); } SlErrorCorrupt(str); } diff --git a/src/tracerestrict.cpp b/src/tracerestrict.cpp index 888f60ca81..7f4ebc12f3 100644 --- a/src/tracerestrict.cpp +++ b/src/tracerestrict.cpp @@ -450,7 +450,6 @@ void TraceRestrictProgram::Execute(const Train* v, const TraceRestrictProgramInp break; } - case TRIT_COND_TRAIN_STATUS: { bool has_status = false; switch (static_cast(GetTraceRestrictValue(item))) { @@ -956,11 +955,21 @@ CommandCost TraceRestrictProgram::Validate(const std::vector TraceRestrictItem item = items[i]; TraceRestrictItemType type = GetTraceRestrictType(item); + auto validation_error = [i](StringID str) -> CommandCost { + CommandCost result(str); + result.SetResultData((1 << 31) | (uint)i); + return result; + }; + + auto unknown_instruction = [&]() { + return validation_error(STR_TRACE_RESTRICT_ERROR_VALIDATE_UNKNOWN_INSTRUCTION); + }; + // check multi-word instructions if (IsTraceRestrictDoubleItem(item)) { i++; if (i >= size) { - return_cmd_error(STR_TRACE_RESTRICT_ERROR_OFFSET_TOO_LARGE); // instruction ran off end + return validation_error(STR_TRACE_RESTRICT_ERROR_OFFSET_TOO_LARGE); // instruction ran off end } } @@ -969,12 +978,12 @@ CommandCost TraceRestrictProgram::Validate(const std::vector if (type == TRIT_COND_ENDIF) { if (condstack.empty()) { - return_cmd_error(STR_TRACE_RESTRICT_ERROR_VALIDATE_NO_IF); // else/endif with no starting if + return validation_error(STR_TRACE_RESTRICT_ERROR_VALIDATE_NO_IF); // else/endif with no starting if } if (condflags & TRCF_ELSE) { // else if (condstack.back() & TRCSF_SEEN_ELSE) { - return_cmd_error(STR_TRACE_RESTRICT_ERROR_VALIDATE_DUP_ELSE); // Two else clauses + return validation_error(STR_TRACE_RESTRICT_ERROR_VALIDATE_DUP_ELSE); // Two else clauses } HandleCondition(condstack, condflags, true); condstack.back() |= TRCSF_SEEN_ELSE; @@ -985,15 +994,221 @@ CommandCost TraceRestrictProgram::Validate(const std::vector } else { if (condflags & (TRCF_OR | TRCF_ELSE)) { // elif/orif if (condstack.empty()) { - return_cmd_error(STR_TRACE_RESTRICT_ERROR_VALIDATE_ELIF_NO_IF); // Pre-empt assertions in HandleCondition + return validation_error(STR_TRACE_RESTRICT_ERROR_VALIDATE_ELIF_NO_IF); // Pre-empt assertions in HandleCondition } if (condstack.back() & TRCSF_SEEN_ELSE) { - return_cmd_error(STR_TRACE_RESTRICT_ERROR_VALIDATE_DUP_ELSE); // else clause followed by elif/orif + return validation_error(STR_TRACE_RESTRICT_ERROR_VALIDATE_DUP_ELSE); // else clause followed by elif/orif } } HandleCondition(condstack, condflags, true); } + const TraceRestrictCondOp condop = GetTraceRestrictCondOp(item); + auto invalid_condition = [&]() -> bool { + switch (condop) { + case TRCO_IS: + case TRCO_ISNOT: + case TRCO_LT: + case TRCO_LTE: + case TRCO_GT: + case TRCO_GTE: + return false; + default: + return true; + } + }; + auto invalid_binary_condition = [&]() -> bool { + switch (condop) { + case TRCO_IS: + case TRCO_ISNOT: + return false; + default: + return true; + } + }; + auto invalid_order_condition = [&]() -> bool { + if (invalid_binary_condition()) return true; + switch (static_cast(GetTraceRestrictAuxField(item))) { + case TROCAF_STATION: + case TROCAF_WAYPOINT: + case TROCAF_DEPOT: + return false; + default: + return true; + } + }; + + /* Validation action instruction */ + switch (GetTraceRestrictType(item)) { + case TRIT_COND_ENDIF: + case TRIT_COND_UNDEFINED: + break; + + case TRIT_COND_TRAIN_LENGTH: + case TRIT_COND_MAX_SPEED: + case TRIT_COND_LOAD_PERCENT: + case TRIT_COND_COUNTER_VALUE: + case TRIT_COND_RESERVED_TILES: + if (invalid_condition()) return unknown_instruction(); + break; + + case TRIT_COND_CARGO: + case TRIT_COND_TRAIN_GROUP: + case TRIT_COND_TRAIN_IN_SLOT: + case TRIT_COND_TRAIN_OWNER: + case TRIT_COND_RESERVATION_THROUGH: + if (invalid_binary_condition()) return unknown_instruction(); + break; + + case TRIT_COND_CURRENT_ORDER: + case TRIT_COND_NEXT_ORDER: + case TRIT_COND_LAST_STATION: + if (invalid_order_condition()) return unknown_instruction(); + break; + + case TRIT_COND_ENTRY_DIRECTION: + if (invalid_binary_condition()) return unknown_instruction(); + switch (GetTraceRestrictValue(item)) { + case TRNTSV_NE: + case TRNTSV_SE: + case TRNTSV_SW: + case TRNTSV_NW: + case TRDTSV_FRONT: + case TRDTSV_BACK: + case TRDTSV_TUNBRIDGE_ENTER: + case TRDTSV_TUNBRIDGE_EXIT: + break; + + default: + return unknown_instruction(); + } + break; + + case TRIT_COND_PBS_ENTRY_SIGNAL: + if (invalid_binary_condition()) return unknown_instruction(); + switch (static_cast(GetTraceRestrictAuxField(item))) { + case TRPESAF_VEH_POS: + case TRPESAF_RES_END: + case TRPESAF_RES_END_TILE: + break; + + default: + return unknown_instruction(); + } + break; + + + case TRIT_COND_PHYS_PROP: + if (invalid_condition()) return unknown_instruction(); + switch (static_cast(GetTraceRestrictAuxField(item))) { + case TRPPCAF_WEIGHT: + case TRPPCAF_POWER: + case TRPPCAF_MAX_TE: + break; + + default: + return unknown_instruction(); + } + break; + + case TRIT_COND_PHYS_RATIO: + if (invalid_condition()) return unknown_instruction(); + switch (static_cast(GetTraceRestrictAuxField(item))) { + case TRPPRCAF_POWER_WEIGHT: + case TRPPRCAF_MAX_TE_WEIGHT: + break; + + default: + return unknown_instruction(); + } + break; + + case TRIT_COND_TIME_DATE_VALUE: + if (invalid_condition()) return unknown_instruction(); + switch (static_cast(GetTraceRestrictValue(item))) { + case TRTDVF_MINUTE: + case TRTDVF_HOUR: + case TRTDVF_HOUR_MINUTE: + case TRTDVF_DAY: + case TRTDVF_MONTH: + break; + + default: + return unknown_instruction(); + } + break; + + case TRIT_COND_CATEGORY: + if (invalid_binary_condition()) return unknown_instruction(); + switch (static_cast(GetTraceRestrictAuxField(item))) { + case TRCCAF_ENGINE_CLASS: + break; + + default: + return unknown_instruction(); + } + break; + + case TRIT_COND_TARGET_DIRECTION: + if (invalid_binary_condition()) return unknown_instruction(); + switch (static_cast(GetTraceRestrictAuxField(item))) { + case TRTDCAF_CURRENT_ORDER: + case TRTDCAF_NEXT_ORDER: + break; + + default: + return unknown_instruction(); + } + switch (GetTraceRestrictValue(item)) { + case DIAGDIR_NE: + case DIAGDIR_SE: + case DIAGDIR_SW: + case DIAGDIR_NW: + break; + + default: + return unknown_instruction(); + } + break; + + case TRIT_COND_TRAIN_STATUS: + if (invalid_binary_condition()) return unknown_instruction(); + switch (static_cast(GetTraceRestrictValue(item))) { + case TRTSVF_EMPTY: + case TRTSVF_FULL: + case TRTSVF_BROKEN_DOWN: + case TRTSVF_NEEDS_REPAIR: + case TRTSVF_REVERSING: + case TRTSVF_HEADING_TO_STATION_WAYPOINT: + case TRTSVF_HEADING_TO_DEPOT: + case TRTSVF_LOADING: + case TRTSVF_WAITING: + case TRTSVF_LOST: + case TRTSVF_REQUIRES_SERVICE: + break; + + default: + return unknown_instruction(); + } + break; + + case TRIT_COND_SLOT_OCCUPANCY: + if (invalid_condition()) return unknown_instruction(); + switch (static_cast(GetTraceRestrictAuxField(item))) { + case TRSOCAF_OCCUPANTS: + case TRSOCAF_REMAINING: + break; + + default: + return unknown_instruction(); + } + break; + + default: + return unknown_instruction(); + } + + /* Determine actions_used_flags */ switch (GetTraceRestrictType(item)) { case TRIT_COND_ENDIF: case TRIT_COND_UNDEFINED: @@ -1043,13 +1258,29 @@ CommandCost TraceRestrictProgram::Validate(const std::vector break; default: - return_cmd_error(STR_TRACE_RESTRICT_ERROR_VALIDATE_UNKNOWN_INSTRUCTION); + /* Validation has already been done, above */ + NOT_REACHED(); } } else { switch (GetTraceRestrictType(item)) { case TRIT_PF_DENY: + actions_used_flags |= TRPAUF_PF; + break; + case TRIT_PF_PENALTY: actions_used_flags |= TRPAUF_PF; + + switch (static_cast(GetTraceRestrictAuxField(item))) { + case TRPPAF_VALUE: + break; + + case TRPPAF_PRESET: + if (GetTraceRestrictValue(item) >= TRPPPI_END) return unknown_instruction(); + break; + + default: + return unknown_instruction(); + } break; case TRIT_RESERVE_THROUGH: @@ -1078,8 +1309,7 @@ CommandCost TraceRestrictProgram::Validate(const std::vector break; default: - NOT_REACHED(); - break; + return unknown_instruction(); } break; @@ -1123,8 +1353,7 @@ CommandCost TraceRestrictProgram::Validate(const std::vector break; default: - NOT_REACHED(); - break; + return unknown_instruction(); } break; @@ -1139,8 +1368,7 @@ CommandCost TraceRestrictProgram::Validate(const std::vector break; default: - NOT_REACHED(); - break; + return unknown_instruction(); } break; @@ -1150,26 +1378,72 @@ CommandCost TraceRestrictProgram::Validate(const std::vector case TRIT_NEWS_CONTROL: actions_used_flags |= TRPAUF_TRAIN_NOT_STUCK; + + switch (static_cast(GetTraceRestrictValue(item))) { + case TRNCF_TRAIN_NOT_STUCK: + case TRNCF_CANCEL_TRAIN_NOT_STUCK: + break; + + default: + return unknown_instruction(); + } break; case TRIT_COUNTER: actions_used_flags |= TRPAUF_CHANGE_COUNTER; + + switch (static_cast(GetTraceRestrictCondOp(item))) { + case TRCCOF_INCREASE: + case TRCCOF_DECREASE: + case TRCCOF_SET: + break; + + default: + return unknown_instruction(); + } break; case TRIT_PF_PENALTY_CONTROL: actions_used_flags |= TRPAUF_NO_PBS_BACK_PENALTY; + + switch (static_cast(GetTraceRestrictValue(item))) { + case TRPPCF_NO_PBS_BACK_PENALTY: + case TRPPCF_CANCEL_NO_PBS_BACK_PENALTY: + break; + + default: + return unknown_instruction(); + } break; case TRIT_SPEED_ADAPTATION_CONTROL: actions_used_flags |= TRPAUF_SPEED_ADAPTATION; + + switch (static_cast(GetTraceRestrictValue(item))) { + case TRSACF_SPEED_ADAPT_EXEMPT: + case TRSACF_REMOVE_SPEED_ADAPT_EXEMPT: + break; + + default: + return unknown_instruction(); + } break; case TRIT_SIGNAL_MODE_CONTROL: actions_used_flags |= TRPAUF_CMB_SIGNAL_MODE_CTRL; + + switch (static_cast(GetTraceRestrictValue(item))) { + case TRSMCF_NORMAL_ASPECT: + case TRSMCF_SHUNT_ASPECT: + break; + + default: + return unknown_instruction(); + } break; default: - return_cmd_error(STR_TRACE_RESTRICT_ERROR_VALIDATE_UNKNOWN_INSTRUCTION); + return unknown_instruction(); } } }