diff --git a/data/maps.s b/data/maps.s index 0d9eff61b5..95553fb013 100644 --- a/data/maps.s +++ b/data/maps.s @@ -1,3 +1,4 @@ +#include "constants/global.h" #include "constants/layouts.h" #include "constants/map_types.h" #include "constants/maps.h" diff --git a/include/constants/event_objects.h b/include/constants/event_objects.h index f28f315b5e..d4e606e9af 100644 --- a/include/constants/event_objects.h +++ b/include/constants/event_objects.h @@ -252,6 +252,9 @@ #define TRACKS_FOOT 1 #define TRACKS_BIKE_TIRE 2 +#define OBJ_KIND_NORMAL 0 +#define OBJ_KIND_CLONE 255 // Exclusive to FRLG + #define OBJ_EVENT_ID_PLAYER 0xFF #define OBJ_EVENT_ID_CAMERA 0x7F diff --git a/include/constants/global.h b/include/constants/global.h index cd60f0be48..08c6606c40 100644 --- a/include/constants/global.h +++ b/include/constants/global.h @@ -89,4 +89,13 @@ #define BAG_BERRIES 4 #define BAG_KEYITEMS 5 +#define CONNECTION_INVALID -1 +#define CONNECTION_NONE 0 +#define CONNECTION_SOUTH 1 +#define CONNECTION_NORTH 2 +#define CONNECTION_WEST 3 +#define CONNECTION_EAST 4 +#define CONNECTION_DIVE 5 +#define CONNECTION_EMERGE 6 + #endif // GUARD_CONSTANTS_GLOBAL_H diff --git a/include/constants/layouts.h b/include/constants/layouts.h index f0008036cd..fed1ce25e3 100755 --- a/include/constants/layouts.h +++ b/include/constants/layouts.h @@ -1,6 +1,10 @@ #ifndef GUARD_CONSTANTS_LAYOUTS_H #define GUARD_CONSTANTS_LAYOUTS_H +// +// DO NOT MODIFY THIS FILE! It is auto-generated from data/layouts/layouts.json +// + #define LAYOUT_PETALBURG_CITY 1 #define LAYOUT_SLATEPORT_CITY 2 #define LAYOUT_MAUVILLE_CITY 3 diff --git a/include/constants/map_groups.h b/include/constants/map_groups.h index 97668d6df3..bffa347ea0 100755 --- a/include/constants/map_groups.h +++ b/include/constants/map_groups.h @@ -1,6 +1,10 @@ #ifndef GUARD_CONSTANTS_MAP_GROUPS_H #define GUARD_CONSTANTS_MAP_GROUPS_H +// +// DO NOT MODIFY THIS FILE! It is auto-generated from data/maps/map_groups.json +// + // gMapGroup_TownsAndRoutes #define MAP_PETALBURG_CITY (0 | (0 << 8)) #define MAP_SLATEPORT_CITY (1 | (0 << 8)) diff --git a/include/global.fieldmap.h b/include/global.fieldmap.h index 036ccfb146..11febc6d45 100644 --- a/include/global.fieldmap.h +++ b/include/global.fieldmap.h @@ -16,16 +16,6 @@ #define METATILE_ID(tileset, name) (METATILE_##tileset##_##name) -enum -{ - CONNECTION_SOUTH = 1, - CONNECTION_NORTH, - CONNECTION_WEST, - CONNECTION_EAST, - CONNECTION_DIVE, - CONNECTION_EMERGE -}; - typedef void (*TilesetCB)(void); struct Tileset diff --git a/include/macros/map.inc b/include/macros/map.inc index f5f09dea9d..0b90d63a44 100644 --- a/include/macros/map.inc +++ b/include/macros/map.inc @@ -1,83 +1,126 @@ - .macro map map_id +@ Most of the macros in this file are for arranging map event data, and are output by mapjson using data from each map's JSON file. + + @ Takes a MAP constant and outputs the map group and map number as separate bytes + .macro map map_id:req .byte \map_id >> 8 @ map group .byte \map_id & 0xFF @ map num .endm - .macro map_script type, address + @ Defines a map script. 'type' is any MAP_SCRIPT_* constant (see include/constants/map_scripts.h) + .macro map_script type:req, script:req .byte \type - .4byte \address + .4byte \script .endm - .macro map_script_2 word1, word2, address - .2byte \word1 - .2byte \word2 - .4byte \address + @ Defines an entry in a map script table (for either ON_WARP_INTO_MAP_TABLE or ON_FRAME_TABLE) + .macro map_script_2 var:req, compare:req, script:req + .2byte \var + .2byte \compare + .4byte \script .endm - .macro object_event index, gfx, replacement, x, y, elevation, movement_type, x_radius, y_radius, trainer_type, sight_radius_tree_etc, script, event_flag - .byte \index, \gfx, \replacement, 0 - .2byte \x - .2byte \y - .byte \elevation, \movement_type, ((\y_radius << 4) | \x_radius), 0 - .2byte \trainer_type, \sight_radius_tree_etc + @ Defines an object event template for map data, to be used by a normal object. Mirrors the struct layout of ObjectEventTemplate in include/global.fieldmap.h + .macro object_event index:req, gfx:req, x:req, y:req, elevation:req, movement_type:req, x_radius:req, y_radius:req, trainer_type:req, sight_radius_tree_etc:req, script:req, event_flag:req + .byte \index + .byte \gfx + .byte OBJ_KIND_NORMAL + .space 1 @ Padding + .2byte \x, \y + .byte \elevation + .byte \movement_type + .byte ((\y_radius << 4) | \x_radius) + .space 1 @ Padding + .2byte \trainer_type + .2byte \sight_radius_tree_etc .4byte \script .2byte \event_flag - .2byte 0 + .space 2 @ Padding inc _num_npcs .endm - .macro warp_def x, y, elevation, warp, map_id + @ Defines an object event template for map data, to be used by a clone object. Mirrors the struct layout of ObjectEventTemplate in include/global.fieldmap.h + @ NOTE: The handling for this type of event does not exist in Ruby/Sapphire by default; it is exclusive to FRLG. + .macro clone_event index:req, gfx:req, x:req, y:req, target_local_id:req, target_map_id:req + .byte \index + .byte \gfx + .byte OBJ_KIND_CLONE + .space 1 @ Padding .2byte \x, \y - .byte \elevation, \warp + .byte \target_local_id + .space 3 @ Padding + .2byte \target_map_id & 0xFF @ map num + .2byte \target_map_id >> 8 @ map group + .space 8 @ Padding + inc _num_npcs + .endm + + @ Defines a warp event for map data. Mirrors the struct layout of WarpEvent in include/global.fieldmap.h + .macro warp_def x:req, y:req, elevation:req, warpId:req, map_id:req + .2byte \x, \y + .byte \elevation + .byte \warpId .byte \map_id & 0xFF @ map num .byte \map_id >> 8 @ map group inc _num_warps .endm - .macro coord_event x, y, elevation, trigger, index, script + @ Defines a coord event for map data. Mirrors the struct layout of CoordEvent in include/global.fieldmap.h + .macro coord_event x:req, y:req, elevation:req, var:req, varValue:req, script:req .2byte \x, \y - .byte \elevation, 0 - .2byte \trigger, \index, 0 + .byte \elevation + .space 1 @ Padding + .2byte \var + .2byte \varValue + .space 2 @ Padding .4byte \script inc _num_traps .endm - .macro coord_weather_event x, y, elevation, weather - .2byte \x, \y - .byte \elevation, 0 - .2byte \weather - .2byte 0, 0 - .4byte 0 - inc _num_traps + @ Defines a weather coord event for map data. Any coord event is treated as a weather coord event if its script is NULL + .macro coord_weather_event x:req, y:req, elevation:req, weather:req + coord_event \x, \y, \elevation, \weather, 0, NULL .endm - .macro bg_event x, y, elevation, kind, arg6, arg7, arg8 + @ Defines a generic background event for map data. Mirrors the struct layout of BgEvent in include/global.fieldmap.h + @ 'kind' is any BG_EVENT_* constant (see include/constants/event_bg.h). + @ 'arg6' and 'arg7' are used differently depending on the bg event type. See macros below + .macro bg_event x:req, y:req, elevation:req, kind:req, arg6:req, arg7 .2byte \x, \y - .byte \elevation, \kind - .2byte 0 - .if \kind < 5 - .4byte \arg6 + .byte \elevation + .byte \kind + .space 2 @ Padding + .if \kind != BG_EVENT_HIDDEN_ITEM + .4byte \arg6 .else - .2byte \arg6 - .byte \arg7, \arg8 + .2byte \arg6 + .2byte \arg7 .endif inc _num_signs .endm - .macro bg_hidden_item_event x, y, height, item, flag - bg_event \x, \y, \height, 7, \item, ((\flag) - FLAG_HIDDEN_ITEMS_START), 0 + @ Defines a background sign event for map data. 'facing_dir' is any of the BG_EVENT_PLAYER_FACING_* constants (see include/constants/event_bg.h) + .macro bg_sign_event x:req, y:req, elevation:req, facing_dir:req, script:req + bg_event \x, \y, \elevation, \facing_dir, \script .endm - .macro bg_secret_base_event x, y, height, secret_base_id - bg_event \x, \y, \height, 8, \secret_base_id, 0, 0 - .endm + @ Defines a background hidden item event for map data + .macro bg_hidden_item_event x:req, y:req, elevation:req, item:req, flag:req + bg_event \x, \y, \elevation, BG_EVENT_HIDDEN_ITEM, \item, ((\flag) - FLAG_HIDDEN_ITEMS_START) + .endm + + @ Defines a background secret base event for map data + .macro bg_secret_base_event x:req, y:req, elevation:req, secret_base_id:req + bg_event \x, \y, \elevation, BG_EVENT_SECRET_BASE, \secret_base_id + .endm - .macro map_events npcs, warps, traps, signs + @ Defines the table of event data for a map. Mirrors the struct layout of MapEvents in include/global.fieldmap.h + .macro map_events npcs:req, warps:req, traps:req, signs:req .byte _num_npcs, _num_warps, _num_traps, _num_signs .4byte \npcs, \warps, \traps, \signs reset_map_events .endm + @ Resets the event counters used to track how many events a map has. Run when the events table is created by map_events .macro reset_map_events .set _num_npcs, 0 .set _num_warps, 0 @@ -85,19 +128,24 @@ .set _num_signs, 0 .endm + @ Initialize the event counters for the first map reset_map_events - .equiv connection_down, 1 - .equiv connection_up, 2 - .equiv connection_left, 3 - .equiv connection_right, 4 - .equiv connection_dive, 5 - .equiv connection_emerge, 6 + @ Directions for connecting maps + @ The map.json files will only have e.g. "down" as direction data, and this will be appended to "connection_" by the connection macro + .equiv connection_down, CONNECTION_SOUTH + .equiv connection_up, CONNECTION_NORTH + .equiv connection_left, CONNECTION_WEST + .equiv connection_right, CONNECTION_EAST + .equiv connection_dive, CONNECTION_DIVE + .equiv connection_emerge, CONNECTION_EMERGE - .macro connection direction, offset, map, filler - .4byte connection_\direction + @ Defines a map connection. Mirrors the struct layout of MapConnection in include/global.fieldmap.h + .macro connection direction:req, offset:req, map:req + .byte connection_\direction + .space 3 @ Padding .4byte \offset map \map - .space 2 + .space 2 @ Padding .endm diff --git a/tools/mapjson/mapjson.cpp b/tools/mapjson/mapjson.cpp index 4e52b0b95e..e53ac7924d 100755 --- a/tools/mapjson/mapjson.cpp +++ b/tools/mapjson/mapjson.cpp @@ -29,6 +29,7 @@ using json11::Json; #include "mapjson.h" +string version; string read_text_file(string filepath) { ifstream in_file(filepath); @@ -60,14 +61,47 @@ void write_text_file(string filepath, string text) { out_file.close(); } -string generate_map_header_text(Json map_data, Json layouts_data, string version) { - string map_layout_id = map_data["layout"].string_value(); + +string json_to_string(const Json &data, const string &field = "", bool silent = false) { + const Json value = !field.empty() ? data[field] : data; + string output = ""; + switch (value.type()) { + case Json::Type::STRING: + output = value.string_value(); + break; + case Json::Type::NUMBER: + output = std::to_string(value.int_value()); + break; + case Json::Type::BOOL: + output = value.bool_value() ? "TRUE" : "FALSE"; + break; + case Json::Type::NUL: + output = ""; + break; + default:{ + if (!silent) { + string s = !field.empty() ? ("Value for '" + field + "'") : "JSON field"; + FATAL_ERROR("%s is unexpected type; expected string, number, or bool.\n", s.c_str()); + } + } + } + + if (!silent && output.empty()) { + string s = !field.empty() ? ("Value for '" + field + "'") : "JSON field"; + FATAL_ERROR("%s cannot be empty.\n", s.c_str()); + } + + return output; +} + +string generate_map_header_text(Json map_data, Json layouts_data) { + string map_layout_id = json_to_string(map_data, "layout"); vector matched; - for (auto &field : layouts_data["layouts"].array_items()) { - if (map_layout_id == field["id"].string_value()) - matched.push_back(field); + for (auto &layout : layouts_data["layouts"].array_items()) { + if (map_layout_id == json_to_string(layout, "id", true)) + matched.push_back(layout); } if (matched.size() != 1) @@ -77,43 +111,52 @@ string generate_map_header_text(Json map_data, Json layouts_data, string version ostringstream text; - text << map_data["name"].string_value() << ":\n" - << "\t.4byte " << layout["name"].string_value() << "\n"; + string mapName = json_to_string(map_data, "name"); + + text << "@\n@ DO NOT MODIFY THIS FILE! It is auto-generated from data/maps/" << mapName << "/map.json\n@\n\n"; + + text << mapName << ":\n" + << "\t.4byte " << json_to_string(layout, "name") << "\n"; if (map_data.object_items().find("shared_events_map") != map_data.object_items().end()) - text << "\t.4byte " << map_data["shared_events_map"].string_value() << "_MapEvents\n"; + text << "\t.4byte " << json_to_string(map_data, "shared_events_map") << "_MapEvents\n"; else - text << "\t.4byte " << map_data["name"].string_value() << "_MapEvents\n"; + text << "\t.4byte " << mapName << "_MapEvents\n"; if (map_data.object_items().find("shared_scripts_map") != map_data.object_items().end()) - text << "\t.4byte " << map_data["shared_scripts_map"].string_value() << "_MapScripts\n"; + text << "\t.4byte " << json_to_string(map_data, "shared_scripts_map") << "_MapScripts\n"; else - text << "\t.4byte " << map_data["name"].string_value() << "_MapScripts\n"; + text << "\t.4byte " << mapName << "_MapScripts\n"; if (map_data.object_items().find("connections") != map_data.object_items().end() - && map_data["connections"].array_items().size() > 0) - text << "\t.4byte " << map_data["name"].string_value() << "_MapConnections\n"; + && map_data["connections"].array_items().size() > 0 && json_to_string(map_data, "connections_no_include", true) != "TRUE") + text << "\t.4byte " << mapName << "_MapConnections\n"; else - text << "\t.4byte 0x0\n"; + text << "\t.4byte NULL\n"; + + text << "\t.2byte " << json_to_string(map_data, "music") << "\n" + << "\t.2byte " << json_to_string(layout, "id") << "\n" + << "\t.byte " << json_to_string(map_data, "region_map_section") << "\n" + << "\t.byte " << json_to_string(map_data, "requires_flash") << "\n" + << "\t.byte " << json_to_string(map_data, "weather") << "\n" + << "\t.byte " << json_to_string(map_data, "map_type") << "\n"; - text << "\t.2byte " << map_data["music"].string_value() << "\n" - << "\t.2byte " << layout["id"].string_value() << "\n" - << "\t.byte " << map_data["region_map_section"].string_value() << "\n" - << "\t.byte " << map_data["requires_flash"].bool_value() << "\n" - << "\t.byte " << map_data["weather"].string_value() << "\n" - << "\t.byte " << map_data["map_type"].string_value() << "\n" - << "\t.2byte 0\n"; + if (version != "firered") + text << "\t.2byte 0\n"; if (version == "ruby") - text << "\t.byte " << map_data["show_map_name"].bool_value() << "\n"; - else if (version == "emerald") + text << "\t.byte " << json_to_string(map_data, "show_map_name") << "\n"; + else if (version == "emerald" || version == "firered") text << "\tmap_header_flags " - << "allow_bike=" << map_data["allow_bike"].bool_value() << ", " - << "allow_escape_rope=" << map_data["allow_escape_rope"].bool_value() << ", " - << "allow_run=" << map_data["allow_running"].bool_value() << ", " - << "show_map_name=" << map_data["show_map_name"].bool_value() << "\n"; + << "allow_cycling=" << json_to_string(map_data, "allow_cycling") << ", " + << "allow_escaping=" << json_to_string(map_data, "allow_escaping") << ", " + << "allow_running=" << json_to_string(map_data, "allow_running") << ", " + << "show_map_name=" << json_to_string(map_data, "show_map_name") << "\n"; - text << "\t.byte " << map_data["battle_scene"].string_value() << "\n\n"; + if (version == "firered") + text << "\t.byte " << json_to_string(map_data, "floor_number") << "\n"; + + text << "\t.byte " << json_to_string(map_data, "battle_scene") << "\n\n"; return text.str(); } @@ -124,18 +167,22 @@ string generate_map_connections_text(Json map_data) { ostringstream text; - text << map_data["name"].string_value() << "_MapConnectionsList:\n"; + string mapName = json_to_string(map_data, "name"); + + text << "@\n@ DO NOT MODIFY THIS FILE! It is auto-generated from data/maps/" << mapName << "/map.json\n@\n\n"; + + text << mapName << "_MapConnectionsList:\n"; for (auto &connection : map_data["connections"].array_items()) { text << "\tconnection " - << connection["direction"].string_value() << ", " - << connection["offset"].int_value() << ", " - << connection["map"].string_value() << "\n"; + << json_to_string(connection, "direction") << ", " + << json_to_string(connection, "offset") << ", " + << json_to_string(connection, "map") << "\n"; } - text << "\n" << map_data["name"].string_value() << "_MapConnections:\n" + text << "\n" << mapName << "_MapConnections:\n" << "\t.4byte " << map_data["connections"].array_items().size() << "\n" - << "\t.4byte " << map_data["name"].string_value() << "_MapConnectionsList\n\n"; + << "\t.4byte " << mapName << "_MapConnectionsList\n\n"; return text.str(); } @@ -146,107 +193,137 @@ string generate_map_events_text(Json map_data) { ostringstream text; + string mapName = json_to_string(map_data, "name"); + + text << "@\n@ DO NOT MODIFY THIS FILE! It is auto-generated from data/maps/" << mapName << "/map.json\n@\n\n"; + string objects_label, warps_label, coords_label, bgs_label; if (map_data["object_events"].array_items().size() > 0) { - objects_label = map_data["name"].string_value() + "_ObjectEvents"; + objects_label = mapName + "_ObjectEvents"; text << objects_label << ":\n"; for (unsigned int i = 0; i < map_data["object_events"].array_items().size(); i++) { auto obj_event = map_data["object_events"].array_items()[i]; - text << "\tobject_event " << i + 1 << ", " - << obj_event["graphics_id"].string_value() << ", 0, " - << obj_event["x"].int_value() << ", " - << obj_event["y"].int_value() << ", " - << obj_event["elevation"].int_value() << ", " - << obj_event["movement_type"].string_value() << ", " - << obj_event["movement_range_x"].int_value() << ", " - << obj_event["movement_range_y"].int_value() << ", " - << obj_event["trainer_type"].string_value() << ", " - << obj_event["trainer_sight_or_berry_tree_id"].string_value() << ", " - << obj_event["script"].string_value() << ", " - << obj_event["flag"].string_value() << "\n"; + string type = json_to_string(obj_event, "type", true); + + // If no type field is present, assume it's a regular object event. + if (type == "" || type == "object") { + text << "\tobject_event " << i + 1 << ", " + << json_to_string(obj_event, "graphics_id") << ", " + << json_to_string(obj_event, "x") << ", " + << json_to_string(obj_event, "y") << ", " + << json_to_string(obj_event, "elevation") << ", " + << json_to_string(obj_event, "movement_type") << ", " + << json_to_string(obj_event, "movement_range_x") << ", " + << json_to_string(obj_event, "movement_range_y") << ", " + << json_to_string(obj_event, "trainer_type") << ", " + << json_to_string(obj_event, "trainer_sight_or_berry_tree_id") << ", " + << json_to_string(obj_event, "script") << ", " + << json_to_string(obj_event, "flag") << "\n"; + } else if (type == "clone") { + text << "\tclone_event " << i + 1 << ", " + << json_to_string(obj_event, "graphics_id") << ", " + << json_to_string(obj_event, "x") << ", " + << json_to_string(obj_event, "y") << ", " + << json_to_string(obj_event, "target_local_id") << ", " + << json_to_string(obj_event, "target_map") << "\n"; + } else { + FATAL_ERROR("Unknown object event type '%s'. Expected 'object' or 'clone'.\n", type.c_str()); + } } text << "\n"; } else { - objects_label = "0x0"; + objects_label = "NULL"; } if (map_data["warp_events"].array_items().size() > 0) { - warps_label = map_data["name"].string_value() + "_MapWarps"; + warps_label = mapName + "_MapWarps"; text << warps_label << ":\n"; for (auto &warp_event : map_data["warp_events"].array_items()) { text << "\twarp_def " - << warp_event["x"].int_value() << ", " - << warp_event["y"].int_value() << ", " - << warp_event["elevation"].int_value() << ", " - << warp_event["dest_warp_id"].string_value() << ", " - << warp_event["dest_map"].string_value() << "\n"; + << json_to_string(warp_event, "x") << ", " + << json_to_string(warp_event, "y") << ", " + << json_to_string(warp_event, "elevation") << ", " + << json_to_string(warp_event, "dest_warp_id") << ", " + << json_to_string(warp_event, "dest_map") << "\n"; } text << "\n"; } else { - warps_label = "0x0"; + warps_label = "NULL"; } if (map_data["coord_events"].array_items().size() > 0) { - coords_label = map_data["name"].string_value() + "_MapCoordEvents"; + coords_label = mapName + "_MapCoordEvents"; text << coords_label << ":\n"; for (auto &coord_event : map_data["coord_events"].array_items()) { - if (coord_event["type"].string_value() == "trigger") { + string type = json_to_string(coord_event, "type"); + if (type == "trigger") { text << "\tcoord_event " - << coord_event["x"].int_value() << ", " - << coord_event["y"].int_value() << ", " - << coord_event["elevation"].int_value() << ", " - << coord_event["var"].string_value() << ", " - << coord_event["var_value"].string_value() << ", " - << coord_event["script"].string_value() << "\n"; + << json_to_string(coord_event, "x") << ", " + << json_to_string(coord_event, "y") << ", " + << json_to_string(coord_event, "elevation") << ", " + << json_to_string(coord_event, "var") << ", " + << json_to_string(coord_event, "var_value") << ", " + << json_to_string(coord_event, "script") << "\n"; } - else if (coord_event["type"] == "weather") { + else if (type == "weather") { text << "\tcoord_weather_event " - << coord_event["x"].int_value() << ", " - << coord_event["y"].int_value() << ", " - << coord_event["elevation"].int_value() << ", " - << coord_event["weather"].string_value() << "\n"; + << json_to_string(coord_event, "x") << ", " + << json_to_string(coord_event, "y") << ", " + << json_to_string(coord_event, "elevation") << ", " + << json_to_string(coord_event, "weather") << "\n"; + } else { + FATAL_ERROR("Unknown coord event type '%s'. Expected 'trigger' or 'weather'.\n", type.c_str()); } } text << "\n"; } else { - coords_label = "0x0"; + coords_label = "NULL"; } if (map_data["bg_events"].array_items().size() > 0) { - bgs_label = map_data["name"].string_value() + "_MapBGEvents"; + bgs_label = mapName + "_MapBGEvents"; text << bgs_label << ":\n"; for (auto &bg_event : map_data["bg_events"].array_items()) { - if (bg_event["type"] == "sign") { - text << "\tbg_event " - << bg_event["x"].int_value() << ", " - << bg_event["y"].int_value() << ", " - << bg_event["elevation"].int_value() << ", " - << bg_event["player_facing_dir"].string_value() << ", " - << bg_event["script"].string_value() << "\n"; + string type = json_to_string(bg_event, "type"); + if (type == "sign") { + text << "\tbg_sign_event " + << json_to_string(bg_event, "x") << ", " + << json_to_string(bg_event, "y") << ", " + << json_to_string(bg_event, "elevation") << ", " + << json_to_string(bg_event, "player_facing_dir") << ", " + << json_to_string(bg_event, "script") << "\n"; } - else if (bg_event["type"] == "hidden_item") { + else if (type == "hidden_item") { text << "\tbg_hidden_item_event " - << bg_event["x"].int_value() << ", " - << bg_event["y"].int_value() << ", " - << bg_event["elevation"].int_value() << ", " - << bg_event["item"].string_value() << ", " - << bg_event["flag"].string_value() << "\n"; + << json_to_string(bg_event, "x") << ", " + << json_to_string(bg_event, "y") << ", " + << json_to_string(bg_event, "elevation") << ", " + << json_to_string(bg_event, "item") << ", " + << json_to_string(bg_event, "flag"); + if (version == "firered") { + text << ", " + << json_to_string(bg_event, "quantity") << ", " + << json_to_string(bg_event, "underfoot"); + } + text << "\n"; } - else if (bg_event["type"] == "secret_base") { + else if (type == "secret_base") { text << "\tbg_secret_base_event " - << bg_event["x"].int_value() << ", " - << bg_event["y"].int_value() << ", " - << bg_event["elevation"].int_value() << ", " - << bg_event["secret_base_id"].string_value() << "\n"; + << json_to_string(bg_event, "x") << ", " + << json_to_string(bg_event, "y") << ", " + << json_to_string(bg_event, "elevation") << ", " + << json_to_string(bg_event, "secret_base_id") << "\n"; + } else { + FATAL_ERROR("Unknown bg event type '%s'. Expected 'sign', 'hidden_item', or 'secret_base'.\n", type.c_str()); } } text << "\n"; } else { - bgs_label = "0x0"; + bgs_label = "NULL"; } - text << map_data["name"].string_value() << "_MapEvents::\n" + text << mapName << "_MapEvents::\n" << "\tmap_events " << objects_label << ", " << warps_label << ", " << coords_label << ", " << bgs_label << "\n\n"; @@ -259,7 +336,7 @@ string get_directory_name(string filename) { return filename.substr(0, dir_pos + 1); } -void process_map(string map_filepath, string layouts_filepath, string version) { +void process_map(string map_filepath, string layouts_filepath) { string mapdata_err, layouts_err; string mapdata_json_text = read_text_file(map_filepath); @@ -273,7 +350,7 @@ void process_map(string map_filepath, string layouts_filepath, string version) { if (layouts_data == Json()) FATAL_ERROR("%s\n", layouts_err.c_str()); - string header_text = generate_map_header_text(map_data, layouts_data, version); + string header_text = generate_map_header_text(map_data, layouts_data); string events_text = generate_map_events_text(map_data); string connections_text = generate_map_connections_text(map_data); @@ -286,18 +363,20 @@ void process_map(string map_filepath, string layouts_filepath, string version) { string generate_groups_text(Json groups_data) { ostringstream text; + text << "@\n@ DO NOT MODIFY THIS FILE! It is auto-generated from data/maps/map_groups.json\n@\n\n"; + for (auto &key : groups_data["group_order"].array_items()) { - string group = key.string_value(); + string group = json_to_string(key); text << group << "::\n"; auto maps = groups_data[group].array_items(); for (Json &map_name : maps) - text << "\t.4byte " << map_name.string_value() << "\n"; + text << "\t.4byte " << json_to_string(map_name) << "\n"; text << "\n"; } text << "\t.align 2\n" << "gMapGroups::\n"; for (auto &group : groups_data["group_order"].array_items()) - text << "\t.4byte " << group.string_value() << "\n"; + text << "\t.4byte " << json_to_string(group) << "\n"; text << "\n"; return text.str(); @@ -307,7 +386,7 @@ string generate_connections_text(Json groups_data) { vector map_names; for (auto &group : groups_data["group_order"].array_items()) - for (auto map_name : groups_data[group.string_value()].array_items()) + for (auto map_name : groups_data[json_to_string(group)].array_items()) map_names.push_back(map_name); vector connections_include_order = groups_data["connections_include_order"].array_items(); @@ -325,8 +404,10 @@ string generate_connections_text(Json groups_data) { ostringstream text; + text << "@\n@ DO NOT MODIFY THIS FILE! It is auto-generated from data/maps/map_groups.json\n@\n\n"; + for (Json map_name : map_names) - text << "\t.include \"data/maps/" << map_name.string_value() << "/connections.inc\"\n"; + text << "\t.include \"data/maps/" << json_to_string(map_name) << "/connections.inc\"\n"; return text.str(); } @@ -335,11 +416,13 @@ string generate_headers_text(Json groups_data) { vector map_names; for (auto &group : groups_data["group_order"].array_items()) - for (auto map_name : groups_data[group.string_value()].array_items()) - map_names.push_back(map_name.string_value()); + for (auto map_name : groups_data[json_to_string(group)].array_items()) + map_names.push_back(json_to_string(map_name)); ostringstream text; + text << "@\n@ DO NOT MODIFY THIS FILE! It is auto-generated from data/maps/map_groups.json\n@\n\n"; + for (string map_name : map_names) text << "\t.include \"data/maps/" << map_name << "/header.inc\"\n"; @@ -350,11 +433,13 @@ string generate_events_text(Json groups_data) { vector map_names; for (auto &group : groups_data["group_order"].array_items()) - for (auto map_name : groups_data[group.string_value()].array_items()) - map_names.push_back(map_name.string_value()); + for (auto map_name : groups_data[json_to_string(group)].array_items()) + map_names.push_back(json_to_string(map_name)); ostringstream text; + text << "@\n@ DO NOT MODIFY THIS FILE! It is auto-generated from data/maps/map_groups.json\n@\n\n"; + for (string map_name : map_names) text << "\t.include \"data/maps/" << map_name << "/events.inc\"\n"; @@ -370,25 +455,31 @@ string generate_map_constants_text(string groups_filepath, Json groups_data) { text << "#ifndef GUARD_CONSTANTS_MAP_GROUPS_H\n" << "#define GUARD_CONSTANTS_MAP_GROUPS_H\n\n"; + text << "//\n// DO NOT MODIFY THIS FILE! It is auto-generated from data/maps/map_groups.json\n//\n\n"; + int group_num = 0; for (auto &group : groups_data["group_order"].array_items()) { - text << "// " << group.string_value() << "\n"; - vector map_ids; + string groupName = json_to_string(group); + text << "// " << groupName << "\n"; + vector map_ids; size_t max_length = 0; - for (auto &map_name : groups_data[group.string_value()].array_items()) { - string header_filepath = file_dir + map_name.string_value() + dir_separator + "map.json"; + for (auto &map_name : groups_data[groupName].array_items()) { + string map_filepath = file_dir + json_to_string(map_name) + dir_separator + "map.json"; string err_str; - Json map_data = Json::parse(read_text_file(header_filepath), err_str); - map_ids.push_back(map_data["id"]); - if (map_data["id"].string_value().length() > max_length) - max_length = map_data["id"].string_value().length(); + Json map_data = Json::parse(read_text_file(map_filepath), err_str); + if (map_data == Json()) + FATAL_ERROR("%s: %s\n", map_filepath.c_str(), err_str.c_str()); + string id = json_to_string(map_data, "id", true); + map_ids.push_back(id); + if (id.length() > max_length) + max_length = id.length(); } int map_id_num = 0; - for (Json map_id : map_ids) { - text << "#define " << map_id.string_value() << string((max_length - map_id.string_value().length() + 1), ' ') + for (string map_id : map_ids) { + text << "#define " << map_id << string((max_length - map_id.length() + 1), ' ') << "(" << map_id_num++ << " | (" << group_num << " << 8))\n"; } text << "\n"; @@ -428,21 +519,31 @@ void process_groups(string groups_filepath) { string generate_layout_headers_text(Json layouts_data) { ostringstream text; + text << "@\n@ DO NOT MODIFY THIS FILE! It is auto-generated from data/layouts/layouts.json\n@\n\n"; + for (auto &layout : layouts_data["layouts"].array_items()) { - string border_label = layout["name"].string_value() + "_Border"; - string blockdata_label = layout["name"].string_value() + "_Blockdata"; + if (layout == Json::object()) continue; + string layoutName = json_to_string(layout, "name"); + string border_label = layoutName + "_Border"; + string blockdata_label = layoutName + "_Blockdata"; text << border_label << "::\n" - << "\t.incbin \"" << layout["border_filepath"].string_value() << "\"\n\n" + << "\t.incbin \"" << json_to_string(layout, "border_filepath") << "\"\n\n" << blockdata_label << "::\n" - << "\t.incbin \"" << layout["blockdata_filepath"].string_value() << "\"\n\n" + << "\t.incbin \"" << json_to_string(layout, "blockdata_filepath") << "\"\n\n" << "\t.align 2\n" - << layout["name"].string_value() << "::\n" - << "\t.4byte " << layout["width"].int_value() << "\n" - << "\t.4byte " << layout["height"].int_value() << "\n" + << layoutName << "::\n" + << "\t.4byte " << json_to_string(layout, "width") << "\n" + << "\t.4byte " << json_to_string(layout, "height") << "\n" << "\t.4byte " << border_label << "\n" << "\t.4byte " << blockdata_label << "\n" - << "\t.4byte " << layout["primary_tileset"].string_value() << "\n" - << "\t.4byte " << layout["secondary_tileset"].string_value() << "\n\n"; + << "\t.4byte " << json_to_string(layout, "primary_tileset") << "\n" + << "\t.4byte " << json_to_string(layout, "secondary_tileset") << "\n"; + if (version == "firered") { + text << "\t.byte " << json_to_string(layout, "border_width") << "\n" + << "\t.byte " << json_to_string(layout, "border_height") << "\n" + << "\t.2byte 0\n"; + } + text << "\n"; } return text.str(); @@ -451,11 +552,16 @@ string generate_layout_headers_text(Json layouts_data) { string generate_layouts_table_text(Json layouts_data) { ostringstream text; + text << "@\n@ DO NOT MODIFY THIS FILE! It is auto-generated from data/layouts/layouts.json\n@\n\n"; + text << "\t.align 2\n" - << layouts_data["layouts_table_label"].string_value() << "::\n"; + << json_to_string(layouts_data, "layouts_table_label") << "::\n"; - for (auto &layout : layouts_data["layouts"].array_items()) - text << "\t.4byte " << layout["name"].string_value() << "\n"; + for (auto &layout : layouts_data["layouts"].array_items()) { + string layout_name = json_to_string(layout, "name", true); + if (layout_name.empty()) layout_name = "NULL"; + text << "\t.4byte " << layout_name << "\n"; + } return text.str(); } @@ -466,9 +572,14 @@ string generate_layouts_constants_text(Json layouts_data) { text << "#ifndef GUARD_CONSTANTS_LAYOUTS_H\n" << "#define GUARD_CONSTANTS_LAYOUTS_H\n\n"; - int i = 0; - for (auto &layout : layouts_data["layouts"].array_items()) - text << "#define " << layout["id"].string_value() << " " << ++i << "\n"; + text << "//\n// DO NOT MODIFY THIS FILE! It is auto-generated from data/layouts/layouts.json\n//\n\n"; + + int i = 1; + for (auto &layout : layouts_data["layouts"].array_items()) { + if (layout != Json::object()) + text << "#define " << json_to_string(layout, "id") << " " << i << "\n"; + i++; + } text << "\n#endif // GUARD_CONSTANTS_LAYOUTS_H\n"; @@ -499,9 +610,9 @@ int main(int argc, char *argv[]) { FATAL_ERROR("USAGE: mapjson [options]\n"); char *version_arg = argv[2]; - string version(version_arg); - if (version != "emerald" && version != "ruby") - FATAL_ERROR("ERROR: must be 'emerald' or 'ruby'.\n"); + version = string(version_arg); + if (version != "emerald" && version != "ruby" && version != "firered") + FATAL_ERROR("ERROR: must be 'emerald', 'firered', or 'ruby'.\n"); char *mode_arg = argv[1]; string mode(mode_arg); @@ -515,7 +626,7 @@ int main(int argc, char *argv[]) { string filepath(argv[3]); string layouts_filepath(argv[4]); - process_map(filepath, layouts_filepath, version); + process_map(filepath, layouts_filepath); } else if (mode == "groups") { if (argc != 4)