5#include <unordered_map>
15constexpr auto masks_array_name =
"sMetatileAttrMasks";
16constexpr auto shifts_array_name =
"sMetatileAttrShifts";
17constexpr auto tileset_struct_name =
"Tileset";
18constexpr auto attributes_member_name =
"metatileAttributes";
20[[nodiscard]] std::vector<InferenceArrayEntry> to_inference_entries(
const std::vector<IndexedArrayEntry> &entries)
22 std::vector<InferenceArrayEntry> out;
23 out.reserve(entries.size());
24 for (
const auto &entry : entries) {
25 std::optional<std::uint32_t> value;
26 if (entry.value.has_value()) {
27 value =
static_cast<std::uint32_t
>(entry.value.value());
29 out.push_back(InferenceArrayEntry{entry.index_name, value});
38[[nodiscard]] BehaviorsHeaderScan scan_behaviors_header(
const std::filesystem::path &path,
const TextFormatter *format)
40 BehaviorsHeaderScan scan;
41 scan.path = path.string();
42 if (!std::filesystem::exists(path)) {
46 CParserFacade facade{path, format};
48 auto defines = facade.parse_defines_tolerant();
49 if (defines.has_value()) {
50 for (
const auto &define : defines.value().defines) {
51 if (define.name().starts_with(
"MB_")) {
58 auto enums = facade.parse_enums_tolerant();
59 if (enums.has_value()) {
60 for (
const auto &decl : enums.value().enums) {
61 for (
const auto &member : decl.members) {
62 if (member.name.starts_with(
"MB_")) {
81[[nodiscard]] AttributeDeclarationScan declaration_from(
const std::vector<StructDefinition> &defs)
83 bool saw_tileset_struct =
false;
84 for (
const auto &def : defs) {
85 if (def.name != tileset_struct_name) {
88 saw_tileset_struct =
true;
89 for (
const auto &member : def.members) {
90 if (member.member_name == attributes_member_name) {
91 return AttributeDeclarationScan{
96 return AttributeDeclarationScan{
104 std::filesystem::path project_root,
105 gsl::not_null<const TextFormatter *> format,
106 gsl::not_null<const UserDiagnostics *> diag)
107 : project_root_{std::move(project_root)}, format_{format}, diag_{diag}
113 const std::filesystem::path fieldmap_header_rel = std::filesystem::path{
"include"} /
"global.fieldmap.h";
114 const std::filesystem::path fieldmap_source_rel = std::filesystem::path{
"src"} /
"fieldmap.c";
115 const std::filesystem::path behaviors_header_rel =
116 std::filesystem::path{
"include"} /
"constants" /
"metatile_behaviors.h";
120 const auto fieldmap_header = project_root_ / fieldmap_header_rel;
122 if (!std::filesystem::exists(fieldmap_header)) {
129 auto defines_result = header_facade.parse_defines_tolerant();
130 if (!defines_result.has_value()) {
133 "Could not scan '{}' for attribute masks; skipping schema inference.",
143 auto enums_result = header_facade.parse_enums_tolerant();
145 enums_result.has_value(),
"enum scan failed on a fieldmap header whose define scan already succeeded");
147 const auto &header_defines = defines_result.value().defines;
148 const auto &header_enums = enums_result.value().enums;
151 std::unordered_map<std::string, std::int64_t> seeds;
153 for (
const auto &define : header_defines) {
154 if (define.has_int_value()) {
156 seeds[define.name()] = define.int_value();
159 for (
const auto &decl : header_enums) {
160 for (
const auto &member : decl.members) {
162 if (member.value.has_value()) {
163 seeds[member.name] = member.value.value();
170 const auto fieldmap_source = project_root_ / fieldmap_source_rel;
171 if (std::filesystem::exists(fieldmap_source)) {
172 CParserFacade source_facade{fieldmap_source, format_, seeds};
173 if (
auto arrays = source_facade.parse_indexed_arrays(); arrays.has_value()) {
174 for (
const auto &array : arrays.value()) {
175 if (array.name == masks_array_name) {
176 scan.
masks_array = to_inference_entries(array.entries);
181 else if (array.name == shifts_array_name) {
182 scan.
shifts_array = to_inference_entries(array.entries);
193 "Could not scan '{}' for the metatile attribute mask and shift tables; continuing from the header "
199 for (
const auto &
warning : source_facade.scan_warnings()) {
204 scan.
behaviors_header = scan_behaviors_header(project_root_ / behaviors_header_rel, format_);
210 "Could not scan '{}' for behavior constants.",
216 for (
const auto &
warning : header_facade.scan_warnings()) {
225 auto struct_defs = header_facade.parse_struct_definitions(std::string{tileset_struct_name});
227 struct_defs.has_value(),
"struct scan failed on a fieldmap header whose define scan already succeeded");
228 scan.
declaration = declaration_from(struct_defs.value());
High-level facade for parsing C/C++ source files.
static const Style bold
Bold text formatting.
virtual void warning(const std::string &tag, const std::vector< std::string > &lines) const =0
Display a tagged warning message.
void assert_or_panic(bool condition, const StringViewSourceLoc &s)
Conditionally panics if the given condition is false.
@ no_attributes_member
struct Tileset declares no metatileAttributes member
@ header_unreadable
the fieldmap header exists but could not be scanned
@ no_tileset_struct
the header was scanned and declares no struct Tileset
@ declared
the member is declared, and the declarator fields describe how
@ no_fieldmap_header
the project has no include/global.fieldmap.h
BehaviorsHeaderSource
What the scan found where the behavior constants header should be.
@ unreadable
the header exists but could not be scanned
@ no_constants
the header was scanned and declares no MB_ name
@ declared
the header declares at least one MB_ name
@ absent
the project has no include/constants/metatile_behaviors.h
@ warning
Emit a warning and continue decompilation.
constexpr auto metatile_attr_inference_tag
The diagnostic tag the infra-layer scanner emits its warnings under.
AttributeDeclarationSource source
One integer #define gathered from a base game's fieldmap header.
One enum member gathered from a base game's fieldmap header.