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