diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 8c0197f801..baf210d945 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -239,6 +239,8 @@ add_files( newgrf_airporttiles.h newgrf_animation_base.h newgrf_animation_type.h + newgrf_analysis.cpp + newgrf_analysis.h newgrf_cache_check.h newgrf_callbacks.h newgrf_canal.cpp diff --git a/src/newgrf_analysis.cpp b/src/newgrf_analysis.cpp new file mode 100644 index 0000000000..64e0c624da --- /dev/null +++ b/src/newgrf_analysis.cpp @@ -0,0 +1,313 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file newgrf_analysis.cpp NewGRF analysis. */ + +#include "stdafx.h" +#include "newgrf_analysis.h" +#include "newgrf_industrytiles_analysis.h" +#include "newgrf_spritegroup.h" + +#include "safeguards.h" + +void DeterministicSpriteGroup::AnalyseCallbacks(AnalyseCallbackOperation &op) const +{ + auto res = op.seen.insert(this); + if (!res.second) { + /* Already seen this group */ + return; + } + + if (op.mode == ACOM_INDUSTRY_TILE && op.data.indtile->anim_state_at_offset) return; + + auto check_1A_range = [&]() -> bool { + if (this->GroupMayBeBypassed()) { + /* Not clear why some GRFs do this, perhaps a way of commenting out a branch */ + uint32 value = (this->adjusts.size() == 1) ? EvaluateDeterministicSpriteGroupAdjust(this->size, this->adjusts[0], nullptr, 0, UINT_MAX) : 0; + for (const auto &range : this->ranges) { + if (range.low <= value && value <= range.high) { + if (range.group != nullptr) range.group->AnalyseCallbacks(op); + return true; + } + } + if (this->default_group != nullptr) this->default_group->AnalyseCallbacks(op); + return true; + } + return false; + }; + + if (op.mode == ACOM_FIND_CB_RESULT) { + if (this->calculated_result) { + op.result_flags |= ACORF_CB_RESULT_FOUND; + return; + } else if (!(op.result_flags & ACORF_CB_RESULT_FOUND)) { + if (check_1A_range()) return; + auto check_var_filter = [&](uint8 var, uint value) -> bool { + if (this->adjusts.size() == 1 && this->adjusts[0].variable == var && (this->adjusts[0].operation == DSGA_OP_ADD || this->adjusts[0].operation == DSGA_OP_RST)) { + const auto &adjust = this->adjusts[0]; + if (adjust.shift_num == 0 && (adjust.and_mask & 0xFF) == 0xFF && adjust.type == DSGA_TYPE_NONE) { + for (const auto &range : this->ranges) { + if (range.low == range.high && range.low == value) { + if (range.group != nullptr) range.group->AnalyseCallbacks(op); + return true; + } + } + if (this->default_group != nullptr) this->default_group->AnalyseCallbacks(op); + return true; + } + } + return false; + }; + if (check_var_filter(0xC, op.data.cb_result.callback)) return; + if (op.data.cb_result.check_var_10 && check_var_filter(0x10, op.data.cb_result.var_10_value)) return; + for (const auto &range : this->ranges) { + if (range.group != nullptr) range.group->AnalyseCallbacks(op); + } + if (this->default_group != nullptr) this->default_group->AnalyseCallbacks(op); + } + return; + } + + if (check_1A_range()) return; + + if ((op.mode == ACOM_CB_VAR || op.mode == ACOM_CB_REFIT_CAPACITY) && this->var_scope != VSG_SCOPE_SELF) { + op.result_flags |= ACORF_CB_REFIT_CAP_NON_WHITELIST_FOUND; + } + + auto find_cb_result = [&](const SpriteGroup *group, AnalyseCallbackOperation::FindCBResultData data) -> bool { + if (group == nullptr) return false; + AnalyseCallbackOperation cbr_op; + cbr_op.mode = ACOM_FIND_CB_RESULT; + cbr_op.data.cb_result = data; + group->AnalyseCallbacks(cbr_op); + return (cbr_op.result_flags & ACORF_CB_RESULT_FOUND); + }; + + if (this->adjusts.size() == 1 && !this->calculated_result && (this->adjusts[0].operation == DSGA_OP_ADD || this->adjusts[0].operation == DSGA_OP_RST)) { + const auto &adjust = this->adjusts[0]; + if (op.mode == ACOM_CB_VAR && adjust.variable == 0xC) { + if (adjust.shift_num == 0 && (adjust.and_mask & 0xFF) == 0xFF && adjust.type == DSGA_TYPE_NONE) { + bool found_refit_cap = false; + for (const auto &range : this->ranges) { + if (range.low == range.high) { + switch (range.low) { + case CBID_VEHICLE_32DAY_CALLBACK: + op.callbacks_used |= SGCU_VEHICLE_32DAY_CALLBACK; + break; + + case CBID_VEHICLE_REFIT_COST: + op.callbacks_used |= SGCU_VEHICLE_REFIT_COST; + break; + + case CBID_RANDOM_TRIGGER: + op.callbacks_used |= SGCU_RANDOM_TRIGGER; + break; + + case CBID_VEHICLE_MODIFY_PROPERTY: + if (range.group != nullptr) { + AnalyseCallbackOperation cb36_op; + cb36_op.mode = ACOM_CB36_PROP; + range.group->AnalyseCallbacks(cb36_op); + op.properties_used |= cb36_op.properties_used; + op.callbacks_used |= cb36_op.callbacks_used; + } + break; + + case CBID_VEHICLE_REFIT_CAPACITY: + found_refit_cap = true; + if (range.group != nullptr) { + AnalyseCallbackOperation cb_refit_op; + cb_refit_op.mode = ACOM_CB_REFIT_CAPACITY; + range.group->AnalyseCallbacks(cb_refit_op); + op.result_flags |= (cb_refit_op.result_flags & (ACORF_CB_REFIT_CAP_NON_WHITELIST_FOUND | ACORF_CB_REFIT_CAP_SEEN_VAR_47)); + } + break; + } + } else { + if (range.group != nullptr) range.group->AnalyseCallbacks(op); + } + } + if (this->default_group != nullptr) { + AnalyseCallbackOperationResultFlags prev_result = op.result_flags; + this->default_group->AnalyseCallbacks(op); + if (found_refit_cap) { + const AnalyseCallbackOperationResultFlags save_mask = ACORF_CB_REFIT_CAP_NON_WHITELIST_FOUND | ACORF_CB_REFIT_CAP_SEEN_VAR_47; + op.result_flags &= ~save_mask; + op.result_flags |= (prev_result & save_mask); + } + } + return; + } + } + if (op.mode == ACOM_CB36_PROP && adjust.variable == 0x10) { + if (adjust.shift_num == 0 && (adjust.and_mask & 0xFF) == 0xFF && adjust.type == DSGA_TYPE_NONE) { + for (const auto &range : this->ranges) { + if (range.low == range.high) { + if (range.low < 64) { + if (find_cb_result(range.group, { CBID_VEHICLE_MODIFY_PROPERTY, true, (uint8)range.low })) { + SetBit(op.properties_used, range.low); + if (range.low == 0x9) { + /* Speed */ + if (range.group != nullptr) { + AnalyseCallbackOperation cb36_speed; + cb36_speed.mode = ACOM_CB36_SPEED; + range.group->AnalyseCallbacks(cb36_speed); + op.callbacks_used |= cb36_speed.callbacks_used; + } + } + } + } + } else { + if (range.group != nullptr) range.group->AnalyseCallbacks(op); + } + } + if (this->default_group != nullptr) this->default_group->AnalyseCallbacks(op); + return; + } + } + if (op.mode == ACOM_CB36_PROP && adjust.variable == 0xC) { + if (adjust.shift_num == 0 && (adjust.and_mask & 0xFF) == 0xFF && adjust.type == DSGA_TYPE_NONE) { + for (const auto &range : this->ranges) { + if (range.low <= CBID_VEHICLE_MODIFY_PROPERTY && CBID_VEHICLE_MODIFY_PROPERTY <= range.high) { + if (range.group != nullptr) range.group->AnalyseCallbacks(op); + return; + } + } + if (this->default_group != nullptr) this->default_group->AnalyseCallbacks(op); + return; + } + } + if (op.mode == ACOM_CB36_SPEED && adjust.variable == 0x4A) { + op.callbacks_used |= SGCU_CB36_SPEED_RAILTYPE; + return; + } + if (op.mode == ACOM_INDUSTRY_TILE && adjust.variable == 0xC) { + if (adjust.shift_num == 0 && (adjust.and_mask & 0xFF) == 0xFF && adjust.type == DSGA_TYPE_NONE) { + /* Callback switch, skip to the default/graphics chain */ + for (const auto &range : this->ranges) { + if (range.low == 0) { + if (range.group != nullptr) range.group->AnalyseCallbacks(op); + return; + } + } + if (this->default_group != nullptr) this->default_group->AnalyseCallbacks(op); + return; + } + } + if (op.mode == ACOM_INDUSTRY_TILE && adjust.variable == 0x44 && this->var_scope == VSG_SCOPE_PARENT) { + if (adjust.shift_num == 0 && (adjust.and_mask & 0xFF) == 0xFF && adjust.type == DSGA_TYPE_NONE) { + /* Layout index switch */ + for (const auto &range : this->ranges) { + if (range.low <= op.data.indtile->layout_index && op.data.indtile->layout_index <= range.high) { + if (range.group != nullptr) range.group->AnalyseCallbacks(op); + return; + } + } + if (this->default_group != nullptr) this->default_group->AnalyseCallbacks(op); + return; + } + } + if (op.mode == ACOM_INDUSTRY_TILE && adjust.variable == 0x43 && this->var_scope == VSG_SCOPE_SELF) { + if (adjust.shift_num == 0 && adjust.and_mask == 0xFFFF && adjust.type == DSGA_TYPE_NONE) { + /* Relative position switch */ + uint64 default_mask = op.data.indtile->check_mask; + for (const auto &range : this->ranges) { + if (range.high - range.low < 32) { + uint64 new_check_mask = 0; + for (uint i = range.low; i <= range.high; i++) { + int16 x = i & 0xFF; + int16 y = (i >> 8) & 0xFF; + for (uint bit : SetBitIterator(op.data.indtile->check_mask)) { + const TileIndexDiffC &ti = (*(op.data.indtile->layout))[bit].ti; + if (ti.x == x && ti.y == y) { + SetBit(new_check_mask, bit); + } + } + } + default_mask &= ~new_check_mask; + if (range.group != nullptr) { + AnalyseCallbackOperationIndustryTileData data = *(op.data.indtile); + data.check_mask = new_check_mask; + + AnalyseCallbackOperation sub_op; + sub_op.mode = ACOM_INDUSTRY_TILE; + sub_op.data.indtile = &data; + range.group->AnalyseCallbacks(sub_op); + + if (data.anim_state_at_offset) { + op.data.indtile->anim_state_at_offset = true; + return; + } + } + } else { + if (range.group != nullptr) range.group->AnalyseCallbacks(op); + } + } + if (this->default_group != nullptr) { + AnalyseCallbackOperationIndustryTileData data = *(op.data.indtile); + data.check_mask = default_mask; + + AnalyseCallbackOperation sub_op; + sub_op.mode = ACOM_INDUSTRY_TILE; + sub_op.data.indtile = &data; + + this->default_group->AnalyseCallbacks(sub_op); + } + return; + } + } + } + for (const auto &adjust : this->adjusts) { + if (op.mode == ACOM_CB_VAR && adjust.variable == 0xC) { + op.callbacks_used |= SGCU_ALL; + } + if (op.mode == ACOM_CB36_PROP && adjust.variable == 0x10) { + if (find_cb_result(this, { CBID_VEHICLE_MODIFY_PROPERTY, false, 0 })) { + op.properties_used |= UINT64_MAX; + } + } + if ((op.mode == ACOM_CB_VAR || op.mode == ACOM_CB_REFIT_CAPACITY) && !(adjust.variable == 0xC || adjust.variable == 0x1A || adjust.variable == 0x47 || adjust.variable == 0x7D || adjust.variable == 0x7E)) { + op.result_flags |= ACORF_CB_REFIT_CAP_NON_WHITELIST_FOUND; + } + if ((op.mode == ACOM_CB_VAR || op.mode == ACOM_CB_REFIT_CAPACITY) && adjust.variable == 0x47) { + op.result_flags |= ACORF_CB_REFIT_CAP_SEEN_VAR_47; + } + if (adjust.variable == 0x7E && adjust.subroutine != nullptr) { + adjust.subroutine->AnalyseCallbacks(op); + } + if (op.mode == ACOM_INDUSTRY_TILE && this->var_scope == VSG_SCOPE_SELF && (adjust.variable == 0x44 || (adjust.variable == 0x61 && adjust.parameter == 0))) { + *(op.data.indtile->result_mask) &= ~op.data.indtile->check_mask; + return; + } + if (op.mode == ACOM_INDUSTRY_TILE && ((this->var_scope == VSG_SCOPE_SELF && adjust.variable == 0x61) || (this->var_scope == VSG_SCOPE_PARENT && adjust.variable == 0x63))) { + op.data.indtile->anim_state_at_offset = true; + return; + } + } + if (!this->calculated_result) { + for (const auto &range : this->ranges) { + if (range.group != nullptr) range.group->AnalyseCallbacks(op); + } + if (this->default_group != nullptr) this->default_group->AnalyseCallbacks(op); + } +} + +void CallbackResultSpriteGroup::AnalyseCallbacks(AnalyseCallbackOperation &op) const +{ + if (op.mode == ACOM_FIND_CB_RESULT) op.result_flags |= ACORF_CB_RESULT_FOUND; +} + +void RandomizedSpriteGroup::AnalyseCallbacks(AnalyseCallbackOperation &op) const +{ + op.result_flags |= ACORF_CB_REFIT_CAP_NON_WHITELIST_FOUND; + + if (op.mode == ACOM_CB_VAR) op.callbacks_used |= SGCU_RANDOM_TRIGGER; + + for (const SpriteGroup *group: this->groups) { + if (group != nullptr) group->AnalyseCallbacks(op); + } +} diff --git a/src/newgrf_analysis.h b/src/newgrf_analysis.h new file mode 100644 index 0000000000..c455d7e8ca --- /dev/null +++ b/src/newgrf_analysis.h @@ -0,0 +1,56 @@ +/* + * This file is part of OpenTTD. + * OpenTTD is free software; you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, version 2. + * OpenTTD is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. + * See the GNU General Public License for more details. You should have received a copy of the GNU General Public License along with OpenTTD. If not, see . + */ + +/** @file newgrf_analysis.h NewGRF analysis. */ + +#ifndef NEWGRF_ANALYSIS_H +#define NEWGRF_ANALYSIS_H + +#include "newgrf_commons.h" + +#include "3rdparty/cpp-btree/btree_set.h" + +struct SpriteGroup; + +enum AnalyseCallbackOperationMode : uint8 { + ACOM_CB_VAR, + ACOM_CB36_PROP, + ACOM_FIND_CB_RESULT, + ACOM_CB36_SPEED, + ACOM_INDUSTRY_TILE, + ACOM_CB_REFIT_CAPACITY, +}; + +struct AnalyseCallbackOperationIndustryTileData; + +enum AnalyseCallbackOperationResultFlags : uint8 { + ACORF_NONE = 0, + ACORF_CB_RESULT_FOUND = 1 << 0, + ACORF_CB_REFIT_CAP_NON_WHITELIST_FOUND = 1 << 1, + ACORF_CB_REFIT_CAP_SEEN_VAR_47 = 1 << 2, +}; +DECLARE_ENUM_AS_BIT_SET(AnalyseCallbackOperationResultFlags) + +struct AnalyseCallbackOperation { + struct FindCBResultData { + uint16 callback; + bool check_var_10; + uint8 var_10_value; + }; + + btree::btree_set seen; + AnalyseCallbackOperationMode mode = ACOM_CB_VAR; + SpriteGroupCallbacksUsed callbacks_used = SGCU_NONE; + AnalyseCallbackOperationResultFlags result_flags = ACORF_NONE; + uint64 properties_used = 0; + union { + FindCBResultData cb_result; + AnalyseCallbackOperationIndustryTileData *indtile; + } data; +}; + +#endif /* NEWGRF_ANALYSIS_H */ diff --git a/src/newgrf_engine.cpp b/src/newgrf_engine.cpp index 9ecd8a61b2..5a439e1626 100644 --- a/src/newgrf_engine.cpp +++ b/src/newgrf_engine.cpp @@ -26,6 +26,7 @@ #include "ship.h" #include "scope_info.h" #include "newgrf_extension.h" +#include "newgrf_analysis.h" #include "safeguards.h" diff --git a/src/newgrf_industrytiles.cpp b/src/newgrf_industrytiles.cpp index fb02689ac1..ac4e05b32d 100644 --- a/src/newgrf_industrytiles.cpp +++ b/src/newgrf_industrytiles.cpp @@ -17,6 +17,7 @@ #include "command_func.h" #include "water.h" #include "newgrf_animation_base.h" +#include "newgrf_analysis.h" #include "newgrf_industrytiles_analysis.h" #include "table/strings.h" diff --git a/src/newgrf_spritegroup.cpp b/src/newgrf_spritegroup.cpp index 617457c18e..86b39fc294 100644 --- a/src/newgrf_spritegroup.cpp +++ b/src/newgrf_spritegroup.cpp @@ -16,7 +16,6 @@ #include "newgrf_cache_check.h" #include "string_func.h" #include "newgrf_extension.h" -#include "newgrf_industrytiles_analysis.h" #include "scope.h" #include "debug_settings.h" @@ -311,288 +310,6 @@ const SpriteGroup *DeterministicSpriteGroup::Resolve(ResolverObject &object) con return SpriteGroup::Resolve(this->default_group, object, false); } -void DeterministicSpriteGroup::AnalyseCallbacks(AnalyseCallbackOperation &op) const -{ - auto res = op.seen.insert(this); - if (!res.second) { - /* Already seen this group */ - return; - } - - if (op.mode == ACOM_INDUSTRY_TILE && op.data.indtile->anim_state_at_offset) return; - - auto check_1A_range = [&]() -> bool { - if (this->GroupMayBeBypassed()) { - /* Not clear why some GRFs do this, perhaps a way of commenting out a branch */ - uint32 value = (this->adjusts.size() == 1) ? EvaluateDeterministicSpriteGroupAdjust(this->size, this->adjusts[0], nullptr, 0, UINT_MAX) : 0; - for (const auto &range : this->ranges) { - if (range.low <= value && value <= range.high) { - if (range.group != nullptr) range.group->AnalyseCallbacks(op); - return true; - } - } - if (this->default_group != nullptr) this->default_group->AnalyseCallbacks(op); - return true; - } - return false; - }; - - if (op.mode == ACOM_FIND_CB_RESULT) { - if (this->calculated_result) { - op.result_flags |= ACORF_CB_RESULT_FOUND; - return; - } else if (!(op.result_flags & ACORF_CB_RESULT_FOUND)) { - if (check_1A_range()) return; - auto check_var_filter = [&](uint8 var, uint value) -> bool { - if (this->adjusts.size() == 1 && this->adjusts[0].variable == var && (this->adjusts[0].operation == DSGA_OP_ADD || this->adjusts[0].operation == DSGA_OP_RST)) { - const auto &adjust = this->adjusts[0]; - if (adjust.shift_num == 0 && (adjust.and_mask & 0xFF) == 0xFF && adjust.type == DSGA_TYPE_NONE) { - for (const auto &range : this->ranges) { - if (range.low == range.high && range.low == value) { - if (range.group != nullptr) range.group->AnalyseCallbacks(op); - return true; - } - } - if (this->default_group != nullptr) this->default_group->AnalyseCallbacks(op); - return true; - } - } - return false; - }; - if (check_var_filter(0xC, op.data.cb_result.callback)) return; - if (op.data.cb_result.check_var_10 && check_var_filter(0x10, op.data.cb_result.var_10_value)) return; - for (const auto &range : this->ranges) { - if (range.group != nullptr) range.group->AnalyseCallbacks(op); - } - if (this->default_group != nullptr) this->default_group->AnalyseCallbacks(op); - } - return; - } - - if (check_1A_range()) return; - - if ((op.mode == ACOM_CB_VAR || op.mode == ACOM_CB_REFIT_CAPACITY) && this->var_scope != VSG_SCOPE_SELF) { - op.result_flags |= ACORF_CB_REFIT_CAP_NON_WHITELIST_FOUND; - } - - auto find_cb_result = [&](const SpriteGroup *group, AnalyseCallbackOperation::FindCBResultData data) -> bool { - if (group == nullptr) return false; - AnalyseCallbackOperation cbr_op; - cbr_op.mode = ACOM_FIND_CB_RESULT; - cbr_op.data.cb_result = data; - group->AnalyseCallbacks(cbr_op); - return (cbr_op.result_flags & ACORF_CB_RESULT_FOUND); - }; - - if (this->adjusts.size() == 1 && !this->calculated_result && (this->adjusts[0].operation == DSGA_OP_ADD || this->adjusts[0].operation == DSGA_OP_RST)) { - const auto &adjust = this->adjusts[0]; - if (op.mode == ACOM_CB_VAR && adjust.variable == 0xC) { - if (adjust.shift_num == 0 && (adjust.and_mask & 0xFF) == 0xFF && adjust.type == DSGA_TYPE_NONE) { - bool found_refit_cap = false; - for (const auto &range : this->ranges) { - if (range.low == range.high) { - switch (range.low) { - case CBID_VEHICLE_32DAY_CALLBACK: - op.callbacks_used |= SGCU_VEHICLE_32DAY_CALLBACK; - break; - - case CBID_VEHICLE_REFIT_COST: - op.callbacks_used |= SGCU_VEHICLE_REFIT_COST; - break; - - case CBID_RANDOM_TRIGGER: - op.callbacks_used |= SGCU_RANDOM_TRIGGER; - break; - - case CBID_VEHICLE_MODIFY_PROPERTY: - if (range.group != nullptr) { - AnalyseCallbackOperation cb36_op; - cb36_op.mode = ACOM_CB36_PROP; - range.group->AnalyseCallbacks(cb36_op); - op.properties_used |= cb36_op.properties_used; - op.callbacks_used |= cb36_op.callbacks_used; - } - break; - - case CBID_VEHICLE_REFIT_CAPACITY: - found_refit_cap = true; - if (range.group != nullptr) { - AnalyseCallbackOperation cb_refit_op; - cb_refit_op.mode = ACOM_CB_REFIT_CAPACITY; - range.group->AnalyseCallbacks(cb_refit_op); - op.result_flags |= (cb_refit_op.result_flags & (ACORF_CB_REFIT_CAP_NON_WHITELIST_FOUND | ACORF_CB_REFIT_CAP_SEEN_VAR_47)); - } - break; - } - } else { - if (range.group != nullptr) range.group->AnalyseCallbacks(op); - } - } - if (this->default_group != nullptr) { - AnalyseCallbackOperationResultFlags prev_result = op.result_flags; - this->default_group->AnalyseCallbacks(op); - if (found_refit_cap) { - const AnalyseCallbackOperationResultFlags save_mask = ACORF_CB_REFIT_CAP_NON_WHITELIST_FOUND | ACORF_CB_REFIT_CAP_SEEN_VAR_47; - op.result_flags &= ~save_mask; - op.result_flags |= (prev_result & save_mask); - } - } - return; - } - } - if (op.mode == ACOM_CB36_PROP && adjust.variable == 0x10) { - if (adjust.shift_num == 0 && (adjust.and_mask & 0xFF) == 0xFF && adjust.type == DSGA_TYPE_NONE) { - for (const auto &range : this->ranges) { - if (range.low == range.high) { - if (range.low < 64) { - if (find_cb_result(range.group, { CBID_VEHICLE_MODIFY_PROPERTY, true, (uint8)range.low })) { - SetBit(op.properties_used, range.low); - if (range.low == 0x9) { - /* Speed */ - if (range.group != nullptr) { - AnalyseCallbackOperation cb36_speed; - cb36_speed.mode = ACOM_CB36_SPEED; - range.group->AnalyseCallbacks(cb36_speed); - op.callbacks_used |= cb36_speed.callbacks_used; - } - } - } - } - } else { - if (range.group != nullptr) range.group->AnalyseCallbacks(op); - } - } - if (this->default_group != nullptr) this->default_group->AnalyseCallbacks(op); - return; - } - } - if (op.mode == ACOM_CB36_PROP && adjust.variable == 0xC) { - if (adjust.shift_num == 0 && (adjust.and_mask & 0xFF) == 0xFF && adjust.type == DSGA_TYPE_NONE) { - for (const auto &range : this->ranges) { - if (range.low <= CBID_VEHICLE_MODIFY_PROPERTY && CBID_VEHICLE_MODIFY_PROPERTY <= range.high) { - if (range.group != nullptr) range.group->AnalyseCallbacks(op); - return; - } - } - if (this->default_group != nullptr) this->default_group->AnalyseCallbacks(op); - return; - } - } - if (op.mode == ACOM_CB36_SPEED && adjust.variable == 0x4A) { - op.callbacks_used |= SGCU_CB36_SPEED_RAILTYPE; - return; - } - if (op.mode == ACOM_INDUSTRY_TILE && adjust.variable == 0xC) { - if (adjust.shift_num == 0 && (adjust.and_mask & 0xFF) == 0xFF && adjust.type == DSGA_TYPE_NONE) { - /* Callback switch, skip to the default/graphics chain */ - for (const auto &range : this->ranges) { - if (range.low == 0) { - if (range.group != nullptr) range.group->AnalyseCallbacks(op); - return; - } - } - if (this->default_group != nullptr) this->default_group->AnalyseCallbacks(op); - return; - } - } - if (op.mode == ACOM_INDUSTRY_TILE && adjust.variable == 0x44 && this->var_scope == VSG_SCOPE_PARENT) { - if (adjust.shift_num == 0 && (adjust.and_mask & 0xFF) == 0xFF && adjust.type == DSGA_TYPE_NONE) { - /* Layout index switch */ - for (const auto &range : this->ranges) { - if (range.low <= op.data.indtile->layout_index && op.data.indtile->layout_index <= range.high) { - if (range.group != nullptr) range.group->AnalyseCallbacks(op); - return; - } - } - if (this->default_group != nullptr) this->default_group->AnalyseCallbacks(op); - return; - } - } - if (op.mode == ACOM_INDUSTRY_TILE && adjust.variable == 0x43 && this->var_scope == VSG_SCOPE_SELF) { - if (adjust.shift_num == 0 && adjust.and_mask == 0xFFFF && adjust.type == DSGA_TYPE_NONE) { - /* Relative position switch */ - uint64 default_mask = op.data.indtile->check_mask; - for (const auto &range : this->ranges) { - if (range.high - range.low < 32) { - uint64 new_check_mask = 0; - for (uint i = range.low; i <= range.high; i++) { - int16 x = i & 0xFF; - int16 y = (i >> 8) & 0xFF; - for (uint bit : SetBitIterator(op.data.indtile->check_mask)) { - const TileIndexDiffC &ti = (*(op.data.indtile->layout))[bit].ti; - if (ti.x == x && ti.y == y) { - SetBit(new_check_mask, bit); - } - } - } - default_mask &= ~new_check_mask; - if (range.group != nullptr) { - AnalyseCallbackOperationIndustryTileData data = *(op.data.indtile); - data.check_mask = new_check_mask; - - AnalyseCallbackOperation sub_op; - sub_op.mode = ACOM_INDUSTRY_TILE; - sub_op.data.indtile = &data; - range.group->AnalyseCallbacks(sub_op); - - if (data.anim_state_at_offset) { - op.data.indtile->anim_state_at_offset = true; - return; - } - } - } else { - if (range.group != nullptr) range.group->AnalyseCallbacks(op); - } - } - if (this->default_group != nullptr) { - AnalyseCallbackOperationIndustryTileData data = *(op.data.indtile); - data.check_mask = default_mask; - - AnalyseCallbackOperation sub_op; - sub_op.mode = ACOM_INDUSTRY_TILE; - sub_op.data.indtile = &data; - - this->default_group->AnalyseCallbacks(sub_op); - } - return; - } - } - } - for (const auto &adjust : this->adjusts) { - if (op.mode == ACOM_CB_VAR && adjust.variable == 0xC) { - op.callbacks_used |= SGCU_ALL; - } - if (op.mode == ACOM_CB36_PROP && adjust.variable == 0x10) { - if (find_cb_result(this, { CBID_VEHICLE_MODIFY_PROPERTY, false, 0 })) { - op.properties_used |= UINT64_MAX; - } - } - if ((op.mode == ACOM_CB_VAR || op.mode == ACOM_CB_REFIT_CAPACITY) && !(adjust.variable == 0xC || adjust.variable == 0x1A || adjust.variable == 0x47 || adjust.variable == 0x7D || adjust.variable == 0x7E)) { - op.result_flags |= ACORF_CB_REFIT_CAP_NON_WHITELIST_FOUND; - } - if ((op.mode == ACOM_CB_VAR || op.mode == ACOM_CB_REFIT_CAPACITY) && adjust.variable == 0x47) { - op.result_flags |= ACORF_CB_REFIT_CAP_SEEN_VAR_47; - } - if (adjust.variable == 0x7E && adjust.subroutine != nullptr) { - adjust.subroutine->AnalyseCallbacks(op); - } - if (op.mode == ACOM_INDUSTRY_TILE && this->var_scope == VSG_SCOPE_SELF && (adjust.variable == 0x44 || (adjust.variable == 0x61 && adjust.parameter == 0))) { - *(op.data.indtile->result_mask) &= ~op.data.indtile->check_mask; - return; - } - if (op.mode == ACOM_INDUSTRY_TILE && ((this->var_scope == VSG_SCOPE_SELF && adjust.variable == 0x61) || (this->var_scope == VSG_SCOPE_PARENT && adjust.variable == 0x63))) { - op.data.indtile->anim_state_at_offset = true; - return; - } - } - if (!this->calculated_result) { - for (const auto &range : this->ranges) { - if (range.group != nullptr) range.group->AnalyseCallbacks(op); - } - if (this->default_group != nullptr) this->default_group->AnalyseCallbacks(op); - } -} - bool DeterministicSpriteGroup::GroupMayBeBypassed() const { if (this->calculated_result) return false; @@ -601,11 +318,6 @@ bool DeterministicSpriteGroup::GroupMayBeBypassed() const return false; } -void CallbackResultSpriteGroup::AnalyseCallbacks(AnalyseCallbackOperation &op) const -{ - if (op.mode == ACOM_FIND_CB_RESULT) op.result_flags |= ACORF_CB_RESULT_FOUND; -} - const SpriteGroup *RandomizedSpriteGroup::Resolve(ResolverObject &object) const { ScopeResolver *scope = object.GetScope(this->var_scope, this->count); @@ -626,17 +338,6 @@ const SpriteGroup *RandomizedSpriteGroup::Resolve(ResolverObject &object) const return SpriteGroup::Resolve(this->groups[index], object, false); } -void RandomizedSpriteGroup::AnalyseCallbacks(AnalyseCallbackOperation &op) const -{ - op.result_flags |= ACORF_CB_REFIT_CAP_NON_WHITELIST_FOUND; - - if (op.mode == ACOM_CB_VAR) op.callbacks_used |= SGCU_RANDOM_TRIGGER; - - for (const SpriteGroup *group: this->groups) { - if (group != nullptr) group->AnalyseCallbacks(op); - } -} - const SpriteGroup *RealSpriteGroup::Resolve(ResolverObject &object) const { return object.ResolveReal(this); diff --git a/src/newgrf_spritegroup.h b/src/newgrf_spritegroup.h index 1ec22d3b5a..a71eafcfdf 100644 --- a/src/newgrf_spritegroup.h +++ b/src/newgrf_spritegroup.h @@ -50,43 +50,7 @@ enum SpriteGroupType : uint8 { struct SpriteGroup; typedef uint32 SpriteGroupID; struct ResolverObject; - -enum AnalyseCallbackOperationMode : uint8 { - ACOM_CB_VAR, - ACOM_CB36_PROP, - ACOM_FIND_CB_RESULT, - ACOM_CB36_SPEED, - ACOM_INDUSTRY_TILE, - ACOM_CB_REFIT_CAPACITY, -}; - -struct AnalyseCallbackOperationIndustryTileData; - -enum AnalyseCallbackOperationResultFlags : uint8 { - ACORF_NONE = 0, - ACORF_CB_RESULT_FOUND = 1 << 0, - ACORF_CB_REFIT_CAP_NON_WHITELIST_FOUND = 1 << 1, - ACORF_CB_REFIT_CAP_SEEN_VAR_47 = 1 << 2, -}; -DECLARE_ENUM_AS_BIT_SET(AnalyseCallbackOperationResultFlags) - -struct AnalyseCallbackOperation { - struct FindCBResultData { - uint16 callback; - bool check_var_10; - uint8 var_10_value; - }; - - btree::btree_set seen; - AnalyseCallbackOperationMode mode = ACOM_CB_VAR; - SpriteGroupCallbacksUsed callbacks_used = SGCU_NONE; - AnalyseCallbackOperationResultFlags result_flags = ACORF_NONE; - uint64 properties_used = 0; - union { - FindCBResultData cb_result; - AnalyseCallbackOperationIndustryTileData *indtile; - } data; -}; +struct AnalyseCallbackOperation; /* SPRITE_WIDTH is 24. ECS has roughly 30 sprite groups per real sprite. * Adding an 'extra' margin would be assuming 64 sprite groups per real