From 6ae3c7ac36dc4fce8331f1c7b7aba7e61c955207 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sun, 28 Apr 2024 12:31:15 +0200 Subject: [PATCH 001/146] Fix build errors with curl disabled --- src/unittest/test_utilities.cpp | 2 +- src/util/colorize.cpp | 2 +- src/util/colorize.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/unittest/test_utilities.cpp b/src/unittest/test_utilities.cpp index 16032d130..910d785f2 100644 --- a/src/unittest/test_utilities.cpp +++ b/src/unittest/test_utilities.cpp @@ -728,7 +728,7 @@ void TestUtilities::testIsBlockInSight() void TestUtilities::testColorizeURL() { -#ifdef USE_CURL +#if USE_CURL #define RED COLOR_CODE("#faa") #define GREY COLOR_CODE("#aaa") #define WHITE COLOR_CODE("#fff") diff --git a/src/util/colorize.cpp b/src/util/colorize.cpp index 4cfe46a09..8fc33b561 100644 --- a/src/util/colorize.cpp +++ b/src/util/colorize.cpp @@ -17,7 +17,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "colorize.h" -#ifdef USE_CURL +#if USE_CURL #include #include "log.h" diff --git a/src/util/colorize.h b/src/util/colorize.h index dfd1ef921..3cde3e3dc 100644 --- a/src/util/colorize.h +++ b/src/util/colorize.h @@ -22,7 +22,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #define COLOR_CODE(color) "\x1b(c@" color ")" -#ifdef USE_CURL +#if USE_CURL /** * Colorize URL to highlight the hostname and any unsafe characters From 9111b7ff083f06675775eaaa3223f8efaff76522 Mon Sep 17 00:00:00 2001 From: sfence Date: Sun, 28 Apr 2024 17:55:04 +0200 Subject: [PATCH 002/146] Allow `nil` puncher in `object:punch` (#14319) --- builtin/game/knockback.lua | 10 ++++++---- doc/lua_api.md | 8 ++++---- games/devtest/mods/callbacks/entities.lua | 23 ++++++++++++++++++++++ games/devtest/mods/callbacks/init.lua | 1 + games/devtest/mods/callbacks/players.lua | 11 +++++++++++ src/script/cpp_api/s_entity.cpp | 7 ++++--- src/script/cpp_api/s_player.cpp | 5 ++++- src/script/lua_api/l_object.cpp | 15 +++++++++----- src/server/luaentity_sao.cpp | 24 +++++++++++++---------- src/server/player_sao.cpp | 19 ++++++++++-------- src/tool.cpp | 2 +- 11 files changed, 89 insertions(+), 36 deletions(-) create mode 100644 games/devtest/mods/callbacks/players.lua diff --git a/builtin/game/knockback.lua b/builtin/game/knockback.lua index 979d13a14..b119cdeb9 100644 --- a/builtin/game/knockback.lua +++ b/builtin/game/knockback.lua @@ -22,14 +22,16 @@ local function vector_absmax(v) return max(max(abs(v.x), abs(v.y)), abs(v.z)) end -core.register_on_punchplayer(function(player, hitter, time_from_last_punch, tool_capabilities, unused_dir, damage) +core.register_on_punchplayer(function(player, hitter, time_from_last_punch, tool_capabilities, dir, damage) if player:get_hp() == 0 then return -- RIP end - -- Server::handleCommand_Interact() adds eye offset to one but not the other - -- so the direction is slightly off, calculate it ourselves - local dir = vector.subtract(player:get_pos(), hitter:get_pos()) + if hitter then + -- Server::handleCommand_Interact() adds eye offset to one but not the other + -- so the direction is slightly off, calculate it ourselves + dir = vector.subtract(player:get_pos(), hitter:get_pos()) + end local d = vector.length(dir) if d ~= 0.0 then dir = vector.divide(dir, d) diff --git a/doc/lua_api.md b/doc/lua_api.md index 43a103c3f..b4c1613c2 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -5729,7 +5729,7 @@ Call these functions only at load time! * Called when a player is punched * Note: This callback is invoked even if the punched player is dead. * `player`: ObjectRef - Player that was punched - * `hitter`: ObjectRef - Player that hit + * `hitter`: ObjectRef - Player that hit. Can be nil. * `time_from_last_punch`: Meant for disallowing spamming of clicks (can be nil). * `tool_capabilities`: Capability table of used item (can be nil) @@ -7825,11 +7825,11 @@ child will follow movement and rotation of that bone. * no-op if object is attached * `punch(puncher, time_from_last_punch, tool_capabilities, dir)` * punches the object, triggering all consequences a normal punch would have - * `puncher`: another `ObjectRef` which punched the object + * `puncher`: another `ObjectRef` which punched the object or `nil` * `dir`: direction vector of punch * Other arguments: See `on_punch` for entities - * All arguments except `puncher` can be `nil`, in which case a default - value will be used + * Arguments `time_from_last_punch`, `tool_capabilities`, and `dir` + will be replaced with a default value when the caller sets them to `nil`. * `right_click(clicker)`: * simulates using the 'place/use' key on the object * triggers all consequences as if a real player had done this diff --git a/games/devtest/mods/callbacks/entities.lua b/games/devtest/mods/callbacks/entities.lua index e8379cb6f..528c985da 100644 --- a/games/devtest/mods/callbacks/entities.lua +++ b/games/devtest/mods/callbacks/entities.lua @@ -76,3 +76,26 @@ minetest.register_entity("callbacks:callback_step", { message("on_step callback entity: on_step! pos="..spos(self).."; dtime="..dtime) end, }) + +-- Callback punch with nil puncher +minetest.register_entity("callbacks:callback_puncher", { + initial_properties = { + visual = "upright_sprite", + textures = { "callbacks_callback_entity.png" }, + infotext = "Callback entity for nil puncher test.", + }, + + on_punch = function(self, puncher, time_from_last_punch, tool_capabilities, dir, damage) + if puncher then + puncher:punch(nil, time_from_last_punch, tool_capabilities, dir) + self.object:punch(nil, time_from_last_punch, tool_capabilities, dir) + else + message( + "Callback entity: on_punch with nil puncher ".. + "pos="..spos(self).."; ".. + "time_from_last_punch="..time_from_last_punch.."; ".. + "tool_capabilities="..dump(tool_capabilities).."; ".. + "dir="..dump(dir).."; damage="..damage) + end + end, +}) diff --git a/games/devtest/mods/callbacks/init.lua b/games/devtest/mods/callbacks/init.lua index 8f719a3f8..77cee1db2 100644 --- a/games/devtest/mods/callbacks/init.lua +++ b/games/devtest/mods/callbacks/init.lua @@ -1,3 +1,4 @@ dofile(minetest.get_modpath("callbacks").."/items.lua") dofile(minetest.get_modpath("callbacks").."/nodes.lua") dofile(minetest.get_modpath("callbacks").."/entities.lua") +dofile(minetest.get_modpath("callbacks").."/players.lua") diff --git a/games/devtest/mods/callbacks/players.lua b/games/devtest/mods/callbacks/players.lua new file mode 100644 index 000000000..15bb076dc --- /dev/null +++ b/games/devtest/mods/callbacks/players.lua @@ -0,0 +1,11 @@ + +local message = function(msg) + minetest.log("action", "[callbacks] "..msg) + minetest.chat_send_all(msg) +end + +core.register_on_punchplayer(function(player, hitter, time_from_last_punch, tool_capabilities, dir, damage) + if not hitter then + message("Player "..player:get_player_name().." punched without hitter.") + end +end) diff --git a/src/script/cpp_api/s_entity.cpp b/src/script/cpp_api/s_entity.cpp index 7b96e65ed..559837e0d 100644 --- a/src/script/cpp_api/s_entity.cpp +++ b/src/script/cpp_api/s_entity.cpp @@ -262,8 +262,6 @@ bool ScriptApiEntity::luaentity_Punch(u16 id, { SCRIPTAPI_PRECHECKHEADER - assert(puncher); - int error_handler = PUSH_ERROR_HANDLER(L); // Get core.luaentities[id] @@ -278,7 +276,10 @@ bool ScriptApiEntity::luaentity_Punch(u16 id, } luaL_checktype(L, -1, LUA_TFUNCTION); lua_pushvalue(L, object); // self - objectrefGetOrCreate(L, puncher); // Clicker reference + if (puncher) + objectrefGetOrCreate(L, puncher); // Puncher reference + else + lua_pushnil(L); lua_pushnumber(L, time_from_last_punch); push_tool_capabilities(L, *toolcap); push_v3f(L, dir); diff --git a/src/script/cpp_api/s_player.cpp b/src/script/cpp_api/s_player.cpp index b5c662dae..7de445c20 100644 --- a/src/script/cpp_api/s_player.cpp +++ b/src/script/cpp_api/s_player.cpp @@ -68,7 +68,10 @@ bool ScriptApiPlayer::on_punchplayer(ServerActiveObject *player, lua_getfield(L, -1, "registered_on_punchplayers"); // Call callbacks objectrefGetOrCreate(L, player); - objectrefGetOrCreate(L, hitter); + if (hitter) + objectrefGetOrCreate(L, hitter); + else + lua_pushnil(L); lua_pushnumber(L, time_from_last_punch); push_tool_capabilities(L, *toolcap); push_v3f(L, dir); diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index ad4b7af41..c78a87317 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -170,16 +170,21 @@ int ObjectRef::l_punch(lua_State *L) { NO_MAP_LOCK_REQUIRED; ObjectRef *ref = checkObject(L, 1); - ObjectRef *puncher_ref = checkObject(L, 2); + ObjectRef *puncher_ref = lua_isnoneornil(L, 2) ? nullptr : checkObject(L, 2); ServerActiveObject *sao = getobject(ref); - ServerActiveObject *puncher = getobject(puncher_ref); - if (sao == nullptr || puncher == nullptr) + ServerActiveObject *puncher = puncher_ref ? getobject(puncher_ref) : nullptr; + if (sao == nullptr) return 0; float time_from_last_punch = readParam(L, 3, 1000000.0f); ToolCapabilities toolcap = read_tool_capabilities(L, 4); - v3f dir = readParam(L, 5, sao->getBasePosition() - puncher->getBasePosition()); - dir.normalize(); + v3f dir; + if (puncher) { + dir = readParam(L, 5, sao->getBasePosition() - puncher->getBasePosition()); + dir.normalize(); + } else { + dir = readParam(L, 5, v3f(0)); + } u32 wear = sao->punch(dir, &toolcap, puncher, time_from_last_punch); lua_pushnumber(L, wear); diff --git a/src/server/luaentity_sao.cpp b/src/server/luaentity_sao.cpp index 963fd63dc..020f13fa5 100644 --- a/src/server/luaentity_sao.cpp +++ b/src/server/luaentity_sao.cpp @@ -332,16 +332,16 @@ u32 LuaEntitySAO::punch(v3f dir, return 0; } - FATAL_ERROR_IF(!puncher, "Punch action called without SAO"); - s32 old_hp = getHP(); ItemStack selected_item, hand_item; - ItemStack tool_item = puncher->getWieldedItem(&selected_item, &hand_item); + ItemStack tool_item; + if (puncher) + tool_item = puncher->getWieldedItem(&selected_item, &hand_item); PunchDamageResult result = getPunchDamage( m_armor_groups, toolcap, - &tool_item, + puncher ? &tool_item : nullptr, time_from_last_punch, initial_wear); @@ -355,12 +355,16 @@ u32 LuaEntitySAO::punch(v3f dir, } } - actionstream << puncher->getDescription() << " (id=" << puncher->getId() << - ", hp=" << puncher->getHP() << ") punched " << - getDescription() << " (id=" << m_id << ", hp=" << m_hp << - "), damage=" << (old_hp - (s32)getHP()) << - (damage_handled ? " (handled by Lua)" : "") << std::endl; - + if (puncher) { + actionstream << puncher->getDescription() << " (id=" << puncher->getId() << + ", hp=" << puncher->getHP() << ")"; + } else { + actionstream << "(none)"; + } + actionstream << " punched " << + getDescription() << " (id=" << m_id << ", hp=" << m_hp << + "), damage=" << (old_hp - (s32)getHP()) << + (damage_handled ? " (handled by Lua)" : "") << std::endl; // TODO: give Lua control over wear return result.wear; } diff --git a/src/server/player_sao.cpp b/src/server/player_sao.cpp index 0806ffd73..29bd8045f 100644 --- a/src/server/player_sao.cpp +++ b/src/server/player_sao.cpp @@ -462,11 +462,9 @@ u32 PlayerSAO::punch(v3f dir, if (!toolcap) return 0; - FATAL_ERROR_IF(!puncher, "Punch action called without SAO"); - // No effect if PvP disabled or if immortal if (isImmortal() || !g_settings->getBool("enable_pvp")) { - if (puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) { + if (puncher && puncher->getType() == ACTIVEOBJECT_TYPE_PLAYER) { // create message and add to list sendPunchCommand(); return 0; @@ -493,11 +491,16 @@ u32 PlayerSAO::punch(v3f dir, } } - actionstream << puncher->getDescription() << " (id=" << puncher->getId() << - ", hp=" << puncher->getHP() << ") punched " << - getDescription() << " (id=" << m_id << ", hp=" << m_hp << - "), damage=" << (old_hp - (s32)getHP()) << - (damage_handled ? " (handled by Lua)" : "") << std::endl; + if (puncher) { + actionstream << puncher->getDescription() << " (id=" << puncher->getId() << + ", hp=" << puncher->getHP() << ")"; + } else { + actionstream << "(none)"; + } + actionstream << " puched " << + getDescription() << " (id=" << m_id << ", hp=" << m_hp << + "), damage=" << (old_hp - (s32)getHP()) << + (damage_handled ? " (handled by Lua)" : "") << std::endl; return hitparams.wear; } diff --git a/src/tool.cpp b/src/tool.cpp index 70f553229..70e98a828 100644 --- a/src/tool.cpp +++ b/src/tool.cpp @@ -484,7 +484,7 @@ PunchDamageResult getPunchDamage( { HitParams hitparams = getHitParams(armor_groups, toolcap, time_from_last_punch, - punchitem->wear); + punchitem ? punchitem->wear : 0); result.did_punch = true; result.wear = hitparams.wear; result.damage = hitparams.hp; From f6fc6cbb49f13b25cc873e725dfee12584bba624 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Sun, 28 Apr 2024 19:45:09 +0200 Subject: [PATCH 003/146] Client: fix unknown texture upon shift-move to full inventory list (#14586) Fixes a regression caused by 4245a760 'moveItemSomewhere' attempted to add a leftover stack to an empty stack, resulting in an empty name with non-0 ItemStack count. --- src/inventory.cpp | 5 +++-- src/unittest/test_moveaction.cpp | 24 ++++++++++++++++++++++++ 2 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/inventory.cpp b/src/inventory.cpp index 65bbe0390..3c132c718 100644 --- a/src/inventory.cpp +++ b/src/inventory.cpp @@ -758,8 +758,9 @@ void InventoryList::moveItemSomewhere(u32 i, InventoryList *dest, u32 count) if (!leftover.empty()) { // Add the remaining part back to the source item - ItemStack &source = getItem(i); - source.add(leftover.count); // do NOT use addItem to allow oversized stacks! + // do NOT use addItem to allow oversized stacks! + leftover.add(getItem(i).count); + changeItem(i, leftover); } } diff --git a/src/unittest/test_moveaction.cpp b/src/unittest/test_moveaction.cpp index 0720ba267..966cea8c7 100644 --- a/src/unittest/test_moveaction.cpp +++ b/src/unittest/test_moveaction.cpp @@ -34,6 +34,7 @@ class TestMoveAction : public TestBase void testMove(ServerActiveObject *obj, IGameDef *gamedef); void testMoveFillStack(ServerActiveObject *obj, IGameDef *gamedef); void testMoveSomewhere(ServerActiveObject *obj, IGameDef *gamedef); + void testMoveSomewherePartial(ServerActiveObject *obj, IGameDef *gamedef); void testMoveUnallowed(ServerActiveObject *obj, IGameDef *gamedef); void testMovePartial(ServerActiveObject *obj, IGameDef *gamedef); @@ -71,6 +72,7 @@ void TestMoveAction::runTests(IGameDef *gamedef) TEST(testMove, &obj, gamedef); TEST(testMoveFillStack, &obj, gamedef); TEST(testMoveSomewhere, &obj, gamedef); + TEST(testMoveSomewherePartial, &obj, gamedef); TEST(testMoveUnallowed, &obj, gamedef); TEST(testMovePartial, &obj, gamedef); @@ -143,9 +145,31 @@ void TestMoveAction::testMoveSomewhere(ServerActiveObject *obj, IGameDef *gamede UASSERT(inv.p2.getList("main")->getItem(0).getItemString() == "default:brick 10"); UASSERT(inv.p2.getList("main")->getItem(1).getItemString() == "default:stone 36"); + // Partially moved UASSERT(inv.p2.getList("main")->getItem(2).getItemString() == "default:stone 99"); } +void TestMoveAction::testMoveSomewherePartial(ServerActiveObject *obj, IGameDef *gamedef) +{ + // "Fail" because the destination list is full. + MockInventoryManager inv(gamedef); + + InventoryList *src = inv.p1.addList("main", 3); + src->addItem(0, parse_itemstack("default:brick 10")); + src->changeItem(1, parse_itemstack("default:stone 111")); // oversized + + InventoryList *dst = inv.p2.addList("main", 1); + dst->addItem(0, parse_itemstack("default:stone 98")); + + // No free slots to fit + apply_action("MoveSomewhere 10 player:p1 main 0 player:p2 main", &inv, obj, gamedef); + UASSERT(inv.p1.getList("main")->getItem(0).getItemString() == "default:brick 10"); + + // Only 1 item fits + apply_action("MoveSomewhere 111 player:p1 main 1 player:p2 main", &inv, obj, gamedef); + UASSERT(inv.p1.getList("main")->getItem(1).getItemString() == "default:stone 110"); +} + void TestMoveAction::testMoveUnallowed(ServerActiveObject *obj, IGameDef *gamedef) { MockInventoryManager inv(gamedef); From d480b8800ea9302d96bd28f9aa961e4c5b22ebe2 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 30 Apr 2024 13:50:18 +0200 Subject: [PATCH 004/146] Add minetest.get_gen_notify to mapgen env (#14568) --- src/script/lua_api/l_mapgen.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/script/lua_api/l_mapgen.cpp b/src/script/lua_api/l_mapgen.cpp index 0dcf7b68a..2b61f0e77 100644 --- a/src/script/lua_api/l_mapgen.cpp +++ b/src/script/lua_api/l_mapgen.cpp @@ -1052,14 +1052,15 @@ int ModApiMapgen::l_get_gen_notify(lua_State *L) { NO_MAP_LOCK_REQUIRED; - EmergeManager *emerge = getServer(L)->getEmergeManager(); + auto *emerge = getEmergeManager(L); + push_flags_string(L, flagdesc_gennotify, emerge->gen_notify_on, emerge->gen_notify_on); lua_createtable(L, emerge->gen_notify_on_deco_ids.size(), 0); int i = 1; for (u32 id : emerge->gen_notify_on_deco_ids) { - lua_pushnumber(L, id); + lua_pushinteger(L, id); lua_rawseti(L, -2, i++); } @@ -2054,6 +2055,7 @@ void ModApiMapgen::InitializeEmerge(lua_State *L, int top) API_FCT(get_mapgen_setting); API_FCT(get_mapgen_setting_noiseparams); API_FCT(get_noiseparams); + API_FCT(get_gen_notify); API_FCT(get_decoration_id); API_FCT(save_gen_notify); From 1ee61ef52a92bb823d7a1a17fe1d13e9ed3f3198 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20M=C3=BCller?= <34514239+appgurueu@users.noreply.github.com> Date: Tue, 30 Apr 2024 13:50:31 +0200 Subject: [PATCH 005/146] Document empty string as form name (#14601) --- doc/lua_api.md | 11 ++++++++--- src/script/lua_api/l_server.cpp | 4 ++++ 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/doc/lua_api.md b/doc/lua_api.md index b4c1613c2..60e95711e 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -5808,8 +5808,8 @@ Call these functions only at load time! * Return `true` to mark the command as handled, which means that the default handlers will be prevented. * `minetest.register_on_player_receive_fields(function(player, formname, fields))` - * Called when the server received input from `player` in a formspec with - the given `formname`. Specifically, this is called on any of the + * Called when the server received input from `player`. + Specifically, this is called on any of the following events: * a button was pressed, * Enter was pressed while the focus was on a text field @@ -5820,6 +5820,9 @@ Call these functions only at load time! * an entry was double-clicked in a textlist or table, * a scrollbar was moved, or * the form was actively closed by the player. + * `formname` is the name passed to `minetest.show_formspec`. + Special case: The empty string refers to the player inventory + (the formspec set by the `set_inventory_formspec` player method). * Fields are sent for formspec elements which define a field. `fields` is a table containing each formspecs element value (as string), with the `name` parameter as index for each. The value depends on the @@ -6418,7 +6421,8 @@ Formspec * `minetest.show_formspec(playername, formname, formspec)` * `playername`: name of player to show formspec * `formname`: name passed to `on_player_receive_fields` callbacks. - It should follow the `"modname:"` naming convention + It should follow the `"modname:"` naming convention. + `formname` must not be empty. * `formspec`: formspec to display * `minetest.close_formspec(playername, formname)` * `playername`: name of player to close formspec @@ -9660,6 +9664,7 @@ Used by `minetest.register_node`. on_receive_fields = function(pos, formname, fields, sender), -- fields = {name1 = value1, name2 = value2, ...} + -- formname should be the empty string; you **must not** use formname. -- Called when an UI form (e.g. sign text input) returns data. -- See minetest.register_on_player_receive_fields for more info. -- default: nil diff --git a/src/script/lua_api/l_server.cpp b/src/script/lua_api/l_server.cpp index 185287dd7..af9a526e0 100644 --- a/src/script/lua_api/l_server.cpp +++ b/src/script/lua_api/l_server.cpp @@ -426,6 +426,10 @@ int ModApiServer::l_show_formspec(lua_State *L) NO_MAP_LOCK_REQUIRED; const char *playername = luaL_checkstring(L, 1); const char *formname = luaL_checkstring(L, 2); + if (*formname == '\0') { + log_deprecated(L, "Deprecated call to `minetest.show_formspec`:" + "`formname` must not be empty"); + } const char *formspec = luaL_checkstring(L, 3); if(getServer(L)->showFormspec(playername,formspec,formname)) From c75860c45d532c357e890cfdace1b937a1b3f6ef Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 23 Apr 2024 19:43:08 +0200 Subject: [PATCH 006/146] Trivially optimize iteration order in loops Due to how node data is stored iterating X last provides better cache locality. --- src/collision.cpp | 4 ++-- src/environment.cpp | 4 ++-- src/serverenvironment.cpp | 48 +++++++++++++++++++-------------------- src/voxelalgorithms.cpp | 19 +++++++--------- 4 files changed, 36 insertions(+), 39 deletions(-) diff --git a/src/collision.cpp b/src/collision.cpp index 5d4339f62..de463126c 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -295,9 +295,9 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef, jesus = jesus && g_settings->getBool("jesus"); v3s16 p; - for (p.X = min.X; p.X <= max.X; p.X++) + for (p.Z = min.Z; p.Z <= max.Z; p.Z++) for (p.Y = min.Y; p.Y <= max.Y; p.Y++) - for (p.Z = min.Z; p.Z <= max.Z; p.Z++) { + for (p.X = min.X; p.X <= max.X; p.X++) { bool is_position_valid; MapNode n = map->getNode(p, &is_position_valid); diff --git a/src/environment.cpp b/src/environment.cpp index b10c396d9..51a624052 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -187,9 +187,9 @@ void Environment::continueRaycast(RaycastState *state, PointedThing *result_p) } // For each untested node - for (s16 x = new_nodes.MinEdge.X; x <= new_nodes.MaxEdge.X; x++) + for (s16 z = new_nodes.MinEdge.Z; z <= new_nodes.MaxEdge.Z; z++) for (s16 y = new_nodes.MinEdge.Y; y <= new_nodes.MaxEdge.Y; y++) - for (s16 z = new_nodes.MinEdge.Z; z <= new_nodes.MaxEdge.Z; z++) { + for (s16 x = new_nodes.MinEdge.X; x <= new_nodes.MaxEdge.X; x++) { MapNode n; v3s16 np(x, y, z); bool is_valid_position; diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index 0c3ff8fb4..41f859434 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -266,29 +266,29 @@ void LBMManager::applyLBMs(ServerEnvironment *env, MapBlock *block, content_t previous_c = CONTENT_IGNORE; const std::vector *lbm_list = nullptr; - for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++) - for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++) - for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++) { - n = block->getNodeNoCheck(pos); - c = n.getContent(); - - // If content_t are not matching perform an LBM lookup - if (previous_c != c) { - lbm_list = it->second.lookup(c); - previous_c = c; - } + for (pos.Z = 0; pos.Z < MAP_BLOCKSIZE; pos.Z++) + for (pos.Y = 0; pos.Y < MAP_BLOCKSIZE; pos.Y++) + for (pos.X = 0; pos.X < MAP_BLOCKSIZE; pos.X++) { + n = block->getNodeNoCheck(pos); + c = n.getContent(); + + // If content_t are not matching perform an LBM lookup + if (previous_c != c) { + lbm_list = it->second.lookup(c); + previous_c = c; + } - if (!lbm_list) - continue; - for (auto lbmdef : *lbm_list) { - lbmdef->trigger(env, pos + pos_of_block, n, dtime_s); - if (block->isOrphan()) - return; - n = block->getNodeNoCheck(pos); - if (n.getContent() != c) - break; // The node was changed and the LBMs no longer apply - } - } + if (!lbm_list) + continue; + for (auto lbmdef : *lbm_list) { + lbmdef->trigger(env, pos + pos_of_block, n, dtime_s); + if (block->isOrphan()) + return; + n = block->getNodeNoCheck(pos); + if (n.getContent() != c) + break; // The node was changed and the LBMs no longer apply + } + } } } @@ -935,9 +935,9 @@ class ABMHandler bool want_contents_cached = block->contents.empty() && !block->do_not_cache_contents; v3s16 p0; - for(p0.X=0; p0.XgetNodeNoCheck(p0); content_t c = n.getContent(); diff --git a/src/voxelalgorithms.cpp b/src/voxelalgorithms.cpp index bd155a7e7..f0b07c814 100644 --- a/src/voxelalgorithms.cpp +++ b/src/voxelalgorithms.cpp @@ -618,9 +618,8 @@ void update_lighting_nodes(Map *map, modified_blocks); // Initialize light values for light spreading. for (u8 i = 0; i <= LIGHT_SUN; i++) { - const std::vector &lights = light_sources.lights[i]; - for (std::vector::const_iterator it = lights.begin(); - it < lights.end(); ++it) { + const auto &lights = light_sources.lights[i]; + for (auto it = lights.begin(); it < lights.end(); ++it) { MapNode n = it->block->getNodeNoCheck(it->rel_position); n.setLight(bank, i, ndef->getLightingFlags(n)); it->block->setNodeNoCheck(it->rel_position, n); @@ -737,9 +736,8 @@ void update_block_border_lighting(Map *map, MapBlock *block, modified_blocks); // Initialize light values for light spreading. for (u8 i = 0; i <= LIGHT_SUN; i++) { - const std::vector &lights = light_sources.lights[i]; - for (std::vector::const_iterator it = lights.begin(); - it < lights.end(); ++it) { + const auto &lights = light_sources.lights[i]; + for (auto it = lights.begin(); it < lights.end(); ++it) { MapNode n = it->block->getNodeNoCheck(it->rel_position); n.setLight(bank, i, ndef->getLightingFlags(n)); it->block->setNodeNoCheck(it->rel_position, n); @@ -984,8 +982,8 @@ void finish_bulk_light_update(Map *map, mapblock_v3 minblock, // Skip not existing blocks continue; // For each node in the block: - for (relpos.X = 0; relpos.X < MAP_BLOCKSIZE; relpos.X++) for (relpos.Z = 0; relpos.Z < MAP_BLOCKSIZE; relpos.Z++) + for (relpos.X = 0; relpos.X < MAP_BLOCKSIZE; relpos.X++) for (relpos.Y = 0; relpos.Y < MAP_BLOCKSIZE; relpos.Y++) { MapNode node = block->getNodeNoCheck(relpos.X, relpos.Y, relpos.Z); ContentLightingFlags f = ndef->getLightingFlags(node); @@ -1011,9 +1009,8 @@ void finish_bulk_light_update(Map *map, mapblock_v3 minblock, u8 maxlight = (b == 0) ? LIGHT_MAX : LIGHT_SUN; // Initialize light values for light spreading. for (u8 i = 0; i <= maxlight; i++) { - const std::vector &lights = relight[b].lights[i]; - for (std::vector::const_iterator it = lights.begin(); - it < lights.end(); ++it) { + const auto &lights = relight[b].lights[i]; + for (auto it = lights.begin(); it < lights.end(); ++it) { MapNode n = it->block->getNodeNoCheck(it->rel_position); n.setLight(bank, i, ndef->getLightingFlags(n)); it->block->setNodeNoCheck(it->rel_position, n); @@ -1085,8 +1082,8 @@ void blit_back_with_light(Map *map, MMVManip *vm, // For each border of the block: for (const VoxelArea &a : block_pad) { // For each node of the border: - for (relpos.X = a.MinEdge.X; relpos.X <= a.MaxEdge.X; relpos.X++) for (relpos.Z = a.MinEdge.Z; relpos.Z <= a.MaxEdge.Z; relpos.Z++) + for (relpos.X = a.MinEdge.X; relpos.X <= a.MaxEdge.X; relpos.X++) for (relpos.Y = a.MinEdge.Y; relpos.Y <= a.MaxEdge.Y; relpos.Y++) { // Get old and new node From a0922980a965d6a7b5584cd174a9aec806261132 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 23 Apr 2024 20:23:31 +0200 Subject: [PATCH 007/146] Optimize env access and structs in collisionMoveSimple --- src/collision.cpp | 168 +++++++++++++++++++--------------------------- 1 file changed, 70 insertions(+), 98 deletions(-) diff --git a/src/collision.cpp b/src/collision.cpp index de463126c..c52fa2a0a 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -39,45 +39,47 @@ with this program; if not, write to the Free Software Foundation, Inc., #warning "-ffast-math is known to cause bugs in collision code, do not use!" #endif +namespace { + struct NearbyCollisionInfo { // node - NearbyCollisionInfo(bool is_ul, int bouncy, const v3s16 &pos, - const aabb3f &box) : - is_unloaded(is_ul), + NearbyCollisionInfo(bool is_ul, int bouncy, v3s16 pos, const aabb3f &box) : obj(nullptr), - bouncy(bouncy), + box(box), position(pos), - box(box) + bouncy(bouncy), + is_unloaded(is_ul), + is_step_up(false) {} // object - NearbyCollisionInfo(ActiveObject *obj, int bouncy, - const aabb3f &box) : - is_unloaded(false), + NearbyCollisionInfo(ActiveObject *obj, int bouncy, const aabb3f &box) : obj(obj), + box(box), bouncy(bouncy), - box(box) + is_unloaded(false), + is_step_up(false) {} inline bool isObject() const { return obj != nullptr; } - bool is_unloaded; - bool is_step_up = false; ActiveObject *obj; - int bouncy; - v3s16 position; aabb3f box; + v3s16 position; + u8 bouncy; + // bitfield to save space + bool is_unloaded:1, is_step_up:1; }; // Helper functions: // Truncate floating point numbers to specified number of decimal places // in order to move all the floating point error to one side of the correct value -static inline f32 truncate(const f32 val, const f32 factor) +inline f32 truncate(const f32 val, const f32 factor) { return truncf(val * factor) / factor; } -static inline v3f truncate(const v3f& vec, const f32 factor) +inline v3f truncate(const v3f vec, const f32 factor) { return v3f( truncate(vec.X, factor), @@ -86,6 +88,8 @@ static inline v3f truncate(const v3f& vec, const f32 factor) ); } +} + // Helper function: // Checks for collision of a moving aabbox with a static aabbox // Returns -1 if no collision, 0 if X collision, 1 if Y collision, 2 if Z collision @@ -273,11 +277,12 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef, /* Collect node boxes in movement range */ - std::vector cinfo; - { - //TimeTaker tt2("collisionMoveSimple collect boxes"); - ScopeProfiler sp2(g_profiler, PROFILER_NAME("collision collect boxes"), SPT_AVG, PRECISION_MICRO); + // cached allocation + thread_local std::vector cinfo; + cinfo.clear(); + + { v3f minpos_f( MYMIN(pos_f->X, newpos_f.X), MYMIN(pos_f->Y, newpos_f.Y) + 0.01f * BS, // bias rounding, player often at +/-n.5 @@ -291,9 +296,12 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef, v3s16 min = floatToInt(minpos_f + box_0.MinEdge, BS) - v3s16(1, 1, 1); v3s16 max = floatToInt(maxpos_f + box_0.MaxEdge, BS) + v3s16(1, 1, 1); + const auto *nodedef = gamedef->getNodeDefManager(); bool any_position_valid = false; jesus = jesus && g_settings->getBool("jesus"); + thread_local std::vector nodeboxes; + v3s16 p; for (p.Z = min.Z; p.Z <= max.Z; p.Z++) for (p.Y = min.Y; p.Y <= max.Y; p.Y++) @@ -305,7 +313,6 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef, // Object collides into walkable nodes any_position_valid = true; - const NodeDefManager *nodedef = gamedef->getNodeDefManager(); const ContentFeatures &f = nodedef->get(n); if (!(f.walkable || (jesus && f.isLiquid() && p.Y < pos_f->Y/BS && speed_f->Y <= 0))) @@ -313,36 +320,10 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef, // Negative bouncy may have a meaning, but we need +value here. int n_bouncy_value = abs(itemgroup_get(f.groups, "bouncy")); - int neighbors = 0; - if (f.drawtype == NDT_NODEBOX && - f.node_box.type == NODEBOX_CONNECTED) { - v3s16 p2 = p; - - p2.Y++; - getNeighborConnectingFace(p2, nodedef, map, n, 1, &neighbors); + u8 neighbors = n.getNeighbors(p, map); - p2 = p; - p2.Y--; - getNeighborConnectingFace(p2, nodedef, map, n, 2, &neighbors); - - p2 = p; - p2.Z--; - getNeighborConnectingFace(p2, nodedef, map, n, 4, &neighbors); - - p2 = p; - p2.X--; - getNeighborConnectingFace(p2, nodedef, map, n, 8, &neighbors); - - p2 = p; - p2.Z++; - getNeighborConnectingFace(p2, nodedef, map, n, 16, &neighbors); - - p2 = p; - p2.X++; - getNeighborConnectingFace(p2, nodedef, map, n, 32, &neighbors); - } - std::vector nodeboxes; - n.getCollisionBoxes(gamedef->ndef(), &nodeboxes, neighbors); + nodeboxes.clear(); + n.getCollisionBoxes(nodedef, &nodeboxes, neighbors); // Calculate float position only once v3f posf = intToFloat(p, BS); @@ -368,19 +349,28 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef, return result; } - } // tt2 + } - if(collideWithObjects) - { - /* add object boxes to cinfo */ + /* + Collect object boxes in movement range + */ + + auto process_object = [] (ActiveObject *object) { + if (object && object->collideWithObjects()) { + aabb3f box; + if (object->getCollisionBox(&box)) + cinfo.emplace_back(object, 0, box); + } + }; + + if (collideWithObjects) { + // Calculate distance by speed, add own extent and 1.5m of tolerance + const f32 distance = speed_f->getLength() * dtime + + box_0.getExtent().getLength() + 1.5f * BS; - std::vector objects; #ifndef SERVER ClientEnvironment *c_env = dynamic_cast(env); - if (c_env != 0) { - // Calculate distance by speed, add own extent and 1.5m of tolerance - f32 distance = speed_f->getLength() * dtime + - box_0.getExtent().getLength() + 1.5f * BS; + if (c_env) { std::vector clientobjects; c_env->getActiveObjects(*pos_f, distance, clientobjects); @@ -388,58 +378,40 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef, // Do collide with everything but itself and the parent CAO if (!self || (self != clientobject.obj && self != clientobject.obj->getParent())) { - objects.push_back((ActiveObject*) clientobject.obj); + process_object(clientobject.obj); } } - } - else -#endif - { - if (s_env != NULL) { - // Calculate distance by speed, add own extent and 1.5m of tolerance - f32 distance = speed_f->getLength() * dtime + - box_0.getExtent().getLength() + 1.5f * BS; - - // search for objects which are not us, or we are not its parent - // we directly use the callback to populate the result to prevent - // a useless result loop here - auto include_obj_cb = [self, &objects] (ServerActiveObject *obj) { - if (!obj->isGone() && - (!self || (self != obj && self != obj->getParent()))) { - objects.push_back((ActiveObject *)obj); - } - return false; - }; - - std::vector s_objects; - s_env->getObjectsInsideRadius(s_objects, *pos_f, distance, include_obj_cb); - } - } - - for (std::vector::const_iterator iter = objects.begin(); - iter != objects.end(); ++iter) { - ActiveObject *object = *iter; - if (object && object->collideWithObjects()) { - aabb3f object_collisionbox; - if (object->getCollisionBox(&object_collisionbox)) - cinfo.emplace_back(object, 0, object_collisionbox); - } - } -#ifndef SERVER - if (self && c_env) { + // add collision with local player LocalPlayer *lplayer = c_env->getLocalPlayer(); if (lplayer->getParent() == nullptr) { aabb3f lplayer_collisionbox = lplayer->getCollisionbox(); v3f lplayer_pos = lplayer->getPosition(); lplayer_collisionbox.MinEdge += lplayer_pos; lplayer_collisionbox.MaxEdge += lplayer_pos; - ActiveObject *obj = (ActiveObject*) lplayer->getCAO(); + auto *obj = (ActiveObject*) lplayer->getCAO(); cinfo.emplace_back(obj, 0, lplayer_collisionbox); } } + else #endif - } //tt3 + if (s_env) { + // search for objects which are not us, or we are not its parent. + // we directly process the object in this callback to avoid useless + // looping afterwards. + auto include_obj_cb = [self, &process_object] (ServerActiveObject *obj) { + if (!obj->isGone() && + (!self || (self != obj && self != obj->getParent()))) { + process_object(obj); + } + return false; + }; + + // nothing is put into this vector + std::vector s_objects; + s_env->getObjectsInsideRadius(s_objects, *pos_f, distance, include_obj_cb); + } + } /* Collision detection @@ -575,7 +547,7 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef, if (is_collision) { info.axis = nearest_collided; - result.collisions.push_back(info); + result.collisions.push_back(std::move(info)); } } } From a10cd55683a7623f1e1f6fceb51d98e524cc1548 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 23 Apr 2024 21:12:31 +0200 Subject: [PATCH 008/146] Optimize pushing collision data for entity on_step Since this is fixed overhead for every entity, this is important to optimize. This optimizes one very common case. before: push_collision_move_result [us] _____________ 64512x 3.562 after: push_collision_move_result [us] _____________ 72636x 0.831 --- builtin/game/misc_s.lua | 22 +++++++++++++++++++++ src/script/common/c_content.cpp | 21 ++++++++++++++++++++ src/script/common/c_internal.h | 3 ++- src/script/cpp_api/s_base.cpp | 34 +++++++++++++++++++-------------- 4 files changed, 65 insertions(+), 15 deletions(-) diff --git a/builtin/game/misc_s.lua b/builtin/game/misc_s.lua index af4002bf2..18812131a 100644 --- a/builtin/game/misc_s.lua +++ b/builtin/game/misc_s.lua @@ -97,3 +97,25 @@ function core.encode_png(width, height, data, compression) return o_encode_png(width, height, data, compression or 6) end + +-- Helper that pushes a collisionMoveResult structure +if core.set_push_moveresult1 then + -- must match CollisionAxis in collision.h + local AXES = {"x", "y", "z"} + -- <=> script/common/c_content.cpp push_collision_move_result() + core.set_push_moveresult1(function(b0, b1, b2, axis, npx, npy, npz, v0x, v0y, v0z, v1x, v1y, v1z) + return { + touching_ground = b0, + collides = b1, + standing_on_object = b2, + collisions = {{ + type = "node", + axis = AXES[axis], + node_pos = vector.new(npx, npy, npz), + old_velocity = vector.new(v0x, v0y, v0z), + new_velocity = vector.new(v1x, v1y, v1z), + }}, + } + end) + core.set_push_moveresult1 = nil +end diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index 24eba011c..fde47d452 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -2579,6 +2579,27 @@ static const char *collision_axis_str[] = { void push_collision_move_result(lua_State *L, const collisionMoveResult &res) { + // use faster Lua helper if possible + if (res.collisions.size() == 1 && res.collisions.front().type == COLLISION_NODE) { + lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_PUSH_MOVERESULT1); + const auto &c = res.collisions.front(); + lua_pushboolean(L, res.touching_ground); + lua_pushboolean(L, res.collides); + lua_pushboolean(L, res.standing_on_object); + assert(c.axis != COLLISION_AXIS_NONE); + lua_pushinteger(L, static_cast(c.axis)); + lua_pushinteger(L, c.node_p.X); + lua_pushinteger(L, c.node_p.Y); + lua_pushinteger(L, c.node_p.Z); + for (v3f v : {c.old_speed / BS, c.new_speed / BS}) { + lua_pushnumber(L, v.X); + lua_pushnumber(L, v.Y); + lua_pushnumber(L, v.Z); + } + lua_call(L, 3 + 1 + 3 + 3 * 2, 1); + return; + } + lua_createtable(L, 0, 4); setboolfield(L, -1, "touching_ground", res.touching_ground); diff --git a/src/script/common/c_internal.h b/src/script/common/c_internal.h index 78ce48e7e..a67b0dad9 100644 --- a/src/script/common/c_internal.h +++ b/src/script/common/c_internal.h @@ -58,12 +58,13 @@ enum { CUSTOM_RIDX_HTTP_API_LUA, CUSTOM_RIDX_METATABLE_MAP, - // The following four functions are implemented in Lua because LuaJIT can + // The following functions are implemented in Lua because LuaJIT can // trace them and optimize tables/string better than from the C API. CUSTOM_RIDX_READ_VECTOR, CUSTOM_RIDX_PUSH_VECTOR, CUSTOM_RIDX_READ_NODE, CUSTOM_RIDX_PUSH_NODE, + CUSTOM_RIDX_PUSH_MOVERESULT1, }; diff --git a/src/script/cpp_api/s_base.cpp b/src/script/cpp_api/s_base.cpp index cafdcd4f4..43f14ff6c 100644 --- a/src/script/cpp_api/s_base.cpp +++ b/src/script/cpp_api/s_base.cpp @@ -142,6 +142,11 @@ ScriptApiBase::ScriptApiBase(ScriptingType type): return 0; }); lua_setfield(m_luastack, -2, "set_push_node"); + lua_pushcfunction(m_luastack, [](lua_State *L) -> int { + lua_rawseti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_PUSH_MOVERESULT1); + return 0; + }); + lua_setfield(m_luastack, -2, "set_push_moveresult1"); // Finally, put the table into the global environment: lua_setglobal(m_luastack, "core"); @@ -196,29 +201,30 @@ void ScriptApiBase::clientOpenLibs(lua_State *L) } #endif +#define CHECK(ridx, name) do { \ + lua_rawgeti(L, LUA_REGISTRYINDEX, ridx); \ + FATAL_ERROR_IF(lua_type(L, -1) != LUA_TFUNCTION, "missing " name); \ + lua_pop(L, 1); \ + } while (0) + void ScriptApiBase::checkSetByBuiltin() { lua_State *L = getStack(); if (m_gamedef) { - lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_READ_VECTOR); - FATAL_ERROR_IF(lua_type(L, -1) != LUA_TFUNCTION, "missing read_vector"); - lua_pop(L, 1); - - lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_PUSH_VECTOR); - FATAL_ERROR_IF(lua_type(L, -1) != LUA_TFUNCTION, "missing push_vector"); - lua_pop(L, 1); - - lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_READ_NODE); - FATAL_ERROR_IF(lua_type(L, -1) != LUA_TFUNCTION, "missing read_node"); - lua_pop(L, 1); + CHECK(CUSTOM_RIDX_READ_VECTOR, "read_vector"); + CHECK(CUSTOM_RIDX_PUSH_VECTOR, "push_vector"); + CHECK(CUSTOM_RIDX_READ_NODE, "read_node"); + CHECK(CUSTOM_RIDX_PUSH_NODE, "push_node"); + } - lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_PUSH_NODE); - FATAL_ERROR_IF(lua_type(L, -1) != LUA_TFUNCTION, "missing push_node"); - lua_pop(L, 1); + if (getType() == ScriptingType::Server) { + CHECK(CUSTOM_RIDX_PUSH_MOVERESULT1, "push_moveresult1"); } } +#undef CHECK + std::string ScriptApiBase::getCurrentModNameInsecure(lua_State *L) { lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_CURRENT_MOD_NAME); From b75f6365e45ca88dc3b46be7dedc517d1c90fc73 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 23 Apr 2024 21:50:47 +0200 Subject: [PATCH 009/146] Reduce wasteful memory allocations in update_lighting_nodes() --- src/voxelalgorithms.cpp | 59 ++++++++++++++++++++++++++--------------- 1 file changed, 38 insertions(+), 21 deletions(-) diff --git a/src/voxelalgorithms.cpp b/src/voxelalgorithms.cpp index f0b07c814..607d6716c 100644 --- a/src/voxelalgorithms.cpp +++ b/src/voxelalgorithms.cpp @@ -17,6 +17,8 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include + #include "voxelalgorithms.h" #include "nodedef.h" #include "mapblock.h" @@ -51,12 +53,12 @@ typedef v3s16 mapblock_v3; //! Contains information about a node whose light is about to change. struct ChangingLight { + //! Pointer to the node's block. + MapBlock *block = nullptr; //! Relative position of the node in its map block. relative_v3 rel_position; //! Position of the node's block. mapblock_v3 block_position; - //! Pointer to the node's block. - MapBlock *block = NULL; /*! * Direction from the node that caused this node's changing * to this node. @@ -67,9 +69,9 @@ struct ChangingLight { ChangingLight(relative_v3 rel_pos, mapblock_v3 block_pos, MapBlock *b, direction source_dir) : + block(b), rel_position(rel_pos), block_position(block_pos), - block(b), source_direction(source_dir) {} }; @@ -81,7 +83,7 @@ struct ChangingLight { */ struct LightQueue { //! For each light level there is a vector. - std::vector lights[LIGHT_SUN + 1]; + std::array, LIGHT_SUN + 1> lights; //! Light of the brightest ChangingLight in the queue. u8 max_light; @@ -92,9 +94,15 @@ struct LightQueue { LightQueue(size_t reserve) { max_light = LIGHT_SUN; - for (u8 i = 0; i <= LIGHT_SUN; i++) { - lights[i].reserve(reserve); - } + for (auto &l : lights) + l.reserve(reserve); + } + + //! Clears a LightQueue. + void clear() { + max_light = LIGHT_SUN; + for (auto &l : lights) + l.clear(); } /*! @@ -456,7 +464,7 @@ bool is_sunlight_above(Map *map, v3s16 pos, const NodeDefManager *ndef) return sunlight; } -static const LightBank banks[] = { LIGHTBANK_DAY, LIGHTBANK_NIGHT }; +static constexpr LightBank banks[] = { LIGHTBANK_DAY, LIGHTBANK_NIGHT }; void update_lighting_nodes(Map *map, const std::vector> &oldnodes, @@ -466,10 +474,14 @@ void update_lighting_nodes(Map *map, // For node getter functions bool is_valid_position; + // cached allocations + thread_local UnlightQueue disappearing_lights(1); + thread_local ReLightQueue light_sources(4); + // Process each light bank separately for (LightBank bank : banks) { - UnlightQueue disappearing_lights(256); - ReLightQueue light_sources(256); + disappearing_lights.clear(); + light_sources.clear(); // Nodes that are brighter than the brightest modified node was // won't change, since they didn't get their light from a // modified node. @@ -634,14 +646,16 @@ void update_lighting_nodes(Map *map, * Borders of a map block in relative node coordinates. * Compatible with type 'direction'. */ -const VoxelArea block_borders[] = { - VoxelArea(v3s16(15, 0, 0), v3s16(15, 15, 15)), //X+ - VoxelArea(v3s16(0, 15, 0), v3s16(15, 15, 15)), //Y+ - VoxelArea(v3s16(0, 0, 15), v3s16(15, 15, 15)), //Z+ - VoxelArea(v3s16(0, 0, 0), v3s16(15, 15, 0)), //Z- - VoxelArea(v3s16(0, 0, 0), v3s16(15, 0, 15)), //Y- - VoxelArea(v3s16(0, 0, 0), v3s16(0, 15, 15)) //X- +#define B_1 (MAP_BLOCKSIZE - 1) +const static VoxelArea block_borders[] = { + VoxelArea(v3s16(B_1, 0, 0), v3s16(B_1, B_1, B_1)), //X+ + VoxelArea(v3s16(0, B_1, 0), v3s16(B_1, B_1, B_1)), //Y+ + VoxelArea(v3s16(0, 0, B_1), v3s16(B_1, B_1, B_1)), //Z+ + VoxelArea(v3s16(0, 0, 0), v3s16(B_1, B_1, 0)), //Z- + VoxelArea(v3s16(0, 0, 0), v3s16(B_1, 0, B_1)), //Y- + VoxelArea(v3s16(0, 0, 0), v3s16(0, B_1, B_1)) //X- }; +#undef B_1 /*! * Returns true if: @@ -679,11 +693,14 @@ void update_block_border_lighting(Map *map, MapBlock *block, std::map &modified_blocks) { const NodeDefManager *ndef = map->getNodeDefManager(); + // Since invalid light is not common, do not allocate + // memory if not needed. + UnlightQueue disappearing_lights(0); + ReLightQueue light_sources(0); + for (LightBank bank : banks) { - // Since invalid light is not common, do not allocate - // memory if not needed. - UnlightQueue disappearing_lights(0); - ReLightQueue light_sources(0); + disappearing_lights.clear(); + light_sources.clear(); // Get incorrect lights for (direction d = 0; d < 6; d++) { // For each direction From d0fd51fbde7f914690586ee55a19b8c52fed8e70 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 24 Apr 2024 22:08:12 +0200 Subject: [PATCH 010/146] Optimize code patterns around raycasting --- src/environment.cpp | 26 +++++++++--------- src/serverenvironment.cpp | 55 +++++++++++++++++++++------------------ src/util/pointedthing.cpp | 30 --------------------- src/util/pointedthing.h | 50 ++++++++++++++++++++++++----------- 4 files changed, 77 insertions(+), 84 deletions(-) diff --git a/src/environment.cpp b/src/environment.cpp index 51a624052..bab106bf3 100644 --- a/src/environment.cpp +++ b/src/environment.cpp @@ -131,9 +131,8 @@ void Environment::continueRaycast(RaycastState *state, PointedThing *result_p) if (state->m_objects_pointable) { std::vector found; getSelectedActiveObjects(state->m_shootline, found, state->m_pointabilities); - for (const PointedThing &pointed : found) { - state->m_found.push(pointed); - } + for (auto &pointed : found) + state->m_found.push(std::move(pointed)); } // Set search range core::aabbox3d maximal_exceed = nodedef->getSelectionBoxIntUnion(); @@ -152,14 +151,10 @@ void Environment::continueRaycast(RaycastState *state, PointedThing *result_p) } Map &map = getMap(); - // If a node is found, this is the center of the - // first nodebox the shootline meets. - v3f found_boxcenter(0, 0, 0); - // The untested nodes are in this range. - core::aabbox3d new_nodes; + std::vector boxes; while (state->m_iterator.m_current_index <= lastIndex) { // Test the nodes around the current node in search_range. - new_nodes = state->m_search_range; + core::aabbox3d new_nodes = state->m_search_range; new_nodes.MinEdge += state->m_iterator.m_current_node_pos; new_nodes.MaxEdge += state->m_iterator.m_current_node_pos; @@ -207,7 +202,7 @@ void Environment::continueRaycast(RaycastState *state, PointedThing *result_p) PointedThing result; - std::vector boxes; + boxes.clear(); n.getSelectionBoxes(nodedef, &boxes, n.getNeighbors(np, &map)); @@ -217,6 +212,9 @@ void Environment::continueRaycast(RaycastState *state, PointedThing *result_p) float min_distance_sq = 10000000; // ID of the current box (loop counter) u16 id = 0; + // If a node is found, this is the center of the + // first nodebox the shootline meets. + v3f found_boxcenter(0, 0, 0); // Do calculations relative to the node center // to translate the ray rather than the boxes @@ -255,7 +253,7 @@ void Environment::continueRaycast(RaycastState *state, PointedThing *result_p) result.node_undersurface = np; result.distanceSq = min_distance_sq; // Set undersurface and abovesurface nodes - f32 d = 0.002 * BS; + const f32 d = 0.002 * BS; v3f fake_intersection = result.intersection_point; found_boxcenter += npf; // translate back to world coords // Move intersection towards its source block. @@ -278,8 +276,9 @@ void Environment::continueRaycast(RaycastState *state, PointedThing *result_p) fake_intersection, BS); result.node_abovesurface = result.node_real_undersurface + floatToInt(result.intersection_normal, 1.0f); + // Push found PointedThing - state->m_found.push(result); + state->m_found.push(std::move(result)); // If this is nearer than the old nearest object, // the search can be shorter s16 newIndex = state->m_iterator.getIndex( @@ -299,9 +298,8 @@ void Environment::continueRaycast(RaycastState *state, PointedThing *result_p) } else { *result_p = state->m_found.top(); state->m_found.pop(); - if (result_p->pointability == PointabilityType::POINTABLE_BLOCKING) { + if (result_p->pointability == PointabilityType::POINTABLE_BLOCKING) result_p->type = POINTEDTHING_NOTHING; - } } } diff --git a/src/serverenvironment.cpp b/src/serverenvironment.cpp index 41f859434..0d5040df9 100644 --- a/src/serverenvironment.cpp +++ b/src/serverenvironment.cpp @@ -1817,17 +1817,14 @@ void ServerEnvironment::getSelectedActiveObjects( std::vector &objects, const std::optional &pointabilities) { - std::vector objs; - getObjectsInsideRadius(objs, shootline_on_map.getMiddle(), - 0.5 * shootline_on_map.getLength() + 5 * BS, nullptr); const v3f line_vector = shootline_on_map.getVector(); - for (auto obj : objs) { + auto process = [&] (ServerActiveObject *obj) -> bool { if (obj->isGone()) - continue; + return false; aabb3f selection_box; if (!obj->getSelectionBox(&selection_box)) - continue; + return false; v3f pos = obj->getBasePosition(); v3f rel_pos = shootline_on_map.start - pos; @@ -1847,29 +1844,37 @@ void ServerEnvironment::getSelectedActiveObjects( ¤t_intersection, ¤t_normal); current_raw_normal = current_normal; } - if (collision) { - PointabilityType pointable; - if (pointabilities) { - if (LuaEntitySAO* lsao = dynamic_cast(obj)) { - pointable = pointabilities->matchObject(lsao->getName(), - usao->getArmorGroups()).value_or(props->pointable); - } else if (PlayerSAO* psao = dynamic_cast(obj)) { - pointable = pointabilities->matchPlayer(psao->getArmorGroups()).value_or( - props->pointable); - } else { - pointable = props->pointable; - } + if (!collision) + return false; + + PointabilityType pointable; + if (pointabilities) { + if (LuaEntitySAO* lsao = dynamic_cast(obj)) { + pointable = pointabilities->matchObject(lsao->getName(), + usao->getArmorGroups()).value_or(props->pointable); + } else if (PlayerSAO* psao = dynamic_cast(obj)) { + pointable = pointabilities->matchPlayer(psao->getArmorGroups()).value_or( + props->pointable); } else { pointable = props->pointable; } - if (pointable != PointabilityType::POINTABLE_NOT) { - current_intersection += pos; - objects.emplace_back( - (s16) obj->getId(), current_intersection, current_normal, current_raw_normal, - (current_intersection - shootline_on_map.start).getLengthSQ(), pointable); - } + } else { + pointable = props->pointable; } - } + if (pointable != PointabilityType::POINTABLE_NOT) { + current_intersection += pos; + f32 d_sq = (current_intersection - shootline_on_map.start).getLengthSQ(); + objects.emplace_back( + (s16) obj->getId(), current_intersection, current_normal, + current_raw_normal, d_sq, pointable); + } + return false; + }; + + // Use "logic in callback" pattern to avoid useless vector filling + std::vector tmp; + getObjectsInsideRadius(tmp, shootline_on_map.getMiddle(), + 0.5 * shootline_on_map.getLength() + 5 * BS, process); } /* diff --git a/src/util/pointedthing.cpp b/src/util/pointedthing.cpp index f87021dc6..467e466d5 100644 --- a/src/util/pointedthing.cpp +++ b/src/util/pointedthing.cpp @@ -23,31 +23,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "exceptions.h" #include -PointedThing::PointedThing(const v3s16 &under, const v3s16 &above, - const v3s16 &real_under, const v3f &point, const v3f &normal, - u16 box_id, f32 distSq, PointabilityType pointab): - type(POINTEDTHING_NODE), - node_undersurface(under), - node_abovesurface(above), - node_real_undersurface(real_under), - intersection_point(point), - intersection_normal(normal), - box_id(box_id), - distanceSq(distSq), - pointability(pointab) -{} - -PointedThing::PointedThing(u16 id, const v3f &point, const v3f &normal, - const v3f &raw_normal, f32 distSq, PointabilityType pointab) : - type(POINTEDTHING_OBJECT), - object_id(id), - intersection_point(point), - intersection_normal(normal), - raw_intersection_normal(raw_normal), - distanceSq(distSq), - pointability(pointab) -{} - std::string PointedThing::dump() const { std::ostringstream os(std::ios::binary); @@ -131,8 +106,3 @@ bool PointedThing::operator==(const PointedThing &pt2) const } return true; } - -bool PointedThing::operator!=(const PointedThing &pt2) const -{ - return !(*this == pt2); -} diff --git a/src/util/pointedthing.h b/src/util/pointedthing.h index c44bcc2a8..5c8bc7ca1 100644 --- a/src/util/pointedthing.h +++ b/src/util/pointedthing.h @@ -37,6 +37,8 @@ struct PointedThing { //! The type of the pointed object. PointedThingType type = POINTEDTHING_NOTHING; + //! How the object or node can be pointed at. + PointabilityType pointability = PointabilityType::POINTABLE_NOT; /*! * Only valid if type is POINTEDTHING_NODE. * The coordinates of the node which owns the @@ -63,6 +65,11 @@ struct PointedThing * The ID of the object the ray hit. */ u16 object_id = 0; + /*! + * Only valid if type isn't POINTEDTHING_NONE. + * Indicates which selection box is selected, if there are more of them. + */ + u16 box_id = 0; /*! * Only valid if type isn't POINTEDTHING_NONE. * First intersection point of the ray and the nodebox in irrlicht @@ -81,36 +88,49 @@ struct PointedThing * Raw normal vector of the intersection before applying rotation. */ v3f raw_intersection_normal; - /*! - * Only valid if type isn't POINTEDTHING_NONE. - * Indicates which selection box is selected, if there are more of them. - */ - u16 box_id = 0; /*! * Square of the distance between the pointing * ray's start point and the intersection point in irrlicht coordinates. */ f32 distanceSq = 0; - /*! - * How the object or node has been pointed at. - */ - PointabilityType pointability = PointabilityType::POINTABLE_NOT; //! Constructor for POINTEDTHING_NOTHING PointedThing() = default; //! Constructor for POINTEDTHING_NODE - PointedThing(const v3s16 &under, const v3s16 &above, - const v3s16 &real_under, const v3f &point, const v3f &normal, - u16 box_id, f32 distSq, PointabilityType pointability); + inline PointedThing(const v3s16 under, const v3s16 above, + const v3s16 real_under, const v3f point, const v3f normal, + u16 box_id, f32 distSq, PointabilityType pointab) : + type(POINTEDTHING_NODE), + pointability(pointab), + node_undersurface(under), + node_abovesurface(above), + node_real_undersurface(real_under), + box_id(box_id), + intersection_point(point), + intersection_normal(normal), + distanceSq(distSq) + {} //! Constructor for POINTEDTHING_OBJECT - PointedThing(u16 id, const v3f &point, const v3f &normal, const v3f &raw_normal, f32 distSq, - PointabilityType pointability); + inline PointedThing(u16 id, const v3f point, const v3f normal, + const v3f raw_normal, f32 distSq, PointabilityType pointab) : + type(POINTEDTHING_OBJECT), + pointability(pointab), + object_id(id), + intersection_point(point), + intersection_normal(normal), + raw_intersection_normal(raw_normal), + distanceSq(distSq) + {} + std::string dump() const; void serialize(std::ostream &os) const; void deSerialize(std::istream &is); + /*! * This function ignores the intersection point and normal. */ bool operator==(const PointedThing &pt2) const; - bool operator!=(const PointedThing &pt2) const; + bool operator!=(const PointedThing &pt2) const { + return !(*this == pt2); + } }; From 86333a65a994c7d072314af45383a22ee8987a6a Mon Sep 17 00:00:00 2001 From: DS Date: Fri, 3 May 2024 16:29:02 +0200 Subject: [PATCH 011/146] Warn on unknown CMAKE_BUILD_TYPE values (#14600) --- src/CMakeLists.txt | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index ff7f53cd2..bad7e2afb 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -27,10 +27,15 @@ mark_as_advanced( CMAKE_CXX_FLAGS_SEMIDEBUG CMAKE_C_FLAGS_SEMIDEBUG ) +set(SUPPORTED_BUILD_TYPES None Release Debug SemiDebug RelWithDebInfo MinSizeRel) set(CMAKE_BUILD_TYPE "${CMAKE_BUILD_TYPE}" CACHE STRING - "Choose the type of build. Options are: None Debug SemiDebug RelWithDebInfo MinSizeRel." + "Choose the type of build. Options are: ${SUPPORTED_BUILD_TYPES}." FORCE ) +if(NOT (CMAKE_BUILD_TYPE IN_LIST SUPPORTED_BUILD_TYPES)) + message(WARNING + "Unknown CMAKE_BUILD_TYPE: ${CMAKE_BUILD_TYPE}. Options are: ${SUPPORTED_BUILD_TYPES}.") +endif() # Set some random things default to not being visible in the GUI From 689eee65524a2c01ee91603bd48e14e9e3c117e5 Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Tue, 30 Apr 2024 20:14:20 +0200 Subject: [PATCH 012/146] Chat console: Prevent input loss on double open --- src/gui/guiChatConsole.cpp | 4 ++++ src/gui/mainmenumanager.h | 8 ++++---- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/src/gui/guiChatConsole.cpp b/src/gui/guiChatConsole.cpp index 243828df0..c04eeebc3 100644 --- a/src/gui/guiChatConsole.cpp +++ b/src/gui/guiChatConsole.cpp @@ -106,9 +106,13 @@ GUIChatConsole::~GUIChatConsole() void GUIChatConsole::openConsole(f32 scale) { + if (m_open) + return; + assert(scale > 0.0f && scale <= 1.0f); m_open = true; + m_desired_height_fraction = scale; m_desired_height = scale * m_screensize.Y; reformatConsole(); diff --git a/src/gui/mainmenumanager.h b/src/gui/mainmenumanager.h index 05d7c8974..25ff475f4 100644 --- a/src/gui/mainmenumanager.h +++ b/src/gui/mainmenumanager.h @@ -48,14 +48,14 @@ class MainMenuManager : public IMenuManager public: virtual void createdMenu(gui::IGUIElement *menu) { -#ifndef NDEBUG - for (gui::IGUIElement *i : m_stack) { - assert(i != menu); + for (gui::IGUIElement *e : m_stack) { + if (e == menu) + return; } -#endif if(!m_stack.empty()) m_stack.back()->setVisible(false); + m_stack.push_back(menu); guienv->setFocus(m_stack.back()); } From 80326ef7a55606cd876ebc67b29039e35a5428f5 Mon Sep 17 00:00:00 2001 From: Lars Mueller Date: Sat, 4 May 2024 01:58:37 +0200 Subject: [PATCH 013/146] Fix wrong name for bone override interpolation field --- src/script/lua_api/l_object.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/script/lua_api/l_object.cpp b/src/script/lua_api/l_object.cpp index c78a87317..e21d37176 100644 --- a/src/script/lua_api/l_object.cpp +++ b/src/script/lua_api/l_object.cpp @@ -604,7 +604,7 @@ int ObjectRef::l_set_bone_override(lua_State *L) prop.absolute = lua_toboolean(L, -1); lua_pop(L, 1); - lua_getfield(L, -1, "interpolate"); + lua_getfield(L, -1, "interpolation"); if (lua_isnumber(L, -1)) prop.interp_timer = lua_tonumber(L, -1); lua_pop(L, 1); @@ -655,7 +655,7 @@ static void push_bone_override(lua_State *L, const BoneOverride &props) push_v3f(L, vec); lua_setfield(L, -2, "vec"); lua_pushnumber(L, prop.interp_timer); - lua_setfield(L, -2, "interpolate"); + lua_setfield(L, -2, "interpolation"); lua_pushboolean(L, prop.absolute); lua_setfield(L, -2, "absolute"); lua_setfield(L, -2, name); From 32cde703ae8c5105aae7092cd391b37e95d4adf4 Mon Sep 17 00:00:00 2001 From: grorp Date: Sun, 5 May 2024 13:28:50 +0200 Subject: [PATCH 014/146] Fix shootline not being updated if press and release happen in the same step (#14606) --- src/gui/touchscreengui.cpp | 4 +++- src/gui/touchscreengui.h | 3 +++ 2 files changed, 6 insertions(+), 1 deletion(-) diff --git a/src/gui/touchscreengui.cpp b/src/gui/touchscreengui.cpp index d35d6f18d..2a2fa36ed 100644 --- a/src/gui/touchscreengui.cpp +++ b/src/gui/touchscreengui.cpp @@ -703,6 +703,7 @@ void TouchScreenGUI::translateEvent(const SEvent &event) m_move_pos = touch_pos; // DON'T reset m_tap_state here, otherwise many short taps // will be ignored if you tap very fast. + m_had_move_id = true; } } } @@ -821,13 +822,14 @@ void TouchScreenGUI::step(float dtime) // Note that the shootline isn't used if touch_use_crosshair is enabled. // Only updating when m_has_move_id means that the shootline will stay at // it's last in-world position when the player doesn't need it. - if (!m_draw_crosshair && m_has_move_id) { + if (!m_draw_crosshair && (m_has_move_id || m_had_move_id)) { v2s32 pointer_pos = getPointerPos(); m_shootline = m_device ->getSceneManager() ->getSceneCollisionManager() ->getRayFromScreenCoordinates(pointer_pos); } + m_had_move_id = false; } void TouchScreenGUI::resetHotbarRects() diff --git a/src/gui/touchscreengui.h b/src/gui/touchscreengui.h index 014cb6fef..ca6c05e18 100644 --- a/src/gui/touchscreengui.h +++ b/src/gui/touchscreengui.h @@ -259,6 +259,9 @@ class TouchScreenGUI u64 m_move_downtime = 0; // m_move_pos stays valid even after m_move_id has been released. v2s32 m_move_pos; + // This is needed so that we don't miss if m_has_move_id is true for less + // than one client step, i.e. press and release happen in the same step. + bool m_had_move_id = false; bool m_has_joystick_id = false; size_t m_joystick_id; From 0b15b87209ff3632a2acf3a2a15846402ad59db0 Mon Sep 17 00:00:00 2001 From: grorp Date: Sun, 5 May 2024 13:28:59 +0200 Subject: [PATCH 015/146] Add object's own position for each collision to moveresult (#14608) --- builtin/game/features.lua | 1 + builtin/game/misc_s.lua | 7 ++++--- doc/lua_api.md | 5 +++++ src/collision.cpp | 1 + src/collision.h | 1 + src/script/common/c_content.cpp | 9 ++++++--- 6 files changed, 18 insertions(+), 6 deletions(-) diff --git a/builtin/game/features.lua b/builtin/game/features.lua index 286c3f146..051d2aa2a 100644 --- a/builtin/game/features.lua +++ b/builtin/game/features.lua @@ -40,6 +40,7 @@ core.features = { lsystem_decoration_type = true, item_meta_range = true, node_interaction_actor = true, + moveresult_new_pos = true, } function core.has_feature(arg) diff --git a/builtin/game/misc_s.lua b/builtin/game/misc_s.lua index 18812131a..19708983a 100644 --- a/builtin/game/misc_s.lua +++ b/builtin/game/misc_s.lua @@ -103,7 +103,7 @@ if core.set_push_moveresult1 then -- must match CollisionAxis in collision.h local AXES = {"x", "y", "z"} -- <=> script/common/c_content.cpp push_collision_move_result() - core.set_push_moveresult1(function(b0, b1, b2, axis, npx, npy, npz, v0x, v0y, v0z, v1x, v1y, v1z) + core.set_push_moveresult1(function(b0, b1, b2, axis, npx, npy, npz, v0x, v0y, v0z, v1x, v1y, v1z, v2x, v2y, v2z) return { touching_ground = b0, collides = b1, @@ -112,8 +112,9 @@ if core.set_push_moveresult1 then type = "node", axis = AXES[axis], node_pos = vector.new(npx, npy, npz), - old_velocity = vector.new(v0x, v0y, v0z), - new_velocity = vector.new(v1x, v1y, v1z), + new_pos = vector.new(v0x, v0y, v0z), + old_velocity = vector.new(v1x, v1y, v1z), + new_velocity = vector.new(v2x, v2y, v2z), }}, } end) diff --git a/doc/lua_api.md b/doc/lua_api.md index 60e95711e..75df60bbb 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -5124,6 +5124,9 @@ Collision info passed to `on_step` (`moveresult` argument): axis = string, -- "x", "y" or "z" node_pos = vector, -- if type is "node" object = ObjectRef, -- if type is "object" + -- The position of the entity when the collision occurred. + -- Available since feature "moveresult_new_pos". + new_pos = vector, old_velocity = vector, new_velocity = vector, }, @@ -5437,6 +5440,8 @@ Utilities -- Allow passing an optional "actor" ObjectRef to the following functions: -- minetest.place_node, minetest.dig_node, minetest.punch_node (5.9.0) node_interaction_actor = true, + -- "new_pos" field in entity moveresult (5.9.0) + moveresult_new_pos = true, } ``` diff --git a/src/collision.cpp b/src/collision.cpp index c52fa2a0a..bdfaad55a 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -513,6 +513,7 @@ collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef, info.node_p = nearest_info.position; info.object = nearest_info.obj; + info.new_pos = *pos_f; info.old_speed = *speed_f; info.plane = nearest_collided; diff --git a/src/collision.h b/src/collision.h index 026ace841..306f0a0e3 100644 --- a/src/collision.h +++ b/src/collision.h @@ -49,6 +49,7 @@ struct CollisionInfo CollisionAxis axis = COLLISION_AXIS_NONE; v3s16 node_p = v3s16(-32768,-32768,-32768); // COLLISION_NODE ActiveObject *object = nullptr; // COLLISION_OBJECT + v3f new_pos; v3f old_speed; v3f new_speed; int plane = -1; diff --git a/src/script/common/c_content.cpp b/src/script/common/c_content.cpp index fde47d452..19a377da3 100644 --- a/src/script/common/c_content.cpp +++ b/src/script/common/c_content.cpp @@ -2591,12 +2591,12 @@ void push_collision_move_result(lua_State *L, const collisionMoveResult &res) lua_pushinteger(L, c.node_p.X); lua_pushinteger(L, c.node_p.Y); lua_pushinteger(L, c.node_p.Z); - for (v3f v : {c.old_speed / BS, c.new_speed / BS}) { + for (v3f v : {c.new_pos / BS, c.old_speed / BS, c.new_speed / BS}) { lua_pushnumber(L, v.X); lua_pushnumber(L, v.Y); lua_pushnumber(L, v.Z); } - lua_call(L, 3 + 1 + 3 + 3 * 2, 1); + lua_call(L, 3 + 1 + 3 + 3 * 3, 1); return; } @@ -2610,7 +2610,7 @@ void push_collision_move_result(lua_State *L, const collisionMoveResult &res) lua_createtable(L, res.collisions.size(), 0); int i = 1; for (const auto &c : res.collisions) { - lua_createtable(L, 0, 5); + lua_createtable(L, 0, 6); lua_pushstring(L, collision_type_str[c.type]); lua_setfield(L, -2, "type"); @@ -2627,6 +2627,9 @@ void push_collision_move_result(lua_State *L, const collisionMoveResult &res) lua_setfield(L, -2, "object"); } + push_v3f(L, c.new_pos / BS); + lua_setfield(L, -2, "new_pos"); + push_v3f(L, c.old_speed / BS); lua_setfield(L, -2, "old_velocity"); From e665008083fd31fb2f9ab561cd0f792e5d031a98 Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Sun, 5 May 2024 13:27:17 +0100 Subject: [PATCH 016/146] Allow quoting hypertext attribute values (#14550) --- doc/lua_api.md | 8 +++-- games/devtest/mods/testformspec/formspec.lua | 22 ++++++------ src/gui/guiHyperText.cpp | 37 ++++++++++++++++---- 3 files changed, 47 insertions(+), 20 deletions(-) diff --git a/doc/lua_api.md b/doc/lua_api.md index 75df60bbb..eb7a294e7 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -3535,10 +3535,12 @@ Markup language used in `hypertext[]` elements uses tags that look like HTML tag The markup language is currently unstable and subject to change. Use with caution. Some tags can enclose text, they open with `` and close with ``. Tags can have attributes, in that case, attributes are in the opening tag in -form of a key/value separated with equal signs. Attribute values should not be quoted. +form of a key/value separated with equal signs. +Attribute values should be quoted using either " or '. -If you want to insert a literal greater-than sign or a backslash into the text, -you must escape it by preceding it with a backslash. +If you want to insert a literal greater-than, less-than, or a backslash into the text, +you must escape it by preceding it with a backslash. In a quoted attribute value, you +can insert a literal quote mark by preceding it with a backslash. These are the technically basic tags but see below for usual tags. Base tags are: diff --git a/games/devtest/mods/testformspec/formspec.lua b/games/devtest/mods/testformspec/formspec.lua index 758bad631..c2074e6e0 100644 --- a/games/devtest/mods/testformspec/formspec.lua +++ b/games/devtest/mods/testformspec/formspec.lua @@ -69,9 +69,9 @@ local hypertext_basic = [[A hypertext element This is a normal text. style test - - . - + + . + Tag test normal @@ -88,20 +88,20 @@ This is a normal text. Custom tag test - - - - + + + + color=green -Action: color=green -Action: hovercolor=yellow -Action URL: open URL +Action: color=green +Action: hovercolor=yellow +Action URL: open URL size=24 font=mono color=green font=mono size=24 action test -action +action img test Normal: diff --git a/src/gui/guiHyperText.cpp b/src/gui/guiHyperText.cpp index 3db6f0071..45e5d6e98 100644 --- a/src/gui/guiHyperText.cpp +++ b/src/gui/guiHyperText.cpp @@ -421,14 +421,16 @@ u32 ParsedText::parseTag(const wchar_t *text, u32 cursor) AttrsList attrs; while (c != L'>') { std::string attr_name = ""; - core::stringw attr_val = L""; + std::wstring attr_val = L""; + // Consume whitespace while (c == ' ') { c = text[++cursor]; if (c == L'\0' || c == L'=') return 0; } + // Read attribute name while (c != L' ' && c != L'=') { attr_name += (char)c; c = text[++cursor]; @@ -436,28 +438,51 @@ u32 ParsedText::parseTag(const wchar_t *text, u32 cursor) return 0; } + // Consume whitespace while (c == L' ') { c = text[++cursor]; if (c == L'\0' || c == L'>') return 0; } + // Skip equals if (c != L'=') return 0; - c = text[++cursor]; - if (c == L'\0') return 0; - while (c != L'>' && c != L' ') { - attr_val += c; + // Read optional quote + wchar_t quote_used = 0; + if (c == L'"' || c == L'\'') { + quote_used = c; c = text[++cursor]; if (c == L'\0') return 0; } - attrs[attr_name] = stringw_to_utf8(attr_val); + // Read attribute value + bool escape = false; + while (escape || (quote_used && c != quote_used) || (!quote_used && c != L'>' && c != L' ')) { + if (quote_used && !escape && c == L'\\') { + escape = true; + } else { + escape = false; + attr_val += c; + } + c = text[++cursor]; + if (c == L'\0') + return 0; + } + + // Remove quote + if (quote_used) { + if (c != quote_used) + return 0; + c = text[++cursor]; + } + + attrs[attr_name] = wide_to_utf8(attr_val); } ++cursor; // Last ">" From 976b4598b3c32e45240dea3442e23815ed90e8c3 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sun, 5 May 2024 22:54:40 +0200 Subject: [PATCH 017/146] Add unit tests for MapBlock (de)serialization (#14560) --- src/unittest/CMakeLists.txt | 1 + src/unittest/test_mapblock.cpp | 358 +++++++++++++++++++++++++++++++++ 2 files changed, 359 insertions(+) create mode 100644 src/unittest/test_mapblock.cpp diff --git a/src/unittest/CMakeLists.txt b/src/unittest/CMakeLists.txt index b249b0f75..ec52ee6bf 100644 --- a/src/unittest/CMakeLists.txt +++ b/src/unittest/CMakeLists.txt @@ -15,6 +15,7 @@ set (UNITTEST_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/test_irrptr.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_lua.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_map.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/test_mapblock.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_map_settings_manager.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_mapnode.cpp ${CMAKE_CURRENT_SOURCE_DIR}/test_modchannels.cpp diff --git a/src/unittest/test_mapblock.cpp b/src/unittest/test_mapblock.cpp new file mode 100644 index 000000000..7f3f860b4 --- /dev/null +++ b/src/unittest/test_mapblock.cpp @@ -0,0 +1,358 @@ +// Minetest +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "test.h" + +#include +#include "gamedef.h" +#include "nodedef.h" +#include "mapblock.h" +#include "serialization.h" +#include "noise.h" +#include "inventory.h" + +class TestMapBlock : public TestBase +{ +public: + TestMapBlock() { TestManager::registerTestModule(this); } + const char *getName() { return "TestMapBlock"; } + + void runTests(IGameDef *gamedef); + + void testSaveLoad(IGameDef *gamedef, u8 ver); + inline void testSaveLoadLowest(IGameDef *gamedef) { + testSaveLoad(gamedef, SER_FMT_VER_LOWEST_WRITE); + } + + void testSave29(IGameDef *gamedef); + + void testLoad29(IGameDef *gamedef); + + // Tests loading a MapBlock from Minetest-c55 0.3 + void testLoad20(IGameDef *gamedef); + + // Tests loading a non-standard MapBlock + void testLoadNonStd(IGameDef *gamedef); +}; + +static TestMapBlock g_test_instance; + +void TestMapBlock::runTests(IGameDef *gamedef) +{ + TEST(testSaveLoad, gamedef, SER_FMT_VER_HIGHEST_WRITE); + TEST(testSaveLoadLowest, gamedef); + TEST(testSave29, gamedef); + TEST(testLoad29, gamedef); + TEST(testLoad20, gamedef); + TEST(testLoadNonStd, gamedef); +} + +//////////////////////////////////////////////////////////////////////////////// + +void TestMapBlock::testSaveLoad(IGameDef *gamedef, const u8 version) +{ + // Use the bottom node ids for this test + content_t max = 0; + { + auto *ndef = gamedef->getNodeDefManager(); + const auto &unknown_name = ndef->get(CONTENT_UNKNOWN).name; + while (ndef->get(max).name != unknown_name) + max++; + } + UASSERT(max >= 2); + UASSERT(max <= CONTENT_UNKNOWN); + + constexpr u64 seed = 0x207366616e3520ULL; + std::stringstream ss; + { + MapBlock block({}, gamedef); + // Fill with data + PcgRandom r(seed); + for (size_t i = 0; i < MapBlock::nodecount; ++i) { + u32 rval = r.next(); + block.getData()[i] = + MapNode(rval % max, (rval >> 16) & 0xff, (rval >> 24) & 0xff); + } + + // Serialize + block.serialize(ss, version, true, -1); + } + + { + MapBlock block({}, gamedef); + // Deserialize + block.deSerialize(ss, version, true); + + // Check data + PcgRandom r(seed); + for (size_t i = 0; i < MapBlock::nodecount; ++i) { + u32 rval = r.next(); + auto expect = + MapNode(rval % max, (rval >> 16) & 0xff, (rval >> 24) & 0xff); + UASSERT(block.getData()[i] == expect); + } + } +} + +#define SS2_CHECK() UASSERT(!ss2.fail()) + +void TestMapBlock::testSave29(IGameDef *gamedef) +{ + auto *ndef = gamedef->getNodeDefManager(); + std::stringstream ss; + + { + // Prepare test block + MapBlock block({}, gamedef); + for (size_t i = 0; i < MapBlock::nodecount; ++i) + block.getData()[i] = MapNode(CONTENT_AIR); + block.setNode({0, 0, 0}, MapNode(t_CONTENT_STONE)); + + block.serialize(ss, 29, true, -1); + } + + // Pick it apart a bit: + std::stringstream ss2; + decompressZstd(ss, ss2); // first zstd + + u8 flags = readU8(ss2); + SS2_CHECK(); + UASSERT(flags & 0x02); // !is_air == true + UASSERT(flags & 0x08); // !is_genereated == true + + ss2.seekg(2+4, std::ios_base::cur); // lighting_complete & timestamp + SS2_CHECK(); + + // nimap + u8 ver = readU8(ss2); + u16 count = readU16(ss2); + SS2_CHECK(); + UASSERTEQ(int, ver, 0); + // Invariant 1: map only contains nodes that are actually present in the data + // and no duplicates. + UASSERTEQ(int, count, 2); + std::string nn[2]; + content_t ii[2]; + for (int i = 0; i < 2; i++) { + ii[i] = readU16(ss2); + nn[i] = deSerializeString16(ss2); + } + // no particular order is guaranteed + if (nn[0] > nn[1]) + std::swap(nn[0], nn[1]); + UASSERTEQ(auto, nn[0], ndef->get(CONTENT_AIR).name); + UASSERTEQ(auto, nn[1], ndef->get(t_CONTENT_STONE).name); + // Invariant 2: IDs must start from zero + if (ii[0] > ii[1]) + std::swap(ii[0], ii[1]); + UASSERTEQ(int, ii[0], 0x0000); + UASSERTEQ(int, ii[1], 0x0001); + + /* + * Quick note: + * Minetest deals with MapBlocks that violate these invariants (mostly) fine, + * but we should still be careful about them as third-party tools may rely on them. + */ + + u8 content_width, params_width; + content_width = readU8(ss2); + params_width = readU8(ss2); + SS2_CHECK(); + UASSERT(content_width == 2 && params_width == 2); +} + +#undef SS2_CHECK + +// The array was generated with: minetestmapper -i testworld --dumpblock 6,0,0 | +// python -c 'import sys;d=bytes.fromhex(sys.stdin.read().strip());print(",".join("%d"%c for c in d))' + +static const u8 coded_mapblock29[] = { + 29,40,181,47,253,0,88,77,11,0,2,138,32,38,160,23,36,29,255,255,103,85,85, + 213,136,105,255,110,103,27,53,43,218,89,210,205,184,194,207,24,226,254,135, + 7,16,191,92,232,249,141,144,20,175,126,149,175,29,143,211,164,99,16,206,80, + 28,115,77,190,197,176,52,212,147,14,12,130,34,88,75,42,45,65,21,24,44,215, + 16,102,229,107,199,191,192,232,52,26,47,182,136,216,22,91,108,177,45,182, + 88,44,98,199,127,148,30,253,41,49,36,205,81,120,167,33,197,232,124,108,73, + 14,178,225,1,89,96,74,192,233,226,7,34,0,78,36,138,124,168,193,182,41,40,42, + 148,182,14,48,131,17,74,155,12,99,18,0,113,80,150,98,137,8,98,56,144,8,20, + 33,66,66,136,128,8,8,17,32,225,7,205,42,26,14,52,88,86,216,141,181,243,179, + 3,4,215,245,139,22,83,172,157,47,73,82,216,114,3,60,78,223,0,155,247,80,5, + 12,253,12,187,13,205,139,12,84,48,218,228,35,240,86,70,203,121,239,228,210, + 96,86,116,12,5,10,97,49,111,52,238,79,12,36,119,59,39,118,53,49,89,216,181, + 248,68,147,193,146,59,21,88,196,217,4,120,51,69,7,12,32,80,227,49,80,232,6, + 163,221,171,38,126,130,81,154,216,165,93,120,40,186,136,115,223,1,231,48, + 101,208,254,192,23,23,206,172,130,253,9,225,127,116,192,192,13,149,20,201, + 60,244,151,226,114,71,81,114,126,198,247,177,98,70,70,62,181,18,74,140,239, + 245,248,62,71,1,107,238,209,77,222,10,141,7,71,29,14,62,168,83,126,138,139, + 26,106 +}; + +void TestMapBlock::testLoad29(IGameDef *gamedef) +{ + UASSERT(MAP_BLOCKSIZE == 16); + const std::string_view buf(reinterpret_cast(coded_mapblock29), sizeof(coded_mapblock29)); + + // this node is not part of the test gamedef, so we also test handling of + // unknown nodes here. + auto *ndef = gamedef->getNodeDefManager(); + UASSERT(ndef->getId("default:chest") == CONTENT_IGNORE); + + std::istringstream iss; + iss.str(std::string(buf)); + u8 version = readU8(iss); + UASSERTEQ(int, version, 29); + MapBlock block({}, gamedef); + block.deSerialize(iss, version, true); + + auto content_chest = ndef->getId("default:chest"); + UASSERT(content_chest != CONTENT_IGNORE); + + // there are bricks at each corner + const v3s16 pl[] = { + {0, 0, 0}, {15, 0, 0}, {0, 15, 0}, {0, 0, 15}, + {15, 15, 0}, {15, 0, 15}, {0, 15, 15}, {15, 15, 15}, + }; + for (auto p : pl) + UASSERTEQ(int, block.getNodeNoEx(p).getContent(), t_CONTENT_BRICK); + + // and a chest with an item inside + UASSERTEQ(int, block.getNodeNoEx({0, 1, 0}).getContent(), content_chest); + auto *meta = block.m_node_metadata.get({0, 1, 0}); + UASSERT(meta); + auto *inv = meta->getInventory(); + UASSERT(inv); + auto *ilist = inv->getList("main"); + UASSERT(ilist); + UASSERTEQ(int, ilist->getSize(), 32); + UASSERTEQ(auto, ilist->getItem(1).name, "default:stone"); +} + +static const u8 coded_mapblock20[] = { + 20,2,120,156,237,150,91,114,131,48,12,69,197,63,30,88,2,75,242,138,24,47, + 189,230,145,196,186,184,22,170,12,161,33,183,51,105,78,41,182,142,95,48,142, + 33,132,113,250,152,126,17,45,95,95,159,143,43,75,22,30,199,132,89,144,173, + 57,186,253,49,209,29,39,125,98,190,4,254,1,252,3,212,179,97,185,247,242,253, + 156,195,134,197,246,229,36,109,210,234,255,98,169,127,99,125,106,95,105,184, + 132,254,202,161,217,159,115,218,98,128,122,180,220,199,217,54,206,183,106, + 248,149,33,240,39,240,15,224,179,139,203,93,234,252,181,203,79,153,156,191, + 218,23,248,215,206,254,114,176,73,203,223,22,2,255,0,62,74,206,20,91,222,43, + 21,20,44,137,229,208,72,172,126,240,3,37,129,5,127,235,98,189,124,222,241, + 40,187,82,132,13,126,194,139,141,34,31,63,27,66,46,119,26,157,156,204,163, + 250,94,67,112,119,255,111,238,154,206,181,212,186,206,77,233,58,162,149,187, + 132,219,233,219,146,137,95,52,51,11,178,53,105,95,57,174,209,197,236,55,91, + 57,226,190,11,59,135,252,188,149,184,239,150,121,185,206,109,216,9,220,150, + 175,203,122,98,92,58,187,220,143,192,71,207,45,175,183,181,50,140,31,178, + 109,60,8,234,111,26,206,120,93,203,77,220,89,41,187,29,236,56,151,135,7,215, + 151,46,4,245,82,3,140,215,247,48,175,191,117,86,238,74,108,240,143,119,178, + 250,43,113,218,62,219,254,18,175,62,42,182,36,174,45,94,191,153,29,172,93, + 231,138,92,255,96,215,101,94,172,201,140,17,247,137,103,121,203,86,247,14, + 230,139,19,185,230,236,217,99,63,154,213,71,121,253,71,153,33,219,71,201, + 209,220,21,249,220,28,242,98,241,143,114,185,211,232,228,76,47,154,151,90, + 144,103,103,123,26,223,203,255,155,187,134,104,232,135,248,49,101,121,227, + 137,188,254,129,200,251,244,202,131,137,18,62,52,105,95,57,174,210,197,170, + 155,240,179,23,240,247,224,239,193,95,63,26,3,167,161,200,195,134,213,253, + 101,42,40,245,201,253,188,151,184,102,223,57,223,226,191,31,177,58,82,35,15, + 190,34,199,159,158,181,214,35,15,69,94,118,34,99,134,194,112,213,72,226,227, + 193,47,203,186,214,117,243,47,249,30,224,47,250,2,239,110,248,47,7,155,180, + 252,235,198,131,159,204,197,226,50,115,201,217,92,176,45,139,74,234,3,167, + 123,47,50,11,236,213,237,94,62,118,246,222,158,119,60,202,174,20,97,131,159, + 240,98,163,200,199,207,134,144,203,157,70,39,7,253,233,110,67,112,119,255, + 111,238,154,31,244,135,233,2,120,156,99,96,100,96,100,206,102,16,96,232,242, + 201,44,46,81,72,43,77,205,81,48,228,114,205,45,40,169,228,114,205,75,241, + 204,43,75,205,43,201,47,170,4,201,114,129,149,20,23,37,19,80,145,2,196,38, + 48,21,152,36,186,30,100,1,46,3,5,3,5,0,187,133,49,88,0,0,0,0,0,0,210,62,6 +}; + +void TestMapBlock::testLoad20(IGameDef *gamedef) +{ + UASSERT(MAP_BLOCKSIZE == 16); + const std::string_view buf(reinterpret_cast(coded_mapblock20), sizeof(coded_mapblock20)); + + // Conversion of minerals does not work if these nodes are not already + // defined at load time. (Is this a bug? Does anyone even care?) + gamedef->allocateUnknownNodeId("default:stone_with_coal"); + gamedef->allocateUnknownNodeId("default:stone_with_iron"); + + std::istringstream iss; + iss.str(std::string(buf)); + u8 version = readU8(iss); + UASSERTEQ(int, version, 20); + MapBlock block({}, gamedef); + block.deSerialize(iss, version, true); + + auto *ndef = gamedef->getNodeDefManager(); + auto get_node = [&] (s16 x, s16 y, s16 z) -> std::string_view { + MapNode n = block.getNodeNoEx({x, y, z}); + return ndef->get(n).name; + }; + + // These names come from content_mapnode.cpp and are hardcoded in the engine. + UASSERTEQ(auto, get_node(11, 0, 3), "default:stone"); + UASSERTEQ(auto, get_node(11, 1, 3), "default:stone_with_coal"); + UASSERTEQ(auto, get_node(11, 2, 3), "default:dirt"); + UASSERTEQ(auto, get_node(10, 5, 4), "default:dirt_with_grass"); + UASSERTEQ(auto, get_node(10, 6, 4), "air"); + UASSERTEQ(auto, get_node(11, 6, 3), "default:furnace"); + + for (size_t i = 0; i < MapBlock::nodecount; ++i) + UASSERT(block.getData()[i].getContent() != CONTENT_IGNORE); + + // metadata is also translated + auto *meta = block.m_node_metadata.get({11, 6, 3}); + UASSERT(meta); + UASSERT(meta->contains("formspec")); + auto *inv = meta->getInventory(); + UASSERT(inv); + auto *ilist = inv->getList("dst"); + UASSERT(ilist); + UASSERTEQ(int, ilist->getSize(), 4); +} + +static const u8 coded_mapblock_nonstd[] = { + 27,0,255,255,1,2,120,218,237,208,177,13,130,64,0,0,192,127,21,5,31,176,182, + 182,34,177,103,3,18,6,176,115,20,72,76,156,137,45,88,199,222,26,38,128,228, + 110,132,139,113,233,179,18,0,0,0,0,0,0,0,128,205,75,227,235,59,63,223,237, + 227,222,76,67,247,235,243,99,125,169,138,243,33,102,101,186,93,195,201,16,0, + 0,0,236,223,31,36,149,14,142,120,218,99,0,0,0,1,0,1,0,0,0,255,255,255,255,0, + 0,3,0,0,0,3,97,105,114,0,1,0,8,116,101,115,116,58,111,110,101,8,0,0,8,116, + 101,115,116,58,116,119,111,10,0,0 +}; + +void TestMapBlock::testLoadNonStd(IGameDef *gamedef) +{ + /* + * Node IDs were originally 8-bit, then some special format that allowed exactly + * 2176 IDs (but only the first 128 could use the full range of param2), and + * finally 16-bit like today. + * The content_width field in MapBlocks was used around that time during the + * transition to 16-bit: + * A content_width of 1 would normally never appear with a version > 24, but + * it is in fact perfectly possible to serialize a block in todays format with + * shortened IDs. + * + * This test checks that such a block is deserialized correctly. + * To future readers: I think it's worth not breaking this quirk (~sfan5) + */ + + UASSERT(MAP_BLOCKSIZE == 16); + const std::string_view buf(reinterpret_cast(coded_mapblock_nonstd), sizeof(coded_mapblock_nonstd)); + + std::istringstream iss; + iss.str(std::string(buf)); + u8 version = readU8(iss); + UASSERT(version > 24); + MapBlock block({}, gamedef); + block.deSerialize(iss, version, true); + + auto *ndef = gamedef->getNodeDefManager(); + UASSERTEQ(int, block.getNodeNoEx({0, 0, 0}).getContent(), ndef->getId("test:one")); + UASSERTEQ(int, block.getNodeNoEx({0, 1, 0}).getContent(), ndef->getId("test:two")); + + // Check that param2 was handeled correctly + const static u8 data_lo[] = {8, 3, 14, 7, 13, 9, 6, 2, 1, 5, 12, 11, 15, 10, 0, 4}; + const static u8 data_hi[] = {11, 125, 85, 131, 204, 44, 92, 55, 35, 25, 41, 181, 124, 70, 245, 73}; + for (s16 i = 0; i < 16; i++) + UASSERTEQ(int, block.getNodeNoEx({i, 0, 0}).param2, data_hi[i]); + for (s16 i = 0; i < 16; i++) + UASSERTEQ(int, block.getNodeNoEx({i, 1, 0}).param2, data_lo[i]); +} From a447284f8a1c09b45329342c9c75610ebee32952 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Thu, 9 May 2024 00:00:33 +0200 Subject: [PATCH 018/146] Fix strings in modified_reason_strings one was also missing --- src/mapblock.cpp | 19 ++++++++++--------- 1 file changed, 10 insertions(+), 9 deletions(-) diff --git a/src/mapblock.cpp b/src/mapblock.cpp index 3985324bb..b3e951b1f 100644 --- a/src/mapblock.cpp +++ b/src/mapblock.cpp @@ -42,21 +42,22 @@ with this program; if not, write to the Free Software Foundation, Inc., static const char *modified_reason_strings[] = { "reallocate or initial", "setIsUnderground", - "setLightingExpired", + "setLightingComplete", "setGenerated", "setNode", "setTimestamp", - "NodeMetaRef::reportMetadataChange", + "reportMetadataChange", "clearAllObjects", "Timestamp expired (step)", "addActiveObjectRaw", - "removeRemovedObjects/remove", - "removeRemovedObjects/deactivate", - "Stored list cleared in activateObjects due to overflow", - "deactivateFarObjects: Static data moved in", - "deactivateFarObjects: Static data moved out", - "deactivateFarObjects: Static data changed considerably", - "finishBlockMake: expireDayNightDiff", + "removeRemovedObjects: remove", + "removeRemovedObjects: deactivate", + "objects cleared due to overflow", + "deactivateFarObjects: static data moved in", + "deactivateFarObjects: static data moved out", + "deactivateFarObjects: static data changed", + "finishBlockMake: expireIsAir", + "MMVManip::blitBackAll", "unknown", }; From 0f62a8528c4c506f8be3fb48cac8bf276a8e62d7 Mon Sep 17 00:00:00 2001 From: jordan4ibanez Date: Wed, 8 May 2024 04:20:27 -0400 Subject: [PATCH 019/146] Add forgotten opensuse dependency gcc-c++ --- doc/compiling/linux.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/compiling/linux.md b/doc/compiling/linux.md index 3bb796a16..efebcf0ae 100644 --- a/doc/compiling/linux.md +++ b/doc/compiling/linux.md @@ -29,7 +29,7 @@ For Fedora users: For openSUSE users: - sudo zypper install gcc cmake libjpeg8-devel libpng16-devel openal-soft-devel libcurl-devel sqlite3-devel luajit-devel libzstd-devel Mesa-libGL-devel libvorbis-devel freetype2-devel SDL2-devel + sudo zypper install gcc gcc-c++ cmake libjpeg8-devel libpng16-devel openal-soft-devel libcurl-devel sqlite3-devel luajit-devel libzstd-devel Mesa-libGL-devel libvorbis-devel freetype2-devel SDL2-devel For Arch users: From 270b95a09ff5847704908d8a97b58c7f2244c30d Mon Sep 17 00:00:00 2001 From: sfan5 Date: Thu, 9 May 2024 11:31:10 +0200 Subject: [PATCH 020/146] Fix curl compatibility issues with colorize_url (#14615) Also move the escape code safety check to guiOpenURL. --- src/gui/guiOpenURL.cpp | 19 ++++++++++++++----- src/unittest/test_utilities.cpp | 8 +++++--- src/util/colorize.cpp | 17 +++++++---------- src/util/colorize.h | 12 ++++++++++-- 4 files changed, 36 insertions(+), 20 deletions(-) diff --git a/src/gui/guiOpenURL.cpp b/src/gui/guiOpenURL.cpp index 547d52c17..33be88162 100644 --- a/src/gui/guiOpenURL.cpp +++ b/src/gui/guiOpenURL.cpp @@ -42,6 +42,19 @@ GUIOpenURLMenu::GUIOpenURLMenu(gui::IGUIEnvironment* env, { } +static std::string maybe_colorize_url(const std::string &url) +{ + // Forbid escape codes in URL + if (url.find('\x1b') != std::string::npos) { + throw std::runtime_error("URL contains escape codes"); + } + +#ifdef HAVE_COLORIZE_URL + return colorize_url(url); +#else + return url; +#endif +} void GUIOpenURLMenu::regenerateGui(v2u32 screensize) { @@ -70,16 +83,12 @@ void GUIOpenURLMenu::regenerateGui(v2u32 screensize) */ bool ok = true; std::string text; -#ifdef USE_CURL try { - text = colorize_url(url); + text = maybe_colorize_url(url); } catch (const std::exception &e) { text = std::string(e.what()) + " (url = " + url + ")"; ok = false; } -#else - text = url; -#endif /* Add stuff diff --git a/src/unittest/test_utilities.cpp b/src/unittest/test_utilities.cpp index 910d785f2..996b418e3 100644 --- a/src/unittest/test_utilities.cpp +++ b/src/unittest/test_utilities.cpp @@ -728,16 +728,18 @@ void TestUtilities::testIsBlockInSight() void TestUtilities::testColorizeURL() { -#if USE_CURL +#ifdef HAVE_COLORIZE_URL #define RED COLOR_CODE("#faa") #define GREY COLOR_CODE("#aaa") #define WHITE COLOR_CODE("#fff") std::string result = colorize_url("http://example.com/"); - UASSERT(result == (GREY "http://" WHITE "example.com" GREY "/")); + UASSERTEQ(auto, result, (GREY "http://" WHITE "example.com" GREY "/")); result = colorize_url(u8"https://u:p@wikipedi\u0430.org:1234/heIIoll?a=b#c"); - UASSERT(result == + UASSERTEQ(auto, result, (GREY "https://u:p@" WHITE "wikipedi" RED "%d0%b0" WHITE ".org" GREY ":1234/heIIoll?a=b#c")); +#else + warningstream << "Test skipped." << std::endl; #endif } diff --git a/src/util/colorize.cpp b/src/util/colorize.cpp index 8fc33b561..873ec06fc 100644 --- a/src/util/colorize.cpp +++ b/src/util/colorize.cpp @@ -17,7 +17,7 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #include "colorize.h" -#if USE_CURL +#ifdef HAVE_COLORIZE_URL #include #include "log.h" @@ -26,18 +26,13 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. std::string colorize_url(const std::string &url) { - // Forbid escape codes in URL - if (url.find('\x1b') != std::string::npos) { - throw std::runtime_error("Unable to open URL as it contains escape codes"); - } - auto urlHandleRAII = std::unique_ptr( curl_url(), curl_url_cleanup); CURLU *urlHandle = urlHandleRAII.get(); auto rc = curl_url_set(urlHandle, CURLUPART_URL, url.c_str(), 0); if (rc != CURLUE_OK) { - throw std::runtime_error("Unable to open URL as it is not valid"); + throw std::runtime_error("URL is not valid"); } auto url_get = [&] (CURLUPart what) -> std::string { @@ -56,7 +51,11 @@ std::string colorize_url(const std::string &url) auto path = url_get(CURLUPART_PATH); auto query = url_get(CURLUPART_QUERY); auto fragment = url_get(CURLUPART_FRAGMENT); +#if LIBCURL_VERSION_NUM >= 0x074100 auto zoneid = url_get(CURLUPART_ZONEID); +#else + std::string zoneid; +#endif std::ostringstream os; @@ -75,9 +74,7 @@ std::string colorize_url(const std::string &url) // Print hostname, escaping unsafe characters os << white; bool was_alphanum = true; - std::string host_s = host; - for (size_t i = 0; i < host_s.size(); i++) { - char c = host_s[i]; + for (char c : host) { bool is_alphanum = isalnum(c) || ispunct(c); if (is_alphanum == was_alphanum) { // skip diff --git a/src/util/colorize.h b/src/util/colorize.h index 3cde3e3dc..cb7ae7c30 100644 --- a/src/util/colorize.h +++ b/src/util/colorize.h @@ -23,11 +23,19 @@ OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. #define COLOR_CODE(color) "\x1b(c@" color ")" #if USE_CURL +#include +// curl_url functions since 7.62.0 +#if LIBCURL_VERSION_NUM >= 0x073e00 +#define HAVE_COLORIZE_URL +#endif +#endif + +#ifdef HAVE_COLORIZE_URL /** - * Colorize URL to highlight the hostname and any unsafe characters + * Colorize URL to highlight the hostname and any unsafe characters. * - * Throws an exception if the url is invalid + * Throws an exception if the url is invalid. */ std::string colorize_url(const std::string &url); From a391070d433795c93df11a4045c69405975e559c Mon Sep 17 00:00:00 2001 From: Licaon_Kter Date: Thu, 9 May 2024 09:31:22 +0000 Subject: [PATCH 021/146] Cleanup Removed Java 17 Options (#14626) ref: https://docs.oracle.com/en/java/javase/17/docs/specs/man/java.html#removed-java-options --- android/gradle.properties | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/android/gradle.properties b/android/gradle.properties index 8d5c2efb6..333d3ad95 100644 --- a/android/gradle.properties +++ b/android/gradle.properties @@ -1,7 +1,7 @@ <#if isLowMemory> -org.gradle.jvmargs=-Xmx4G -XX:MaxPermSize=2G -XX:+HeapDumpOnOutOfMemoryError +org.gradle.jvmargs=-Xmx4G -XX:+HeapDumpOnOutOfMemoryError <#else> -org.gradle.jvmargs=-Xmx16G -XX:MaxPermSize=8G -XX:+HeapDumpOnOutOfMemoryError +org.gradle.jvmargs=-Xmx16G -XX:+HeapDumpOnOutOfMemoryError org.gradle.daemon=true org.gradle.parallel=true From 148571c2b23e746557e1d9dd8ef4287007f3a829 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sun, 5 May 2024 22:29:50 +0200 Subject: [PATCH 022/146] Add benchmarks for server::ActiveObjectMgr --- src/benchmark/CMakeLists.txt | 1 + src/benchmark/benchmark_activeobjectmgr.cpp | 111 ++++++++++++++++++++ src/server/serveractiveobject.h | 5 - 3 files changed, 112 insertions(+), 5 deletions(-) create mode 100644 src/benchmark/benchmark_activeobjectmgr.cpp diff --git a/src/benchmark/CMakeLists.txt b/src/benchmark/CMakeLists.txt index a94f55da5..f79fcf1ef 100644 --- a/src/benchmark/CMakeLists.txt +++ b/src/benchmark/CMakeLists.txt @@ -1,5 +1,6 @@ set (BENCHMARK_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/benchmark.cpp + ${CMAKE_CURRENT_SOURCE_DIR}/benchmark_activeobjectmgr.cpp ${CMAKE_CURRENT_SOURCE_DIR}/benchmark_lighting.cpp ${CMAKE_CURRENT_SOURCE_DIR}/benchmark_serialize.cpp ${CMAKE_CURRENT_SOURCE_DIR}/benchmark_mapblock.cpp diff --git a/src/benchmark/benchmark_activeobjectmgr.cpp b/src/benchmark/benchmark_activeobjectmgr.cpp new file mode 100644 index 000000000..a21137a68 --- /dev/null +++ b/src/benchmark/benchmark_activeobjectmgr.cpp @@ -0,0 +1,111 @@ +// Minetest +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "benchmark_setup.h" +#include "server/activeobjectmgr.h" +#include "util/numeric.h" + +namespace { + +class TestObject : public ServerActiveObject { +public: + TestObject(v3f pos) : ServerActiveObject(nullptr, pos) + {} + + ActiveObjectType getType() const { + return ACTIVEOBJECT_TYPE_TEST; + } + bool getCollisionBox(aabb3f *toset) const { + return false; + } + bool getSelectionBox(aabb3f *toset) const { + return false; + } + bool collideWithObjects() const { + return true; + } +}; + +constexpr float POS_RANGE = 2001; + +inline v3f randpos() +{ + return v3f(myrand_range(-POS_RANGE, POS_RANGE), + myrand_range(-20, 60), + myrand_range(-POS_RANGE, POS_RANGE)); +} + +inline void fill(server::ActiveObjectMgr &mgr, size_t n) +{ + mgr.clear(); + for (size_t i = 0; i < n; i++) { + auto obj = std::make_unique(randpos()); + bool ok = mgr.registerObject(std::move(obj)); + REQUIRE(ok); + } +} + +} + +template +void benchGetObjectsInsideRadius(Catch::Benchmark::Chronometer &meter) +{ + server::ActiveObjectMgr mgr; + size_t x; + std::vector result; + + auto cb = [&x] (ServerActiveObject *obj) -> bool { + x += obj->m_static_exists ? 0 : 1; + return false; + }; + fill(mgr, N); + meter.measure([&] { + x = 0; + mgr.getObjectsInsideRadius(randpos(), 30.0f, result, cb); + return x; + }); + REQUIRE(result.empty()); + + mgr.clear(); // implementation expects this +} + +template +void benchGetObjectsInArea(Catch::Benchmark::Chronometer &meter) +{ + server::ActiveObjectMgr mgr; + size_t x; + std::vector result; + + auto cb = [&x] (ServerActiveObject *obj) -> bool { + x += obj->m_static_exists ? 0 : 1; + return false; + }; + fill(mgr, N); + meter.measure([&] { + x = 0; + v3f pos = randpos(); + v3f off(50, 50, 50); + off[myrand_range(0, 2)] = 10; + mgr.getObjectsInArea({pos, pos + off}, result, cb); + return x; + }); + REQUIRE(result.empty()); + + mgr.clear(); // implementation expects this +} + +#define BENCH_INSIDE_RADIUS(_count) \ + BENCHMARK_ADVANCED("inside_radius_" #_count)(Catch::Benchmark::Chronometer meter) \ + { benchGetObjectsInsideRadius<_count>(meter); }; + +#define BENCH_IN_AREA(_count) \ + BENCHMARK_ADVANCED("in_area_" #_count)(Catch::Benchmark::Chronometer meter) \ + { benchGetObjectsInArea<_count>(meter); }; + +TEST_CASE("ActiveObjectMgr") { + BENCH_INSIDE_RADIUS(200) + BENCH_INSIDE_RADIUS(1450) + + BENCH_IN_AREA(200) + BENCH_IN_AREA(1450) +} diff --git a/src/server/serveractiveobject.h b/src/server/serveractiveobject.h index 4d620b768..2d09e34c1 100644 --- a/src/server/serveractiveobject.h +++ b/src/server/serveractiveobject.h @@ -73,11 +73,6 @@ class ServerActiveObject : public ActiveObject void markForRemoval(); void markForDeactivation(); - // Create a certain type of ServerActiveObject - static ServerActiveObject* create(ActiveObjectType type, - ServerEnvironment *env, u16 id, v3f pos, - const std::string &data); - /* Some simple getters/setters */ From 0b55255d38947f9d571a8b230fcdbac0368d676e Mon Sep 17 00:00:00 2001 From: grorp Date: Thu, 9 May 2024 19:15:50 +0200 Subject: [PATCH 023/146] Android CI: Additionally make an AAB for uploading to the Play Store (#14584) --- .github/workflows/android.yml | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 04c650c27..20da8e551 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -34,8 +34,17 @@ jobs: run: | sudo apt-get update sudo apt-get install -y --no-install-recommends gettext openjdk-11-jdk-headless - - name: Build with Gradle + - name: Build AAB with Gradle + # We build an AAB as well for uploading to the the Play Store. + run: cd android; ./gradlew bundlerelease + - name: Build APKs with Gradle + # "assemblerelease" is very fast after "bundlerelease". run: cd android; ./gradlew assemblerelease + - name: Save AAB artifact + uses: actions/upload-artifact@v4 + with: + name: Minetest-release.aab + path: android/app/build/outputs/bundle/release/app-release.aab - name: Save armeabi artifact uses: actions/upload-artifact@v4 with: From d24a7bc676c8f22859060e9a1d155f2e0995cff3 Mon Sep 17 00:00:00 2001 From: grorp Date: Thu, 9 May 2024 19:16:08 +0200 Subject: [PATCH 024/146] Close formspecs with a single tap outside (#14605) --- src/gui/guiFormSpecMenu.cpp | 9 +++ src/gui/modalMenu.cpp | 110 +++++++++++++++++++++--------------- src/gui/modalMenu.h | 28 +++++---- 3 files changed, 91 insertions(+), 56 deletions(-) diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 7ac391c83..577e2ee68 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -4740,6 +4740,8 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) list_selected->changeItem(m_selected_item->i, stack_from); } + bool absorb_event = false; + // Possibly send inventory action to server if (move_amount > 0) { // Send IAction::Move @@ -4882,6 +4884,10 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) a->from_i = m_selected_item->i; m_invmgr->inventoryAction(a); + // Formspecs usually close when you click outside them, we absorb + // the event to prevent that. See GUIModalMenu::remapClickOutside. + absorb_event = true; + } else if (craft_amount > 0) { assert(s.isValid()); @@ -4911,6 +4917,9 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) m_selected_dragging = false; } m_old_pointer = m_pointer; + + if (absorb_event) + return true; } if (event.EventType == EET_GUI_EVENT) { diff --git a/src/gui/modalMenu.cpp b/src/gui/modalMenu.cpp index bb287ecd1..d95311035 100644 --- a/src/gui/modalMenu.cpp +++ b/src/gui/modalMenu.cpp @@ -25,19 +25,39 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "client/renderingengine.h" #include "modalMenu.h" #include "gettext.h" +#include "gui/guiInventoryList.h" #include "porting.h" #include "settings.h" #include "touchscreengui.h" +PointerAction PointerAction::fromEvent(const SEvent &event) { + switch (event.EventType) { + case EET_MOUSE_INPUT_EVENT: + return {v2s32(event.MouseInput.X, event.MouseInput.Y), porting::getTimeMs()}; + case EET_TOUCH_INPUT_EVENT: + return {v2s32(event.TouchInput.X, event.TouchInput.Y), porting::getTimeMs()}; + default: + FATAL_ERROR("SEvent given to PointerAction::fromEvent has wrong EventType"); + } +} + +bool PointerAction::isRelated(PointerAction previous) { + u64 time_delta = porting::getDeltaMs(previous.time, time); + v2s32 pos_delta = pos - previous.pos; + f32 distance_sq = (f32)pos_delta.X * pos_delta.X + (f32)pos_delta.Y * pos_delta.Y; + + return time_delta < 400 && distance_sq < (30.0f * 30.0f); +} + GUIModalMenu::GUIModalMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent, - s32 id, IMenuManager *menumgr, bool remap_dbl_click) : + s32 id, IMenuManager *menumgr, bool remap_click_outside) : IGUIElement(gui::EGUIET_ELEMENT, env, parent, id, core::rect(0, 0, 100, 100)), #ifdef __ANDROID__ m_jni_field_name(""), #endif m_menumgr(menumgr), - m_remap_dbl_click(remap_dbl_click) + m_remap_click_outside(remap_click_outside) { m_gui_scale = std::max(g_settings->getFloat("gui_scaling"), 0.5f); const float screen_dpi_scale = RenderingEngine::getDisplayDensity(); @@ -50,9 +70,6 @@ GUIModalMenu::GUIModalMenu(gui::IGUIEnvironment* env, gui::IGUIElement* parent, setVisible(true); m_menumgr->createdMenu(this); - - m_last_touch.time = 0; - m_last_touch.pos = v2s32(0, 0); } GUIModalMenu::~GUIModalMenu() @@ -115,42 +132,53 @@ static bool isChild(gui::IGUIElement *tocheck, gui::IGUIElement *parent) return false; } -bool GUIModalMenu::remapDoubleClick(const SEvent &event) +bool GUIModalMenu::remapClickOutside(const SEvent &event) { - /* The following code is for capturing double-clicks of the mouse button - * and translating the double-click into an EET_KEY_INPUT_EVENT event - * -- which closes the form -- under some circumstances. - * - * There have been many github issues reporting this as a bug even though it - * was an intended feature. For this reason, remapping the double-click as - * an ESC must be explicitly set when creating this class via the - * /p remap_dbl_click parameter of the constructor. - */ - - if (!m_remap_dbl_click) + if (!m_remap_click_outside || event.EventType != EET_MOUSE_INPUT_EVENT || + (event.MouseInput.Event != EMIE_LMOUSE_PRESSED_DOWN && + event.MouseInput.Event != EMIE_LMOUSE_LEFT_UP)) return false; - if (event.EventType != EET_MOUSE_INPUT_EVENT || - event.MouseInput.Event != EMIE_LMOUSE_DOUBLE_CLICK) - return false; + // The formspec must only be closed if both the EMIE_LMOUSE_PRESSED_DOWN and + // the EMIE_LMOUSE_LEFT_UP event haven't been absorbed by something else. + + PointerAction last = m_last_click_outside; + m_last_click_outside = {}; // always reset + PointerAction current = PointerAction::fromEvent(event); - // Only exit if the double-click happened outside the menu. gui::IGUIElement *hovered = - Environment->getRootGUIElement()->getElementFromPoint(m_pointer); + Environment->getRootGUIElement()->getElementFromPoint(current.pos); if (isChild(hovered, this)) return false; - // Translate double-click to escape. - SEvent translated{}; - translated.EventType = EET_KEY_INPUT_EVENT; - translated.KeyInput.Key = KEY_ESCAPE; - translated.KeyInput.Control = false; - translated.KeyInput.Shift = false; - translated.KeyInput.PressedDown = true; - translated.KeyInput.Char = 0; - OnEvent(translated); - - return true; + // Dropping items is also done by tapping outside the formspec. If an item + // is selected, make sure it is dropped without closing the formspec. + // We have to explicitly restrict this to GUIInventoryList because other + // GUI elements like text fields like to absorb events for no reason. + GUIInventoryList *focused = dynamic_cast(Environment->getFocus()); + if (focused && focused->OnEvent(event)) + // Return true since the event was handled, even if it wasn't handled by us. + return true; + + if (event.MouseInput.Event == EMIE_LMOUSE_PRESSED_DOWN) { + m_last_click_outside = current; + return true; + } + + if (event.MouseInput.Event == EMIE_LMOUSE_LEFT_UP && + current.isRelated(last)) { + SEvent translated{}; + translated.EventType = EET_KEY_INPUT_EVENT; + translated.KeyInput.Key = KEY_ESCAPE; + translated.KeyInput.Control = false; + translated.KeyInput.Shift = false; + translated.KeyInput.PressedDown = true; + translated.KeyInput.Char = 0; + OnEvent(translated); + return true; + } + + return false; } bool GUIModalMenu::simulateMouseEvent(ETOUCH_INPUT_EVENT touch_event, bool second_try) @@ -319,20 +347,12 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event) // Detect double-taps and convert them into double-click events. if (event.TouchInput.Event == ETIE_PRESSED_DOWN) { - u64 time_now = porting::getTimeMs(); - u64 time_delta = porting::getDeltaMs(m_last_touch.time, time_now); - - v2s32 pos_delta = m_pointer - m_last_touch.pos; - f32 distance_sq = (f32)pos_delta.X * pos_delta.X + - (f32)pos_delta.Y * pos_delta.Y; - - if (time_delta < 400 && distance_sq < (30 * 30)) { + PointerAction current = PointerAction::fromEvent(event); + if (current.isRelated(m_last_touch)) { // ETIE_COUNT is used for double-tap events. simulateMouseEvent(ETIE_COUNT); } - - m_last_touch.time = time_now; - m_last_touch.pos = m_pointer; + m_last_touch = current; } return ret; @@ -361,7 +381,7 @@ bool GUIModalMenu::preprocessEvent(const SEvent &event) m_touch_hovered.reset(); } - if (remapDoubleClick(event)) + if (remapClickOutside(event)) return true; } diff --git a/src/gui/modalMenu.h b/src/gui/modalMenu.h index 9bb55ffec..7ee8531d0 100644 --- a/src/gui/modalMenu.h +++ b/src/gui/modalMenu.h @@ -31,6 +31,14 @@ enum class PointerType { Touch, }; +struct PointerAction { + v2s32 pos; + u64 time; // ms + + static PointerAction fromEvent(const SEvent &event); + bool isRelated(PointerAction other); +}; + class GUIModalMenu; class IMenuManager @@ -94,14 +102,14 @@ class GUIModalMenu : public gui::IGUIElement private: IMenuManager *m_menumgr; - /* If true, remap a double-click (or double-tap) action to ESC. This is so - * that, for example, Android users can double-tap to close a formspec. - * - * This value can (currently) only be set by the class constructor - * and the default value for the setting is true. + /* If true, remap a click outside the formspec to ESC. This is so that, for + * example, touchscreen users can close formspecs. + * The default for this setting is true. Currently, it's set to false for + * the mainmenu to prevent Minetest from closing unexpectedly. */ - bool m_remap_dbl_click; - bool remapDoubleClick(const SEvent &event); + bool m_remap_click_outside; + bool remapClickOutside(const SEvent &event); + PointerAction m_last_click_outside{}; // This might be necessary to expose to the implementation if it // wants to launch other menus @@ -111,13 +119,11 @@ class GUIModalMenu : public gui::IGUIElement irr_ptr m_touch_hovered; + // Converts touches into clicks. bool simulateMouseEvent(ETOUCH_INPUT_EVENT touch_event, bool second_try=false); void enter(gui::IGUIElement *element); void leave(); // Used to detect double-taps and convert them into double-click events. - struct { - v2s32 pos; - s64 time; - } m_last_touch; + PointerAction m_last_touch{}; }; From 247643ced3dcad5411f351608efc42cc2304117f Mon Sep 17 00:00:00 2001 From: Benjamin Wheeler <34549440+benjamin051000@users.noreply.github.com> Date: Fri, 10 May 2024 06:00:15 -0400 Subject: [PATCH 025/146] Dockerfile: Use the default branch when cloning libspatialindex repository (#14621) --- Dockerfile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/Dockerfile b/Dockerfile index 701c0492a..39974a1c3 100644 --- a/Dockerfile +++ b/Dockerfile @@ -1,7 +1,6 @@ ARG DOCKER_IMAGE=alpine:3.19 FROM $DOCKER_IMAGE AS dev -ENV SPATIALINDEX_VERSION master ENV LUAJIT_VERSION v2.1 RUN apk add --no-cache git build-base cmake curl-dev zlib-dev zstd-dev \ @@ -19,7 +18,7 @@ RUN git clone --recursive https://github.com/jupp0r/prometheus-cpp && \ cmake --build build && \ cmake --install build && \ cd /usr/src/ && \ - git clone --recursive https://github.com/libspatialindex/libspatialindex -b ${SPATIALINDEX_VERSION} && \ + git clone --recursive https://github.com/libspatialindex/libspatialindex && \ cd libspatialindex && \ cmake -B build \ -DCMAKE_INSTALL_PREFIX=/usr/local && \ From af01eb70562d2c67c64b608f0cbbdf4547691473 Mon Sep 17 00:00:00 2001 From: grorp Date: Fri, 10 May 2024 18:54:22 +0200 Subject: [PATCH 026/146] Close buttonbars when hiding TouchScreenGUI (#14630) To open the inventory or the pause menu, you first need to open the buttonbar containing the respective button. Before this commit, the buttonbar is still open after closing the menu, so you have to tap twice before you can continue playing. After this commit, the buttonbar is already closed after closing the menu, so you only have to tap once before you can continue playing. --- src/gui/touchscreengui.cpp | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/gui/touchscreengui.cpp b/src/gui/touchscreengui.cpp index 2a2fa36ed..2504c4eb5 100644 --- a/src/gui/touchscreengui.cpp +++ b/src/gui/touchscreengui.cpp @@ -857,8 +857,10 @@ void TouchScreenGUI::setVisible(bool visible) if (!visible) { while (!m_pointer_pos.empty()) handleReleaseEvent(m_pointer_pos.begin()->first); - for (AutoHideButtonBar &bar : m_buttonbars) + for (AutoHideButtonBar &bar : m_buttonbars) { + bar.deactivate(); bar.hide(); + } } else { for (AutoHideButtonBar &bar : m_buttonbars) bar.show(); From 3ac5699909f3de367784ab0049d118bfd4696a90 Mon Sep 17 00:00:00 2001 From: grorp Date: Sun, 12 May 2024 11:46:39 +0200 Subject: [PATCH 027/146] Fix crash if zip file cannot be opened (#14636) Helped-by: savilli <78875209+savilli@users.noreply.github.com> --- builtin/mainmenu/content/contentdb.lua | 4 +++- src/filesys.cpp | 2 ++ 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/builtin/mainmenu/content/contentdb.lua b/builtin/mainmenu/content/contentdb.lua index eb06c8b61..849f7152d 100644 --- a/builtin/mainmenu/content/contentdb.lua +++ b/builtin/mainmenu/content/contentdb.lua @@ -71,7 +71,9 @@ local function download_and_extract(param) os.remove(filename) if tempfolder == "" then return { - msg = fgettext_ne("Failed to extract \"$1\" (unsupported file type or broken archive)", package.title), + msg = fgettext_ne("Failed to extract \"$1\" " .. + "(insufficient disk space, unsupported file type or broken archive)", + package.title), } end diff --git a/src/filesys.cpp b/src/filesys.cpp index 2a7874ad5..bf7950d3d 100644 --- a/src/filesys.cpp +++ b/src/filesys.cpp @@ -997,6 +997,8 @@ bool extractZipFile(io::IFileSystem *fs, const char *filename, const std::string } irr_ptr opened_zip(zip_loader->createArchive(filename, false, false)); + if (!opened_zip) + return false; const io::IFileList* files_in_zip = opened_zip->getFileList(); for (u32 i = 0; i < files_in_zip->getFileCount(); i++) { From e2e8c663134d45a4e0d6d76b06e71a9ba92f667f Mon Sep 17 00:00:00 2001 From: chmodsayshello Date: Sun, 12 May 2024 14:20:18 +0200 Subject: [PATCH 028/146] Add option for random mod load order (#14637) --- builtin/settingtypes.txt | 3 +++ src/content/mod_configuration.cpp | 10 ++++++++++ src/defaultsettings.cpp | 5 +++++ 3 files changed, 18 insertions(+) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index 4feb1274a..a4c4b5891 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -1778,6 +1778,9 @@ deprecated_lua_api_handling (Deprecated Lua API handling) enum log none,log,erro # Enable random user input (only used for testing). random_input (Random input) bool false +# Enable random mod loading (mainly used for testing). +random_mod_load_order (Random mod load order) bool false + # Enable mod channels support. enable_mod_channels (Mod channels) bool false diff --git a/src/content/mod_configuration.cpp b/src/content/mod_configuration.cpp index 9e0461f56..47ad3e0dc 100644 --- a/src/content/mod_configuration.cpp +++ b/src/content/mod_configuration.cpp @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "filesys.h" #include "gettext.h" #include "exceptions.h" +#include "util/numeric.h" std::string ModConfiguration::getUnsatisfiedModsError() const @@ -226,6 +227,15 @@ void ModConfiguration::resolveDependencies() modnames.insert(mod.name); } + // Step 1.5 (optional): shuffle unsatisfied mods so non declared depends get found by their devs + if (g_settings->getBool("random_mod_load_order")) { + MyRandGenerator rg; + std::shuffle(m_unsatisfied_mods.begin(), + m_unsatisfied_mods.end(), + rg + ); + } + // Step 2: get dependencies (including optional dependencies) // of each mod, split mods into satisfied and unsatisfied std::vector satisfied; diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index aa3f0fbdf..bbd5fd91b 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -522,6 +522,11 @@ void set_default_settings() // Server settings->setDefault("disable_escape_sequences", "false"); settings->setDefault("strip_color_codes", "false"); +#ifndef NDEBUG + settings->setDefault("random_mod_load_order", "true"); +#else + settings->setDefault("random_mod_load_order", "false"); +#endif #if USE_PROMETHEUS settings->setDefault("prometheus_listener_address", "127.0.0.1:30000"); #endif From 18e20e9013385fe3cbb1e211fc81b8e4947928ea Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sat, 11 May 2024 11:36:47 +0200 Subject: [PATCH 029/146] Fix invalid glDrawBuffer call on GLES closes #14445 --- irr/src/OpenGL/ExtensionHandler.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/irr/src/OpenGL/ExtensionHandler.h b/irr/src/OpenGL/ExtensionHandler.h index 8f96f0d4a..74c3e0b8a 100644 --- a/irr/src/OpenGL/ExtensionHandler.h +++ b/irr/src/OpenGL/ExtensionHandler.h @@ -138,7 +138,8 @@ class COpenGL3ExtensionHandler : public COGLESCoreExtensionHandler inline void irrGlDrawBuffer(GLenum mode) { - GL.DrawBuffer(mode); + // GLES only has DrawBuffers, so use that + GL.DrawBuffers(1, &mode); } inline void irrGlDrawBuffers(GLsizei n, const GLenum *bufs) From 29ebba024887b7acd29d079b19b23385f376597d Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sat, 11 May 2024 11:37:23 +0200 Subject: [PATCH 030/146] Deduplicate GL extension detection --- irr/src/OpenGL/ExtensionHandler.cpp | 37 ++--------------------------- irr/src/OpenGL/ExtensionHandler.h | 14 ++++------- irr/src/OpenGL3/Driver.cpp | 2 +- irr/src/OpenGLES2/Driver.cpp | 5 +--- 4 files changed, 9 insertions(+), 49 deletions(-) diff --git a/irr/src/OpenGL/ExtensionHandler.cpp b/irr/src/OpenGL/ExtensionHandler.cpp index 3ac212987..3c248ba0a 100644 --- a/irr/src/OpenGL/ExtensionHandler.cpp +++ b/irr/src/OpenGL/ExtensionHandler.cpp @@ -12,47 +12,14 @@ #include "os.h" #include -// FIXME: this basically duplicates what mt_opengl.h already does - namespace irr { namespace video { -void COpenGL3ExtensionHandler::initExtensionsOld() -{ - auto extensions_string = reinterpret_cast(GL.GetString(GL_EXTENSIONS)); - const char *pos = extensions_string; - while (const char *next = strchr(pos, ' ')) { - addExtension(std::string{pos, next}); - pos = next + 1; - } - addExtension(pos); - extensionsLoaded(); -} - -void COpenGL3ExtensionHandler::initExtensionsNew() -{ - int ext_count = GetInteger(GL_NUM_EXTENSIONS); - for (int k = 0; k < ext_count; k++) - addExtension(reinterpret_cast(GL.GetStringi(GL_EXTENSIONS, k))); - extensionsLoaded(); -} - -void COpenGL3ExtensionHandler::addExtension(std::string &&name) -{ - Extensions.emplace(std::move(name)); -} - -bool COpenGL3ExtensionHandler::queryExtension(const std::string &name) const noexcept -{ - return Extensions.find(name) != Extensions.end(); -} -void COpenGL3ExtensionHandler::extensionsLoaded() +void COpenGL3ExtensionHandler::initExtensions() { - os::Printer::log((std::string("Loaded ") + std::to_string(Extensions.size()) + " extensions:").c_str(), ELL_DEBUG); - for (const auto &it : Extensions) - os::Printer::log((std::string(" ") + it).c_str(), ELL_DEBUG); + // reading extensions happens in mt_opengl.cpp for (size_t j = 0; j < IRR_OGLES_Feature_Count; ++j) FeatureAvailable[j] = queryExtension(getFeatureString(j)); } diff --git a/irr/src/OpenGL/ExtensionHandler.h b/irr/src/OpenGL/ExtensionHandler.h index 74c3e0b8a..8f76c0eea 100644 --- a/irr/src/OpenGL/ExtensionHandler.h +++ b/irr/src/OpenGL/ExtensionHandler.h @@ -28,11 +28,13 @@ class COpenGL3ExtensionHandler : public COGLESCoreExtensionHandler COpenGL3ExtensionHandler() : COGLESCoreExtensionHandler() {} - void initExtensionsOld(); - void initExtensionsNew(); + void initExtensions(); /// Checks whether a named extension is present - bool queryExtension(const std::string &name) const noexcept; + inline bool queryExtension(const std::string &name) const noexcept + { + return GL.IsExtensionPresent(name); + } bool queryFeature(video::E_VIDEO_DRIVER_FEATURE feature) const { @@ -159,12 +161,6 @@ class COpenGL3ExtensionHandler : public COGLESCoreExtensionHandler bool AnisotropicFilterSupported = false; bool BlendMinMaxSupported = false; - -private: - void addExtension(std::string &&name); - void extensionsLoaded(); - - std::unordered_set Extensions; }; } diff --git a/irr/src/OpenGL3/Driver.cpp b/irr/src/OpenGL3/Driver.cpp index 8196a8117..fd018b91d 100644 --- a/irr/src/OpenGL3/Driver.cpp +++ b/irr/src/OpenGL3/Driver.cpp @@ -36,7 +36,7 @@ void COpenGL3Driver::initFeatures() { assert(Version.Spec == OpenGLSpec::Compat); assert(isVersionAtLeast(3, 2)); - initExtensionsNew(); + initExtensions(); TextureFormats[ECF_A1R5G5B5] = {GL_RGB5_A1, GL_BGRA, GL_UNSIGNED_SHORT_1_5_5_5_REV}; // WARNING: may not be renderable TextureFormats[ECF_R5G6B5] = {GL_RGB, GL_RGB, GL_UNSIGNED_SHORT_5_6_5}; // GL_RGB565 is an extension until 4.1 diff --git a/irr/src/OpenGLES2/Driver.cpp b/irr/src/OpenGLES2/Driver.cpp index 6278ee9c3..249b6caef 100644 --- a/irr/src/OpenGLES2/Driver.cpp +++ b/irr/src/OpenGLES2/Driver.cpp @@ -31,10 +31,7 @@ void COpenGLES2Driver::initFeatures() { assert(Version.Spec == OpenGLSpec::ES); assert(Version.Major >= 2); - if (Version.Major >= 3) - initExtensionsNew(); - else - initExtensionsOld(); + initExtensions(); static const GLenum BGRA8_EXT = 0x93A1; From 6d346108883f09dfb888e8bef19c4d5873ee3d91 Mon Sep 17 00:00:00 2001 From: Josiah VanderZee Date: Tue, 14 May 2024 09:03:11 -0500 Subject: [PATCH 031/146] Do not bother to assert that a u8 is >= 0 This fixes a warning seen in GCC 7 on CI about a comparison in mapblock.h that is always true. --- src/mapblock.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/mapblock.h b/src/mapblock.h index a32f9b312..843daf6ef 100644 --- a/src/mapblock.h +++ b/src/mapblock.h @@ -167,7 +167,7 @@ class MapBlock inline void setLightingComplete(LightBank bank, u8 direction, bool is_complete) { - assert(direction >= 0 && direction <= 5); + assert(direction <= 5); if (bank == LIGHTBANK_NIGHT) { direction += 6; } @@ -182,7 +182,7 @@ class MapBlock inline bool isLightingComplete(LightBank bank, u8 direction) { - assert(direction >= 0 && direction <= 5); + assert(direction <= 5); if (bank == LIGHTBANK_NIGHT) { direction += 6; } From 58a6f525648dd96c9ebb0e5aebeab24a027c790c Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 8 May 2024 20:37:10 +0200 Subject: [PATCH 032/146] Introduce proper error handling for file streams --- src/client/client.cpp | 7 +- src/client/filecache.cpp | 20 ++--- src/client/shader.cpp | 4 +- src/client/shadows/dynamicshadowsrender.cpp | 3 +- src/content/content.cpp | 38 +++------ src/database/database-files.cpp | 17 ++-- src/filesys.cpp | 89 ++++++++++++++------- src/filesys.h | 63 ++++++++++++++- src/gettext.cpp | 21 +++-- src/gui/guiEngine.cpp | 5 +- src/log.cpp | 23 +++--- src/map_settings_manager.cpp | 8 +- src/mapgen/mg_schematic.cpp | 7 +- src/porting.cpp | 17 +++- src/porting.h | 3 + src/script/lua_api/l_areastore.cpp | 2 +- src/server.cpp | 12 +-- src/server/ban.cpp | 3 +- src/settings.cpp | 5 +- src/texture_override.cpp | 3 +- src/unittest/test.cpp | 6 +- src/unittest/test_ban.cpp | 4 +- src/unittest/test_filesys.cpp | 26 ++++++ 23 files changed, 236 insertions(+), 150 deletions(-) diff --git a/src/client/client.cpp b/src/client/client.cpp index 1affb298f..96c64097b 100644 --- a/src/client/client.cpp +++ b/src/client/client.cpp @@ -285,9 +285,7 @@ void Client::scanModSubfolder(const std::string &mod_name, const std::string &mo << "\" as \"" << vfs_path << "\"." << std::endl; std::string contents; - if (!fs::ReadFile(real_path, contents)) { - errorstream << "Client::scanModSubfolder(): Can't read file \"" - << real_path << "\"." << std::endl; + if (!fs::ReadFile(real_path, contents, true)) { continue; } @@ -2088,8 +2086,7 @@ void Client::makeScreenshot() while (serial < SCREENSHOT_MAX_SERIAL_TRIES) { filename = filename_base + (serial > 0 ? ("_" + itos(serial)) : "") + filename_ext; - std::ifstream tmp(filename.c_str()); - if (!tmp.good()) + if (!fs::PathExists(filename)) break; // File did not apparently exist, we'll go with it serial++; } diff --git a/src/client/filecache.cpp b/src/client/filecache.cpp index cb9f5440b..ffd011866 100644 --- a/src/client/filecache.cpp +++ b/src/client/filecache.cpp @@ -38,13 +38,9 @@ void FileCache::createDir() bool FileCache::loadByPath(const std::string &path, std::ostream &os) { - std::ifstream fis(path.c_str(), std::ios_base::binary); - - if(!fis.good()){ - verbosestream<<"FileCache: File not found in cache: " - <(is)), - std::istreambuf_iterator()); + fs::ReadFile(spec.path + DIR_DELIM + "description.txt", spec.desc); } } diff --git a/src/database/database-files.cpp b/src/database/database-files.cpp index 518c776ea..27f42fc97 100644 --- a/src/database/database-files.cpp +++ b/src/database/database-files.cpp @@ -165,11 +165,9 @@ void PlayerDatabaseFiles::savePlayer(RemotePlayer *player) } // Open and deserialize file to check player name - std::ifstream is(path.c_str(), std::ios_base::binary); - if (!is.good()) { - errorstream << "Failed to open " << path << std::endl; + auto is = open_ifstream(path.c_str(), true); + if (!is.good()) return; - } deSerialize(&testplayer, is, path, NULL); is.close(); @@ -205,7 +203,7 @@ bool PlayerDatabaseFiles::removePlayer(const std::string &name) RemotePlayer temp_player("", NULL); for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) { // Open file and deserialize - std::ifstream is(path.c_str(), std::ios_base::binary); + auto is = open_ifstream(path.c_str(), false); if (!is.good()) continue; @@ -231,7 +229,7 @@ bool PlayerDatabaseFiles::loadPlayer(RemotePlayer *player, PlayerSAO *sao) const std::string player_to_load = player->getName(); for (u32 i = 0; i < PLAYER_FILE_ALTERNATE_TRIES; i++) { // Open file and deserialize - std::ifstream is(path.c_str(), std::ios_base::binary); + auto is = open_ifstream(path.c_str(), false); if (!is.good()) continue; @@ -260,7 +258,7 @@ void PlayerDatabaseFiles::listPlayers(std::vector &res) const std::string &filename = it->name; std::string full_path = m_savedir + DIR_DELIM + filename; - std::ifstream is(full_path.c_str(), std::ios_base::binary); + auto is = open_ifstream(full_path.c_str(), true); if (!is.good()) continue; @@ -332,7 +330,7 @@ void AuthDatabaseFiles::reload() bool AuthDatabaseFiles::readAuthFile() { std::string path = m_savedir + DIR_DELIM + "auth.txt"; - std::ifstream file(path, std::ios::binary); + auto file = open_ifstream(path.c_str(), false); if (!file.good()) { return false; } @@ -528,8 +526,7 @@ Json::Value *ModStorageDatabaseFiles::getOrCreateJson(const std::string &modname std::string path = m_storage_dir + DIR_DELIM + modname; if (fs::PathExists(path)) { - std::ifstream is(path.c_str(), std::ios_base::binary); - + auto is = open_ifstream(path.c_str(), true); Json::CharReaderBuilder builder; builder.settings_["collectComments"] = false; std::string errs; diff --git a/src/filesys.cpp b/src/filesys.cpp index bf7950d3d..91e4ccb69 100644 --- a/src/filesys.cpp +++ b/src/filesys.cpp @@ -41,6 +41,13 @@ with this program; if not, write to the Free Software Foundation, Inc., #endif #endif +// Error from last OS call as string +#ifdef _WIN32 +#define LAST_OS_ERROR() porting::ConvertError(GetLastError()) +#else +#define LAST_OS_ERROR() strerror(errno) +#endif + namespace fs { @@ -849,40 +856,44 @@ const char *GetFilenameFromPath(const char *path) bool safeWriteToFile(const std::string &path, std::string_view content) { - std::string tmp_file = path + ".~mt"; + // Note: this is not safe if two MT processes try this at the same time (FIXME?) + const std::string tmp_file = path + ".~mt"; - // Write to a tmp file - bool tmp_success = false; + // Write data to a temporary file + std::string write_error; #ifdef _WIN32 // We've observed behavior suggesting that the MSVC implementation of std::ofstream::flush doesn't // actually flush, so we use win32 APIs. - HANDLE tmp_handle = CreateFile( - tmp_file.c_str(), GENERIC_WRITE, 0, nullptr, CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); - if (tmp_handle == INVALID_HANDLE_VALUE) { + HANDLE handle = CreateFile(tmp_file.c_str(), GENERIC_WRITE, 0, nullptr, + CREATE_ALWAYS, FILE_ATTRIBUTE_NORMAL, nullptr); + if (handle == INVALID_HANDLE_VALUE) { + errorstream << "Failed to open file: " << LAST_OS_ERROR() << std::endl; return false; } DWORD bytes_written; - tmp_success = (WriteFile(tmp_handle, content.data(), content.size(), &bytes_written, nullptr) && - FlushFileBuffers(tmp_handle)); - CloseHandle(tmp_handle); + if (!WriteFile(handle, content.data(), content.size(), &bytes_written, nullptr)) + write_error = LAST_OS_ERROR(); + else if (!FlushFileBuffers(handle)) + write_error = LAST_OS_ERROR(); + CloseHandle(handle); #else - std::ofstream os(tmp_file.c_str(), std::ios::binary); - if (!os.good()) { + auto os = open_ofstream(tmp_file.c_str(), true); + if (!os.good()) return false; - } - os << content; - os.flush(); + os << content << std::flush; os.close(); - tmp_success = !os.fail(); + if (os.fail()) + write_error = "iostream fail"; #endif - if (!tmp_success) { + if (!write_error.empty()) { + errorstream << "Failed to write file: " << write_error << std::endl; remove(tmp_file.c_str()); return false; } - bool rename_success = false; + std::string rename_error; // Move the finished temporary file over the real file #ifdef _WIN32 @@ -890,22 +901,25 @@ bool safeWriteToFile(const std::string &path, std::string_view content) // to query the file. This can make the move file call below fail. // We retry up to 5 times, with a 1ms sleep between, before we consider the whole operation failed for (int attempt = 0; attempt < 5; attempt++) { - rename_success = MoveFileEx(tmp_file.c_str(), path.c_str(), + auto ok = MoveFileEx(tmp_file.c_str(), path.c_str(), MOVEFILE_REPLACE_EXISTING | MOVEFILE_WRITE_THROUGH); - if (rename_success) + if (ok) { + rename_error.clear(); break; + } + rename_error = LAST_OS_ERROR(); sleep_ms(1); } #else // On POSIX compliant systems rename() is specified to be able to swap the // file in place of the destination file, making this a truly error-proof // transaction. - rename_success = rename(tmp_file.c_str(), path.c_str()) == 0; + if (rename(tmp_file.c_str(), path.c_str()) != 0) + rename_error = LAST_OS_ERROR(); #endif - if (!rename_success) { - warningstream << "Failed to write to file: " << path.c_str() << std::endl; - // Remove the temporary file because moving it over the target file - // failed. + + if (!rename_error.empty()) { + errorstream << "Failed to overwrite \"" << path << "\": " << rename_error << std::endl; remove(tmp_file.c_str()); return false; } @@ -1014,7 +1028,7 @@ bool extractZipFile(io::IFileSystem *fs, const char *filename, const std::string irr_ptr toread(opened_zip->createAndOpenFile(i)); - std::ofstream os(fullpath.c_str(), std::ios::binary); + auto os = open_ofstream(fullpath.c_str(), true); if (!os.good()) return false; @@ -1041,12 +1055,11 @@ bool extractZipFile(io::IFileSystem *fs, const char *filename, const std::string } #endif -bool ReadFile(const std::string &path, std::string &out) +bool ReadFile(const std::string &path, std::string &out, bool log_error) { - std::ifstream is(path, std::ios::binary | std::ios::ate); - if (!is.good()) { + auto is = open_ifstream(path.c_str(), log_error, std::ios::ate); + if (!is.good()) return false; - } auto size = is.tellg(); out.resize(size); @@ -1061,4 +1074,22 @@ bool Rename(const std::string &from, const std::string &to) return rename(from.c_str(), to.c_str()) == 0; } +bool OpenStream(std::filebuf &stream, const char *filename, + std::ios::openmode mode, bool log_error, bool log_warn) +{ + assert((mode & std::ios::in) || (mode & std::ios::out)); + assert(!stream.is_open()); + // C++ dropped the ball hard for file opening error handling, there's not even + // an implementation-defined mechanism for returning any kind of error code or message. + if (!stream.open(filename, mode)) { + if (log_warn || log_error) { + std::string msg = LAST_OS_ERROR(); + (log_error ? errorstream : warningstream) + << "Failed to open \"" << filename << "\": " << msg << std::endl; + } + return false; + } + return true; +} + } // namespace fs diff --git a/src/filesys.h b/src/filesys.h index ac961c37c..2b901ce05 100644 --- a/src/filesys.h +++ b/src/filesys.h @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include +#include #ifdef _WIN32 #define DIR_DELIM "\\" @@ -148,6 +149,9 @@ std::string AbsolutePath(const std::string &path); // delimiter is found. const char *GetFilenameFromPath(const char *path); +// Replace the content of a file on disk in a way that is safe from +// corruption/truncation on a crash. +// logs and returns false on error bool safeWriteToFile(const std::string &path, std::string_view content); bool safeAppendToFile(const std::string &path, std::string_view content); @@ -155,8 +159,65 @@ bool safeAppendToFile(const std::string &path, std::string_view content); bool extractZipFile(irr::io::IFileSystem *fs, const char *filename, const std::string &destination); #endif -bool ReadFile(const std::string &path, std::string &out); +bool ReadFile(const std::string &path, std::string &out, bool log_error = false); bool Rename(const std::string &from, const std::string &to); +/** + * Open a file buffer with error handling, commonly used with `std::fstream.rdbuf()`. + * + * @param stream stream references, must not already be open + * @param filename filename to open + * @param mode mode bits (used as-is) + * @param log_error log failure to errorstream? + * @param log_warn log failure to warningstream? + * @return true if success +*/ +bool OpenStream(std::filebuf &stream, const char *filename, + std::ios::openmode mode, bool log_error, bool log_warn); + } // namespace fs + +// outside of namespace for convenience: + +/** + * Helper function to open an output file stream (= writing). + * + * For compatibility with fopen() binary mode and truncate are always set. + * Use `fs::OpenStream` for more control. + * @param name file name + * @param log if true, failure to open the file will be logged to errorstream + * @param mode additional mode bits (e.g. std::ios::app) + * @return file stream, will be !good in case of error +*/ +inline std::ofstream open_ofstream(const char *name, bool log, + std::ios::openmode mode = std::ios::openmode()) +{ + mode |= std::ios::out | std::ios::binary; + if (!(mode & std::ios::app)) + mode |= std::ios::trunc; + std::ofstream ofs; + if (!fs::OpenStream(*ofs.rdbuf(), name, mode, log, false)) + ofs.setstate(std::ios::failbit); + return ofs; +} + +/** + * Helper function to open an input file stream (= reading). + * + * For compatibility with fopen() binary mode is always set. + * Use `fs::OpenStream` for more control. + * @param name file name + * @param log if true, failure to open the file will be logged to errorstream + * @param mode additional mode bits (e.g. std::ios::ate) + * @return file stream, will be !good in case of error +*/ +inline std::ifstream open_ifstream(const char *name, bool log, + std::ios::openmode mode = std::ios::openmode()) +{ + mode |= std::ios::in | std::ios::binary; + std::ifstream ifs; + if (!fs::OpenStream(*ifs.rdbuf(), name, mode, log, false)) + ifs.setstate(std::ios::failbit); + return ifs; +} diff --git a/src/gettext.cpp b/src/gettext.cpp index 68b6f728a..58efebb7c 100644 --- a/src/gettext.cpp +++ b/src/gettext.cpp @@ -147,18 +147,15 @@ static void MSVC_LocaleWorkaround(int argc, char* argv[]) exit(0); // NOTREACHED } else { - char buffer[1024]; - - FormatMessage(FORMAT_MESSAGE_FROM_SYSTEM, NULL, GetLastError(), - MAKELANGID(LANG_NEUTRAL,SUBLANG_DEFAULT), buffer, - sizeof(buffer) - 1, NULL); - - errorstream << "*******************************************************" << std::endl; - errorstream << "CMD: " << app_name << std::endl; - errorstream << "Failed to restart with current locale: " << std::endl; - errorstream << buffer; - errorstream << "Expect language to be broken!" << std::endl; - errorstream << "*******************************************************" << std::endl; + auto e = GetLastError(); + + errorstream + << "*******************************************************" << '\n' + << "CMD: " << app_name << '\n'; + << "Failed to restart with current locale: "; + << porting::ConvertError(e) << '\n'; + << "Expect language to be broken!" << '\n'; + << "*******************************************************" << std::endl; } } diff --git a/src/gui/guiEngine.cpp b/src/gui/guiEngine.cpp index 4d35f38ab..66f9f9864 100644 --- a/src/gui/guiEngine.cpp +++ b/src/gui/guiEngine.cpp @@ -620,10 +620,9 @@ bool GUIEngine::setTexture(texture_layer layer, const std::string &texturepath, bool GUIEngine::downloadFile(const std::string &url, const std::string &target) { #if USE_CURL - std::ofstream target_file(target.c_str(), std::ios::out | std::ios::binary); - if (!target_file.good()) { + auto target_file = open_ofstream(target.c_str(), true); + if (!target_file.good()) return false; - } HTTPFetchRequest fetch_request; HTTPFetchResult fetch_result; diff --git a/src/log.cpp b/src/log.cpp index 8ab0ca4ba..5ee66c070 100644 --- a/src/log.cpp +++ b/src/log.cpp @@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "exceptions.h" #include "util/numeric.h" #include "log.h" +#include "filesys.h" #ifdef __ANDROID__ #include @@ -36,7 +37,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include -#include #include class LevelTarget : public LogTarget { @@ -299,25 +299,24 @@ void FileLogOutput::setFile(const std::string &filename, s64 file_size_max) bool is_too_large = false; if (file_size_max > 0) { std::ifstream ifile(filename, std::ios::binary | std::ios::ate); - is_too_large = ifile.tellg() > file_size_max; - ifile.close(); + if (ifile.good()) + is_too_large = ifile.tellg() > file_size_max; } - if (is_too_large) { std::string filename_secondary = filename + ".1"; actionstream << "The log file grew too big; it is moved to " << filename_secondary << std::endl; - remove(filename_secondary.c_str()); - rename(filename.c_str(), filename_secondary.c_str()); + fs::DeleteSingleFileOrEmptyDirectory(filename_secondary); + fs::Rename(filename, filename_secondary); } - m_stream.open(filename, std::ios::app | std::ios::ate); - if (!m_stream.good()) - throw FileNotGoodException("Failed to open log file " + - filename + ": " + strerror(errno)); + // Intentionally not using open_ofstream() to keep the text mode + if (!fs::OpenStream(*m_stream.rdbuf(), filename.c_str(), std::ios::out | std::ios::app, true, false)) + throw FileNotGoodException("Failed to open log file"); + m_stream << "\n\n" - "-------------" << std::endl << - " Separator" << std::endl << + "-------------\n" << + " Separator\n" << "-------------\n" << std::endl; } diff --git a/src/map_settings_manager.cpp b/src/map_settings_manager.cpp index 264a941c2..bef7ad1c6 100644 --- a/src/map_settings_manager.cpp +++ b/src/map_settings_manager.cpp @@ -96,13 +96,9 @@ bool MapSettingsManager::setMapSettingNoiseParams( bool MapSettingsManager::loadMapMeta() { - std::ifstream is(m_map_meta_path.c_str(), std::ios_base::binary); - - if (!is.good()) { - errorstream << "loadMapMeta: could not open " - << m_map_meta_path << std::endl; + auto is = open_ifstream(m_map_meta_path.c_str(), true); + if (!is.good()) return false; - } if (!m_map_settings->parseConfigLines(is)) { errorstream << "loadMapMeta: Format error. '[end_of_params]' missing?" << std::endl; diff --git a/src/mapgen/mg_schematic.cpp b/src/mapgen/mg_schematic.cpp index b6b78f9e9..5ce0b8844 100644 --- a/src/mapgen/mg_schematic.cpp +++ b/src/mapgen/mg_schematic.cpp @@ -485,12 +485,9 @@ bool Schematic::serializeToLua(std::ostream *os, bool use_comments, bool Schematic::loadSchematicFromFile(const std::string &filename, const NodeDefManager *ndef, StringMap *replace_names) { - std::ifstream is(filename.c_str(), std::ios_base::binary); - if (!is.good()) { - errorstream << __FUNCTION__ << ": unable to open file '" - << filename << "'" << std::endl; + auto is = open_ifstream(filename.c_str(), true); + if (!is.good()) return false; - } if (!m_ndef) m_ndef = ndef; diff --git a/src/porting.cpp b/src/porting.cpp index db769d901..2b2405d38 100644 --- a/src/porting.cpp +++ b/src/porting.cpp @@ -582,7 +582,7 @@ static void createCacheDirTag() if (fs::PathExists(path)) return; fs::CreateAllDirs(path_cache); - std::ofstream ofs(path, std::ios::out | std::ios::binary); + auto ofs = open_ofstream(path.c_str(), false); if (!ofs.good()) return; ofs << "Signature: 8a477f597d28d172789f06886806bc55\n" @@ -800,6 +800,21 @@ std::string QuoteArgv(const std::string &arg) ret.push_back('"'); return ret; } + +std::string ConvertError(DWORD error_code) +{ + wchar_t buffer[320]; + + auto r = FormatMessageW( + FORMAT_MESSAGE_FROM_SYSTEM | FORMAT_MESSAGE_IGNORE_INSERTS, + nullptr, error_code, 0, buffer, ARRLEN(buffer) - 1, nullptr); + if (!r) + return std::to_string(error_code); + + if (!buffer[0]) // should not happen normally + return "?"; + return wide_to_utf8(buffer); +} #endif int mt_snprintf(char *buf, const size_t buf_size, const char *fmt, ...) diff --git a/src/porting.h b/src/porting.h index 0f97a1ca3..a98e66bcf 100644 --- a/src/porting.h +++ b/src/porting.h @@ -293,6 +293,9 @@ void attachOrCreateConsole(); #ifdef _WIN32 // Quotes an argument for use in a CreateProcess() commandline (not cmd.exe!!) std::string QuoteArgv(const std::string &arg); + +// Convert an error code (e.g. from GetLastError()) into a string. +std::string ConvertError(DWORD error_code); #endif int mt_snprintf(char *buf, const size_t buf_size, const char *fmt, ...); diff --git a/src/script/lua_api/l_areastore.cpp b/src/script/lua_api/l_areastore.cpp index 82fb945ae..c261338a4 100644 --- a/src/script/lua_api/l_areastore.cpp +++ b/src/script/lua_api/l_areastore.cpp @@ -298,7 +298,7 @@ int LuaAreaStore::l_from_file(lua_State *L) const char *filename = luaL_checkstring(L, 2); CHECK_SECURE_PATH(L, filename, false); - std::ifstream is(filename, std::ios::binary); + auto is = open_ifstream(filename, true); return deserialization_helper(L, o->as, is); } diff --git a/src/server.cpp b/src/server.cpp index 6e12017bc..316f349b2 100644 --- a/src/server.cpp +++ b/src/server.cpp @@ -2545,9 +2545,7 @@ bool Server::addMediaFile(const std::string &filename, // Read data std::string filedata; - if (!fs::ReadFile(filepath, filedata)) { - errorstream << "Server::addMediaFile(): Failed to open \"" - << filename << "\" for reading" << std::endl; + if (!fs::ReadFile(filepath, filedata, true)) { return false; } @@ -2715,9 +2713,7 @@ void Server::sendRequestedMedia(session_t peer_id, // Read data std::string data; - if (!fs::ReadFile(m.path, data)) { - errorstream << "Server::sendRequestedMedia(): Failed to read \"" - << name << "\"" << std::endl; + if (!fs::ReadFile(m.path, data, true)) { continue; } file_size_bunch_total += data.size(); @@ -3618,7 +3614,7 @@ namespace { auto filepath = fs::CreateTempFile(); if (filepath.empty()) return ""; - std::ofstream os(filepath, std::ios::binary); + auto os = open_ofstream(filepath.c_str(), true); if (!os.good()) return ""; os << content; @@ -4200,7 +4196,7 @@ Translations *Server::getTranslationLanguage(const std::string &lang_code) for (const auto &i : m_media) { if (str_ends_with(i.first, suffix)) { std::string data; - if (fs::ReadFile(i.second.path, data)) { + if (fs::ReadFile(i.second.path, data, true)) { translations->loadTranslation(data); } } diff --git a/src/server/ban.cpp b/src/server/ban.cpp index 47a6ccd5e..4af108795 100644 --- a/src/server/ban.cpp +++ b/src/server/ban.cpp @@ -48,9 +48,8 @@ void BanManager::load() { MutexAutoLock lock(m_mutex); infostream<<"BanManager: loading from "< #include @@ -48,7 +49,7 @@ static const std::map override_LUT = { TextureOverrideSource::TextureOverrideSource(const std::string &filepath) { - std::ifstream infile(filepath.c_str()); + auto infile = open_ifstream(filepath.c_str(), false); std::string line; int line_index = 0; while (std::getline(infile, line)) { diff --git a/src/unittest/test.cpp b/src/unittest/test.cpp index e2d19b3e1..7141ee0c6 100644 --- a/src/unittest/test.cpp +++ b/src/unittest/test.cpp @@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "modchannels.h" #include "util/numeric.h" #include "porting.h" +#include "debug.h" content_t t_CONTENT_STONE; content_t t_CONTENT_GRASS; @@ -348,11 +349,14 @@ void TestBase::runTest(const char *name, std::function &&test) rawstream << " at " << e.file << ":" << e.line << std::endl; rawstream << "[FAIL] "; num_tests_failed++; - } catch (std::exception &e) { + } +#if CATCH_UNHANDLED_EXCEPTIONS == 1 + catch (std::exception &e) { rawstream << "Caught unhandled exception: " << e.what() << std::endl; rawstream << "[FAIL] "; num_tests_failed++; } +#endif num_tests_run++; u64 tdiff = porting::getTimeMs() - t1; rawstream << name << " - " << tdiff << "ms" << std::endl; diff --git a/src/unittest/test_ban.cpp b/src/unittest/test_ban.cpp index 6b36211ab..58132e20a 100644 --- a/src/unittest/test_ban.cpp +++ b/src/unittest/test_ban.cpp @@ -80,13 +80,13 @@ void TestBan::testCreate() BanManager bm(m_testbm); } - UASSERT(std::ifstream(m_testbm, std::ios::binary).is_open()); + UASSERT(fs::IsFile(m_testbm)); // test manual save { BanManager bm(m_testbm2); bm.save(); - UASSERT(std::ifstream(m_testbm2, std::ios::binary).is_open()); + UASSERT(fs::IsFile(m_testbm2)); } } diff --git a/src/unittest/test_filesys.cpp b/src/unittest/test_filesys.cpp index 302371361..8fc9db3d8 100644 --- a/src/unittest/test_filesys.cpp +++ b/src/unittest/test_filesys.cpp @@ -41,6 +41,7 @@ class TestFileSys : public TestBase void testRemoveRelativePathComponent(); void testSafeWriteToFile(); void testCopyFileContents(); + void testNonExist(); }; static TestFileSys g_test_instance; @@ -54,6 +55,7 @@ void TestFileSys::runTests(IGameDef *gamedef) TEST(testRemoveRelativePathComponent); TEST(testSafeWriteToFile); TEST(testCopyFileContents); + TEST(testNonExist); } //////////////////////////////////////////////////////////////////////////////// @@ -311,3 +313,27 @@ void TestFileSys::testCopyFileContents() UASSERT(fs::ReadFile(file2, contents_actual)); UASSERTEQ(auto, contents_actual, test_data); } + +void TestFileSys::testNonExist() +{ + const auto path = getTestTempFile(); + fs::DeleteSingleFileOrEmptyDirectory(path); + + UASSERT(!fs::IsFile(path)); + UASSERT(!fs::IsDir(path)); + UASSERT(!fs::IsExecutable(path)); + + std::string s; + UASSERT(!fs::ReadFile(path, s)); + UASSERT(s.empty()); + + UASSERT(!fs::Rename(path, getTestTempFile())); + + std::filebuf buf; + // with logging enabled to test that code path + UASSERT(!fs::OpenStream(buf, path.c_str(), std::ios::in, false, true)); + UASSERT(!buf.is_open()); + + auto ifs = open_ifstream(path.c_str(), false); + UASSERT(!ifs.good()); +} From bf572d80f41fe68e08cb9f8f26f5b38b4592bed9 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 14 May 2024 23:12:49 +0200 Subject: [PATCH 033/146] Fix unintentional error message with dynamic media --- src/client/filecache.cpp | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/client/filecache.cpp b/src/client/filecache.cpp index ffd011866..1f7e605d1 100644 --- a/src/client/filecache.cpp +++ b/src/client/filecache.cpp @@ -38,7 +38,7 @@ void FileCache::createDir() bool FileCache::loadByPath(const std::string &path, std::ostream &os) { - auto fis = open_ifstream(path.c_str(), true); + auto fis = open_ifstream(path.c_str(), false); if (!fis.good()) return false; From f7c14adcbe72de4ab1c5fda9a4eb597ff722c2cd Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20M=C3=BCller?= <34514239+appgurueu@users.noreply.github.com> Date: Wed, 15 May 2024 19:56:25 +0200 Subject: [PATCH 034/146] Fix some clang compiler warnings (#14654) * Fix some clang compiler warnings * Get rid of sdl_supports_primary_selection * Fix draw2DImage hiding overloaded virtual function --- irr/src/COSOperator.cpp | 23 ++++------------------- irr/src/OpenGL/Driver.cpp | 6 ------ irr/src/OpenGL/Driver.h | 2 ++ irr/src/OpenGL/MaterialRenderer.h | 10 +++++----- src/client/mapblock_mesh.cpp | 27 --------------------------- src/client/shadows/dynamicshadows.cpp | 1 - src/collision.cpp | 8 -------- src/gui/guiTable.cpp | 1 - src/irrlicht_changes/static_text.cpp | 1 - src/network/connection_internal.h | 2 +- 10 files changed, 12 insertions(+), 69 deletions(-) diff --git a/irr/src/COSOperator.cpp b/irr/src/COSOperator.cpp index b26463799..d53223916 100644 --- a/irr/src/COSOperator.cpp +++ b/irr/src/COSOperator.cpp @@ -29,18 +29,6 @@ #include "fast_atof.h" -#if defined(_IRR_COMPILE_WITH_SDL_DEVICE_) -static const bool sdl_supports_primary_selection = [] { -#if SDL_VERSION_ATLEAST(2, 25, 0) - SDL_version linked_version; - SDL_GetVersion(&linked_version); - return (linked_version.major == 2 && linked_version.minor >= 25) || linked_version.major > 2; -#else - return false; -#endif -}(); -#endif - namespace irr { @@ -131,8 +119,7 @@ void COSOperator::copyToPrimarySelection(const c8 *text) const #if defined(_IRR_COMPILE_WITH_SDL_DEVICE_) #if SDL_VERSION_ATLEAST(2, 25, 0) - if (sdl_supports_primary_selection) - SDL_SetPrimarySelectionText(text); + SDL_SetPrimarySelectionText(text); #endif #elif defined(_IRR_COMPILE_WITH_X11_DEVICE_) @@ -195,11 +182,9 @@ const c8 *COSOperator::getTextFromPrimarySelection() const { #if defined(_IRR_COMPILE_WITH_SDL_DEVICE_) #if SDL_VERSION_ATLEAST(2, 25, 0) - if (sdl_supports_primary_selection) { - SDL_free(PrimarySelectionText); - PrimarySelectionText = SDL_GetPrimarySelectionText(); - return PrimarySelectionText; - } + SDL_free(PrimarySelectionText); + PrimarySelectionText = SDL_GetPrimarySelectionText(); + return PrimarySelectionText; #endif return 0; diff --git a/irr/src/OpenGL/Driver.cpp b/irr/src/OpenGL/Driver.cpp index bf9334e15..0dd6ea1bb 100644 --- a/irr/src/OpenGL/Driver.cpp +++ b/irr/src/OpenGL/Driver.cpp @@ -704,12 +704,6 @@ IRenderTarget *COpenGL3DriverBase::addRenderTarget() return renderTarget; } -// small helper function to create vertex buffer object adress offsets -static inline u8 *buffer_offset(const long offset) -{ - return ((u8 *)0 + offset); -} - //! draws a vertex primitive list void COpenGL3DriverBase::drawVertexPrimitiveList(const void *vertices, u32 vertexCount, const void *indexList, u32 primitiveCount, diff --git a/irr/src/OpenGL/Driver.h b/irr/src/OpenGL/Driver.h index 80eeaf0da..51554334b 100644 --- a/irr/src/OpenGL/Driver.h +++ b/irr/src/OpenGL/Driver.h @@ -102,6 +102,8 @@ class COpenGL3DriverBase : public CNullDriver, public IMaterialRendererServices, // internally used virtual void draw2DImage(const video::ITexture *texture, u32 layer, bool flip); + using CNullDriver::draw2DImage; + void draw2DImageBatch(const video::ITexture *texture, const core::array> &positions, const core::array> &sourceRects, diff --git a/irr/src/OpenGL/MaterialRenderer.h b/irr/src/OpenGL/MaterialRenderer.h index be74461e3..fa37cfff3 100644 --- a/irr/src/OpenGL/MaterialRenderer.h +++ b/irr/src/OpenGL/MaterialRenderer.h @@ -37,15 +37,15 @@ class COpenGL3MaterialRenderer : public IMaterialRenderer, public IMaterialRende GLuint getProgram() const; virtual void OnSetMaterial(const SMaterial &material, const SMaterial &lastMaterial, - bool resetAllRenderstates, IMaterialRendererServices *services); + bool resetAllRenderstates, IMaterialRendererServices *services) override; - virtual bool OnRender(IMaterialRendererServices *service, E_VERTEX_TYPE vtxtype); + virtual bool OnRender(IMaterialRendererServices *service, E_VERTEX_TYPE vtxtype) override; - virtual void OnUnsetMaterial(); + virtual void OnUnsetMaterial() override; - virtual bool isTransparent() const; + virtual bool isTransparent() const override; - virtual s32 getRenderCapability() const; + virtual s32 getRenderCapability() const override; void setBasicRenderStates(const SMaterial &material, const SMaterial &lastMaterial, bool resetAllRenderstates) override; diff --git a/src/client/mapblock_mesh.cpp b/src/client/mapblock_mesh.cpp index fb2f4aacf..7aca7dfb0 100644 --- a/src/client/mapblock_mesh.cpp +++ b/src/client/mapblock_mesh.cpp @@ -335,32 +335,6 @@ void final_color_blend(video::SColor *result, Mesh generation helpers */ -// This table is moved outside getNodeVertexDirs to avoid the compiler using -// a mutex to initialize this table at runtime right in the hot path. -// For details search the internet for "cxa_guard_acquire". -static const v3s16 vertex_dirs_table[] = { - // ( 1, 0, 0) - v3s16( 1,-1, 1), v3s16( 1,-1,-1), - v3s16( 1, 1,-1), v3s16( 1, 1, 1), - // ( 0, 1, 0) - v3s16( 1, 1,-1), v3s16(-1, 1,-1), - v3s16(-1, 1, 1), v3s16( 1, 1, 1), - // ( 0, 0, 1) - v3s16(-1,-1, 1), v3s16( 1,-1, 1), - v3s16( 1, 1, 1), v3s16(-1, 1, 1), - // invalid - v3s16(), v3s16(), v3s16(), v3s16(), - // ( 0, 0,-1) - v3s16( 1,-1,-1), v3s16(-1,-1,-1), - v3s16(-1, 1,-1), v3s16( 1, 1,-1), - // ( 0,-1, 0) - v3s16( 1,-1, 1), v3s16(-1,-1, 1), - v3s16(-1,-1,-1), v3s16( 1,-1,-1), - // (-1, 0, 0) - v3s16(-1,-1,-1), v3s16(-1,-1, 1), - v3s16(-1, 1, 1), v3s16(-1, 1,-1) -}; - /* Gets nth node tile (0 <= n <= 5). */ @@ -1202,7 +1176,6 @@ video::SColor encode_light(u16 light, u8 emissive_light) u8 get_solid_sides(MeshMakeData *data) { std::unordered_map results; - v3s16 ofs; v3s16 blockpos_nodes = data->m_blockpos * MAP_BLOCKSIZE; const NodeDefManager *ndef = data->nodedef; diff --git a/src/client/shadows/dynamicshadows.cpp b/src/client/shadows/dynamicshadows.cpp index f717cc8b0..11db9bea7 100644 --- a/src/client/shadows/dynamicshadows.cpp +++ b/src/client/shadows/dynamicshadows.cpp @@ -42,7 +42,6 @@ static v3f quantizeDirection(v3f direction, float step) void DirectionalLight::createSplitMatrices(const Camera *cam) { static const float COS_15_DEG = 0.965926f; - v3f newCenter; v3f look = cam->getDirection().normalize(); // if current look direction is < 15 degrees away from the captured diff --git a/src/collision.cpp b/src/collision.cpp index bdfaad55a..ce4898d33 100644 --- a/src/collision.cpp +++ b/src/collision.cpp @@ -221,14 +221,6 @@ bool wouldCollideWithCeiling( return false; } -static inline void getNeighborConnectingFace(const v3s16 &p, - const NodeDefManager *nodedef, Map *map, MapNode n, int v, int *neighbors) -{ - MapNode n2 = map->getNode(p); - if (nodedef->nodeboxConnects(n, n2, v)) - *neighbors |= v; -} - collisionMoveResult collisionMoveSimple(Environment *env, IGameDef *gamedef, f32 pos_max_d, const aabb3f &box_0, f32 stepheight, f32 dtime, diff --git a/src/gui/guiTable.cpp b/src/gui/guiTable.cpp index 530580124..498090413 100644 --- a/src/gui/guiTable.cpp +++ b/src/gui/guiTable.cpp @@ -183,7 +183,6 @@ void GUITable::setTable(const TableOptions &options, } // Handle table options - video::SColor default_color(255, 255, 255, 255); s32 opendepth = 0; for (const Option &option : options) { const std::string &name = option.name; diff --git a/src/irrlicht_changes/static_text.cpp b/src/irrlicht_changes/static_text.cpp index 61f4da5e1..eba397963 100644 --- a/src/irrlicht_changes/static_text.cpp +++ b/src/irrlicht_changes/static_text.cpp @@ -94,7 +94,6 @@ void StaticText::draw() getTextWidth(); } - irr::video::SColor previous_color(255, 255, 255, 255); for (const EnrichedString &str : BrokenText) { if (HAlign == EGUIA_LOWERRIGHT) { diff --git a/src/network/connection_internal.h b/src/network/connection_internal.h index 95f523be2..a528f3fef 100644 --- a/src/network/connection_internal.h +++ b/src/network/connection_internal.h @@ -455,7 +455,7 @@ class UDPPeer final : public Peer void PutReliableSendCommand(ConnectionCommandPtr &c, unsigned int max_packet_size) override; - virtual const Address &getAddress() const { + virtual const Address &getAddress() const override { return address; } From 4aba6e890834a9ada68f69297d815f3d89c1c630 Mon Sep 17 00:00:00 2001 From: ROllerozxa Date: Wed, 15 May 2024 19:56:41 +0200 Subject: [PATCH 035/146] Don't bundle Development Test on macOS --- .github/workflows/macos.yml | 1 + 1 file changed, 1 insertion(+) diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index 24c2b9f51..a7c670ed7 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -55,6 +55,7 @@ jobs: - name: CPack run: | cd build + cmake .. -DINSTALL_DEVTEST=FALSE cpack -G ZIP -B macos - uses: actions/upload-artifact@v4 From ea13e174a0d653bfe57222d36877c00a25d6524f Mon Sep 17 00:00:00 2001 From: AFCMS Date: Wed, 15 May 2024 19:56:50 +0200 Subject: [PATCH 036/146] Fixes to Docker GitHub Actions workflow --- .github/workflows/docker_image.yml | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/.github/workflows/docker_image.yml b/.github/workflows/docker_image.yml index eaac7bf1b..421d2edf4 100644 --- a/.github/workflows/docker_image.yml +++ b/.github/workflows/docker_image.yml @@ -9,7 +9,7 @@ on: push: branches: [ "master" ] # Publish semver tags as releases. - tags: [ "*.*.*" ] + tags: [ "*" ] pull_request: # Build docker image on pull requests. (but do not publish) paths: @@ -25,6 +25,12 @@ on: - '.dockerignore' - '.github/workflows/docker_image.yml' workflow_dispatch: + inputs: + use_cache: + description: "Use build cache" + required: true + type: boolean + default: true env: REGISTRY: ghcr.io @@ -82,6 +88,7 @@ jobs: labels: ${{ steps.meta.outputs.labels }} cache-from: type=gha cache-to: type=gha,mode=max + no-cache: ${{ (github.event_name == 'workflow_dispatch' && !inputs.use_cache) || startsWith(github.ref, 'refs/tags/') }} - name: Test Docker Image run: | From 7ff390519a2ca541b9b8cb59ab5e6228fc872ff9 Mon Sep 17 00:00:00 2001 From: Muhammad Rifqi Priyo Susanto Date: Thu, 16 May 2024 21:00:00 +0700 Subject: [PATCH 037/146] Trivial fix (forget to remove semicolon) in gettext.cpp --- src/gettext.cpp | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/gettext.cpp b/src/gettext.cpp index 58efebb7c..bb99e39bc 100644 --- a/src/gettext.cpp +++ b/src/gettext.cpp @@ -151,10 +151,10 @@ static void MSVC_LocaleWorkaround(int argc, char* argv[]) errorstream << "*******************************************************" << '\n' - << "CMD: " << app_name << '\n'; - << "Failed to restart with current locale: "; - << porting::ConvertError(e) << '\n'; - << "Expect language to be broken!" << '\n'; + << "CMD: " << app_name << '\n' + << "Failed to restart with current locale: " + << porting::ConvertError(e) << '\n' + << "Expect language to be broken!" << '\n' << "*******************************************************" << std::endl; } } From 97d8802183c03ec150d6540bd57836c4a4595501 Mon Sep 17 00:00:00 2001 From: grorp Date: Fri, 17 May 2024 13:27:31 +0200 Subject: [PATCH 038/146] Enable RTTI for Irrlicht on MSVC (#14677) --- irr/src/CMakeLists.txt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/irr/src/CMakeLists.txt b/irr/src/CMakeLists.txt index 10a841669..daff63bc2 100644 --- a/irr/src/CMakeLists.txt +++ b/irr/src/CMakeLists.txt @@ -31,7 +31,7 @@ if(CMAKE_CXX_COMPILER_ID MATCHES "^(GNU|Clang|AppleClang)$") elseif(MSVC) string(APPEND CMAKE_CXX_STANDARD_LIBRARIES " msvcrt.lib") # ???? fuck off - add_compile_options(/GR- /Zl) + add_compile_options(/Zl) # Enable SSE for floating point math on 32-bit x86 by default # reasoning see minetest issue #11810 and https://gcc.gnu.org/wiki/FloatingPointMath From d0f9126ddee9a0773cf13dd3c5316f7bf0116847 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Fri, 17 May 2024 13:00:41 +0200 Subject: [PATCH 039/146] Fix wrong collision axis in Lua moveresult introduced by c24a04d246221e13a626f4a4dd34037a5332a935 --- builtin/game/misc_s.lua | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/builtin/game/misc_s.lua b/builtin/game/misc_s.lua index 19708983a..ab15b230e 100644 --- a/builtin/game/misc_s.lua +++ b/builtin/game/misc_s.lua @@ -110,7 +110,7 @@ if core.set_push_moveresult1 then standing_on_object = b2, collisions = {{ type = "node", - axis = AXES[axis], + axis = AXES[axis + 1], node_pos = vector.new(npx, npy, npz), new_pos = vector.new(v0x, v0y, v0z), old_velocity = vector.new(v1x, v1y, v1z), From 649b458800757384b44372bfd4550fdedaf3b529 Mon Sep 17 00:00:00 2001 From: OgelGames Date: Tue, 21 May 2024 04:36:44 +1000 Subject: [PATCH 040/146] Restore vector type check (#14663) --- src/script/common/c_converter.cpp | 1 + 1 file changed, 1 insertion(+) diff --git a/src/script/common/c_converter.cpp b/src/script/common/c_converter.cpp index 446f88f3d..f0511d586 100644 --- a/src/script/common/c_converter.cpp +++ b/src/script/common/c_converter.cpp @@ -58,6 +58,7 @@ extern "C" { */ static void read_v3_aux(lua_State *L, int index) { + CHECK_POS_TAB(index); lua_pushvalue(L, index); lua_rawgeti(L, LUA_REGISTRYINDEX, CUSTOM_RIDX_READ_VECTOR); lua_insert(L, -2); From fc187af56b680c3ef4bd38ce917aa5ee94149b41 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20M=C3=BCller?= <34514239+appgurueu@users.noreply.github.com> Date: Mon, 20 May 2024 20:37:40 +0200 Subject: [PATCH 041/146] Allow removal of item definition fields (#14675) --- builtin/game/register.lua | 5 ++++- doc/lua_api.md | 10 ++++++++-- 2 files changed, 12 insertions(+), 3 deletions(-) diff --git a/builtin/game/register.lua b/builtin/game/register.lua index 4ecf6ec71..76cb72f87 100644 --- a/builtin/game/register.lua +++ b/builtin/game/register.lua @@ -404,7 +404,7 @@ core.register_item(":", { }) -function core.override_item(name, redefinition) +function core.override_item(name, redefinition, del_fields) if redefinition.name ~= nil then error("Attempt to redefine name of "..name.." to "..dump(redefinition.name), 2) end @@ -418,6 +418,9 @@ function core.override_item(name, redefinition) for k, v in pairs(redefinition) do rawset(item, k, v) end + for _, field in ipairs(del_fields or {}) do + rawset(item, field, nil) + end register_item_raw(item) end diff --git a/doc/lua_api.md b/doc/lua_api.md index eb7a294e7..f6db17faa 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -5613,11 +5613,17 @@ Call these functions only at load time! * `minetest.register_node(name, node definition)` * `minetest.register_craftitem(name, item definition)` * `minetest.register_tool(name, item definition)` -* `minetest.override_item(name, redefinition)` +* `minetest.override_item(name, redefinition, del_fields)` + * `redefinition` is a table of fields `[name] = new_value`, + overwriting fields of or adding fields to the existing definition. + * `del_fields` is a list of field names to be set + to `nil` ("deleted from") the original definition. * Overrides fields of an item registered with register_node/tool/craftitem. * Note: Item must already be defined, (opt)depend on the mod defining it. * Example: `minetest.override_item("default:mese", - {light_source=minetest.LIGHT_MAX})` + {light_source=minetest.LIGHT_MAX}, {"sounds"})`: + Overwrites the `light_source` field, + removes the sounds from the definition of the mese block. * `minetest.unregister_item(name)` * Unregisters the item from the engine, and deletes the entry with key `name` from `minetest.registered_items` and from the associated item table From 70a5e2ee9aa48b6b11652d18eb96ae10b855fcbb Mon Sep 17 00:00:00 2001 From: sfan5 Date: Mon, 20 May 2024 20:37:52 +0200 Subject: [PATCH 042/146] Update SDL for mingw builds (#14678) --- util/buildbot/common.sh | 2 +- util/buildbot/sha256sums.txt | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/util/buildbot/common.sh b/util/buildbot/common.sh index 9da5e8df8..709c44e77 100644 --- a/util/buildbot/common.sh +++ b/util/buildbot/common.sh @@ -15,7 +15,7 @@ zlib_version=1.3.1 zstd_version=1.5.5 libjpeg_version=3.0.1 libpng_version=1.6.40 -sdl2_version=2.28.5 +sdl2_version=2.30.3 download () { local url=$1 diff --git a/util/buildbot/sha256sums.txt b/util/buildbot/sha256sums.txt index fce73a0a9..0587e6ea1 100644 --- a/util/buildbot/sha256sums.txt +++ b/util/buildbot/sha256sums.txt @@ -19,8 +19,8 @@ da6ad10632cf172992158e9ea0977a87914b5d5de93a972c3430b6a412237556 luajit-2024012 2b1dabe83d478b398cf9226d96de7fa62c973365c4aea70d27ba5782fb49d2d0 luajit-20240125-win64.zip e2443451fe5c2066eb564c64b8a1762738a88b7fd749c8b5907fed45c785497b openal-soft-1.23.1-win32.zip cb041445a118469caefbad2647470cb8571c8337bce2adc07634011ab5625417 openal-soft-1.23.1-win64.zip -f9f890af960e92fd3f532f2e9ac00681c33bc67a722e000dfdaeb41b0064f1a0 sdl2-2.28.5-win32.zip -8dde2c6963544b7d8a2e87c128ebbdf51ad0e70c7e2df986ff4e963ce9996d9b sdl2-2.28.5-win64.zip +574e0847e622ff09ab23e2b22b77685a2ab6ee43de3e2932f3e8a14a4d7b9338 sdl2-2.30.3-win32.zip +6127afdfc7b6a4ade8caf9a7267748ffea974f729866dd5be96c7a69d2f0fee7 sdl2-2.30.3-win64.zip 326701086a0ed66e09a9f3ec4d971654c13b6bd79cfdd079c947ecdcd6409525 sqlite3-3.44.2-win32.zip b2d474e3625f8f426b6cc5c0ecac831a1de46f7d1027bf4a9f6267b0b0411d42 sqlite3-3.44.2-win64.zip 8af10515d57dbfee5d2106cd66cafa2adeb4270d4c6047ccbf7e8b5d2d50681c zlib-1.3.1-win32.zip From 834b6ca90039a398cfacd181f091ca3a0a75e8e6 Mon Sep 17 00:00:00 2001 From: sfence Date: Tue, 21 May 2024 15:38:12 +0200 Subject: [PATCH 043/146] Fix inventory: Quickly picking up item and placing it again no longer works (#14657) --- src/gui/guiFormSpecMenu.cpp | 11 ++++++----- 1 file changed, 6 insertions(+), 5 deletions(-) diff --git a/src/gui/guiFormSpecMenu.cpp b/src/gui/guiFormSpecMenu.cpp index 577e2ee68..2e54b32dc 100644 --- a/src/gui/guiFormSpecMenu.cpp +++ b/src/gui/guiFormSpecMenu.cpp @@ -4625,11 +4625,6 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) if (!m_left_dragging) break; - // Abort left-dragging - m_left_dragging = false; - m_client->inhibit_inventory_revert = false; - m_left_drag_stacks.clear(); - // Both the selected item and the hovered item need to be checked // because we don't know exactly when the double-click happened ItemStack slct; @@ -4658,6 +4653,12 @@ bool GUIFormSpecMenu::OnEvent(const SEvent& event) } if (amount > 0) { + if (m_left_dragging) { + // Abort left-dragging + m_left_dragging = false; + m_client->inhibit_inventory_revert = false; + m_left_drag_stacks.clear(); + } IMoveAction *a = new IMoveAction(); a->count = amount; a->from_inv = s.inventoryloc; From 0d0da58b92c5d43ea68b63fd10540b66254f35b7 Mon Sep 17 00:00:00 2001 From: Lars Mueller Date: Tue, 21 May 2024 15:22:29 +0200 Subject: [PATCH 044/146] Add unit test and feature flag for override_item --- builtin/game/features.lua | 1 + doc/lua_api.md | 2 ++ games/devtest/mods/unittests/misc.lua | 12 ++++++++++++ 3 files changed, 15 insertions(+) diff --git a/builtin/game/features.lua b/builtin/game/features.lua index 051d2aa2a..22bf1859d 100644 --- a/builtin/game/features.lua +++ b/builtin/game/features.lua @@ -41,6 +41,7 @@ core.features = { item_meta_range = true, node_interaction_actor = true, moveresult_new_pos = true, + override_item_remove_fields = true, } function core.has_feature(arg) diff --git a/doc/lua_api.md b/doc/lua_api.md index f6db17faa..7331f0a93 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -5444,6 +5444,8 @@ Utilities node_interaction_actor = true, -- "new_pos" field in entity moveresult (5.9.0) moveresult_new_pos = true, + -- Allow removing definition fields in `minetest.override_item` + override_item_remove_fields = true, } ``` diff --git a/games/devtest/mods/unittests/misc.lua b/games/devtest/mods/unittests/misc.lua index 6ff5c7e84..4bd8002b9 100644 --- a/games/devtest/mods/unittests/misc.lua +++ b/games/devtest/mods/unittests/misc.lua @@ -254,3 +254,15 @@ local function test_gennotify_api() assert(#custom == 0, "custom ids not empty") end unittests.register("test_gennotify_api", test_gennotify_api) + +unittests.register("test_item_registration", function() + local itemname = "unittests:test_override_item" + minetest.register_item(itemname, {description = "foo"}) + assert(assert(minetest.registered_items[itemname]).description == "foo") + minetest.override_item(itemname, {description = "bar"}) + assert(assert(minetest.registered_items[itemname]).description == "bar") + minetest.override_item(itemname, {}, {"description"}) + assert(assert(minetest.registered_items[itemname]).description == nil) + minetest.unregister_item("unittests:test_override_item") + assert(minetest.registered_items["unittests:test_override_item"] == nil) +end) From a91ab8aab5b6c7dd951cca13d8976c347656af3f Mon Sep 17 00:00:00 2001 From: Lars Mueller Date: Tue, 21 May 2024 16:27:38 +0200 Subject: [PATCH 045/146] Fix broken unit test Also makes devtest unit test results a bit more prominent --- games/devtest/mods/unittests/init.lua | 28 ++++++++++++++++++---- games/devtest/mods/unittests/load_time.lua | 13 ++++++++++ games/devtest/mods/unittests/misc.lua | 12 ---------- 3 files changed, 37 insertions(+), 16 deletions(-) create mode 100644 games/devtest/mods/unittests/load_time.lua diff --git a/games/devtest/mods/unittests/init.lua b/games/devtest/mods/unittests/init.lua index 6b2cd7a79..1499ccacb 100644 --- a/games/devtest/mods/unittests/init.lua +++ b/games/devtest/mods/unittests/init.lua @@ -160,7 +160,7 @@ function unittests.run_all() -- Print stats assert(#unittests.list == counters.total) print(string.rep("+", 80)) - print(string.format("Unit Test Results: %s", + print(string.format("Devtest Unit Test Results: %s", counters.total == counters.passed and "PASSED" or "FAILED")) print(string.format(" %d / %d failed tests.", counters.total - counters.passed, counters.total)) @@ -185,22 +185,42 @@ dofile(modpath .. "/content_ids.lua") dofile(modpath .. "/metadata.lua") dofile(modpath .. "/raycast.lua") dofile(modpath .. "/inventory.lua") +dofile(modpath .. "/load_time.lua") -------------- +local function send_results(name, ok) + core.chat_send_player(name, + minetest.colorize(ok and "green" or "red", + (ok and "All devtest unit tests passed." or + "There were devtest unit test failures.") .. + " Check the console for detailed output.")) +end + if core.settings:get_bool("devtest_unittests_autostart", false) then + local test_results = nil core.after(0, function() + unittests.on_finished = function(ok) + for _, player in ipairs(minetest.get_connected_players()) do + send_results(player:get_player_name(), ok) + end + test_results = ok + end coroutine.wrap(unittests.run_all)() end) + minetest.register_on_joinplayer(function(player) + if test_results == nil then + return -- tests haven't completed yet + end + send_results(player:get_player_name(), test_results) + end) else core.register_chatcommand("unittests", { privs = {basic_privs=true}, description = "Runs devtest unittests (may modify player or map state)", func = function(name, param) unittests.on_finished = function(ok) - core.chat_send_player(name, - (ok and "All tests passed." or "There were test failures.") .. - " Check the console for detailed output.") + send_results(name, ok) end coroutine.wrap(unittests.run_all)() return true, "" diff --git a/games/devtest/mods/unittests/load_time.lua b/games/devtest/mods/unittests/load_time.lua new file mode 100644 index 000000000..d437fba06 --- /dev/null +++ b/games/devtest/mods/unittests/load_time.lua @@ -0,0 +1,13 @@ +-- Test item (un)registration and overriding +do + local itemname = "unittests:test_override_item" + minetest.register_craftitem(":" .. itemname, {description = "foo"}) + assert(assert(minetest.registered_items[itemname]).description == "foo") + minetest.override_item(itemname, {description = "bar"}) + assert(assert(minetest.registered_items[itemname]).description == "bar") + minetest.override_item(itemname, {}, {"description"}) + -- description has the empty string as a default + assert(assert(minetest.registered_items[itemname]).description == "") + minetest.unregister_item("unittests:test_override_item") + assert(minetest.registered_items["unittests:test_override_item"] == nil) +end diff --git a/games/devtest/mods/unittests/misc.lua b/games/devtest/mods/unittests/misc.lua index 4bd8002b9..6ff5c7e84 100644 --- a/games/devtest/mods/unittests/misc.lua +++ b/games/devtest/mods/unittests/misc.lua @@ -254,15 +254,3 @@ local function test_gennotify_api() assert(#custom == 0, "custom ids not empty") end unittests.register("test_gennotify_api", test_gennotify_api) - -unittests.register("test_item_registration", function() - local itemname = "unittests:test_override_item" - minetest.register_item(itemname, {description = "foo"}) - assert(assert(minetest.registered_items[itemname]).description == "foo") - minetest.override_item(itemname, {description = "bar"}) - assert(assert(minetest.registered_items[itemname]).description == "bar") - minetest.override_item(itemname, {}, {"description"}) - assert(assert(minetest.registered_items[itemname]).description == nil) - minetest.unregister_item("unittests:test_override_item") - assert(minetest.registered_items["unittests:test_override_item"] == nil) -end) From 7e8b1adc767474d99f8723b57456975c91806b81 Mon Sep 17 00:00:00 2001 From: Lars Mueller Date: Tue, 21 May 2024 17:32:58 +0200 Subject: [PATCH 046/146] Fix broken CI --- games/devtest/mods/unittests/init.lua | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/games/devtest/mods/unittests/init.lua b/games/devtest/mods/unittests/init.lua index 1499ccacb..47568d9fc 100644 --- a/games/devtest/mods/unittests/init.lua +++ b/games/devtest/mods/unittests/init.lua @@ -200,11 +200,15 @@ end if core.settings:get_bool("devtest_unittests_autostart", false) then local test_results = nil core.after(0, function() + -- CI adds a mod which sets `unittests.on_finished` + -- to write status information to the filesystem + local old_on_finished = unittests.on_finished unittests.on_finished = function(ok) for _, player in ipairs(minetest.get_connected_players()) do send_results(player:get_player_name(), ok) end test_results = ok + old_on_finished(ok) end coroutine.wrap(unittests.run_all)() end) From 613901c0410187ffcc67d68f25a7de56a2e5ce4d Mon Sep 17 00:00:00 2001 From: Xeno333 <149852758+Xeno333@users.noreply.github.com> Date: Wed, 22 May 2024 09:46:05 -0500 Subject: [PATCH 047/146] Rename "opaque_water" setting to "translucent_liquids" with inverted meaning (#14660) The old setting will be migrated properly. --------- Co-authored-by: grorp --- builtin/settingtypes.txt | 4 ++-- src/defaultsettings.cpp | 2 +- src/main.cpp | 3 +++ src/migratesettings.h | 14 ++++++++++++++ src/nodedef.cpp | 8 ++++---- src/nodedef.h | 2 +- 6 files changed, 25 insertions(+), 8 deletions(-) create mode 100644 src/migratesettings.h diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index a4c4b5891..a215a6dd8 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -283,8 +283,8 @@ undersampling (Undersampling) int 1 1 8 [**Graphics Effects] -# Makes all liquids opaque -opaque_water (Opaque liquids) bool false +# Allows liquids to be translucent. +translucent_liquids (Translucent liquids) bool true # Leaves style: # - Fancy: all faces visible diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index bbd5fd91b..4e004325f 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -385,7 +385,7 @@ void set_default_settings() settings->setDefault("enable_3d_clouds", "true"); settings->setDefault("cloud_radius", "12"); settings->setDefault("menu_clouds", "true"); - settings->setDefault("opaque_water", "false"); + settings->setDefault("translucent_liquids", "true"); settings->setDefault("console_height", "0.6"); settings->setDefault("console_color", "(0,0,0)"); settings->setDefault("console_alpha", "200"); diff --git a/src/main.cpp b/src/main.cpp index d693a6b5c..7f078d9b2 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -28,6 +28,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "filesys.h" #include "version.h" #include "defaultsettings.h" +#include "migratesettings.h" #include "gettext.h" #include "log.h" #include "util/quicktune.h" @@ -687,6 +688,8 @@ static bool init_common(const Settings &cmd_args, int argc, char *argv[]) if (!read_config_file(cmd_args)) return false; + migrate_settings(); + init_log_streams(cmd_args); // Initialize random seed diff --git a/src/migratesettings.h b/src/migratesettings.h new file mode 100644 index 000000000..60435f18a --- /dev/null +++ b/src/migratesettings.h @@ -0,0 +1,14 @@ +// Minetest +// SPDX-License-Identifier: LGPL-2.1-or-later + +#include "settings.h" + +void migrate_settings() +{ + // Converts opaque_water to translucent_liquids + if (g_settings->existsLocal("opaque_water")) { + g_settings->set("translucent_liquids", + g_settings->getBool("opaque_water") ? "false" : "true"); + g_settings->remove("opaque_water"); + } +} diff --git a/src/nodedef.cpp b/src/nodedef.cpp index 407996aed..5cbe80e31 100644 --- a/src/nodedef.cpp +++ b/src/nodedef.cpp @@ -283,7 +283,7 @@ void TileDef::deSerialize(std::istream &is, NodeDrawType drawtype, u16 protocol_ void TextureSettings::readSettings() { connected_glass = g_settings->getBool("connected_glass"); - opaque_water = g_settings->getBool("opaque_water"); + translucent_liquids = g_settings->getBool("translucent_liquids"); bool smooth_lighting = g_settings->getBool("smooth_lighting"); enable_mesh_cache = g_settings->getBool("enable_mesh_cache"); enable_minimap = g_settings->getBool("enable_minimap"); @@ -707,7 +707,7 @@ void ContentFeatures::deSerialize(std::istream &is, u16 protocol_version) if (is.eof()) throw SerializationError(""); post_effect_color_shaded = tmp; - } catch(SerializationError &e) {}; + } catch (SerializationError &e) {}; } #ifndef SERVER @@ -849,14 +849,14 @@ void ContentFeatures::updateTextures(ITextureSource *tsrc, IShaderSource *shdsrc solidness = 0; break; case NDT_LIQUID: - if (tsettings.opaque_water) + if (!tsettings.translucent_liquids) alpha = ALPHAMODE_OPAQUE; solidness = 1; is_liquid = true; break; case NDT_FLOWINGLIQUID: solidness = 0; - if (tsettings.opaque_water) + if (!tsettings.translucent_liquids) alpha = ALPHAMODE_OPAQUE; is_liquid = true; break; diff --git a/src/nodedef.h b/src/nodedef.h index 500ca011b..72b4479d0 100644 --- a/src/nodedef.h +++ b/src/nodedef.h @@ -184,7 +184,7 @@ class TextureSettings { WorldAlignMode world_aligned_mode; AutoScale autoscale_mode; int node_texture_size; - bool opaque_water; + bool translucent_liquids; bool connected_glass; bool enable_mesh_cache; bool enable_minimap; From 9e2b3f2faacedd655a653b228c60ea37cece9e47 Mon Sep 17 00:00:00 2001 From: JosiahWI <41302989+JosiahWI@users.noreply.github.com> Date: Wed, 22 May 2024 11:39:53 -0500 Subject: [PATCH 048/146] Upgrade client active object mgr tests to Catch2 (#14565) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Upgrade client active object mgr tests to Catch2 In addition to invoking Catch2's test runner after Minetest's homemade runner, this refactors the tests to follow the DRY principle, and gives them expressive names and clear assertions. Catch2 is already bundled with Minetest, so there are no added dependencies. * Increment failed modules count for Catch2 tests * Respect --test-module option for Catch2 tests * Improve Catch2 --test-module behavior This switches infostream to rawstream so that test runner output is displayed, and returns the correct boolean depending on the results. The tests are now found by setting the configuration instead of invoking the command line parser. * Test uniqueness of all IDS instead of just one Co-Authored-By: Lars Müller * Include Catch2 test run in timing and logging * Flush std::cout after printing Catch results * Increment total tests run instead of hardcoding to 1 * Flush stderr before printing to stdout It's necessary to flush stderr before printing to stdout in adition to flushing stdout before printing to stderr, to make sure all output is ordered correctly. * Make Catch write to rawstream --------- Co-authored-by: Lars Müller --- CMakeLists.txt | 2 +- lib/catch2/CMakeLists.txt | 3 +- src/CMakeLists.txt | 8 +- src/activeobjectmgr.h | 1 - src/unittest/test.cpp | 37 +++++- src/unittest/test_clientactiveobjectmgr.cpp | 127 ++++++++++---------- 6 files changed, 105 insertions(+), 73 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b8254db7f..4c6c60ba9 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -278,7 +278,7 @@ if(NOT USE_LUAJIT) add_subdirectory(lib/bitop) endif() -if(BUILD_BENCHMARKS) +if(BUILD_UNITTESTS OR BUILD_BENCHMARKS) add_subdirectory(lib/catch2) endif() diff --git a/lib/catch2/CMakeLists.txt b/lib/catch2/CMakeLists.txt index 3c471d49d..077272571 100644 --- a/lib/catch2/CMakeLists.txt +++ b/lib/catch2/CMakeLists.txt @@ -13,4 +13,5 @@ # + return os << std::fixed << duration.value() << ' ' << duration.unitsAsString(); add_library(catch2 INTERFACE) -target_include_directories(catch2 INTERFACE ${CMAKE_CURRENT_SOURCE_DIR}) +add_library(Catch2::Catch2 ALIAS catch2) +target_include_directories(catch2 INTERFACE "${CMAKE_CURRENT_SOURCE_DIR}") diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bad7e2afb..6ea544980 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -615,8 +615,8 @@ if(BUILD_CLIENT) if (USE_SPATIAL) target_link_libraries(${PROJECT_NAME} ${SPATIAL_LIBRARY}) endif() - if(BUILD_BENCHMARKS) - target_link_libraries(${PROJECT_NAME} catch2) + if(BUILD_UNITTESTS OR BUILD_BENCHMARKS) + target_link_libraries(${PROJECT_NAME} Catch2::Catch2) endif() endif(BUILD_CLIENT) @@ -677,8 +677,8 @@ if(BUILD_SERVER) ${CURL_LIBRARY} ) endif() - if(BUILD_BENCHMARKS) - target_link_libraries(${PROJECT_NAME}server catch2) + if(BUILD_UNITTESTS OR BUILD_BENCHMARKS) + target_link_libraries(${PROJECT_NAME}server Catch2::Catch2) endif() endif(BUILD_SERVER) diff --git a/src/activeobjectmgr.h b/src/activeobjectmgr.h index 0205aee85..369e8acf5 100644 --- a/src/activeobjectmgr.h +++ b/src/activeobjectmgr.h @@ -31,7 +31,6 @@ class TestServerActiveObjectMgr; template class ActiveObjectMgr { - friend class ::TestClientActiveObjectMgr; friend class ::TestServerActiveObjectMgr; public: diff --git a/src/unittest/test.cpp b/src/unittest/test.cpp index 7141ee0c6..2fbba5e4a 100644 --- a/src/unittest/test.cpp +++ b/src/unittest/test.cpp @@ -17,6 +17,11 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#define CATCH_CONFIG_RUNNER +// we want to have catch write to rawstream (stderr) instead of stdout +#define CATCH_CONFIG_NOSTDOUT +#include + #include "test.h" #include "nodedef.h" @@ -27,6 +32,21 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "porting.h" #include "debug.h" +#include + +// make catch write everything to rawstream +namespace Catch { + std::ostream& cout() { + return rawstream; + } + std::ostream& clog() { + return rawstream; + } + std::ostream& cerr() { + return rawstream; + } +} + content_t t_CONTENT_STONE; content_t t_CONTENT_GRASS; content_t t_CONTENT_TORCH; @@ -234,6 +254,16 @@ bool run_tests() num_total_tests_run += testmod->num_tests_run; } + rawstream << "Catch test results: " << std::endl; + auto num_catch_tests_failed = Catch::Session().run(); + // We count the all the Catch tests as one test for Minetest's own logging + // because we don't have a way to tell how many individual tests Catch ran. + ++num_total_tests_run; + if (num_catch_tests_failed > 0) { + ++num_modules_failed; + ++num_total_tests_failed; + } + u64 tdiff = porting::getTimeMs() - t1; g_logger.setLevelSilenced(LL_ERROR, false); @@ -269,8 +299,11 @@ bool run_tests(const std::string &module_name) auto testmod = findTestModule(module_name); if (!testmod) { - errorstream << "Test module not found: " << module_name << std::endl; - return 1; + rawstream << "Did not find module, searching Catch tests: " << module_name << std::endl; + Catch::Session session; + session.configData().testsOrTags.push_back(module_name); + auto catch_test_failures = session.run(); + return catch_test_failures == 0; } g_logger.setLevelSilenced(LL_ERROR, true); diff --git a/src/unittest/test_clientactiveobjectmgr.cpp b/src/unittest/test_clientactiveobjectmgr.cpp index d3cd83dc9..2df099fb1 100644 --- a/src/unittest/test_clientactiveobjectmgr.cpp +++ b/src/unittest/test_clientactiveobjectmgr.cpp @@ -17,11 +17,14 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ -#include "client/activeobjectmgr.h" -#include #include "test.h" -#include "profiler.h" +#include "client/activeobjectmgr.h" + +#include + +#include +#include class TestClientActiveObject : public ClientActiveObject { @@ -58,9 +61,6 @@ class TestClientActiveObjectMgr : public TestBase void runTests(IGameDef *gamedef); - void testFreeID(); - void testRegisterObject(); - void testRemoveObject(); void testGetActiveSelectableObjects(); }; @@ -68,75 +68,74 @@ static TestClientActiveObjectMgr g_test_instance; void TestClientActiveObjectMgr::runTests(IGameDef *gamedef) { - TEST(testFreeID); - TEST(testRegisterObject) - TEST(testRemoveObject) TEST(testGetActiveSelectableObjects) } //////////////////////////////////////////////////////////////////////////////// -void TestClientActiveObjectMgr::testFreeID() -{ - client::ActiveObjectMgr caomgr; - std::vector aoids; - - u16 aoid = caomgr.getFreeId(); - // Ensure it's not the same id - UASSERT(caomgr.getFreeId() != aoid); - - aoids.push_back(aoid); - - // Register basic objects, ensure we never found - for (u8 i = 0; i < UINT8_MAX; i++) { - // Register an object - auto tcao_u = std::make_unique(); - auto tcao = tcao_u.get(); - caomgr.registerObject(std::move(tcao_u)); - aoids.push_back(tcao->getId()); - - // Ensure next id is not in registered list - UASSERT(std::find(aoids.begin(), aoids.end(), caomgr.getFreeId()) == - aoids.end()); - } - - caomgr.clear(); +static TestClientActiveObject* register_default_test_object( + client::ActiveObjectMgr &caomgr) { + auto test_obj = std::make_unique(); + auto result = test_obj.get(); + REQUIRE(caomgr.registerObject(std::move(test_obj))); + return result; } -void TestClientActiveObjectMgr::testRegisterObject() +TEST_CASE("test client active object manager") { client::ActiveObjectMgr caomgr; - auto tcao_u = std::make_unique(); - auto tcao = tcao_u.get(); - UASSERT(caomgr.registerObject(std::move(tcao_u))); - - u16 id = tcao->getId(); - - auto tcaoToCompare = caomgr.getActiveObject(id); - UASSERT(tcaoToCompare->getId() == id); - UASSERT(tcaoToCompare == tcao); - - tcao_u = std::make_unique(); - tcao = tcao_u.get(); - UASSERT(caomgr.registerObject(std::move(tcao_u))); - UASSERT(caomgr.getActiveObject(tcao->getId()) == tcao); - UASSERT(caomgr.getActiveObject(tcao->getId()) != tcaoToCompare); - - caomgr.clear(); -} - -void TestClientActiveObjectMgr::testRemoveObject() -{ - client::ActiveObjectMgr caomgr; - auto tcao_u = std::make_unique(); - auto tcao = tcao_u.get(); - UASSERT(caomgr.registerObject(std::move(tcao_u))); + auto tcao1 = register_default_test_object(caomgr); + + SECTION("When we register many client objects, " + "then all the assigned IDs should be unique.") + { + // This should be enough rounds to be pretty confident + // there are no duplicates. + u16 n = 255; + std::unordered_set ids; + ids.insert(tcao1->getId()); + for (u16 i = 0; i < n; ++i) { + auto other_tcao = register_default_test_object(caomgr); + ids.insert(other_tcao->getId()); + } + // n added objects & tcao1 + CHECK(n + 1 == static_cast(ids.size())); + } - u16 id = tcao->getId(); - UASSERT(caomgr.getActiveObject(id) != nullptr) + SECTION("two registered objects") + { + auto tcao2 = register_default_test_object(caomgr); + auto tcao2_id = tcao2->getId(); + + auto obj1 = caomgr.getActiveObject(tcao1->getId()); + REQUIRE(obj1 != nullptr); + + auto obj2 = caomgr.getActiveObject(tcao2_id); + REQUIRE(obj2 != nullptr); + + SECTION("When we query an object by its ID, " + "then we should get back an object with that ID.") + { + CHECK(obj1->getId() == tcao1->getId()); + CHECK(obj2->getId() == tcao2->getId()); + } + + SECTION("When we register and query for an object, " + "its memory location should not have changed.") + { + CHECK(obj1 == tcao1); + CHECK(obj2 == tcao2); + } + } - caomgr.removeObject(tcao->getId()); - UASSERT(caomgr.getActiveObject(id) == nullptr) + SECTION("Given an object has been removed, " + "when we query for it, " + "then we should get nullptr.") + { + auto id = tcao1->getId(); + caomgr.removeObject(tcao1->getId()); // may invalidate tcao1 + CHECK(caomgr.getActiveObject(id) == nullptr); + } caomgr.clear(); } From 3d29035328c4d1960b0d658952546733d1f67308 Mon Sep 17 00:00:00 2001 From: grorp Date: Thu, 23 May 2024 20:36:01 +0200 Subject: [PATCH 049/146] Enable "FULL" debug info for Android build (#14684) --- android/native/build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/android/native/build.gradle b/android/native/build.gradle index 705d14f82..96a7739c0 100644 --- a/android/native/build.gradle +++ b/android/native/build.gradle @@ -36,7 +36,7 @@ android { buildTypes { release { ndk { - debugSymbolLevel 'SYMBOL_TABLE' + debugSymbolLevel 'FULL' } } } From 7adcbe8a1486dab160b68dbbc115397752715b70 Mon Sep 17 00:00:00 2001 From: grorp Date: Thu, 23 May 2024 20:36:13 +0200 Subject: [PATCH 050/146] Fix artifacts with bloom + tonemapping on ogles2 (#14688) --- client/shaders/second_stage/opengl_fragment.glsl | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/client/shaders/second_stage/opengl_fragment.glsl b/client/shaders/second_stage/opengl_fragment.glsl index 3b0d4c844..0a73aa393 100644 --- a/client/shaders/second_stage/opengl_fragment.glsl +++ b/client/shaders/second_stage/opengl_fragment.glsl @@ -61,7 +61,8 @@ vec4 applyBloom(vec4 color, vec2 uv) equation used: ((x * (A * x + C * B) + D * E) / (x * (A * x + B) + D * F)) - E / F */ -vec3 uncharted2Tonemap(vec3 x) +// see https://github.com/minetest/minetest/pull/14688 +highp vec3 uncharted2Tonemap(highp vec3 x) { return ((x * (0.22 * x + 0.03) + 0.002) / (x * (0.22 * x + 0.3) + 0.06)) - 0.03333; } From 7dfda4f6f01722cb0d1c8e7fab2191d572a3bd3a Mon Sep 17 00:00:00 2001 From: grorp Date: Fri, 24 May 2024 12:10:19 +0200 Subject: [PATCH 051/146] Consistent width for internal scrollbars of formspec elements (#14689) also: Make sure that very short, wide scrollbars are still usable --- src/client/clientlauncher.cpp | 2 +- src/gui/guiScrollBar.cpp | 4 ++-- src/gui/guiTable.cpp | 2 +- 3 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/client/clientlauncher.cpp b/src/client/clientlauncher.cpp index 52fadb192..da8118a7e 100644 --- a/src/client/clientlauncher.cpp +++ b/src/client/clientlauncher.cpp @@ -343,7 +343,7 @@ void ClientLauncher::init_guienv(gui::IGUIEnvironment *guienv) float density = rangelim(g_settings->getFloat("gui_scaling"), 0.5f, 20) * RenderingEngine::getDisplayDensity(); skin->setSize(gui::EGDS_CHECK_BOX_WIDTH, (s32)(17.0f * density)); - skin->setSize(gui::EGDS_SCROLLBAR_SIZE, (s32)(14.0f * density)); + skin->setSize(gui::EGDS_SCROLLBAR_SIZE, (s32)(21.0f * density)); skin->setSize(gui::EGDS_WINDOW_BUTTON_WIDTH, (s32)(15.0f * density)); if (density > 1.5f) { std::string sprite_path = porting::path_share + "/textures/base/pack/"; diff --git a/src/gui/guiScrollBar.cpp b/src/gui/guiScrollBar.cpp index 88f101a2b..8e8935b2d 100644 --- a/src/gui/guiScrollBar.cpp +++ b/src/gui/guiScrollBar.cpp @@ -274,10 +274,10 @@ void GUIScrollBar::setPosRaw(const s32 &pos) s32 thumb_min = 0; if (is_horizontal) { - thumb_min = RelativeRect.getHeight(); + thumb_min = std::min(RelativeRect.getHeight(), RelativeRect.getWidth() / 2); thumb_area = RelativeRect.getWidth() - border_size * 2; } else { - thumb_min = RelativeRect.getWidth(); + thumb_min = std::min(RelativeRect.getWidth(), RelativeRect.getHeight() / 2); thumb_area = RelativeRect.getHeight() - border_size * 2; } diff --git a/src/gui/guiTable.cpp b/src/gui/guiTable.cpp index 498090413..93a56ed3c 100644 --- a/src/gui/guiTable.cpp +++ b/src/gui/guiTable.cpp @@ -60,7 +60,7 @@ GUITable::GUITable(gui::IGUIEnvironment *env, m_rowheight = MYMAX(m_rowheight, 1); } - const s32 s = skin->getSize(gui::EGDS_SCROLLBAR_SIZE) * 1.5f; + const s32 s = skin->getSize(gui::EGDS_SCROLLBAR_SIZE); m_scrollbar = new GUIScrollBar(Environment, this, -1, core::rect(RelativeRect.getWidth() - s, 0, From 3fc7a35567e8a5d79126ade9529ea08e8ef1095c Mon Sep 17 00:00:00 2001 From: AFCMS Date: Sun, 26 May 2024 14:28:12 +0200 Subject: [PATCH 052/146] Add documentation for IDE developer setup (#13935) --- .gitignore | 9 +- .vscode/extensions.json | 5 ++ CMakePresets.json | 41 +++++++++ doc/ides/images/jetbrains_cmake_profiles.png | Bin 0 -> 109456 bytes doc/ides/images/jetbrains_ide.png | Bin 0 -> 78742 bytes .../jetbrains_notification_profiles.png | Bin 0 -> 4977 bytes ...jetbrains_open_project_wizard_profiles.png | Bin 0 -> 67392 bytes ...ains_open_project_wizard_windows_cmake.png | Bin 0 -> 64227 bytes ...s_open_project_wizard_windows_compiler.png | Bin 0 -> 46877 bytes doc/ides/images/jetbrains_vcpkg.png | Bin 0 -> 15893 bytes .../images/vscode_cmake_preset_selection.png | Bin 0 -> 12993 bytes doc/ides/images/vscode_toolbar.png | Bin 0 -> 24121 bytes doc/ides/jetbrains.md | 81 ++++++++++++++++++ doc/ides/visual_studio.md | 7 ++ doc/ides/vscode.md | 51 +++++++++++ 15 files changed, 193 insertions(+), 1 deletion(-) create mode 100644 .vscode/extensions.json create mode 100644 CMakePresets.json create mode 100644 doc/ides/images/jetbrains_cmake_profiles.png create mode 100644 doc/ides/images/jetbrains_ide.png create mode 100644 doc/ides/images/jetbrains_notification_profiles.png create mode 100644 doc/ides/images/jetbrains_open_project_wizard_profiles.png create mode 100644 doc/ides/images/jetbrains_open_project_wizard_windows_cmake.png create mode 100644 doc/ides/images/jetbrains_open_project_wizard_windows_compiler.png create mode 100644 doc/ides/images/jetbrains_vcpkg.png create mode 100644 doc/ides/images/vscode_cmake_preset_selection.png create mode 100644 doc/ides/images/vscode_toolbar.png create mode 100644 doc/ides/jetbrains.md create mode 100644 doc/ides/visual_studio.md create mode 100644 doc/ides/vscode.md diff --git a/.gitignore b/.gitignore index a018237d9..fc2558a02 100644 --- a/.gitignore +++ b/.gitignore @@ -1,5 +1,8 @@ ## Editors and development environments *~ +.cmake +CMakeUserPresets.json +Testing/* *.swp *.bak* *.orig @@ -26,7 +29,8 @@ gtags.files # Codelite *.project # Visual Studio Code & plugins -.vscode/ +.vscode/* +!.vscode/extensions.json build/.cmake/ # Fleet .fleet @@ -108,7 +112,10 @@ src/cmake_config_githash.h *.iml test_config.h cmake-build-debug/ +cmake-build-minsizerel/ cmake-build-release/ +cmake-build-relwithdebinfo/ +cmake-build-default/ cmake_config.h cmake_config_githash.h CMakeDoxy* diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 000000000..fd9b2c09b --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,5 @@ +{ + "recommendations": [ + "ms-vscode.cpptools-extension-pack" + ] +} \ No newline at end of file diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 000000000..d1e6a4037 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,41 @@ +{ + "version": 3, + "cmakeMinimumRequired": { + "major": 3, + "minor": 12 + }, + "configurePresets": [ + { + "name": "Debug", + "displayName": "Debug", + "description": "Debug preset with debug symbols and no optimizations", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug" + } + }, + { + "name": "Release", + "displayName": "Release", + "description": "Release preset with optimizations and no debug symbols", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release" + } + }, + { + "name": "RelWithDebInfo", + "displayName": "RelWithDebInfo", + "description": "Release with debug symbols", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "RelWithDebInfo" + } + }, + { + "name": "MinSizeRel", + "displayName": "MinSizeRel", + "description": "Release with minimal code size", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "MinSizeRel" + } + } + ] +} diff --git a/doc/ides/images/jetbrains_cmake_profiles.png b/doc/ides/images/jetbrains_cmake_profiles.png new file mode 100644 index 0000000000000000000000000000000000000000..f0e969a6bc0e52a077ec87282ae2422adce0d12c GIT binary patch literal 109456 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 z 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

FN{8h=bq9;B*VoEf?P zL?Dan-Us>YVWr<34MA2oO-AlvgE+P0*$EbU|D(;Kuhji?O0HlIBx5Kr+R_f^<6jO( zY{jnfuyvqNsUV*~b<2S8H>QabBDg%SQE&W}`4bvzQtjvibuEN0MZeZQcqDs(A?_+p z)?r>{GIoMl`{K;`z1$|Jr1WaPxj71$ak^!Qn5xwk>&Lia<^|Ju&AL|0ps84w+ftw9GcWFwD+ zyvJr2FJ<7jJ^FX-l+|qP1T1m%_hd?; z%;5C*rh}^qfmN}u!L)rVF1r$TWO(&R3BN_jxZJsWW7HN) zo)wn8`ZxX7%!-{Aq^1eyrl_ly)gF;0CITC7Qxa!|OLvn*v~9i)01Iv|x=~iw4_MQs zu`4=F_I+SdALT?+yo3zoCieMV8_d7f&8}g{iCFg@#KYO$I=HwAR6*$><9S)%UeU0@ zMqr#3;&6K757vOyTYz&n)l8;?izc3r3C6A7PC9N1Uayza&Qd*PhU6$EXqW$0M!i?j zjK2g|v^j17SuE0=hr=dEpjrh7UIMOI6}i}n+gJfQlOxoL8Sy$J6#F!be2C6Ktb4FY zr4HlH7>XBbK~9`$cKYWp7K5msUbr89)<18{CoHGGSD0Aa;x|2)G~OCA7V|zYpm!hqVq-Q_*WW z`3G48FyXkbiLxFKgx$}NI;!s7@ji)V zZ;Z5{+brJ$jL!uh9e2ryM@H9Im)Xtit~ctwC{qpCtp~2kHgYt4*@G$7#KrFU zN>w7UHoQe69ve5gPB)Fx;|&Us2bSl=5OF~Jp0|Mnxujw&~H z+2Xst--T?xVS&_GSykKnKY?P0AIJTq+}Dm#PI$xV`A8X~dO+(M>v#C=^BUY7E;FZ%#ulb{ zBmb5*am{_Xd)r}12_Y5V(e&Xtwd4o$ib3a`&l5@JbM~nw%0Ub~LJta!js{pY&m9-( zCRBS$RW~=j5itXB{QH@i^!1aYrUQ> zVlGo{`piG&QE11JG<*f_!YglnC7{$TaYe4Ucrr5C^W`Yn*E?m{!S}-}VV>V%C}${Z zp>dSc&Gb=Z%*8~WavZplxFK;KWq;Ea7{Gh%Vng>e*}kLeY#wlUm!sPr0sqfS*@dSL z#!qU)Zcsr1s+B@MlLHOoGsrv*|3Q0Rk?6m|Du0IZFEIW86DJfGD7rz>f>^XzUT#45Lkes0%AoT1*qHtzTeQ ze@=>7?82W)%Cv#044j)hS!y^aS#vo*7X1TxgAM=xaufcs{9pZqS9un2yZ>)LzuNtO zZU0xt0=ux5O9v%eW~mN1y~I* z9zVINyH;~vSwlVwv*@(fd;Ff+TFztcPT=4!_AICCGDflR#Kr5H3yVW^A#}O2L*C86 z&$Huc$YSn@Fa_v2VcKqTOvBE{lhR%l z?(N}A)>85uXENj+EB~)1VAJ#_y3SsB+Y(tsSncD5@BNlM4J`d4yU` z(={;wB777EzY7y-XOhMw^m)=H3T1~m37W`eW`hHzt!?U+33%d?hvbODe+}Pj_#%zr zDN`&oDKq=xWJdzVYRq*{m%VQ?c3QKXyOo-;xe!_GO)mCKQ(RUpG<)BPYg~bKOFI|47ME=50U+ZB{LiGHaJx2r^j{z?ty#BDC}di~M&A zz}&aWJgY_f4S6G|xwIEyN!@Tmmg7)04VRZ*msZ-oDs| zuw{i+YLDUN;+Py+$kOyX*cuc(x7S#E(S#~$vnEr!cI%>~f3$4>V2{Q@*3&2)(km(2 zEXFQ}i0}ObIeM;kA)LBCq*xuPACw%bmy&==IO{rv9tl&s8KP=orRlesW5AgR6t_h* zSztu!-z*EM-R=Ui<(3X5IEQ_>j7tGAJC;By17SwVx6wIBZ5u|PZ$348jpb+bsf&!P zLF6e2McTe^4OCQYWwkz#;wiU%7wZlSfqmBmdK9BXp8d^FH9f+VO_DrjvQ_wrxU{BI zup?wXiXZ^N|JFtIKQi;yHj1E%P!Jh;n~Oou9Gii&7RO9ZlSTTrAt~3cwoq z_C7BjB_tgRoW3_QH{9L@V`l82`fFkDsk6_yJ(V>0ZN6njlPbm`u*t0NY>(Pzx{@iP zg@7*a@ho4)L{fQY>w9e5y#30?H^hAKvv+@wBCJ^P2*9Rb{VT3W+N~!*p`k{1LW|e5 z61N5IRFir)J2tC&WnDe>(o#=VgZq6SD`6cB@PkvfjO?z=UJu#f$bTqF4hm?%1MnA} z4W$2MSN##bdOc+SL@)j;Pwdax{ZHE1-;65oH?QO-aFpRS?qcYO#$2j<6!++PFcLA8 z1&*&d`jzH-a;pwjC9uLfK^Oqi~o+y%ob9xnq*vfE@}2J-K>`4>G@PXr%u-Plk?(S+b6LG0Ik|m>|pZg z!)nBfAD%(9f|%>0Aa;krcg6_4YPIb?7_B~ez%?%ggO}XIiy3SXIZ_Z9=#!JmZ1}oWWxvbsI1}=3W_n8ZQjCu9DNy*@z0TnmpN9~_((#))8 zzSP=4z=bbnV?2}UFQYKY=stI>TnlXGBw16CB7k zLp78T_0_v2VvA?clv9uHc@&}A91-5?_}gTguc5r^-@*pC2Ti)QYYDNa;k#e=C10IE zWqIFw^v2=6*2s8e#>Y9>fEM--Q-d{4l-8LN;(Z+=*N-YRR}(waW+lq&fii93pBer9 zgK8ohNzpd2E2N8Ydz*WounPO4Y|f=GRN219Bn03ZSaJEOOu>!GRDU zrmV)}ZZcBqdcmQfO2+PLw`}ZFDD=7CkY&o|!scGLp5)*@=8Ro@5Dw;v1)K^%8WyVB zy?~(|zA;E5p!hHHt%YnOa&;A}Ks&0yvzj=&3uXfuym5W>zP5CwWg9eGlxkbJS|T0*GWL5Ns>@znU*K0-9-QD>h? z0#{5fm!RBxIe0lm3)sc|%t`W|Fl^L+Y0||^tS$5yp3p-d;PKq0J}RRV?8?Wm;vO6S z@CwqWf_6>bH2$itJ&$isu@{ST5MzGTpyS~_-}bRlyEu4t6ZPqgtuX#AJK5E{vw`1q zM>z)JUvzLg_|;EwCYO1;+?}8T2bpV)3DUuKK=+-zlec1xQ$IVSd&p8P8Q2&MF=4Ji`{`EnAP~*vFS}hbra7BE z^5>xCbdlH3hlOH7IWB(bhs1?e6~dV+HZB}#utr<&c4JJwM%LUo3d)4^-OkmnGR#W?VUbSXo(V9!IQ#Gc;{WVZtKExB1Nj^XZE}8Pf$?gpyCp zv9``~irYzwf%@66Bk`{~HLtST?!99{01DOJZ@3pX9f834pwz(kMZXM6b=yfsHW@1d|{?a8i#X!33 zhsbX|7kzsE9{z1VWHSR4oTDHx4wBE1qiV> zEJWK5sulG^vzC}x6kwNY8KvZNj)wBvCB(d@hBhvG>XeAOl6M*!GX}*11G2 zmIMvBbBp#d*(k)Y5dtUeZho){eRz`VCerP3cKR}TjA|KEtK{do1^WbIt{-t8K0Uq1 z4+#4lA#IMt{;?5KOdAPB37%~#O|6%YMpLmQIl*Ce4P{KHuaK;!Y|bRvd~WC0ldkUn zo>ffB3`n4PLo6w+QSVUZf`?^M@_T3HFc?x;c1i>M1^^(7wuDNx8V+$u0x=_&zY?FbfPL#VF0VZNt&Mzjix5 zTn9+QT7=>*4}<+?Ya?^gj$d8C=#lEdnmzDj^Y{@x4MK~qXYvMHpqRYg7vKdB#>AGn zAxT86Hr~u-`9TSLfbb6M&T9qczb?>73a2t6r;z+BUPft*@BzK^vDy!vgTJOMb zRq_i=;%h&+R#njdP`xOTS3pLYzRj^-XNI$-`>upcfpN~TlkOwtY>OrWpL{iAn0q-t zM!IY!oLf}1E^Gi9b9^SnrIWA24F#zC0QaaCm352*L(Jt8xdI=3>^;iK2j?f$yn4r+ zK-3RL?qRcOj?_ez-3IvIGGjW87(iK_wQFAtjB6sv;&gEL$RqYPK5T~2>?W#rK|<|E zgj`>ilm|`uLHA{v)Yfz@cxu9?u5QfN@#>8JJvl)TnoC0cu7U{J!w^&{5-tz082>ej zb0|et?)#bJXO%H8VcZ5@P%KZEbGJ`42J1ZyBvKiZEPQ9Dr-#GBHLn%+yyemDJa7r$ zwWt!a(4hvwVA-S4(4(e89_Kn1ieRR?#N^VAW3ntOo{T}BUy--2d*Jn5HYIluxi~}( z9={Fox-ZYnZ9WCwL(3uYub2UVWP9~7M>!n{EE?b_W^F-jp(U@wLWp29`dRZ>IhRK@9*erRA={tv@T`C-u5L&VGCy zOg_5=uy*TKyNqIRop!y{4uUQ2C8LXgivq@%fJFaqEaC zRwGr}52d5|ig?xiY5aC`yU6`!0p2|{p!Yf5{e?tx|V8k_bE3bILIBR4gMl zpl@K*@zJUA!^79HFi3D6qr7=T!biUR&|BXojs&r_Xv^%Bu~Hg0i}rNhhrCea#|4(j zZ}W9oTg6%A6Tbd31g(8_MU?EYfk^C;Ogw~m1v01$YsDV`4L@C>U z0L#rr_X1VvQa<5MjJ5KlTqFRO0~Ne+Q(9Z}DOupUc;mvk+)Go9W|_Ub>X+)HgIxTR zdXSE{0P&A4G!XfC*dNsmR8Uemt$B6*=WT1ZF0!rdtznKfAuEMhJUt} zi0rtSrbQCgTr@AX_?%0Qx7YuAiq1k_`{Yk?nK-M*4KwXLY4RjB)4l`r#Ag|7v)W$f z%65v6PpoC`b*UA7y27dLOUZ=O$S{_~U1Ik7g>5^^tm3f~bbRbK^z*E^bYn5&#(F!c zxNNKzB0;Bl(StG{6}E2`zuF?; zmSP#Myla}5Jg0Adb5JdI&&DGlmfmjyCz|TTJ!FZCw_7!~MbYfsiaq)4=xAnZD+rPS z5-u1l<**Iia&vn$CRXh}qN(ZJmU#`*4f_G`+Gxi}8)sjt_`!5ZuU)*2f#v!Z1Jdl;)Xu`e%j!E}Po3DIbV)2ie3h3A$2#@QPm&qDZaPo$ z=3Cj`IYW6L0cJA`MPp3Xu=@*!9emIOGdH1%4=RLc=G|IR@_f!MdOZ{n@Xp93ZT&^n zMYD1|2RF)a3-s+*(u&$%)0!Jo127$*GVA#Fb2rWgunWNii&rG`%@eC7khe<*!DFFl&sMe7G@p1YMAN0l?p zuJi`X{5Melc@fP2E#>MIhyQvCT>=*@9EUWs-Uv*yE?Xi=FiZ?O$7Gx*gB4i#AYV{h zpM+db`qadPNjX_1mTWj#R)*XkB73Rt3nI2*lVO$Z9tS~y%Nq(H_Hc}xf=Ma=0}g&= zs({Di|BF;YV(^Nn{x29ADCTy5$ou9EkooTmH%KIS#^1i_?D+OqSo~F`9E`Ytb;?7* ze=uO+g{J$6?o(LRU~o!D)jT^t2bPBH4#k{@$N^0sZif(B-}JWRD|Gt$-_VYQqaBiE zlJj)6BIKl8{j8mJ!-f_czE=u_73@2yk_d(GT(L~Lx1i63c)dIlPldZTU$IkPgd_cd zs$ZwrZ*clhbh%=AjZM%`Hi%!O{MPkG-$m!Z-2lz54lM>OdehI|gsbO`m3}bHmQ+7} z5@qAddfujIYtmAQ(pUVP#q&H2n=Jl|AJs>2Pw@cx(q*9P`ikAs`DTZF&{eCe&7MbQ!X1VpVQT;_IXYFU4 zt8KIceeIvkeO=x8tV&039~~)Xxzo?K7j&cYTPSqVX$D9J4x&#EGcD~2=CDNklm_v} zx_!{xK2e4~C=BN=b1_9=>P^kN1T+JesEhr%kOE+julpR(G0QWdgVPHg59dD;@Fp?o zMA%S@G(Q7BIf*1fEFfaN4Iw-5v)jk4U(#!N(`q%93o$f|H zCHSO?{eB9b8a{FGu}u`tvibJ2hcYY(BS?@L)Ox!PBc1PLQY}tFrfM2`P={L_ATd9E z8-PxlAZJ)IwJjM&ZES(^``LVx8eIWwJcB>(DevqqH7J+GP#s(19qi(t&+(J9D)VrO z6!i3VP!u)TB-xk-Hq6zv?q*DG6F{0I{gpOy>yrL7z8zzIJNvXR$qvwy~6UJo5P&ACUb)c-U(me1h%fFuzZuSF@!ykT7w*q8FjZ}to zqu?1|3IE8Pep63&)>w|;7S=2aAx#0#`m_Dn{`YRC!W2Yp>Ddrps;xrfHU+IwGTFPJDpW0=fEZI8qB79Q8;M~uID!C#aRmi?e zA(^{<;WXzQjP54~{aI?LL1c=AcU{t*gL~h-4h^9R-0pFAW?3$x3i4Jf(Z?3m;ToLM zve?2~YTxKjbz)n(t%BTz#P@o2%Je)^^_2js-EGo(@$qU<;F0^$LAgw>POZQyAjKL- zu6(Fe$Br9eeuJ(M9cN3Dn*tkEx*5>R5b4bSi82Gf-t=1{qVc%Kqj(uqS}<=y`N)d3u84M>abT(4*LUkKtEEr;ZvuoGx7Hu-_?2_{}(#b%s&3>Vc zyiJN6Q+cT|(I)_)E?8s83R!HbfF|Q__ejz0Dotb?OJCNf{GO8)U(QH`?rxbGaK_If zPh4Q><2{;Pg4T2wKbzP_&%_)%UDcUeKxP)@5pOjlb88v2bPe4TtqNZFL~OT_k7Duk zj)z<@KQ@N5X`2&)=5V+heE^9aE3Ly<+{qq{wlY*1q~J5@T1~O>4IojXB$c#cMW)dr z+V$v7+NavP^G^m+E)p3R1MYE{Uh)}U<9ka015$zvDv)b?$AwU|vl}@mg!UP6!716y zkCMqczgtN6-^_Vay%pfG8hbQ`GgIYId zp>!kO!+4T|0wx57dd_Mo#w2)({w$G0v5Zhg9uy-~I2u^+js!1U=|(Mdg@FPKF=ti# z3^^ZFIGk=%l3%Rf;-Yg)pgnnFX0&qIYG`zWY^-Bd=!2H9&B)Max-PREB35c_*$u8R z>aZ;~8?J&FOebus%-4rqE#`LE5~|)0J6k6Iaz8w)_fB}Ja$^yc7dLtn@}jhNSwU(g zLEjc6>#tv^$!NMai^LX`f{%T;+;~;wOU;v37>*s?FkyaH#s}DDOuO8aAJ3rLUwK7B--a=Z+&JQP@q4kXD0YOeri z&q{IgH+EyF9cvpf>bkv^2*gcV2ts?Nb_>kecvfRZW%ibyn$FeVd&NsD-k;S92xo6? zu)NUda?Yj)?nWzYd>01#@YFc# z5e`~k0IU$Nq~I`zn=a!Z06F%XH!rI*hud7eJ}!>D7~sk)uaW!~nDcbyliGD7FxTNt zr?%36*ID`d#NT2pug`aECi{Hg}H5QnwR(z4o@Sxp+GP`X_^n5kaAt14R>F<$>QEp1iS(X)6_gT&cbQpVgz=l)aWAB)Y8JJ3kHSrASN$ zx&;Ah`cVKX2HX@h=?!_!uiKLHUndN#j&3(6*HK>G)4K>Y1{>r9@hqh&nd4(tdXai(+~B@dyve zrEk||5tDm%cF_;Qs9dWjKA(pL#czC3b4Dghi%obBTOSH8=K=-VUEdklA&X^}&;bst zGdfmMmaH7XEr+A|IjB)@``7gOfC|?DMX5`SrqLKQRW&>5d18H^CfJCA!s=BNowD_E zq@cf_3hHW!{;s7A2#h51Gym)zP$e~<(&mu`-B5DlqU3W)5mK~+_p;;DZ?=Pbyx76m zby?+iNND_-v8N}PX8Bk^=Hkvsa)25)+u-wK{2ZKiJ3~YB{3AoGaB$Bi_3C=%fEY>{&>?E4`E~F$&I9$tx!gt)5mNUPxEO^<;?f zD6ULecV9E$$vih>F_g~=e6)1RX!w*SG@I^v7xXgTfUvbTj1%- zHJ@z(YV}dB$>*$&Ol{%@`AEmZeL}ZI724A6y{*mxz~hE4l3Cw4=-C5WF_>-dD0s&( z%sP`VRiKjN^p>lrXV^vEWT5HxSXmUhtI@UN9c4y+Byh4xH*=b`qxlgl6$SaX>Uo|0 zd*(kIBGy4A5(ffH>IYCWkz~s}EpX6EN;$$^fi}F_XO1C%>Z44VM4EvkC{o-}hnrUc zK}k)|-=)+p-CsL^SjTa8nMZ+%0<*NY!aLjLN`*LPz21f$**qmJv(8;jjL^ecsVrk_ zzQHxq*G84G0=3H`;`A+ zCnsFXR0TPA6utOBfaN2d4-8TBCf>HVG)XDfuf#zI-%#R6FvTPqiunjo=6zVxo?aDx z^SwckyTf?ESWQD%$=zV1@XJjS7X74clc;=hU`q?<0@(IJejBzPZVOe%#l+PNTaf?jr`D zeSh&dsfzwW#nz!-d$&9(8TZy)d57C0E;1}f|3TtuK`<5RT<+-RF)O;(QUjC3E^`eQqw`v$dqr7JaxNU$QgKbGY9JA5N78jc($ zq0)b0SMK0w809=HAj6X6D9al}%emfaI}9s(Ahub+=JFk3o273!84@B73yL35j=bh6 zSs2b?K+v5#!L8xCWaV=5KeB>rVHst2rUrV<+=MKIWwZ?UtO^d}L?p?$HW+M;Xb-RSHR*1C+@WvXV853-W{q|wQ(x-9dUq`#bsyp zB<-m^O^HY0+j3}0S(F4oT}e6f8#`uiP6-(OUG${nE;&V!aci-?dD?ef@^LY+ZCGhA zqJ~F}g`|8(8H6)W`wj~B>*Y4h#eKn z->5I@5f8AW7l%@eHV$o_WbhRY?HrU;b4v|wA@g0o9clM(R=GsaOHmco;+U{-5y&{R z-AvLH$(iNzRIdxlzhQ(cw!+c-l~tG&o3msN%D0uwN-kT#<&GS9WRsO_-^f*ysXBWw zKJAkYs&>@&|MmWxrORi^*r!(8Yct)O0kH>7WGFte!iS9Gcr%<56@S{V_bta1;VvMc z!5;o3?;C}*AlYQ)Nu*s*2oEP;W(Hi8*fE(Q;=Of3u~2e$X~LW!vMdl&A|R3`q89f3}! zGldA&%c3&U$L;n77d9UsOG2!c`g*P6k38NUNy5gSSwwl(g~ai7PB`Y6QK{>GW#2y` z<@(lV@N&vm&!xH&A|cH~Eopq> zULT%iD>hhR^YawS^P!Z@;4Vv}vbltNf{fbr&h+;)qIOJ9kzxl7kh{a(w3aBw`Ox$s zqy-^e_Azq zvKMUVsCusza@KayczaEy>wCo}dJReXBWZl&mw|#w5>{LFWR6n5xJsuFH2VERqK3#C1-Z#kS3-G&~CTdjtt{nFx#&h)$(ai|jpd`cr zFE5?t3vx@=WwwaHyyU0Cjoc(`ZgdD0Np<5Cb#otIrQ9#gKB$ZyS*auq7%VZMD16jG zfh5Z@wz3^m1FCE@g7bd*lr-MZB&?4F*fQjF7*@M-gV-XTk#Z&B{v6^UWV;WmP9f1@ zK?#!HCc{O%b{}MHEMxA4w`1a~Q)6H)f2O`u(IT&IJ>a8~d&Go>*5^aZP@H=rNp4l+ zq!5frIl1sYJo5TKoV|5i98KFbNJ6mS7Th&JAo$>J!4q7AI}Gj;7#s$N;0Xi^7Th5W z?(RCcyX#JJ-_P^D`|bX*zipbPyQi40y6Wnx>aKI0CFkE(DjY<{}f^KQFM`2)5>rnN0 z`U6Y@U4O4Z_?vuBmZMHIow1LJAADOO-1ie)|$U{Nv^)&Snpu!4H7D{lb+_w&5iRw1&Cf;|BBLc9Y@r51vyT7Uj&275{r~ zyj{4I>yyQodOy{MKoXG=UC*6^fOW<2vV5D0w;Yt z%P-!_83$#>NaFwpg45m*x#gl@li;5ol?EA{F;k?2Oy&aK)INRx(`_tJC~pC9gfYU8 zv5bS{Aarya^Qae72iPw-vb>8^LEI=k-GGH4p%x0ax(~wulQpv&1JO3q>+RD$_KS;M zcj;z!S%CBcc1(+Hi_g9^j)>8=__N2_@#nu)p2--_-5RvgcFjTFOZE&WQ&A|P9)XQW zY*nWs!*s~HaT+S6Z(|eGQXN_f4}RptCcjLnay-T41i}zS**<(I*K@Hf ziKpa$m^8WZbfqDkmk+~q72(0c>GMOC-{E2H#j;Q^1D->A+U-) z0Q7YbT63Svb!&E&)2$3lJ!G{{D4!o-tIcQx0Zl#vZ|hq6s6}EiDFoNVIGfP z;yVnZ7#G^0uC6K3k#PO@zs3RHUTdx{5^u{qs{t7ur+?1>a z(yktX$=Lxc7A)G)IybE}$H<$)kMa(oTYk?93%s8)77u(f!OLCIK7Eq~inKp4+wVH- z30q@$(A%LTSd_FzF>XeFVAGMv$mtykNn{FjCta%2?ny6 z;55309~=CQU>TO%l^)6;gl@UDxyvz*#R?cqBRL$i9V!D3&4!NjmfftnbJoCL%R&)?&_gKC zJLXPWsQnhpVE%f~-G?nN*XA;gfY6VK9VO*tDq-w6$^%syA|-fLd1*WuP3Z?24i;E{ z8|)${#?-gc2OILp)z|d0W_dt0J&q;`zV|Ic>ofnH1+eM?P_DJ()J&vtxc5-w=kYWO z#4{~wp1cf9-UsugexE6mVDHu3(71j}Uvm2*V;4uLCGzOBHqH|eF9rYDhm+48yvsUj zqt@*86pqoJWMlGvspm&I)!Pl#-65knR>ZBB$#XKQ`Ld|r1LlFyDI%Ru7ig&YD<1%j zkG}ylNvk(jXipw>w5jwLwWt$W?Jg;VT+FhSS=%PCMgdKby7MEZgga)wP$(bAx65QT z@#Yp~kT_CHANk;Om3u*x6}%B%hy!vrc@-^K8>jZ;wCG?m*N2;J)h88Q#qp+ui1T%W}v50y*nyzR*SE$8N@84aWN=*N?N_OE8|st&!QLkA2*%zANB6BLT9WCH8GR zm}={A%aI#j?WN-rUfUZ#m(1S4=cWCO?rjcnfH)uk3yg(936e0FYxPBwRF9sEly=B= zFriL@zn_Wu$heTE09@NwpgrGekvCHO9<6rg;uzEj8GSR`maCL4IC)&i`%HIUa~!xj zdp&SncmI6kMU7CK^YM#`v90>%y1}-uU^3)qV22-S1_4<3y(0Q_s1!a2_{!V9%3F{l z=B9{wp*E-TR7CUUxX1Q@sHa{qn{UjwwIQ>8aesDNbEvC4_-Ox2g-qjaBv_X z2nhH0djA~#v#&?`N%b>L(he%@L#McTX7y-tN-EM z(bHA+S$2;jv@FkZh#|Uk{8DY;7A97`+2dI9di^=pqz9Z?K-Pxa<7iNOu7p1&s3F_( zUCg>Yz(e*oa`<<;P7`$TchewfB~9;CZXCFbi|oZ9tYllyNk zn#er$&1{N!J=OR>*{tr~M;tBD;)F|LfhDQ2VbFCMYGfHNOsYj|l;Y&arjX&C*PqH! zp!ONBOBX^3B!MjOXD<{ORfecS&Ix3@u?2eoa0p`dxpqiB75U(wy~b(+@AYg%Qo3w& z)*Dp1SWaS9ImJON`8KXcRP9A6PgvloU>37A0eb5+gkp;JbG=FY+ei}&V^}-YU9#}& zp5`X&ezgtoIs&63auikkYbGL-g^iv}2k~#zkoN+L)kHV_Ma&E-&6le;o8CrDhM4*1 zm*)Emt(n{2%|18m&OT>Oy^xv-=O#CE(jLyXk;pxD31&U2QT3Di!iO~ZQgYjtxCSr@ zOxuww@mv`8e8PY{R+hB;WdcCsQTt_&Y8cM%Jt&V?^=-%+A^Pk_KeQqx$gI z##-H251^z~o9OU{p04i(gE`qo*hb`&ifn}{&z<*L8jR0vzXsvI*@gT0H62sLyi8dM zgTZDcytcWOw$%L_?>Ry6dPK(h%ehYb|HlC+;?gcD{nKd;CzGPC8_^dOO`ee_zDU$ zo-73%XyT>9wuI%{X}fPSn#w?PyE%id2m0;c)qA3xobU0lIj_zacyaJh)gH+{4#&yv9MW91q0xwaKyep-E*%j=tIQ zkNLj3pM!zDD?Pgp1;<1k8UnP}FEo`D zz{>U*1vZ8$91gG3=&e7-Z4caM7?z{n7fz;CaVzn<&-|QT`Mg!<{Mjf<=~7kbH<-@d zkx4pglWZ_W_1J;kHJ#4;Yi(}4EG>>0;O~7zNg7T`x*HS(M4P2;hf--D&})^tf2Ffg zNF5(SMMK{ll&E;wFcC_T_m!5UGt!1-;)S*HODNdjO&kIhyt$gc?<>1|!eW#(q5#!h zdM)t5OXQwN&%HTOe@x?9606cDobyYJgi}JLI`r^;O)-JHd2%28nHoxha89-!hq#oI z?X%1JhjbtsM(L`hlPcMnv6Jryf-N9%Q26%kneg`XK17(QX3LhOyiR=aSQN|k<{Tfs zNlJL&`nMISShf+nBh^y(j@|5CVqYk%D1UB%do0>{t=7A@ei!T6LZg_5hukGV`E{PU8B%UCLhgCXhV*L%655Sz^heZ(Z zz{oS2>pkTGbOb6kAIef3%keYgg}Jy5$P~N7mYYiMtb^ABk7C^nkwp$8B;7p-m~CwwM<$a>~Ru9TjO?OoVl(2#nPIR8TxuNF1{K zVO3Mz-5%#?o@HyM6bHBS(ZbkUS zg1t}i-0q;Um$0cx^z_pI`I@34&a;PtB-=2Zx9u7C>2BBmbgpfAt!aHLrLA>d{^8c& zzk~Uc-w5jmgrFIR)07zFu>UqPY{(2&g}R)b19;NWk59cAAp-~pjSxQy*IA>md>_Op zLPtbv#?c3SjUHamY(wo%jdf24q>Q94Fv;#vyS$@Av>>r>w3WUC8o{+H%#h->`; zRU;>%>7+n<>%x7q-kc~MUXVi#kyacP$qT_I_+2zm@udDu>m{G}_S2+vL-(nOY-N)J z4=+wbM&Kr~Jqi`{Ba9Dy7@7eV8s6ob1j`PHSt`ZgExf2<)X`!v1!z2=;~*jD(zCpldvR&EdvW3dNtV=c_~=))P=b${a`lTCcne zrIXL3mEG;3nQYuCr}0;s*BaCgT+0!(+-I4`_`W$rm_x&sL&>%jLrWU|8W!$x+LR3Z z;&wxl9n2P+PsDxny1vBR@?Ng&gW9dm>M7#tDQ|}_fMQKdAWM?r>Ei^NLkk7>GTdU4 zfydSIV<#nn*Q%Yhgv2+5BMT-K$JO;h90f3k8j=pQm?@;1&0T&J)xWM3o7^Aur~Fyc`E}1=R8M58K5|$?uRE_$noUgsk%2P49>;w+z*xCS zbzRfg+nQPRz+Saev(|NUkZ#{I!-ub7N6RD&v|e7PVQH3)$zWb9csV3?m_pu!y&U2q zQU)0G%Y;dmJnZ+X4B>aaWFsDLQcCTFow(#=h5GIKTp7Y!r4j%FKc=KUCFA~y^YMNu zNooiwqPD~#Kw`j(=)_W9?ipNVcgaYX22_G}uHYp_9vC7zdnzbuVwKG*VqhCAABh@5R#N=3CR z#n`^@2_Lub@v`_Y3sUG5fV%j)qUL>w3BKE->&9kYH}UIbNybJ{Z}*H=Hh*XU&653> z0(YnD**aOfE_Pml2yg+B8~_I&7Rj`R`64J>n=0(VFK`Y~&RDxMn~2`gP&e5xu(vX% zo#nlmr_XztN^!jCo{iinI(AMcKvSm_Jxyp`*+aulIg$tg{Jzn*kc?ZPvD4W`wb?#{ zWeidCFuI`(qK!T2`b@xS+IjhlFJmj_uxa9WpmJ2$C#g?3>r17(P2)^Mg#rhX!zR#p zuYeJ6YpQhGFtY+j=1Z&z-({IL=d-6T*SL{{g$YSo(Bog6_#hd4j!^z`%Cs9e8E1F@ z^hW@RPDfG-vyoCy@_^&RJmOZ6@aAyJCUQtJkK-YTl_YT%Is=0;~qkD2vF5Si?f# z&avh%{&W6*NfhJHgkxQ(g)_+P)BF)0dU2*1Zt$m<@_^%2jw3W4$RN@A+XS*98WgG7 zRN7vQ^ZM6@Je+s=zLK3o)(bu6i7gNr|x?U)C= z=`4VI??3g#L;ZxJHsg50X#Y^nzyihA>cm|j>R*whovHqz9YeVW34?z|P?k1_ua(G< z$uM^Hh0`wkF8}QQ@VQV9SH8ESV*hOv#YkwwI+NRx9d$KUdq&6{iG|8LxzA09y0}Y> zI9$%f{E?~nQt^%p=v^~aJgg6w);M)aNlsfoE>a!{92qVUt+iO^rDxbrb?6l6{aWX1 zEZD4_H7nXcG2@4~3WoyWxT$Ot*8ATJz+PwlK4H*7(lv*gzqCky;$r^=EzpwseuZkr z0ij4YH*`Y;nY3(o5(^$pkEcrYR35YK4Pj4_T2axIlosGVnH3rFxuR|xUM$0G5GQ_i zr-kl0;E>0;Tg^xqjYaabjV6;YT6s;R-=1r^;UN=v^e6z;IPuS>*YsxS=GhwiWHkK= z24Vbbv_DSqFKPninjsPRu5{$P!zwyBU|p`>u6fElNRG<_YnCk$xMFN!j{HKiu??M$ z%|CGS#@o_aw%>b5oPNLClPBTWI3h zFppBx*iCay9MxQ;%Gh#H1rWzsqVyN)BKtM1y63T=v3oLho8L>%K&K4nGPlF2>*~X; z5D+`%j_bfzot~2{BB=tvwybegVnl`0ROxkav>6ihIGb(XRv2_I`hHay))pV^p$B_W$nMua_3%pX>Y^NBy$tn3&|*$|MBu-m@u z6k_1#iHi}aj!G4-egp8yIlle4XRNvqkyJ}W`atfFVmkWKdlak21AXQq<2)l&K>h}X z2oT~-VQ_G(s;S6wr_eY%=?-Ektr_`Py?pqDP<4C8UAlDGtnU|huQ{BzjA9i$a=@rK zVo_9+V39(csg1YNXX#nzdI`;4?rdu^fTE%JH#c`8? zlD{v0Dq_#Ll>8kE#CY?+X92v3#syD@5eZ7i|fNEPhdnLe?aa3A zLX`h+0QuaRv`-8#;BNf?MOFDTLQ>&jl+l6zFh2f+ct`q^i+=#(FdRJWh!XNIEdKc; zh?yD|q|X3D$)9K{3}p;24h6F;Hn_)LaUa@90aizGq2G#;HHSt!UBJ8+eq8KnPxd3~ z_CGwWd1!*nvg2TgdLNS&TfYRvb-)v5Qtikm9b{xOvS5hm#5}}fZokXpbPb0t&I34S zL?FS%F5odysEO;aeDOI0DVqL?&#))r&3i;y1=9oX8`8Un<#GDM3BUOGy!EFbnpq_) zon=akSb3B>U?Bq6(Z#Rz{y5ig9~cV8{aO{PYSMj-=_Dvr1)>VU{!mY(!pRuoeKKPM zY{8D;x$viiX6zAjbaUvLyvSWO8R`@L4tpVmC5}vGK}vSL-IoVbpt->6Ic{4w<>k}E zm31dyU#i30aad3>|M?vCWUkgmD67_3epSU{kKkQ99XB;}v) zhp#E5v|^>5Y}r0m({lOdXmEbp#p(aWgMA%7ZA$#Hzk&$GN^GWUu!>j6X;AYFYs_1h&H%U$DK}M0TIs> znqyco--d$ZwTgjDNdR{=lnz!CI+HF6B9lkDL2-050q#nAN)9tTO=suJNfsnsWnt)g zpWj_QItSnfzz2Z%Qz&SHw4`Q_F-d%}unwE+TIIS}c2`wz=)MpM zpV0b2Pg%1TOU+3oq{j2MSQ*~H4A-bC=+c-z-Xb~Dx<07e97)WEpgjahevm@|oYHdV zDQJ7V2Gd;EwU@jt6?#=O1#QJ0`N_+VoF;H$!x}%&QuRvV+E&_`V#plH@7I@-u(C{j zROsd>6-FpB6|6>BYe4%~S~^`Ni|ZCIdC^}(8KN~LI!P0)gEz=z!OZ$aX$>zgn^*LNE4zMj<} z(mzXi4B?;B!sITZJ4#HM&(VL$;QT6#m{CXR**tFubO@gUs{sT(O1Wuy3YtSub4wSF&2kR?bAW<<#XG=ofJp?_dy?(*X(5|3Q zqqTfPo|(2*!xf>-!54MWNcCq8Ql|?Om#7eNYZ^tN-_H*fz+>gMq6~s{RyJ;sHtSixXpG2uQKcfqS z;PnWyKfg18jHLqqLH57e>q-;BlQ@8M>3lGug(`7oNG|j!JrQYC>h^*Sx;#(i==5IX z>gxzgA4ojIKw5`t;-t5)BbDvH57~ya_Zsbyx8DuIks7z+9@(LK*KZx6Emxg#3&ErI zF59zB;q~77pfEIpHQDRqI%cCaS0|aNazfMphWg(a*5wlaXwHb&Hpb`c8mJcSML?hl z?oN`7)J9j;x78l~NpK66YPtDOS_Mecvw=%x*a3fit@%^k^P38)}3(6Sj+Zj9m7T~Rk7HdE( z^)&a%)K9-t54}p{x}CXbz~5ByEvc`w7RdqQjA^jbbor?kJIiFdjqJ+EXIFR3;+XEy z6{Fn4!oS0+&bNucMU9bHCDGf^u#j7OUMiFVH&ho$%X^d_*QMYk6=2OCpcRA~|DOS% zrNG|6tubQwJ|eljaQpUYe=2vSJ=(T^yMRV54vox1I%zV7E94N`Gr%nZrWIQpv|U%~ z5o6FJesL9c$B+Vq@a@AoU;D3n@=!=X>gd4Czzq&hTR6O z{R9Ej{6>}-L`TYYtTSe7Uy?PEe@%<+;$#775k=7y@*nmEKIsLKGU*}@X}lwTWpld1 zK5qNv2h$mz3i09STQk^5`|tQ_rwi@EIHc+w6aY0lXb7D;!1O4kZKw6~;WGT~!h}+) zfaJYhaP#(>HyKl|-gm=dJS?e+IPur@vW%M@^5I^5XXKjU)zMQyG=ISQ4TYrlVclVp zu*&N^K{Q5PPo{6SWVG@RVt((PS;2j3A(&L@EOCla#QyK4>pI_|vPyFqb%aK(2G}WK zSKXxeRKJz0``{KZV_tuaSq{2rb)R8gq3IE@bGVysWX{OX3JLbWpCswTetI8&tCaDF(&Dl-G1b(;{nuXqYDQvk|iM{)l)#Fxwr)H z>c6l~=?Fvvb$O*cgv-HqvDTzCdf_ul`=ZC#n~0%PB8i&Hn#GVO2!}h3o|R!}q`NU$ zZ*jRmuGZ+mgGpU`;{4qiv5)=!l3%M=&Bj?jPfxu7Oj8dICcKadycdMvYhUtn)RhR# zC>2i;N`;Qt_#Lu6-ky9D2J8({aC>hBwi+~)FStLAme;k7OR}mq3RrLb z5WahAJt}+3$=2|n>E}&xVIiAe7l3BeX|LK5PlU>+&o6$RVSc;b3{Dy!OGPMM)SVf} zEY>m99q(bYz+a_>iP-=nfcNn|vuC#ou2-9No4j9yLO5y4pW|HL(O^=@zycWm2sb?S zu;_{{_^k!EF{l(sam+p{VCb0;o`jb9z^R>xqlYce! z{xW~VsS*Ak%v1j!IznMtgDXS$za{=l0r+RwXXLNv0b`$9os;}S&jcf&`ZHDb6E-M? zt$!mh|4LH)Rkav~-{OIN_=m*lpG^Hp`j=!1mKL`Am%-}4-|<)A0ybp*lju)Q{^jcX zD{C=qt#)*ip622E5urSjd}h`%W)Ei5W4S2ny#(!+Q5>K{`l(oSCZTF>P1U-uBW>|l zf18FrOBWvnqJ=4wbxAtNX(Mbf{jIX(n-4uDX8B*+3*|lddbwMDiZs-l^d?xI89XSs zb2{Z+(gzgib*c$NrZz9T7o)&=OA9G=>vq0Pf(2AnRqHL4)!%DK9EzH-mJir{uV7ln z3Ee}mOO(d_KYi7#pTAZ6_kUUYZrlDBz8#l!hV#hz5t-gqt;`uhU%9w^#hTrQuPI^CMBUmiBEvjiCIKHhwXl#^_}n%GZ)m5Zcqp{DH~coC zkC6y{;CzSO%xfy+SDiL*iHB}E23PlnBhHTn;@ofGc8zRL zaJFEu&4tOFNig(lThp!g@jQ<_Ou^0+8TXhK*J)e>cGv;`EO$pi`#D=BT>#Dc4c2DlSGjL0+T&?Hz)Ou=ay90SvBQ3a za*3>J_PK|paKtycWRTrnBH*FE8Z;uM1X0)P(YAN0Aey9`_!^hD)Uxx;g8CV55e_Zu zN-G{{(W~$f&U=b=iV9Yyj$00&iki0TaP-hXxh~mrZdbgR!aAZnG=ZmP8#p+zucbO) z4j=I$ft-7(V4EEhTGlT)waBFlA-xj`&$4DOoIZYAsHY5!Ll)d$8PHXcBlMM?=Lz!h zoUA4mzhe7gKCzM@sj5lo#j7hr4m}gJ{&-?@zGlo_-K39~hZp#rP&}N&v^2(vy4QBk zUZPGP^m%*V{Yrm60=qJz9o8q&}YGKS_#j+@&M!B?D^&3@&lo(dJOQc#J+GM z*+)0y40ReSwQeF6-TH~wPVftYJ{mI2FNb@tsnYsrHga#+g&v@2Ka7dS;3l$n zdIF*X!X%ZXSyNygAHcROam4dc={%)sn|O58W2gSZki==6w3id3<6nYBd z8mWn@D1VowVZAqED?c2o3+Uf3Ap83qE@oA*qWmc76D*zao*(Wr5fQcs6J!rNqYVT>7y|1eBrZlDwj97Uu<3@nRETnO(~9#a`74q zvlZmKP`o-LJmWa> zR9x>iOpWY*$d4ghLQ|jhqQtvaivcC`Zy&s!HZ|gle z{k{3ZQ=~ij7}g0|mff22u^%`{pdX0oL;i?<JI7sxu zi|qg)2Hys3vlRajroOEzcLN1af9FD7or544`;wPhqvU)QYs|?hB0@FJu`|UPLHN|m zLomkpAq*4*j8-@a3LEgPwl)hxK`E-l$WsSppA!o<`c7_gNUDne9G56a4Rsie!Hv_r zFMLxO)=ei{!^d^bUf&IyMpgLOf8s!j&61nfkF3;DE)XNFD9w=?C<53}zA_0=FP;;p zPnFjDnfrY=R$Mp%{GxM%Jk6=^SJh(hcrW^)0%iG?O8j_Or!!;qwo25!%o~H{QS_&1 z5H#P%x;B{N__D^}R`5#`}==`fY(YyJ&q;PcIqMx1T>{(I3|G zAB?8yK%JCN8zk!vk=XvPFsaLS{LEKS>MBRBzVgl~-6pSzZE>y(xgJzn$S?7%8GJgb zXk+NB!GK(Dg}UX(vFz?J(%lp}Ru}){KTCi7=k&)=f~QXTH7N5F__02N>I`(*R6~Je zvOvyT3p#cfH%jCG_?DU#!-M79l7>pZIhJCw>eOAYjE~zNU-~hxaLGhJLNpSN6Aw=@ z>AWf=iJ_D!lDIUyIQRnEdhQPK4NuB-SdZpi>3*sMuiY?YuXh$NsNyxRR$BnqZtQLQ zOk-t4s6unn((igyEAc1aG@iepMR#GAu4>hxqBHD1QOY0C$2zrws;w zt+%&*9%+xf*$C3#3HD47u0GOp;8n`@@lv}=CP=mAEH9P;8fKmT6!t;0$XemQ=!p5X z{lIi}_(0WfJldc(_X0}l)C=3(ZS|%Em(*dM6XR|N=F^a^&QYtEzW>~fDM<=*ZPK%yw+Cjbdc>PYci4R7{}%fQt#`7ni!5G#y}U$Z;)Y^wA%v2B)h<(@JGEcBtc3NH<4e10+6B7M(*H7B=e*E8HvP*9)9ulj$fDe!XRhP^WYx}d8}Rc#9ZdD^hNAnoBJ>|0wZn=?&q}4L zLn85&bE3f+#0@k&Oe9UwVYP3?>Kz|EZ=ChHoU!Zebrvz;AQ%1*p z-+X&ZhrFS$DhMh8YfvGkFn}}qe|Kx&RE)TY!2kLfw<;4rV?sq8btig!HD>ERMi@W7 z5D@`@6MY$m$5pr_yjMf~LlTeFGiEGGm^(ZYue2h2&>tH ziRG7*M|LXAavopN<|%lEQ`83CDrbMdDwo?;q`y@$(o_0}Z6H!oRn+y5*-U&;?xqeB zl+Yisa5wg)AUC*n`V}##DtG-Fs}+}^@|D*(7suwIx&T}yF8N+C1`-&TQZM=<|z z^OW(#qU84=*!c2ZY4+;%aQQJJ?$PgJ&E<|3KEK%`nd1iaj0{9jmDrP{_fV(H*Fu2; z;o!}rHY51}{PNX9Z$~3D4Xoi36Vg1x<|vbt=U{=g;Z%=pB|s>tlPDmRCqZ1c=8a0Z zTI>*J(GPK=C-Eb4Z7sB3%nEy!u<+Vl(fW!9PtXV7Nlq#!r=tk9v7_qPC{Vkj;6law zU5MNFfHWo=ywaSO3L7IHzXQ&t5jYN)ACCwA4Fb~v^Mmc)37Af+o+1EgAm=0W3=tY* z7Do}4WaS>**T#7>DY=Z--x!kkosGtjn6Dw=cZeeI!I^45GKkrdh=&u)o;cf0omN6_ zLw&sqGej}rp}iO1;oC&-mpLNQ9HUN(_MnJj?Ypt7Ig0Z^Hyxh#pka${|BnY-DieFi z<>%Y9YF@O+G6OXfpxo}Ur!>)* z>B*3rN&=hJ=^AMH$>cv0=8vFw{mF_8ashRE*nDiOyA5Si@Opr&)-2hq`rY9H+K zl?Bj>32jKL3*0@$WVAT_#py=hz^uPNDrCM^6JKEEE@}y7+Yb62vm81)E@&)H8f+H> zT`4wPn@pt4aJ}UT04BI>lgv8qFbVrWuT>%uHZGffZMO{6+r9uf6CAfM2YLTe=b8_m zUI+MW!ue&ohyu0`3;*ew=j69|b$Ad_WruwvZ9KB0+2FNjC=q-+P%k12M~Hep7xM5_ zm^&Po$bU~i4uaApx{`gAeSd+?QE1sso28c+_U>2k7tAji7`_lCfZIlXO#ZkGH$I^s zFNw%i>X8NMZWDTG4)RDhnK`PZX$LUXM`es(7Ri@Hq&siG40PLmW2(yX;lyiJ385yD zQna4Px8JhYFmyg18{bFTP&mZ2w9P!o+mvAu>$lx-ZeC@I=X)%Buk6N`2$NjsAA4iv zZadJ3-_-Eo;bH?bC<3)4Cc;D_knlCDE^-g6M7C0ws%;~XGOMCQ$eL`}-_PDM5)#x} zsJU!eVu6$QWhk=(S8Pa-WukK*xxbu*<}KFp3|9|LoL1``qs&coXYg6<2paj)-?E3% zk}UN3D)3{uX}^G$Z2#>@0_1VnLeOng9qDBYxxDBq*%i*pvWry36R6*`lu?l zmDa0buu2Om8+l8VeDOBW>5{Xgxrsz%_>MPTX!Hi&fX@#-+d!+U|2F1i5#GNG8eNxg z7B(pIrH<_Z)6nOZZn(JtnrRyE*%^<)7&i9)9&Ep1W_GuB^h*@nX)69~G# zKJI37@FzXatoVgHOk=d45L$88Cq*jI8WBkVhUTqsL!EIJ?f=}Xe{Wbq^f#|1Mmv2Z zi&6730v3jXVl>A_2}U)xNA3H2HMXPsM5fFUSs-+iO_4j3l_zQQ^_E0a;C6|rCPww zAYZ#8dFgYfQ)P5_b4ssOhZugB?&6E(@Yi*>Egwl(&0yhr7+-h5n%D$}0AN{kerp)G z6|QtfrY1II#6%d(pO|1Nv0sa|-a?OM&y(%dMW3idhV2(9q5vDC$CkT~9bKrrGmDLn z=0(I2EUZs!r7&AD?`mE!Zss629p-p4Js`fMnhN2l!`E>qpVC9XKLaqmoq^+TL}l#u zar*-71kCH>GWu%`*)j9SR_1S8l`i8}T7>aA536`WEORJY-Xidm=r+b^BSBbEVI8oi zO)h3pZN;VaY7jZz;q$?gV&sGKZHO}?N&XOQ z*pFR*Uq1XLe-}g_pP7&FC%(WGh9SO~+G(zmV?5*R;|>1Eg8zY7JQD|WZ?mc^h?>Vd zzSYaqkexS|_7C0~SS(q|s-aRl)Fa6;{dn!YT;FI-^2GCARl;be`Os~}@YYXEE2XBa zIy+6?^q}z(H<z?Z4{1ruzf619J^J6nxciE9Qq)n-R-_rZKCS#kU z)mN}*HFf4tziyNJ!r{Dzb73%vx#^Hn5-F=yk7$c}!;G_oU#^`Zykg zNf5z@`6rDi>`haX$vp8p3M=-^!igc*_uAH;ZaIUhBmpC#YKNzBV5JdmYpF?iO3;FX z*pphhNb4xe;&a3NgE-rY|9Hy&apT>c_AzU=C6+S=7RYZwo3%P`OAJf*A4$4^1W!C^ky3&7H+LQjbcAxbV8{UuJ*`T7lwR;B1mD%!0! zAJMh$cctTs+zD~5T~)v}XVT5;kC%I-_~4m9dpGDMk|pt*40{ssWxlc~UK7}hHx zF0{ivWQP6si$G49g|}PFv0NlzitjTN-^`{|p*lD)@pago6gCa11Ohu`tTlcRS&k=w zm0+gkMZBQ+VVg~t7@MIH;hp6?dGi<*y%6fS_Iq7YDsm~yn)y*1DbWSKvM^vI4Q;}! zB@LunUTMMfj$)hVNFZW~Ww@LROr>I!oF%sFMfK$atJf_Kk^vIBfaq899EW{9j_Hwqfd$r z4kLsTGQs{{rHD>t)1bVQt&FD3&NT^J6GDf)#+D@N-o@#=Agn4uHX+DaxZTfOk616{ z({nW9U8WybAK)|P?eeLB(CSmNnhvBmE{sh#Z4g7zZjFOaEnkh;iT^+BXiywNxQV?t zL^UplxyAzK4v{TD(Lu+JpzdF?5};Wec9a68=jKKpo-=Di`QV7Y+ZOGDamB*C7b6c8 z;lsLI#J!UoW8f6!I0Ndep@kXB7^dzi4e)b-$wu54Bk~#;#h1RfB{3s)#!=`IPBhlc zz#e38b7O#gbC=_!X6yx7ZMcLF9cP<$E+^q{JSN%>nYLrG zci`5&_LsL`P>XUpel3ium%W8~1I!ivcm&KD$Uo{3nGfLA0#W@Kl?ZN)R`fcULY=A%FOcj2My`J3bLlgK{-6~uZZ9&1bPIi_Gaa;c z3QJhAjX}9W)RZ>dg+J?09pisWsCL$ztASp?SPq(Bs9O@meD4pmk(QK{>_o0Bm?X`9 zIKxvW7y7+@eY76Q*4AC8`~*1`@ry`(YyaR$?TO@myksb{4_#k5xh@x3HFEU5E`9rR z^Rdg*-kWwR*|18RD`l`FlkiFQZfTAzIfW zP^Fldc6`xhK3ZvIqfDcEDEqYBlo0TEb$xSwdJimXHk4@*2(Xx<#4k@88UO4Of~@Ph z$g~o)i#f{Uy7=+Q=-A#rt??xQ{48cXu=x?olcP{@*9U@RN~4eo?+WfDLYRwUjjzz= zevxd1&yYILJsi&QlKPtpV{ok~Gybc@L$+JiJcOM69I;-#Gw)N+7ra8-S@hRKyxWWU z@o8Bhm(jE4=GzrUzP8PTHXktZqG>$JlM^y;@Az>CS)9(!QyTs9K?p7M%9-~5^1 zBo%vD+F(<$u^ZxXk97KqVdtlaLZg{c87rxxGV&`KPY)yBzWX)XFl-FZb2O2ugk8gxc z?i0J?u*yE^vQzsrI27DH;Z@~UAj2LOrLF<)O7?VCeh=bG_2L(E*T&IxS+{&`kjv)K zs@t*S&e@Mr5+-!%C0?^n1lX)kV1N_=8@B#gRAJiBD=54&-|8~&G3Tuwb5q*Gtpw|o z5*+9&xAdtO)i75!SoNO^?cr9Y;I&rv0YAf2)qf{HsxO`lkHVg37Ev@?`n33!bjCYR zRA{`T@bbM~DUv?vP!y=FPO`p!CV{d3Br#xp^^7{h zl@rOYUgp^HEA}<6j=$>}&@?&u^@7M&#QXkC1(ytq^>IS2+PU3J8JY}LsOec3=i(!I z7VG!A%a;qd>m?Rh?Tt$omN!Gu{?>2&>muia8u-CKkqeL+a*!GRbOJfB{W|d;)pxfU z_UZZ0ALK4P3OqA(6JmVEPAiu|&sU&YV7pU4YySLaZix2v!kD6w2;>kz4f`_v*UbK5U#J zWP>LL2gCZAI;9LX0->PA()mn6)JUx^yc`(gXscKl@^pkTE#-Vz- z*73%!p8;B3<)uZefNLCXG=ECAPyb!a5Lz3!s)2p5SeJmqu8InVRD(@D!Z2*^--y-w zfAas3c^!dwSXCr&D74hUqdH{jC9v@*5Xi67*R}TS&ttJSm}PC|G;^D&FlU`tv96oD zXIi@;$7h!C?>B`3d$g-kHfy5MvX%$)8qRlzR(jX3+c>;hW5@6QC3Fr09>kctQ(bEq zY#+{Vqb;}eRc&@n=DETM(Edz!!sZPB5j_4QVD{f#`2VPk!6eaO+;On@#Q$F(|5s!A z|NXy@2&LX^JigvHX*Kevtc8KSbA+;=4!c~QE8Po}6*C-3e?Nc!WmGkXMMsL5 zC?64sq*I!6X<5v1Dc;_s!&a>>_>wUqkJO|hOy|{$FRzeMsF0v&9}X3s!4B}K_SNh~ zG6kF$_iN|I*L9wL_kx^#k;j5f*pSI!I<2N-MxJ+5k9F5yf8oOj(!P`@@7_q6A@{h_ zgaH4x44qg<&!mv>2B~_NOMXAJN-l3K!Uy!^hIM(QY0oS5zC#lpUWVLK$}`+4`h)K4y5aZt#mK1&p1T@8red7%9oiG?yH`njjvW^%#2L zeTzX$S7^fM5*t4r+dK=WSnrKzO8AL0&URAbrVvZ%Fv)aMrm&#HHbP=>#oDVg&lE!H z5I_%Uo^*{^)@;!L)dSFr-lFVnL(1N|&#=RW7yr%G8zp3h?* zc5o)W8QN}@v6^nrsw=+{1-XR#5cURP=ghTvufP|6Z@7-2_Qrt_-ROBCqKPnLWs zm>mzdj8qxsr@~xOZmU|2IDz5Xp(<{rSVi-|FZ!{p6a=N@lShUaOM?}O;VtOGE-7WI zWn=zdD!&+U;h9Nf2n~xlP5|;lgegG@XR6lc>+NGn{kgTq{BBcd?T$8093uZ$V^0}Wx^PfZH8=N$k-W6jD5-b zjK1&td%u6Y*Y!Iy*YjM@a-Z!v=ef`4JomXjwv}GIW`x^p^zsM&K^hFCux$yS5`X3^0PQe~kIDYo7DiaKAkV4N zl!01HtK#XS5F?UfAf+Jg%Q!yy>?7t3s=UOsNCte3dqsZ5NVti(_)TMr^uQ6vq8T39 zn$`mqnw8^`s{41)Op2DRbHNor)0nFiurJJwvkK)~MR0ixVTUNZ9LsK7QU<#`L@CY- zh&l_nPYB)_JzF#>s854`w|%#qcfFqQQlQ;G8>zyc#1jNFGy;t}x^tJii!0-wPAJvmVdY zc7wp}Yy$MW335wR8t{P8W}Lt<9l!;o5DB4CfTjfGJFjpWreWX%506b44u;4KS>1Iy z%9|<^`r%?yPyq$vkmA*)$S;Mx%gE#8)IRJM8*`HV z0i_9eQxG$(IAwT3nF(9aq2_a+wwRfnp*kmoVDUP~N`AiG#lON8y1$DhN2|Ol+)9=; zyGQ8Inp5C#oCn5Lu~CH<+PP&{_4_MAYjV3H>#LYCzaLEVWs~9HQkjTFEjOQPR!dHtAMviA$ z^&u6C&&?_*qyibdc)r5YZl1v04$lr${C1Xmq zLX-P8t_G~$BiCZ2e!IxPYIXt24DCM&`|eK5#Uqq7MGNlNeyU{4V;)muoO&$$*&nvn zkc+((0?Vtw5w;uZ4vd>M|E);@_4&sEJ4t)~$2R`IwJUHLBl*9y>VMQGAn{+6q;iV? zPeuIP;?$7ocex2v?lH-t>AI38X_|&?%IMT>Kfq#3IwE;X{j&#VdH{PUl2sIGaPntQ z^6wAa_($OX&bFX16V(b#EdWuX?igC6HpVl47T+^BCa?0?ufMlb48qFF3a@OuL~kMP z57{KAylpNSYorZf=3(tuh#Nd0!oNOt{obb~P_xUc0kv66yE;d$2_wx`kv(UE{jtA# zP9KR*Rk~}rjAQtKIe}KOO}39co*=fgRDNoBciCJ_-Zm@Z^kiRkojNBzlexHtf~xCo z#L3C6qXERB&tVR?Iw``p>&%6Oc(2u6Dut~T zPwc%!2%iw;M`t}2{Y@^D1a}vaQQA59&P3w)dcl|)JWJy=F8e2E%FntFc$$Whi3MF6 z!2LCq{kQxc3^JN8nwp>4@ja!9+IeArKY&*;lq963DP7|~?2laZlheX0Xua#K_<*2%#+UqRN-fuHn;J>7F&d`E=}B3YZz8D zEYCV{KH#ao4Wq@r*l8+1Muc(rk96ubqkYSDfFmmpx9tJ%UiJIt&V=kZLgPu0x&1Po zi3e}fFC(y>_ab3eGq|?0U@Mdle_qUgW!B2JaVN1^H7s@-1gx}RmywfKXODj?ds$!W zrqTtLYne!qaxVp#5S8Gx!2|VMEFy-K1lGh-J)v+4Y9)b`SJw3T2Bl@{pR!8G!UcN1 z1g?Ci{VUuBWq5aCAAk44@zbD!dGV6QW%HkF-h$-gzE;PMw6PY4yy7umlAB|%$7$#& zA#s*g@aoue-PM^XP{>zT%EJ3?F$wUajqhba<3anWBVsn#(aMaM`mmERvf9uW2*8*c zSZ+aDbCK80h(Y)#bvI8nEzG#hFgjgB${$Uk7l=N;Z}ESQ60}(go3bO4(XXxtTLlL| zHQ;cJQJ_!bRuhX7aI=!y2#=zks?tLe0kh9BLxC9p3_D4sdZ7O**> z!P_)Hsyo=&POMx-PEKu?hJFq_1e_3qJp-hnO@tvGrQ5Kd3l#iMtu?i>jj)ONV>hpQ zyx;F(OI|UQPLI21=-{bXID%%Rjqmx9y>VT&wAEdYW|))yLVh?i6Osok66^B`OQoB` z*}cN4K$|k`n9iEFJp`sKlya&ld~u8dyQ-|U<@LH8(dtt9w@U|pKAYm8vK`|mYb6v@ zX&uV>sf1pr&67g}8X_y>V^G9)SQC|!`I$1Y?uzvLL3```?YNi}{o4_L*Yx65Xf;B! zCDoz+Sswpk{r&by!QXhI`Q70|KgwP68QWyTs0O(+CvJQKK{=S8RN$bO$rg%1*evwW z1tHl_dk>ss^Ul){E#BN78f>l2{8(>2R($y>Yt)7r*<+Y`n7Xbl@*1wJ&ZT$^q$k~{ zn{y%s9}4JS_M{zbst=Zr66yYNU+fAW1D$8iAWzT5&XPqbsQH^W(+xIS(~|7y``tdh z7NMN)CiY^(aV&$jyVHnC-4GMbk29VF74Cl2NW><`l_CDk?4^FNbx>7N#({dA8L8kK zG2-bLh&AU^5WhrmbR+vj`(aQm=${X=kp{n_8nw5h-M3@4-hL2r)`s&%d2FZ$gKqtc zFqp3&rQV5p`={*#&Gc5Kug{H4L?tZ>?iChPB38V=z^4VSkBjcA9IDgJ78beu)z>?o z-EI9G#c|I)G#A&!cUdD^e>{lwb5i&iJ>nZkZzxzDJ(jY1XW_#G+QCitEaeqL0ppJ- zBBsis{K9*O?{_H|rfNS0V7xOETRBi&`Mj^e=<|Wcu5w>qF7&A?rM9ehP$nx__TSDi zbzH1jZdD$geEYr_Rz|#AGdVwZA*jME$t?|S2EXIzt0LHeJ|T+sguQUjb)J0Uw>>lL zc6{d9oSwkxS|(r;=DEnD`^+`^+T7iB8wu?VMhzAgJ-Ol$7^)dC*I z{P~>bMd9Mlu6hMB8D)dsIlD&N`KJ@u>zKgbO)1%@pJX9D**jzC1_C(7CILUn5oJJk zg+eAqzJ+NY&(WjOxD-U8CLj5-QxZ_9UK@;QE62S;DG8K$j;T)37nZe*Rj9<6WgEFiW{Ytip zh&o(hC-Vyn=+8S)upO`?i`3W*K8pYConGhHVPikutx}QLA0j+Ef1bga!;4$s@~t{g zo~a{K=_lam4^}FnV%g#`32z#e@R@RJ?H~ynYJ$&t2sy#%aN`LB(V~XD%sGCpIX2fd z0s>$fQyI?X^eNM-hcX{suI+l+-Fo~~rNl>yfZ4iL3#O|mySLwWSs}-x&}*S>>S5i4 zeYNle&yni5Vb}eNByRql`h#|5ahgj-?hg)=HO;2W9yFW38Zd(|Efui7O)$ORvB zwV7P;T~Hs3da#FP(l~KT>b1wsJV{JE4AnH5J60Mtro~JCg_(9ha7U=FHTC!}Z9l#f z@oP@Bew^@Q7o_saIfC(8=3tMoO|wsiNhh0e-0n>ARp(bG{W_n2-H6Ek8Wh4Mh|3Rb zo0xj+b$Lv6X7HTs$s}m5<2vo~%lq8upC%ngZ=K^GIcO^v)=6IPH=qPZkKRNj_zuS( z(mNQ@Pi?FS>(U{Jy&;lu9Cx$ME|Cw_*!|FZx;R5%S$GeSA{gb5zub*87D~utZd0cl zVg`9U-Sp;cbNy$hN(7)SW=$J|+v!)N4%OWH9SAiSJB$f%-Hs8=E_m}HHJ|q2=nnr9 zvH;Bpdf2f)CIuUM+~-dS@tXXdJ0BPI4U?XL*Pgh5%%WI!_4IyMo=3jBBt&nMjsex{ zi@kL=3;vL@lXj77ys1bz{YLn{!xfp7vqQW!*X#=s4cy*NOchue!Xq&o4xcP{7gc)eAYWLsvE=S{h&L=3ZECsv)L}V=F-{;eVA}(xC;4 zSDU^kQi^|NPA>{ZvVCWh495Mc(U95Lyj1dU#jG9$(H`!xn|GoJ)$nAnjf80H5sZaA z<_*HR$x$+I7Om)wO^2+tV<60~@Go`ZYMB{wD;x!j8>&zUb_~kc`yj($a8LQ(Z|vmp zM+MdnA0ECYrGS7peG2n%gE*HD*91L9^w?PyX(r;{0=#wpK0d4z8)q(4mP_&8@NTz$ zay(Znz!S~Pq7FHrWe`CeIZyS8PDZTm` zNZQi0yK>+sR(}NrObg0p$a!&%KP}JVt;IhSbUoxw=v2lPg<-gi(4T=6meF(t z*0Uy#+#zX|;5i{1vyasoXbxI*gK@`|@O^JJ3>NL1H_QHvi>KuxueuJq+@ydp?r!*zuv3p zidD_{wCPG>=4MpgjS8spY6+HHpMeN`CrY9uJ7 zE+}w`ACMqRmsyu;?#=pwiBs#nf$@Fa9ABL|C$3lD$b~;|)paRrv{M`Amm8R_1c$_suMmv?b8&VzE zXTL;Xdz6dO8k#pF(w!J`L$1{Z(FXF*P} zZF$B%RZ18_RI{pKeUI)mGJu0(xcU@ZZGW~NT*%kl9IODOVXDo$!#l<7PTMzW@CE4@Z!6IccM$bBlN?kxAJYl z)fADfP^KXL^gQd$m)ddm0P_7eKpgJ~N;aN!yJq@9s`OR;!KnbCV7A@@&5!QYs|kS(@<8+N zU35A2>W$(+_6ZP5Ff5FynTxk!CJ4)Aul|d|ii9_ajbE|0s@xA$?vV(W5I1{rbr_be50S1v;2!5#q(h^}TKb#N2ocPgB zCZ;Nk;1~AA)~P5TqV}nyMMgV%67)U3Y}h5VykmH6$NV#NqKeB7o}IIVxZ*Zt9vC0X zk2zxmeiO}4=0*|^z$=6xh_P;YcM|^eGyXNRgI70Q`gv#wTlDMja!CV!LE8im=cA7^ zKb8umsY*J1*Cmqaxy8GX-W~LxX7NS*ehkn!eSRU_J5nQkx$gCnF#36sj@S`GmPyCnOYTplKTLY}b4(tT3g=iAJ zVb!}M`d5BWbtjk{U?Le2PQK8&`se9{(_yoPXW(dZP35a!9%9^Hzqx-rd6gm3Ilo=qbmGJ3E0aIPpqY8}DVl>Z@5yr(W*0&s z^ejLnmt7*|5>mqj|JpB)7viUl=RJ5H#?1t|B zmEk~vkVC@{61FBI{cpdxhI)g`^F}OK*95gZ2zH!KHephoH~>lAYz zrB5D-9hk{Da2cs0+$;&Sr4>)I#?sk#r_ zIPp{@Sc5X$&~I6ydwPiatslk-jyVl&T{6Q_@CL)A!&=Q(22ze=gUc;aj}wZE8f}(f zN58$hm>Rhho_g8Cq^+Mg6FlP!fe9tnm93hQtP&Q5>02wVIj4Zj>NqDR5@&Z|?D%~M z{IdJ;qxR5&35pz@#kZ0@w27=5xx<^aHW7G*Xk|ro$r)KxI3jUed1DyTv{H}cwGc>4 zOG`mP!PwZCf`Wp7mxzFXkA;QQj|FfK@DytZXmRv>Ps@knW#zKFz91zXY&G$ATlSsY z_f98WMNfgg98gJ8g1yqp{M$PuuDccRh{&ps@5R>$RtXrHa2j5Ni=HTdu6J@kEDf@q~Kj-+d3ols6V_(krUhfB%5gP_#1Pbw1lKl69QTWD==R*pzBxA!S-&M3R&! z#$>evYQ04sbjPA8G>&%g-`LV*@EIl8Z~wt8*$U#|B1XX9d&4wm88 zIw4BpY$Qe@RPXZ;kgC5De{T~b0|s;SWZ(Qu z=5I=R2Urm;DL9gDj*GVoEMY1sxRh=Vi~s&bCDD_FSO;`dc~icU|MV;PX&vaOtKxrt z+Lyah4Fom5d+_}8eSu!K8%T~r6_sgD%XL1*sxqL1=u8B;LhM`*UB{Xi4wQqQS8U*e;F${^2$R6#nD6Vkq= z>`&k4`b&lNhrH~(;ch9RwRpVAk_7)Z0ePDMg`5s^t#N?8$0vqog{VueL~^zms*xjxBwEeW>nEQ5pr zccxyP4xS^4X3CPII90?pr>^aVudoP@niYSU=As3zo@83B@7xVLKmm(%eCQa7mV`JT zkZ9%U&#YlZU7vQn;k2w`Ek6hpGFcV`&^p{8l`y-eZ|(NsY+pO+(yAvdVh~2K;(!O( zk*heO51)9*W!<7E8Zc75Z+@ZatcWkpW+F1>9*}PCy$EU&o)vR1T#HHf4)sE8{O?j{F}3S9${$1ukFp z{mav*$G>QlRRNXC+B7oKDO&qX8_|qOSprW2F$a-s@mK|dP0=54-7$iB8L3c>Z|1ON zQKV25C{1o-F&B+fY%3?!4F;Qke_ z-~HtJ9_{I|--UWDilUGEWWbQfi_%Rwi!^}I3l)Oz{}G?l}EU`VbXXfOefAuP#9w?V#22Wows&auVG)>v=USP3b? z&upu8GT}v!s;-xDmCMoS%2SnZ@E)miX5OI#6L2s`Nx$6Dk{Xjd03s$eKTlVg!0~GMapbbFLdx8CUDtujY1ZQZC zG&DbKoNScx;_YoGZr5*IdeVw3+|e?@ni&Kw{_1>MgO{w;AQ$J2dCySfTH)Ad@sZId zY#LG1PP5c-3m;PX5L>qvrJsVO*ypU9BD$4XemsfxI^M=8lM3Inq~}d8RG$zYpVgoq zmPhK_HpQu0=4xx%^(S|nIzQRg4_I^MKWJN(D9RPZY1mts*9yEE}Urvd<{NCz2 zJUo1Bz12tZQG)q&`Nz5c?cI`cH)ET;-t7Y1fYl7p!q-V>}c%d7n+}_OY@-KHIXgh8b@!MLUHVG0xH6Z?}MY-#(a-KYjz>x0Gd?cRM?q0u_wN z1yH`PoBOw!Fi(5e@G#9b`8X0`FOSc#7Owvm!UG}oNbv6FGP=~<{(du+;Ct=FdqS~L zF(X`wcEh^3IwSYAo`C} zE7N_`_A5oWnNLsF?Hf_FGN3OAUs)ik$$DKJpc z+u1DBzCpt10ajgw8smc#6b=+;;lAeP=fg!`+Dq*pA3xrltaK3@nbr`&kYem!pYIHf z#X>@y7TjgbE^Utc$e)CZ&*XP^HU^^ovl#E}%6^Rn&u<(g>Bq@Wpewc;s7Y@mscIe2 zn`Hsq_+HLVY|K6g2bWjK1H#0HK}j$Zo1hi!?ys@ z`{VnB&j_ZLr3M15CL?BSYhg*sI`NEH1*HLY@ur38G^O$xHjw-e@?8NK3aZQS!R zU$PYxI!Q%RnWTq{QYRrq_V^tdr-WVn8gtFy) zMa>QS1g;+q@DV@#gjC;aHc0=;xB;l2eP4rAD^oZ_z*<&b?mnjM?AlY z6&60vEiq(}eS@@ZOM2jyup<6&vr}cBcU+ELc%LYU!g2k~#rp)2v>Ta8We^a` z3bTzF3|>zivw@2lvr%XhDs_l+f5rhES)sSzYDXJ36n=?itw>8w776Uv>I0ccT2C0u zUAci;;8smfLxcxp$A-VA=o(kU@v&I#Pm+QM5;{X(1OaA25yMQpm5($I=I!{Q{i3Xq z5G(3Xkq5)Ipor21$7hbs(?7e67+aeq=!`f1H+{P-7B{J0OyVSm`Gs_p7X{`QOtCdU2!Z^+S)#WmIEPw1PfkTO9|2gCV$_U`(qO#KQGB6 zorr#7FXxS!`1%K}wUjDd&wS0M0Tx`dBymr6auI3XQ`qv>gLmF(c}a5CGUav07yxR} z0gN~SZM5OnJBS)#p6By7gWJ8MAh{pe$q!33>a&)=Zl>B)OY0d1 zSMN=qzS#j61`ETr&uF@sSmEv0@f$?p0XjSlO%2qZZaksnvW82Ro5qJp=&BcoHlA50!t!t`_Ku0wKhUrE+s)OZu6>22? ziM<=TtWOy8O<_~LUgG@eWOlrbyObF9aYYGuQVug?d9F4lQ1+)(ZW@|inu7wS5@1pf zr~dZF{YKhR7fJS=EUP7W#av$Xeb|?VI*<4_+CN#QU*bstl8(!)w+LAsu!Uk)i%Tof zkZ;SsoC_F^PIXeeC?Js-nXA-#c5xT*E0p4~o^~PvOtr&+wl=A`XBq*_^D94)`!y8~ z^v_N$WTRGANUD+wq_40mU=zISp1>Tx zPwbMN1F${$Kl}V&5BYlY`w9e|FMnvN6&;bW=$fwj;^hm1^7QBS*O>WkgKCrPdEe9% z5HCj_<8EFR@zS9?^1t}BRwhOjB%%-Hprl)`PmCuK^8KzW!X;{wlrx&c6FP8GT-2+xuAf%nPZrt#|$mco|!2$5C^ zKTQ>t(6~D|B=0o#v;1raIKx8?VV(7P0CImfUm?mckeNv1T?$EeynpA4>a_f8RASt% zr}tikkX&oZI}MXdS&%BSo7g8AadzA`rpU{faX)*Teh_izYJegM(fP*RY{6+OiP4IM zM?nOmXQXOR97etu{FdiE#xoov(+~cSNO19-*Tf8h?1Z&FffLBiv#bcTt2O0TFy99c z#dKc(e)|J>pPD3oD7-r5J5Cv6&M5h9t<7dN2-G3l=T6jYOhR`LgBBdJ;70npC9b&- za^D#3oq%G_@)%Kaz3kPWyYO~gyCa-P_ zI-oN|)EFJ#nR*GAAv^J`md>{}*Yly0HH~?RSEzl0JtNfE`7tgYV1TI2cY}_Gx{JQ7Y8V%*v+Z?@ z4tt4$pU~9Ms354w^in1^CRs0LPFHK=EH%DTrhre_TX!ZBVZ+wWy8AM&^dPCJtj17? z`#(qFw~*@doXyz{kF%HdrZ%PkE@zRb9czYh_rinvjv2czS|2j(<}}nRz>w#pkYt&G zTcZ+J=j+pA-$FVft8oo8eF>YP1>ttDTL2rwxWo4hmptvYmX`b)=!bzLE2R-08$$;F zdEN=Sd*ib#e?bKam|#C#P-Eu>C8@LX*v$I+q$fA38P|9UzN$#UOce{0&HO^jGDkX= z%!H6(DiPT3M5tHL%fL5KFK0!u@a0A);yd6CY3!DpjI%%=)myud)~x@y`bnG{9PRXv zAd@~mlxU+jfZI2M>DfBf9*%rgAn9-t=UJ86H3$y^!T#$t6vYc@3~UA(NbD@(UU%;# z+`Q&i~BHj6lD3%pyBmAd}^Wx>hHa2&p7ySi-ZaN*r5TOeF7-9V{spR zLR3&_0ZMusynfE0!7LImj#oS{28#%&r&tFq%nCyP+$^zy#~3RGzV~VFZ5n2=@;uvV zd-L*>w1_U+@*MLDZVScMNSVT;`gZTm<7V>gHCqt9^AgXu2Fl*yvl4Jw_HX91!BVBS zko%61OlZCIb56<)Z@thy-vux1~$ zpG^)fnH={$v`-z9Yk7wu`GNJ-gj00DI$+{?sB|%e`j$@<8q7LtsmTR4#d&sHvTSN1 zn5ZAqQgdZco%}%KRsj)OU-~&}#3)Pw`_Wj}u1b*5@Iz_GY`uHisVlS*E9ii`0D!!+ zbq-cf*hMI7>-+rShOW5lMf8|6nnb&1r94(By&pZMDhC!New#A1-9^mW(~|IsoZ<;f zEU=#7FhLXFnPq5uTjgpMBedTDwBi7<)bbnCwcX?9Pt1E+dcK9fB9~Y$=Qo*l1VUn3 z1G;ai>cSwF9$lt(kB|dUK9n;Uh91il1&y0z+lhO3#4%WAkg z-%>F?9(}r+S`DHnHA+b_KfhP{LY1)gB#x}a*cYlGgNqmI#4{rg@8YiH^R3(@5C*ts zXk+3;O!)=p zEJOi<(4h6Z9L~U<$@Q2bh_2Z$*SANUef(>-pY_!hMp7Nr(z7ZTB0kqo4K4(m5J_x~ zpUqGV3{-y~Sl8sXu9uUr<}YtLHAW6SuIJxS6SB4p1FQC2p#zRs{>51m3V>hC zC69;ids%D?nk_S>gXt4cb=3EpKOz9CiZRd)eDzUz_jWaqxr*j?x>SKRH(o5nMJZ!7 z^(x508XK9ai;u-)!l{`GEMTb8)Rj>+1&uRszJHqJ>lg*{#ek4g z;KUU^KZQx?jMo{K6j_sNrra4XcxtyElmdCpr-}j`#-N2304w-iS4S~2d^ZDyA#F&| z?o}8wpI?m;petJtoHGo3&3m%S0Fb4GYyno9d|6Sg1MMEZVp#;it^;$fV-$cPzAQcO zY|tdrnh1Cb&E+0S#OKEVPJqC4tM0kZe*`DCf=;hUbU={nx3lMDZdyb$P6`V~O1A-F zi~>JKp&lpB_j^L4(CaFfdkp{$7&g#s63RpB!oQRa85sm&MS5?5Vh=h<^;M9i$inqn zudG}BXW9Y)+Ru*ss9oQaHH43tz%Rp0On*rRf+qQdCYx%%Q-aI)JH{egWfZiPz5kMW zmY@I4*MYx#b*I7i^!Yz@ z@H!imB9n1rWSPk=B31H^yRGn;IV^za%+5IL{)axSvv=n!aS(GyF2MO!z+@#@(B`5b zA6bB_cQi;i;qTbFE&vhFKPa#D1B-_afHTcYsKaI#XJ9}r z3oMM|co+{Krm+zmjH72F1m@-g49x#Dhynx};oQw(V2Y=pecsKFHNKsOKkVlny5#lO z3PrLBEEA%CSIG~-E)LFWz}ow{;VOWrh$iHbpue$=N3gp@-rPRqAe!C!lZG7 z-=uB?2{M0AakE7{9)^KY%!G`k9ue|o{oBy(aC3T9TKWkC{tt!uu>9xpV@kDNOB9CY z30acT{BR$!aK>7c27DQMZa9WaCS%w!Io8RQ(=&$>8^d zzfJsiRxh(}cY|CGCcXCc;kq+oAd|@CeAEEQkm(83?%s*`un0?lkg930v^NhZ zh;VR-&F=EhNz`y&IKm-+2^;V1Ps}|M!~6PPX>w{x+aI)#vgT#GYF$0!9B6w+H) z2Y`y}zIgZe0H%+26Jxhh9oBCzhjtbsil&YU!7W>*m>moamKkLST4ol8rom$}LuTO3 ztKu3|P1Sc{p$a{>Sa>0bEtFw>;L#b=n$MK37NP#nu)!_GuhZ$fsC6AFy3lESZ;du} z;-PtU2BO-2EtG(Psfe6di80b;PSs}SR8XJ&{hF_F5_;qF%rkteh!~=_0vW8a$n2(`MYGFfJVKZmZp484+TpiehJPnEli`-rgZc3@b=d zkol>Yl1%|dRZ@j-U}8~#Al8`WXLodut^Xxlt;_9j+xoy>dqJ2{L~rfr3Nn1h1;Kq7 zgw~Wv!g81+lR1#^(4qGtm>5}rrD5CBEWZY1CFZ7;ecnqKvj=IbC)DCSd zBw|TPETYoLNHUDiq1hnao3JE#)>FoQa0LD7Y}-3;L@q>sQjs zPjGppHB~CFoI%Z?hz=h`^uRU@GYv5j89x!TUl+`!l@lpQXW@ItK>B^iEcdxy=x~Ji zMdv8_&g^0L)1+b6dSRtP#HMPWqMvQG_EI@V8pS+0j+9lSD`srwcq@5P?W5ywALZT! zpNEd7a37Ks25M@x!|mrSa_ij24}KLIjSiDKoS3DuZyT{gq;LdZm-s6fVYuOrA@GEZKv-#QGPPqWt@Y3=Y{3_$LQ1|(^R49RxY zoVHBAW^8Qm1ik8i%UDw7w8ie=8F7ChV%+$k|7dYGGp23vfTgSbMOJDRQmug-(Jw+7 z?$zTnna7El&d;SnsyUCb65zrAB{4kPJ1U?1AM@^qPVoRT2kV8bW6C`KZydR+KnsLQmIZd{I{#jJP-RuSn;eh%RneSE1LFlbN3NOdwBp3 zJ6~u+g41QtY1K^|2mkU#BcDsD{jZT2e8%hY;R-yiTFy-ihxTL4zaszSe(BzDabGEr zrT^724=E!b$y#rW#fM{m2~--6jePVUxTw;m`k|lN?lklTRcRF9J0me(x4T!++TV}i%Mgxt zz3Q`@?S7{cVCj_MC=TEx#(W)goG5R{c!rIRbkO!-<7Q+t}r7xuFPkfJo^()?qt&As&lh_MV|C!qx3#z{rRxsUY ztL+ma8I45*SrOq`^%_p%23cT4=aZ)fI6m&K>OFds0!aH^8IZ{&Wm$5Y0DwfRScbJfeO_=YV@xHxMD zu2_~jE-4J3MAn)uG6Fr?SUkT<(N{8>sw&zkkEoC4JGEFYRep1POsivo&0CTk@uv{E zT-FfSQ{%B+h%cds{TUzdY!w7@u&gqOqp}-=ZoE7m9Nz|bbP)|z4p=xjVaO#wN-O9w zwY=qH09fhkK{=0CV57lckoQ@}H{?NhV1Uh>YuDA%{50lvL-=akbG-oRZ#iCO6IY9V-s4=zz@XK% z6E@3AHsr)=1?_K_mhWYw#mY?`x=|SKmjf`w0NLLUpNR~z+z97P0uvbfRO{h1RV0%D zVXL(JWGsaQlWXR}FW~h@b=OtVW(Do@>-KZ_6NI&-=5&*J)99*5^!H7zSO0~d8!S<* zVPmE`q`{W=@2dqatyFrW0CM_CeFmZ1l|D^ZjOZ;kIYoH4crdSB*vxh^_r3Y;=lUKFk=T-_n7Kq z-(OGnSN{$E?=1P4z_5R-0{=e0PU}CcAGcyC8XSU}Y zei%6XIlhFz2XQHr4+H=0?a*)bwD2ApA3l`q+tN0AxHt~UOYj~AawMosC|@KovhWjIKA%e`#2HYo3EJzd7T-o`nU zBxS5;XgOFwL>661wGV-|SrFxu)rVu>2EA7;zf$jz^ra>v=$IY*@vh^3?TyoOamZzA z(^-aBHRyE1Yj-iz$L5V{P)Gxd-{W*%jNCggai*b(^U?FkT=AcgE8FFU0KE^H&XZb^ z4Q|Q~jQyoa(|TE5o43hi(abov8hf`a$YY0VC8C$G!-#hf(ipjdl!;(2*< zZlX&oJMYZ4`4Px){WSdv{eXFco0UHK;o(BAlb4l^e=&2FF5cIooqIO5qoaaK{T)$N+-)+N2XUr6d%KytH+$2r z@EZIuOGv3p(cH80pHR^l9 z>=ZtZsW6Wz;WP4c&{MZVEEcD5a%d4<>m8LGnG{FaP>x(;G=S;)SH13CRR$Z>l@-Np zO>`E$S%K7z@l#A-d|f$x2iND_KKT0C*lI3e((<`XI?Rwm%@P__x^WZ`QcyxM$?P(T z6jO=!4xveYotkCTWOa+X5=cum)Kc4JBtxVvG+%d+RD_aUdM{t_p$SL)QWoWeI0gX@ zwiM}3MjE~lX@`^WADCzE2S%D~ct8A}J8JrhIpynfJ}i^dbu-xG$I=P+4UX9a+y1qD z%XmGD_i5TnhT~4{2t~hl6SBjJVrv)E+B@w7-VkuL23nllKOrByAqWYwO4Y8aSof>< zuAfmD4Nc_WmUm|+=dG#twy@Z7XqtQ%O~TFh^_A)vu$crBv7l_71Ec!H ze({~Y@NSgS9Be47S1&Z(wsH8tN^QrGMw4`wx0u~mr7J=+`h&)HP{*%Et5Ne02yj4V zb^OS=(!eoc2FG+bqqLn{3>&?l3e1oGSRh}2=1jEoA|32H^^Xo2V^as{5IJT`D-~|+ z0uQC){uvF1976Ow*u-C`9cXUv(Sln-xO+BZQ0iJ%U0YT5b_xt!`?}*ii+PVk@6+#TBf}duYfPx`-fmw2$d3u8VN~r;-^7^1 zeL6SD|IaPNA=vFVddK|jMLIIr(7ALA)pfVB=$geU6j(y6efMY316h1#lAFVM^I~+% zsZ@Gdqj>b3VzP_11n~_QXV)M5nmCOs;TV+iuqiB3delVkr=`*6v5KlWLMzi^N_iLw zcOOVm2}H3Z@AOw(vbujJnzc3M`$+r5)gzQ$G#X3!b4BfXjToPbpXJ=+^R7h{E#)FMJn{1tDr#VP4G(= zAH@aT?z<3;xW!bp6nonJpaN7@q=k5S{pwGAW8=JTf%fo)7<`Rp+Ay)|5`)VZBY|$9 z$&WO&^!GRO{J8BzXT?o%kP;%Cs0-T-X|=Pm_WA&6qT6}ylEk}%+oFR1BYh5lQA_Ld zO5d5o5Gpt886ij-kLz1eQxxPz7`$$*mFU`M`Ky-R9X!Q6EK^XrxYf`QBxYCn5j2(4 z`64I35DK}SIy(XvN)JBWU(9VN!*#@OZB**tQ?9SroK%fuzg!I7Eo7m+mtJ;V4dYpe zd-=FmTkW=g*TSo$DzufRvi%(|$TCY_35t|Y-e%ZYZtCYmWldeU%s*(AZ?J1w&`SGp z*BYjvR+7(Te3ayiozn8NWGH%d1K}VE^9MhzKgN<%!IU&c4;*_>qO_laV%Bn)Vn6RL z@5)aGAG?wU&EcR6*O|O$34WNh?mzy^24`&UXYyO#D@C*9rfw*f)K_*MARdOV`>G;$ z9J2D>&Kk)Gs{w*wRo#;)T@F0uD!XETbhs@6U;U4)XvkcRzd=?#lRdemNn^}^PiAe9iC5p z^xX#XyzXa1D+(Jw^ec{fd470v#nG@WOB2JPM;Wmgw7ncIx9mKC??-Z?Ai)-CpODB1 zC5+x@XU0n5q1LdPF7*>xvku6-vrhkf|ItM*r_t%-JU-|<4AS5L6fJdW&B~up^0C+R4EV z*#>nhwVX)L5%R8;(qZp^1T;;-<%bSN`UVI^ijg5`4lTQmI21Hu;IYwA>q%ivG}z=c zk)t1lYu{qjt_%V-qQj-UiX94~A=NZ%{ZPR{Amcw!u@(z)HOIMc<*<-&W$pjM}K^2Vk|K!bm3)k5MYT_JIRsx z+K-kgPEx1-XnS{>b7@&t>0tvTHcORWFNG|7BdxjD8ESUTacD*IyT&jr%}=QHl26Uv z!h2DfQYR^=M3l46oM7zD1ThwQS|U4=aJ6ALw_`ZVXQ~Cqx+yw_m{UO3%T>biMKh+` zAU!E-P+(3-mD!E45#=}evC`7ATK_>~ABQ(4 z9H)j(;GWwQG}!wu@OC`I$6vZX*YYg76N0zQxhY-EWJu7OgL_^I0+JBFIV~o@8EUA%GxI4W8%T_du3GlR?OB8wU<7{4TNsTls@n>Z-y&bm}OW)8k;d>Ij^tjvt zlI%2mUJs&UvWZ^zFFP}*W8?j+T5#5*+=6_W8-kU~9N(Ga-so8GAlTeovN^=HPS$%n zFf-wYG-qlF*3t0BWRn#W*5TQc;7Y7$cuy_Pdh$qo4Ld|rdab3a%x@~ilK#rd!;ugN ztIcgi)%MOu9vWWYHXs$h{$~0JvAaywHq#Y z!}sCmw3$)UuhHfEl{3ah?@e4JiaN2K7Zd=Cfo{}*`V={M>$}rbfbZ7n$u()}LO{F! zK76NgTMM8ggk~&`T!@#?3LMYfE9`Z+dM!XYmO9U4&lJFZRFE_KFjmMOmN=qnfUfr4 zU_D%g;L<`2AO7@9{&`bU%qh0fNF=14_tX0!-4~^L`7`CD&0wZ-Ps=bz5}~$8H^dNY z6Wx15&QWP1+pp0obS~obnQtWkpUw-{Tlt9)dsoK`P0~j8#?rSL-(F@ON`Lt_l_(ow zK_!y9XV!t_Enq@zDQR9loY&$$F|qzwBJh1!bH-G2yf~Dx!dC%87JG0@IO-x!eTY%e zc^ng~+CnGr@c-G7K_BqvNSD7}x&e`+R}vCY@ZWQG5X!w5m7J{U^{K0r$+0ytU$$V) zlDmY0l(s&)N7UAW=6P)Wsf!b5$m!in*uI^|r3T$4v);FAqcT1)eoy}Aawg)Zc%pPE zn~)EPs05#JB_&1PquN(~xe77;EOzyB^*vGQA^%g8)(+~M-OD{cqvotl^fTeQEUE=m zBof<)Fhv1SIEABc19({g`}YEBy3l+e0mgmCs+9tla4Rs4WVLww?IHTIU#O~sUPp}} zSJvcX5e-7h$!A!ef_2oRaBREG!eSj=2oEk-T|W=5^WW>pqQwZD#fF1FEQZe*SM z7oUWN-A`bh)mDvYP6`1q--{xNfb<{{4cB65!A&;HQH*gh?in0P09HXznPv zEt0}?<0_#iD5?0ALT%I4`pL&dCO`4<^b2F{(6(AV(%;xdAn(Zlbo!L3?y$M z?E9#%b7EJi;v6c|Yytb9cx*VNA6nV&XA~PtL5vmSU2L_FHZYUR(S9lPfV88Xa{B&8 zZ%*BkS2z)E#hPcxu^`PVfd& z8btGAvBThzk|+rX4=~)*74b(t_HD9PGMWiG9O6E|gj)AcGUT?9P< zneWIXv^N?eF|;V`$eeOSmpA<3vZ-D>x1G=oceE)M0VLwp$g-RYU zAa#2&x$&uI@AtOj!KQ}eaFyVsrGeK$lEaReEyvZuG0$0gQpYv4v&zYVvbxzlz{S60 zx8{mgPZAL3(269K$7N*fW3zX2;O;_rMj-lkF~vi6T|Y+gU-DJDTp@5v z>&g?b&6C@Zt*oPh_v^ne4@TI=o_KfG9)$Z@?j6cB<*!#U(E9;c1f$oTl1cli?8G2( z&vR!aE_dqIf8!Fb0f`Z49|%(GXY5r9R+CdqzTZ+g%muAy^BKK6Ga0ymYV@CIP{c#n zJQCTQWbP^_TZSDAvTxD;ez5r(HgLYj4mhp^IXeIh8Iy3;p-KzKqhfqnSLEu(_)%B7L{(nV%HMV1bp{3cC)mb!vx?LY&P~vojaQ1X3)l$zh!9HJ)wRXdZ zaNtksn0=aobvg#rbkDoI@130Nh=@Z^0`Jum9n2e~e-7{r@ZjM+wI=;dD{|9$bYye( zOoNYFG1v%amZQ0MUc~c1n!OsE-L<{B>odLN!*u$_YxmMn%l|OVtB_irf@{`Nw$L=I zS1RblB3I5v^qx&tc?Rg(`ckpS*g!vJo8(pSUt>2ybcTkETt}Q*`YhGVWYlRF=PUSO zVNMDChtA*S07<+gtL&gLOXR@YDG+)~g2pL$^*22A8o$Z_A^jH(_CJbxH4)&eo&2M3 zl2@g_qQyfV@BdHl-^kZrYasb|kb6Xz)^(@a9|51SgJ#{zD+`-mqybgXxiRQRtGX!) ztRHp*N?!X+A>8CT|IuZ)$vDuZvtc{9qpFi?L-(B#4m=w{)V4~ob8ADT!PPF=)T+qw z8c-ci2OnvZf>Vw*3m_hD3&&6!ncI&^a7(N|h={KWE2Gn|8fp=*`VKz?qn6w9*%1Bl|h2tvdhO$W_8^w5#df5;jiFC@;E?yY70D(OuKH;)N6}|CqLka`|jaXhhK_vR_@Q>Uv4BDY9H-pkda5;9#s$9v1M|WCF^OZc`BNt zSUzT~^txHUTq4FW^K*-X2_JXrJ%q)7B%dSEn1Vi1Q&TVhb|QKGypQ?lg3E}Oo{xY` zR(^fY#uSDOBOnM2v6?dIP1Jk;0MieJdH>5T5)xRW45Iwy&(``7xr1LK*7jrBu3V6E zw83dfI&o}n#jLTdGH!B^VgI10AH)wi?l1B4^qrsa6q4iEP;Aw;%D(0$qsjJY-0xlU zcG@zxlQ#@HdD<=M+vgNvtU9?K1a44q>|ll2Ia^i>^T>U=ApajOhn+}_9>lGq7TX6F zmI(3UuyTKMu?1pSiXdWum9K_%S6}1txPlOYJBVNsl z+Y#jdSubw37!r2JKcsAt4^QW-wF5b{qIxj3BZsRUW)N!e?MB2AGpP{8 zxybcTO~DJGUF|M$|8vZk-M9z|>HYax4FNE@k0zkFG=&?@Iz;V?`(!S(s93PG*((1z zT(@*l-L6jS6!-1JcAhR6&&m54Tj9E=q@)_<$1r4S$UCa>lV|3?=4ZFcYoOu$ru4*k z(88P-VB9-$vZ&N(Zv|-Q~r~5x(7)O{4LRm&; zzQ#c)Pnn5p*OaCz+}w>K2(|UqLT#rWR|MyL`OUY)Tqg{H=ie1)fwSLVAkg3b^tm~TOAs^hwXPJca54Rq!YrhIz9o7-|o9?e3%AYVG>fa zGm{%WmWJ7H-X3gZW!tmJ&KXNbPT60#)62&DY=mT6k1447WY$>av`Ihn|3A{+I;gE~ zivz7fDHICDwMB~;hhP<4g1Z%WPjD$vq_`9(P&7Ej9f~^?f(Lh}KyZ73e)qfg$-H?p zFEjiBob0pD+Dm>*Pfl#+oSq59qP|q=;~&iF*jtOe>(+W04rZcRUD6JO;Ny#IR=5q3 z$bX5$4h;M%%5o)wZ{~}z#4>zf_=S10oAdIN~?@Z2X3DG+l-|6-=a1|EZbE*r-w^4RFZ`y&_oh!_bX1Vuor8+j{36U4A z35vDz$|BFNf(=;fWm>o5LuL!V_%-LJZ2P-`b=OLv0jX6D|4OLisUu*sT1;qS+5*1|u29ZI8& zcivXv{B!0{S%B-htY>w+VPV^^pkvdY10Bl>Gc=3kTfBE>rKL#}2m=pTjRJw8J!UvK z{CV_fl|ks1es6Mdr$y;?D&+CF7Ia$B-#%N?oyq-ifR8;OrgQ%7s%_&T+31g`*Ft}^ zQuvNcR80;d?Zwc^ERKdB{$ne@Xs&|#prN(PljfnW$$o8AYxsuD0nan%05gPKr?0pD z#(FkoER%~92Edlgnj7-T5tvA$d~btC8KBw6(w84LmA}=MyCW`#nj>Ew44ElGt3qhP z%+)D|X`7LV@y@FM5p$fL=@8`N@OHA_Z1i-q_B3=eNw@v1UhzAf8!YvhnB(;8Q8UuL zxI>r?vnkvLXlhu5@EL}D_5I&DK=b#8VzFsEjWrdNn#%0Ri|)aIJUrzDJs@d%njcWk zH~cYhZB)sjZVS1ydoZ9Ys*FsJ-7koQtei-|fJ6wdp?ZeEZ9JnjGGoOVeqas#jnN$) z?h*8LWXz#!*F?%C+wS?i-XuRSx175>8aQfD&$~XkD0tmaFtZ>;So6`Tb|5R^f7V4D zfmYqSXibYwk2TdKnkC%dSYzOIHt<9f1&Xg)sp+<<9I$XSc{g1R6!Ste&sz^?PP06R zz-PYqxw;3)xfj9_AKeVxbSqpOfS1RJ8v&+RuANsH^_y5x@{n-@uCE!`@Uq<;<&m(d z@gjW)IC}HztBPWd$$nJgBw^^%Vsg)WPXRnoh9SFZ2 zIuekx^m{5=S!Bhem1-zz$%HCO^-MD?w&O=4m6_opNMbx`JJ7$db)ZZXASftjuf#rd zQjN$xqTZXt@_XZl)!@bJ+BD?X+~g|o1W8$kI$6=8L33-Po!hV(|AXpb56QDi2or9m zV!xz?S+%eH^WD;#}uWpKWAjj>BIN2ou+x;XOnzKQ4uDW=-TrJ5)_Ynej(amw9A za(ZJ*C{?!X32zB#*mMe?S+W*`BX`3}OR#_(#OugMxj!@{v#o$gf-ipa4NTMe?x#%x zz~4ViEFwaDyE@0~)aKxJaK7mAoSXIJ*r0;=jP9ebbHrX(plZ&^%qg#S@mdlevlOqwjNu!Kvm<8m2f)OYdZf%mURUm@zo(<7nI}N0s}&bc)A@A})igi&KXQQt>3p*R!o2_xQ13K^e*TC5 zn<)NmwF5L(B`sIgVEDa-Jk!I2B$ZnG&SI2kG@Q(yz!Z7}DvVOSD z5}M)MWAg6xquPntt8ULe8JXplMx3wk)6kzi)@RzUJvhq=5V_8oY`iJVPvVKGR5|ePTsjE*7RD8e-W0z5 z{GadL(cw#W46G+02leCM{CfdR75WgV`~D{2kvaSSZ<+d&k|#(uTm{(!0QF*G9Em-f zg#{Aj@5cf*U`16d&)SZ}TmplPW3%T11e`KZh;)Dq}=|K{OYK_z%~ z*laIdT@*}B44Hc7oxOEjS-BY0cdvvU>Vi1*zGkohpnmV-(qLUDg5KxSgHzGDoPcjB+i`ok+fhhVUPCux>3mUi4F4Lv`H> z*mpX)t-N#UPp3Nbr}#Y6cZ}fE!SA$7ZH9V`>6}Vfo!RbW+$kdN z_*9_M-BCR+?tTQ*+r|`fLeiV__=5Ed789MDlgoo|{K?f;o;8kj_k0?Yn8NZJ<7;WH z-W&Y9xtGH|cTe$_dgC@f+S>+tuhn;&tsh#d=H`Z@sk$bbHJ z*|Yf*+sP5j9|<`BE}V|#j%1eNz}BBBD0k-A!EH_}>Zv}}&Kmi1Lw)ssN>l{*35-|0 z4OI!z#mU1{_BM8ii3V0wXG?TR^5wYQ`h4$CvTyTa0L+lNXcsA5D@Z6bE3KqY`s+a0 z?5IBz-ZFB2LjYT-ch`>^a-DP=9my#-;U&^^T z!1H26GbPIp{D(SR2hU|)SyDq-O@ET9xmg{d+xB-Dd!PMKR`jmzQ>3Ij*F%_WANTN^ zGCG1B!YZMeoe20tAo{(f_>yun!Eb9}Lqb?bW4zuOC%Ff}AITSjzyEBlG^55BFAyd> zkc4GILE`Wv|FdlLfxhEtvTCHnH!jP8m!$oN0!Ue8{^@1 zlzp#ZP5kcb0j5vvcMVo>6pn(SWLIpS1bgp*qBLgIP)E@ zIv@*E-I!V#x!%u$de##O?V#)niO_G0E6%BN89+hkQ?@Eb=tRzr;ZX~Czjyrd*N%#G zl+d<2^M2?mdDUeDRagMU9vzDe_!@J+(+R{;)8KL|<5Ngx7dmaKb5lJRCbz$4!@Y9Q zdk;$I5f^v2Rj_=l<)uVJgsk1GzWU}^N!IIU&>XuxuD0IDmy+@QA{+y#W-VUv- zs>Ld~z%;fG{x`D3K?BX?*gZeUPOtK&7tmKZp@mkbG{zn@%R9C;0>%v%aQ_8XroHiv zpEAQb&lK79T*y7$^5oMTfR=s&(2HpYyN1}5`?9gSa1&5TdjA@}86n4lzqQ<#=~w)x zQB^eF{pQ08*jbb*AS|waTj(#aLh2PuqhDCY+23Js>~*tGO&hw|`A1V0cKNG}?#{Qc zb~04EE36D{_r^FCGjr=G%3v7{6pPO?&xAsc1Jl{DNi9X%!N6g*4jIEG!+wt-eF2$B zMJ-1E>A2~P1<-9zll1O53-E%#RD-B+ITQt|S4UTq%?Kfn*dwCDlj8hFNCazp~ z5S`5iK+6!g7^O4jdfy7?KRgL^T(ykuB6(YqD>Xl2@II7zU=v$U&2oKznN=)Ju0w3! zQ_V(kpn=*p_bQL>YGRwm_7Rr0IO~}w<5~qRe60py@64x$7`&5U!ubnK_^w%+te^g~8{{hgU~Xje z?oJ2%jnCcl`dyIk-!pyq&zEKY!S(Nf$cBOY%kF{u{k2F-(6i|+*_hd~j)n56^lQ_@ z>JF22D)j%{VG|@U_uf@B)>_H_qf)u!LKNJn)YyT$HY;aJA!8y}hKsHkw97hrIE?zw z!W1pBYN``eX#gto@2A4lguyf)0eNFnLinM<*OGn_jrEKLYQo8L_JVVcznKa`#|HPx z!A}nZ^?aR^HNETe$6FoqUk|oReP*kWD*d9W%XCvAjsWSA zb;2W~%~`g38Xoaj0Xv)bagMuBxs@CgA%;Z)!duNX6IM7SWe)$+W8%GnX$s7jP|M** zSk7!h(2I#2^U2jK`_sy`5mHy7hReZPa<{7?kEn^o8yNo! zgXZGGq9FoTempV?^16nhx8?t{w1I+YDs1|B{CdZ1+-~rsY8B}PGhkGKHhZOl@UrDq zoxNRwlS$!OG90^l+Yw9&h2HFJznzdyVw!a_y)Bp4g?Un+9hs;$Hl7(z@P! zMJ%^dr#tZuyI16r(DCj(XQ*js13}ZzsY7OtLcRHCn5i>`sRNrmEpZ=RPwcW+R$_j2 zu#Wx9xw`YhJIwp_M7!5>w9Eh%3t&j;Ss&U^kgdzy@9X94B>$e;t_bAg?9Y7yKK{!~ zo8W`DWe8`ec)c^doZI#1PWj@d}md)|f`=rfcvru7bKJlUn3L9nn$E8OiO&I)^moWHSNsl09RtNb9tE_Nj zL(>1q)L)-hcn9fGAm#KLulJNi`lNrI_WupV-`uW%{{ISJag?R<>Mn?Mu3W)@ z-uFNEb#UX09jn}h4oVQOUSpvJ7<`KaT7at}o@N?&k1QDO@AYnt4dpm%N1T#!_AwY% zmS0-w_*^Z<|CseyIT?r}i=Mn$H?rkB7yscN->>-Oe)o4tDujcGgmVqxX=#)_5SpJS z_>Ol)r^*Y@F>35g@?n~omv?l~GVhn$57=E3 zj~#$I=<83oIIesuz0NZ|e$nOJx+F%#bF{@T*p z-22MsbRy%4nA61y-xr!eb`M=HG!E^u;H*9^{nbE7qKi>F%p1>YPths+eMGEl)Lpe7 z?VC&SucteLU=i~O!Xl1jZMI|?yGQj~f7n)et*sz`#7E3&`wtT*liASykI?=$DSY7# z<1GS?(&0fZt%@aXjzDzGIRPh>nm#0njZn=KEI#=bp z8(b1pW}fRa)gSFTGeT=NrHj=;oB7&GER3FsG0y4za?SX+0XsS)l{t%hnt+olcF%yS z+DYx&7bEOtsY`0MB&+Va&Ryj<-<&9mw)ttR+ZCn4VM%rNx2$WDAp%{rMG-TsT}f|K zq(oV&n0{#_+8p zyV6w~MO```Pv0Lh&=;TPK%K(;X6ElK?ARavZG3nM#fHkA>)Wz7B%%){A@zx3mwBGm ztuyx_Rh?F8IoEtTe<4$|mz9^=pOb`yxHo=7RI`3yH z&%fQ(sw{5YFp5USa*vrVmOO<@C^FHs-I!hvxJ;7V*9jooXo&4Ef|xvgVdiu0{w%kn zk(z#~H$$LlYnpt|2s&Uv4s17evtm~zRByAOLL93w+kcD4eS2$wYJbw>b4BWc$D_Y3 zRj+}|pEUeV{tL*P&DIY3HKSQ;GhU*a2k3sMZq)wvK`{v}?#&006;W(S6l_tKD1H54 zktVIB85z?YZyBZiBw6(Q5TQS{SG#s7?kr6^0z*Bn3gZ-Teuyhfr@Ho>uh7Q5QD6iT zw6X`mEH{*XJYktJRq8tuQ2)tK@WqFw6qfw0M0MgCP5d2tX!VIn3C0|6t?TvAt&xxp zIZuKUQ`A;iV>zRrPNhEgF;+_5xs$c010K;-WtO!$wCH}6T@xe+( zV59f!3c|zlli^L=Af{251MsBj1mpomj;M1z-)=t&-`bD^o&YO^&wn1$#HPp=F^3A& zxlkva&TqC#G-TUa7%s!>t9n15riA0-(@b9q7Z+#2ditkF3$(zf)g?r@X&WBgjj57Q ze0(}`CqcqFp{e>GWqHn4`mW{{fXeTwTg{3}MxK;^UHuCl&gzQ6ewvs(8S*Kgok<`C zmT$C+Ia3|HRJWzcFLpK1AWCpfO32V(;$4}IRfP}k4VNB_1fR{<2fns1E~injg4{u-_rTjsTd%d9bGY-K zx9R%Ly+#Ya{jfyV)%6(F_X*&k5(9(ffUy#trL@)*JB`|!34>$IfJ3E*vezamiKqQL z;sl%t*Vj*4YkZ3+F2w8nAPUQYYdHhEm4aTpLVs>Byw0xv%#>!1uFQ*P@sB8qs@3zb z#mcN^9eYL%oMrE&wp~o`EI8;J)+Dy@J-j!qNC)-e_R2v@Hm6bO8_51gN^w#3z6CXL z30Lu(0dDD-;-WK818*FHVKC|5iZ82O9bv6}aq>Xl8^4l~F z+PDtEc%}bBiMw#{h59<@Y~`(cVHs>b2XjH=#rKqB zO#p%To#du5A8tSw3Z9>j?j!>#a~HM(Z!H;b`LEB7O+VR#-jk_TH+R2q8ppLsfDyRm z(AAZTXZmCqoh<&Y-WPTRG&86UPglP?YG5V{DJIhKF1<@0yjZcwwvX>K7t z5;CC@D&!5uHOZf3BPTN+%)7FHUS=_jtmaLw1sL`qz8QY|SpN)(GykiHkb3FgFzKCk z=rh0XRl09ry1(!S%b~7t|Z?UUA#tlybfa8aRm*EN&sh;$C2zn`#&%(ft`l@Q9b&n2no*TRI~@ zP#z~QxHV4II$M{aQpHgMF3oDKkSztOt&q(^$Zfi`Ed0WSOC^w}BKWS>&Ie8#OIO;& z$?1=^C1Ykseqbxo$U7D<8*b3SIqVFma17t)bo4S zZ_H`8U7NlUebQ{%)+8y4C`G z-&Gdu+OLd#xRrw1Oz_u|X~s{T-vrMgbh@@G3PUn?K4PvHbUai23N(Bx&bW#|>se}H zKbOW>WsuMMk(#XaGqw&rJ~$j z3>-JWZ3*h_?%+jyNXG7&oPvz7h4SflwvY)QdIV+xKj+plywJoLtCydxyvUaK56~`4 zFS6hnOLV>%nBp^5S^?A0NI z{x;7C23t$dg$XcX9%?8RC#eG{R$e}U{?F)$-e~4o#m1pMS@`8@39+nW}{Td=gQWHcBx_FZ5r309q z!VTgu-5Ky)(HLC{eYd=45?ayDAIc|Q_clf+pkF_Cm#(NDE`I}5u6Ns z$s>bP_+{%CbF80vyi;G)^;K_BEltdj)7L~aIS#RG*tg)ZS{|^Dxy}S(evopkhITM8 zgt*Iot%8QXpo7eKMpjXRNtCXm%6P|#lLADHq~6of$^&>g1;wVVLvqs8%y{37^hcQA ztf_ml%}tn6%uw1mhMFcTK_<-GyLx26#4%DjuvV%Bs3eRX1|UOA9DeMIAFRxB3!1Ur zT88DrMia}U(4ZAmdLOc@HqF|pELq>gOc+L3+eFK3SvBcA|+eCPk$IXqZH7P^vm#+YEmX zW?(KAH&p;DT5BEJBydAY;6w3I=RaT%HaMC8s2tF-dd=E7YVq>8SYP58yqiL_sydyN zeR8)n-VX7e;)j*w3?fH(5Pj&%3wP6m8fy45y)mkvlKd#~2Bvx(Ln6KDEf6kT^8;>njS6!xpd*Z|2zG0@_u|dWBT1jljTF9pzGkeowc4S} zhEr0ZE05I6oJ-By4o5n8zm#%dOsxLQanBT^T2u2Jc<+omGjwZ+{vGMn#VakZy>UZR zcU!&5yd+iL{H)&ib+?Ig)|2ryx)}MxbA^Y^S-WS|o{jPKK=5dfhWsp4`e=g)iT6{q#cj! zeAM~y9QU{RQwSussAwc!Emy_$5k5E-m>K`>J7GTn0%TcYVLB3t6WM1Y2!6H^g3A&= z1dTRvz|Z0sd9F-D>shM+p)>s8xb;2*E~Ox zm5}NQaNQZ$sI+QX%{PaqfLOE|ZoYh9wdZT74n zaeQ;v+ss;fYV?Y(=rwI*b0yczw(e?Ti?t^YI@?K*$5_2)CDz!bVx9MoI!dZPDQy06 z@3dG

@ktO6}4^UqGNDt$#!3Y_vPnZHwDxIg{^Y$h{%ii+BWEKe`rq#gy6rV{R3 zo_`G9B}fAbZd`aI_br8LaN2&RaWo74sH3 zq&_5RCUJLD_itO2Y+66DTN>Nur(lT2Ik2OyTtfUdQ+ok+d<4Wb5DzJS;%fHyws+%EI64h5$e%SYdyE^?fq(QzV#Upu4k$#yC?E_$L?Roi}d`2XF zl%i$hSoU*b>_$?N#UAQfv6wd9@0s5vI1-H5+hYfHdvJ$a7Lk4Ccj|Nl^ep~{)d{K( z%S1J4HhhAZAKz5&sq5^lWU%>RESL!^Dv(xW)6i1osh=<|H#u~_@|=!5whCO!XTH38KMwZV)bkqqC5X(8eQ1Z+A%WKF0z|%jHQ`d_rlF~In9yi2L3d- za^MI5iwA(~xL@VZXKx8f6O-IVo%enN)xP=4rE(EP9krfiHp749=1REa0$8F71%=*C zZ{vNA2_j8&b#^p%8_fwa>tCC6sXOSOJYs$ElDo$=prq5b!j$&&!udhM%lLk0 znsX&}mxw6ktJQC~ZQry`fhr?7S1rP%@}JqF6uNN`TZ44Vf}Q75Rb3VPiH+{4Xc?LC>cgs*5ZSW34x93yjYUcllUu*Gg(_Lf6B(!Wls zUZZYd<6(RJmKARqQwLhH}^XbMQ zYVV@n__Sb!tvP#K?_XF4dlin2>_iGml5i}#yNQM6;8Rzpom`ZE4YNLW{t z#z%!Jd%2-Av-m(V68>2;PrKK`wCsRy?+;h)!Qq(R`5kSVgzB8!@N`pxAvF6E|$9_fl zG;)rz{8{45Zk3mo!^k)aoh_6TJ$x-5QawvrTPjUT5lB#}OT1Uw(?}5Lz7t9~S;xO8 z3wA#r{)6%@`ZK@O5-)+CINhPMAB>tLWq{FXM|)Vk{(~0zp^@#PI~TbTtE{ZIh~iVCQcPl^tZABw z08;pgPv@UO)gcDwT|lLQANpVQZjFO;kT4)g{cSUovCZ&QNL2~#$$8*)OtVa5Qdo< zPP#3Ec`>)7DA{jfo*e6`e{1Y2QPlC(m)ebHaHX%Pq^@|{B&-tueMX7c89amA!Myx9P)#J$`HErt&tK)jCLSpWr->!x$4GguUZBj!k& zCz9~PXl(fGyz{Fyf~0@Z&|+hE8yNe1|3JA^e&Qi4RHIc#lsX^YwPg2I`EnvAGk$A* zoAZ%!h4;c3#8ZlryQjpIC}+I!n1LJNzQs8*JnL>>NA5k)14;JC8Bcrhza+68&T*nX z78IPQ%g~sIeo_C(Y3@H|!W{XB{$*@GZjaO!lgW#SrQ0|b9xc*vgY2(uc+X>v-OAg1 z$IYc7rG@gpOrr9%Q(NVYo$-x-{&9r1zp1Mju`18a`Ur>>%Yfz(Xj)h>vR`riNIx3o zE*Quk#3?0|2^w4t9!s*2lXWfD8%BmyK>p)s=W;npg7v%4YrTOrD^Wr_th@S=Q?%@CDLRlzI;fG6fp1KJK977x&Qo0^oCuhTQnC{6EVR1N1zt=+Myb%b?o7vk2mx~T)f8+<1 zyyBvXN>=Zysreg5EAN{wx#rT-wq~#6sbUe}(GkWwVfU-mkh3%4>qB=X=KVY_qa0^w znteUp`Nc4*=kc-ZHz|;a*U`|ro)^1r0ZqXYi4sKAPrRWns95l%z9d!n+NDs8GvZfG z|MAhh_ezPX`B&zmYVV5@Q}6Spc;yZ(-eV#pT#5FBx!`Intkn1VghAkaeVI)*(&c;E zLDswVl`lh|wVkN0(dpn1n>LrgXO_1uwPv&yvlI1`={DQuoO3R%J<Vse#XDf0I^7BdFk08?Li3dK zMyCrZj}La$NMup8+(U|0b<#&7Rg7ZgyIZwp(52pfDrG#T^$d|!(^AoBk^N+^;j;GG z1{tA}gwQU>EzG$|j>Q&87AQ~HkrY^N;b>(Ie!P$epUmoAFAt@;&<_hx;>B7pyV4xc z#n0e<)NxcyQ!o*y6g;b}H5t{K4n=88jD_3H=EAt7cj3RSxRM5BWlJpdzEEcLPF40#gQ+ktO!BI+=)i*%T#siG95toI``= zV0V5eMhKn$g=`(I+X1S#%gv>&n`A64Ts(UC^VO@TcXjZ8b3?Y-wC;aen$rZm4-kva zmn4!~Qe4GoNWMo_55GW0|G%iQ56|V@u+)haDWW%*wI{7&Z9 zt-iUr@??O_?}H$x=D0Orf6vYO_Vc$AuHA$2!&;ivAXzsZ%hgG{T5;C7gv}L=dB&r@ zkbo!mRSb6Hz9gVZxQGyw3JS z`f43D!Hi6L2zm2r%Z`3uhe_I6mfe~f62?Q~Xl@f;*YVTdtHX>pcd_cfk1DSLFD!N>%h&V|54wIVp4}%m@lTmzL)q}^JWK6@)8V7d->ovC7 zm6pzD@q>|PWgb%n%K>i`SY`N0UP&f*JH4WrX3J^%68U)G$;k~+cdID@LkVR1@o7=LRo@6Ql`F3KDQ8>!J}WnE#? zK!1fAN?9i-G9l$A)3sV9aA`xG@i`*skJVJS4EZU*De{#T{qeIX*EVPUz}Sw~;J1wM zL`G*CQ)_WK1>Pj#%`BXw{Vv>6PXk-g1U&$B;!2c1RDhTUGx2H)xVss;bIh3G7+Yah z@A9R5S@E?LY5NKrtuuJ*qdFy2&(pUjhV}f2N@4F}WyhAleW2^bjG*>Q{nbTzmNo`e z&!ntRWpTyj?Ke#e@7Ax~gI(2PN3&m^^lBvz8ETx5e+?EgB>Y=ude2J;*ARdfpLK2u zRv&klGUi^Tf?z2Nx?G-F@ys3$eECYO{(9tOHU&a|!)47}-}cqcdg%Nic zn_$qieA~FbwWj6PD+%dm)uT{EKeFtcFmm_8V~&Nn^NGVdJUvPj$!+yRnMuoxqG6rE z*||)-qPMb)bSmC2y01 z=q5fMNHWSOl(i)faS*bG*uM^Z7hQs8N-e?rrMTTiYa| z)N*%*c#YhjM+NaEKMxmNT;pS<4vtN#OJ3u%9BL@)JU?iWz(ST<-6?~6x%EmG+Oghe z6qu=78I~42mdYK-wL8+t9mr!gD2iXw#Q+j~B_et?2-jwu3C4C_;`2O(P>BuhHS_Nx znTLX`hf=>|?wj3$A{Iqp{^GDS%+PnZZ?6=2$>|Lh7E4U5BWRcqf?yR z?=^uivcYXagr#2IhCsL4`8!k5I=eWZOz!7mEyoL#vdrrx?ie0e>V;y^X*Bu&642xRj|_n2*l+Uo7w{#^*#aVHhNh0{kuQ3ByhR@ z%r>BBLdSfbI1trPVJ??qIl zMlP5tbUEsBd3htnGY-vunsJFxuX5^@22J;bId`tHc#-U%bBB6$Ar%Z2#;Pm_v-kWspTPmW5@G61C4tAq6y5w zwNliw?$oQ#!p>j`8W!EUPehHUgN;}?4v&OJXuRhR3SX|PthW3G1#b660hpZwhzPC~Dq@tLHRL!2z7V}w;)M<$dM|s{z zzY)Y3es*6F_pivMc|yv>gLWL}PTPFbJ&&xS(3&hFhFN13Jx*9Dj^n|M)K>GC>mUQ^ky zwOf2O8W&F!+!%i69hxCi{4u*)bH|YT^5^gm@$(c?j@P;4IcnAiVJ#8`+0{ccp}$B3 ztfMQFP{IIs4X)9&{^LZ1Yv9Q(NJEm%k%Vph&E|W^4iva6a*n00KOQLWx z249+8itR9b^#DZXdQx*+C|eCkPbn%*5`^=+uZW;MinDNG|Ne*1-Q{XMpxz%LhyG5W-d}4#Rhq z(hj*BTl=KXTip)Q=tX#Tq=RvWlZK+W6TY*EA z(s!pa32gavYiwOAD%7NO)znS6_mmvH&O*ObJ^TW^ooL_^#8txKu{1727X0QGclE-ky(C`%__dAr?i4IDN9Jlr=Mjg;ZXcRcPUA`$Q< za&cDAFyFS9{px!?TUX_Thoz|zl1g}5D<zPv2e7R;#|MjZ=smBRRIlXI){~_&x8|T+d`eHXaHxmV3|0 zr?o&i-XdY|r5`D3<;N_O!630LG)2K^EJc(;$U+IsO^wl`Iv{N4cu;VXwO0#DA8r;U zEjwQZ+^^=B4pSVd%e|tDGWr&N*=G8r;>82dMKgZ*Kn;WF5p`xXt6tCs14UNC?IvmcEK#m=Iv$p%{ z1G1oDLbo6XsMCxvc#xym$GD*~cFY7XPh)DrQyZPt-;=fW^igk4k|rZ^Px9$sHy!DZ9a`_JbXi-(FDHr!-ATo}5E(3kgtXI{(5* z$j*5FA1S(M8aJ28N)rG*dtBeG0qwD`up5%u<|%%gk?#)zt;Q9<95He+EJz42dGvMD zaw5uolVL@X*;)!aWN#)cZ@(@e&*BBFHD>Q~@$@$4m7 z4+!Ay``q3&IS3RfViNrz`t*z6p_#?^Y2TO3mK+c8o;-jw@x1nlkBjr>R}XV#oogK) zOm$nI>2S86J3q2Q4e5v!1F|sR*bufFfmKBKu^K$LVRZE*+eAjDP&P=ht&u@>Ew~GO z(YX}AK9nkaId@4k=k_Kew}1|29$<9Qz9gJlWluu*lJX@qDwN`8X-#^$KVSgMwk_%A zbA?<_&nIY?EVE2>IZUSN8Mf3rk!kx9crk8v3AWD<^CDpRYso+kKvh!U>>B7YAbINN zf;)uvOXANjbgJfL*d>|C=&R~czsHUY+M(AUW;qKCO)a8Sv#B<+`-*dW`PCEd;dS<|Vi>gQ{UST8D7XZ9)}ejVQy z**OaY`QN9{>`LSI(}qOnq0$!BXHWyeoTr4?$~;YS-j2$B=s}S*n&bVlBKUKYcscw` zMgg?tGb(eCl^R^e)BE5g5}D+jf4r~=ZLT@CP?s-1{AQ(YfqX~Ps}{SR()ecGhO>ma zGMp{9tH$hzbuET`OA=Ln%FO0w``r$EF_i%_Fra#`9opU7N3Tr$FjiGE2PVUGJK)Xi zT^?>b7b?B|Cpe3fJ}fn%QK`LK0g4tooBLD@WTVHss3&fxK3t&+g+1SOa1X?_V)9e> zE8uF^&V)t&Y%K7e*$lgAwJ%{^a1d{#N&v2FB1wN0m;F0Tln#Zz_P?ZuZC-@|ydi6G zVIt^$Cc&ijYq6*9=$Wd|gUweyewd85`QGCcNKhrK1)IpVD>2P+$$A=+g%P?>sys46 z^yHE@x`V2Bf3;jR#>u?Ew<)Q)+wyJKkrMM>>*VXFG_cLds`Ec>I#(4@`C$GS4*nOu z+MuLrZ%%~`9utdLEzhSBIo)74efalE86TY(%;;_z{P&0CPa@9NtFSUQH~Us1LIEdL z4%~we#&aLOhwcL+`<<6AVq+eD05cBj@LqF;tJP;<~h}0gYuX{05c;?`TX+e;44jgiQek6p3aJ z&;mti#A~fE{m??({c3>gN=Q0mNEkRg$tg;TbNZVNxQk+66mN0nv&d4HrNn%Sw^zlt zEs74c*rSle=T`w;(U0LXB}8m)IOf)K`qqMuq%iUsWZR|cXz&>9 zi7UO<`$y%bOL4Bsyxj9F3A>u0=sg=CA31TV9XGSOm&oo~h}alSk4s)$J)r7Dhoc5K zuN#uEkmEKts|{l0_dqkGiXI zpS6JLfD5s=FQkjM$6m|6Jh10tPf9}V`t%`2@mIIa_oP5SY)g`HiH!=Vl0*jVJQZSs zokro92*3Q8I23KX&3Ti)|3&vXa@>$8}km4Gk)~Ba?K!DS*Asky4FDC=;w+Bf#@}bLwq2F?(F(mQ*`62s( z?r>;`pfj_hR3)Qnmdso!n^3mi+JqD)$JG!!Fqc`>?w)h(ivCEjf_WQpQTrn}@L*t$ zWR$Yb3Lrrl%K5qN^b3uZ>K@UtP-N14T+%dKVS0uU4H8k@X@DPVi8hMU_@0pTY7*A5%0X+Vyt{4)|goyIenpKA2jhq$!!!&oJ{u7cF2R{9q~Gdrg! zk5&DOHCPBFG?OZw0$GLlW(I6EA((QO=wgAPy9QAYE~3E1&TI0m1e7=Cs^eRn^~gRi zGPcUY&)jRXx#N8Jp|^y9Fc|e1FXfHhqyHczzyE`+uMBIm>AIx`g|<-Kp}0eFcemhf z#T|+lr_e%ihe8Mx4eqX`xJz&eF2UX9gg(#vz2CXcfj?Z~&OI}GY|om#*HVgIiS%%E zBX9vgZQdl8E-&hU&F!fQCIIo@v?~oKVb}Za_$3{c_afC2EZRe)4Ew7N#wb@i)os9w zd;99oV&cgseyN7CNfv*$8)LI5VLPtYY_`lNQ6QvVZf{IXN>WO6DT4A9@7`4O5`#Jh zp{5Q-N;ZTS@cr#U>9@@RK;W`_i`xk;dlf% zQ?agB#|+}#6WOKRz@|jLHV93Abl-0R%7dSqSfDPRPFM^0uc93qKH!Wzjtr2dDd5*9 zRG!>OZ4bMIT*U!yV`x%&o%c?!85iweGw=sfY~$g-qr4MMPK^%&u+Nxe%DiUnb0c_x zksp%vy4~_ym%yVtqEv^Y z%u$gPpZF?>S}q^iLnuo}l)8-;n((3!5#3Lcg{42)HEse#X|qX`!u*Z3t1H=Hhhj^!a9>COWG`2iBS@ zo@X?e{88RT`+PEKAFRx<4IHp>dy(9p--Tf;>%0#rD$Y~3ru4V9y23f$v&|w$!^wJG z^i@}?U7wi$S^5BmAg~M5sX-r6M+6gX^Yan-@^u!oG_B+pTO`C#4tx-zaNUy^`FG`m ze&(7Jq}{SR7}x$i>tE#|EUYO0453HKm2mr+47Dhj5Gh+F=%eNyVimdgi-G62Utc#` zxiTu?zDCuq6ABvx-XwqFI{x&h%j_}9HmSLDCaSTM_z36&*!3~l<~h9^T=OSt#Tr{4a{IrIU_9h0cRqaf z^ZRo@1pRu_Ei-hMBIdc&tgRr)mrMd@0wPB9x#IbB5GCY4c|HVv(_ZIxtMd)7I@!i+ zzY#|O8sr5&MLT zNbY&1cnG|NziE2#7;%AAIjhjCMcJt5aI7DmCi@p{dWs$Yi`2n+ApcDTcuIr&@C0N0 z_o=_hw14wupB{sw9`OGB-;8-}1F)6|FB>zxtkL6|#<-}ZU)=xm*2SUd?3yeRMa89^ z@{>NtL$;L8p@)6rO&bSaJDq(uFOIZ6L@mtp9@{mRI%$;CgUiWMfxc)K$=f3dXLiBj zW$U7o&kKjU4qVk5By0pjArMw}FjKw9VD>X$m%!<3Dym8#t*Pf}QU2C`Hv@Mi-IC%-8bolP`yU3tIl_CK!$q z*tC5>kHX+Q_%(jtps8g?a(_?ZmhK{t9pv}bFK?47m9wdtV>|`WBgEboPTB04{&>5& z!_7Nd0njtVDp-48q66;1kltiMt7Tauraj7=?DIF4lEbTRsa^y|Y2&@aE>}4Y4>}At zH=b4=xG1Zf3V3se3BS_*=Lh7s&BqjahUT~=CqqcQ%@KwBweRw}W{TLdIMX_{VK&zn z;$G|d*?4NW_eI_qR}o+M#YkqAZa>X#?W={e#hZ4z)UjWXh=JPW3QywgUl>xe=KAh! z;hMx+mx*ex7CE|AG%(MG0F1-SV)ThB5Roc6lW`IeQ}x+|cT?ti{+V0RQb9X*dkGqx zbav`)7(?ONgRePLtEx~p=<`qVcbZmQ;10sSb=^J-8nbb?Bh0Lz`WbVry@W!3&6{ofW>wauLG0W{U;&@L`6=?v#!)5}sd$#_iQpQk%(a?IHx56KQ zV~Qi?T!rY=5Q18{w`?w+9Gz)z;kB+XLGxl7NVxnyEp-Lck3^$c7A8Bon2~#(67tj&~*oOv8ei zaS1Oo4eVrc+1UAARbY`HPOJC9vZ5cnG>{Xeu7jL}cT5(QFgIS_ zAY)?3OAM^`My})YGKDh6&e!zgt9z!c^~L!#sHFEIQxQeq{FWE}2gCdugbcgg;lN~b zSs>nW9W545;t;n3Xbx2*H$TwIaQoMgn1w>hcVOEaqepboIhs~F`>4}HuljQD&lK-} zr7)ySZP~T!NLUTgZXzb-;2a#^f4jc&jPD!`ljo8mZ?VzZyZNY;Wti%gikxy)c|E&s zUz;n0&TCU-*#B}qL|vDsxFS4HOer!g`PRM8yM7&Sw_Iu|#&TpY{esVco9H8VWZ?W_ z4FvrVQ(s*l<6U^RLp9MZMpU=K;vf z48Uc3v#JccwkM)<=ez8Yx|vAzrJR+ zg8cy~6?v7zW4RusQj&G_DSBi4UTnx%k!)BG6vTDvvAOz=kYOgIB@ww$d^Lo%^{ZH4 zg3Jy(Ay>31xs$bwly}$J(Oo>MD*-Nc{L40;IadOIk2xgx4)fpPag|-{e{|sLKC)`t z+DWJwf8ByC6%I&Nv~_U(SpJEb!?hB?5G^GQkR_YEvr5679NFX8<=-nhPPcPinBQE4 zrFps$Sf8$1lzlj}uOB=>W*lTG%%W8@oUZu*2l5d1_CpMEbRPf`~ogwK;qtu z91u};AB?+IR~|^f8&|CV?m>u8tD7rBd0fHoBT`gJet$N+Y|+%bravm=l?-G9#;=ygT&hg}@hyeWQR|n&ja7fTHcDL^yLWbkPEEjJ_8gWPs^2b(5 zeOav{))t0bl3tq-%AfP-6p7E=;mL2+l(NK)BH)lhxS;y2{v3$|n|?ptFuu*jI9lhk zr9{kjCq(#|HVoO-SacT(Jd5si#sA(As~YG`^HU&i)G)4a))4SU!@6d(Q0`yO3G1f| zqkuH12CtxhlkD)Ps~e&ls#BK;(+X$(Gw#^{r!ew-IH*Za zhYwfDc^&$`s(B+mdoL_(yMAlO-5&MELdV{WIgh5Y+ldu1`%(9P%~)Lnjo4grE~XEZ znVp8|B%xW~c=-5>%IDNg8JKB@hh;TZ*AYciC;m)qD|;$vYR2~Eqe;>_6QGCWi(&En z>vaBO#s{@c*OQ8-H}OW2w@&r1Gi_VB;3VfQRef;rY`18hl%alA7Ct(_}U98}odTbejVHlijn`v+F#9 z@Y!FVZVoBIi=;<@&6mzx8yx31RG~~1Bb!qEz{|vh+17(}XMhJb!$;>ZZteWi3B$Bv zsaibp_m_vI5tG!o$^CWsom!bX`4D8tQXC^3q8bA?Nj5i@L@Z{Q33!UoZPH zV}m2D_Bq1xk2~J=YMxW| z3WPx8`1iEXldF^p2|sOH!mnrN5#ZxtW8J?qHFmG(6c@{8y$Wj_I8|!n4)c6Xok*ui zH{kcB`pgBtKRI4}7?ncIqR`UO?8WUDP?vZXfn)2>Ywuvv#pLMHUy4F#{R^+JcT5Wug3Cr=JE>V1YG0*Q@=^mlSm4d! z@qA*lL*QZd9Zz#tG_bd}s^-7oZ{7E;zN@AH9q*NPp3aRg@|bwtZ1LZF&| zHdoL$Ldq0`4E#1yfv_f5xYBS3^u~Dwy@1-=p_ypb7~s9?S!DUTpmkh-x7^NTXnOg` z*b!dE&nJja;YFYI=N3sXG(X}g-~jtVofq`I{C2~w9&9KnREAngVc9SY{SW#f%df5w zza1R9-4`)IqdUZ>4o7bpEq={G8h4$&0?5I2-iI6-0B=3ya7DoS$+3vBM03UFsM4Ct zo69y?%+~#lI4L~)JOR*WmsvS=E5+(q_a!?N+xB z5j~r+4NY?IbC(Dix>(bHAJv60nC^Q;ba*I9WDh*f?a0JqLM<}K90y=ck%gB@aMJ}c zWRJV7VR!1Pn=<-&&3E9wK%F1TfL&|q)oKRA1r*hM;A+qv zT7Ly%V<7Ntb!x1l?<^wis8^f2=V5dH1qHp^Rg`-m?OO}IuZY9e2mur)mMIWQ#&MtP z>pxc&>TK*P#%3Lk(V%Ra5^suxb_~VT>xp`o1F1*?vJmpUHbHmAJ1hBc_1+*;`FRH+ z4^)z<)t4vl1D$d5&f_3Ix{sBnS0ksI6Y4**UXVD?&EHVHqRG=b@R+3PmHbJ|Sg{GN z_zotPqWlnWKlNr}$5lSDhL2y8G*5FNVa2#{;kk>rq@U{h2QNa2EOy=9Q&m$eXZ(v+ z=SD_;0f}to7P9ys%kSR8M+N};0lf5`wJd+5msR1z+ymB0ST%o+U}~yfo`prCl!X9!Wf8PmBlnZq4OX;rsxYHe^vnCd+_b zyd*^#&cImUu3g-vl104EL9YICAc)m9JnbO^SVV||q4B!;>wg9r{|E8LntPs5pYpY| zJWXwNRvwPpE>{Ke@E;ljoim$hIALPh;^rnJNxSLO!E`~&S4HrkDzE8N(@Lv|cai-o znf|llE=!*jOpYlfKL1evZF)6ox5kv7{tHF3A6t3X#STp5j z>Vke^=}BGr#pIjU!wLVIY*R>fsvx#vr*oVRhy3Vim_XrR8;y=AwKZ_h*Z z;WDpWOw42wtvStuPm0sff;f-V;0 z-}E_vwEKChc<5hUb!5=76R`|+LmhOj+}lNR2C~>_Df4L$0laISgZVQd))qv$R_}Gg zgs+%31)4*2Kp|Tryjy~~OEir%(|)-FPJ9ZeLz~bAer%lG_Sn);*Bw~bxDe~u`lv#y zDq8fS59pR{6vN5>No6L0rv<#~s@Su=`scHrGVj~rXeO3q_y@N~J~kZqB@>t2?|g1} z$M6}ZlSyimcg$n8744V*z&UB2c%bM7aKp&t-$Glt3?4{$uin*wQK!pSLeq+ z9aH|TRTG?a`|%JEVoA!lPU8&->9W(Gp1Sn&H9cK~Svh~E7M`8Q2*;X+D~}3Povr`* zdJjAV;6l&qy5-OF^c-DCZ-M)V@1BTnR?i%W^(;4A>u5&y({jV#gbh3hvHr+t<`=3{ z=`Cfn%#-#C2G_b@L~$*V+%~3;SU|p?&uU%jp*bAx`EJeDP3m*S_m?OtxpH^ucpB;rhEo|_f`~g|{g4DyQYwBZiqQ?og@fLi6VSRT9 z$dNYJT>P>}Ox);zK6cEE^qU6WXgFpFc=Wwoh%qf~x}s`dZGObsiQ-7iny#FlQdD@A z?y9Bvp53k0NH9~Kp1si#b)#_&=PYw;7#tLSjFu{)rrX$k0KN`ipw>j9b+V%a~ z1cvZVu+g#DWDvNJY+$zYv>avM1_nzrxj<}5hc0L0@d@)F-#1EUTSqqZZBi5pY?^K4 zKmG65`g^5W*p$ADcF4O*l zeHcRNHi!W{_qe|`XG%G_HFKS&r;$TjYilY5Kv&DdIeE2~HosM9$^X2azpu>|00x`Cd@Uduf+UcD?C6ck^>i6-}&P)L^f#2rXnZaBpSpNhkm$qtvf- zv**gGh3YWSOyhJ!cZR}wGJZg=Dld^bt=S&kfAx@%Hb#Wg-yck((PZ@{@+dNO#uRB; zSY`nDPT<1rD0Irx9MEGxvtim%{qP3W$^i6fD1>?Ez?Zd?llX9aBE(YzkW8^x`JRUv z>DBr&lDh@H{ctG|48f1RtxbH3%~|K=FpJS!QnPp?s(p63*4Xq-Pma% zg=MioLh-d3Z({9B84_*MveRVm{nJ}lU5h>U*^(+=Wb{^J7L+*Uo3THMI$!C7Yc%~(PL6U>6 zXVr8b^l`}+*Y9ks2R)X7-1BA==6Q%)z%d6Pa_2KjG*GqZWoBmPA-8ZK0ZXQAj{m`W z){r=Ctus}aEuG+_nQxgux%S1lK!a*!`5hVpLj4!c zWW3$2yH|DBAIoo{kiB)6kvn4SF|&_;1GWyAJkC@M9X$BS0%eT@CU9CZcnf4{WxYz( z`N5mPr90PWimTNk7QFt88}66q9{x)Ikv}-O z7u+<7Oun@O<5di|w(FPX{U0@3WXY;HTn(9$R2rO4ANF1Xt2zW(Z!I2lra}Crrv#|B z;(9pJ>5h&EdtP_dTMzpK*j`SwQl$}l&4yX3L`9D`h8edjEmMtw5)Sn|OwcMC^>r-w z=5$-`z1uIRLvKpNK*u^&yRnzhS%uYmhYzO^lp z4JIizqwhR3;*O?L&9gp<*nFP-V5Isr#_RlZ+{Y^$E3^FQZy7NKeZ1-Mmc#0Op2Z#v zR9R+p1TI{YomL8pSZmYda9_&l=FoR`hnQ&EDsrWYgEN2thZ>+ZQ=nn5WYm(Dl9CEK zZ5YlMrm|jHz57Gd>(_nyW8Gco4J+_f52rB4El+RDAtj z3?@A;hQt>D-@>vDn!R0n4 zMi@e7*k(0v?q%8%Y3f+BHz|?D@Ga<;nO)df9iwN)*Q*_ieGQFdfsoJp?nR?qTq=h6 zP+ISlcGLZQGjc`ctZ5mYiF6?#o-MKRkKN%f+X4B+u2;`Zr5BQZW)Q#}UI6!AlRF<);YyU4p+APi#bSW*$pdS@-@Sl8-o{53uaHIX$Wd{2 zR%L-Qp9lG-zUY-TUHC)zw~UMeKo;{S%k^ff9T~eeqrMHF@Z}9X6JX>>n&RA&dio$k zxrcrAMIoEik^ z1**VbgAw;Ve!(QXW0G;JyLpzs5m1%r<>6sZ=AWEnZ^Mc&vr@T3l_n#LT+lzUIF+-3 zL=nG0u4WtJ!3}cHB=G)-N;g`~x7h8&Hi{25FJoE309GY(>%ue>U%SIITc7M*ZU;wI z(tB@}!Li(Ou3IEsH5z9#IA#lDB(Dakw03x zpVP$4uL(BD$}o1c;0_!co(`c+vB%0Iot_K^u}n#MpT+>BLrHf)QhbqxGuxl#XNpS) zTD#gfFr6~X?c5GMY@yWKy! zVJoh67$M{*$nRD(k`UA^(0}$k-qZO&dE6olZ@J&K8j^*Hs2&e4wLL6k$}*X-PGiXI zWcwyz>r%@}N6Ge>DT7t)7+}(@g(Rd%oU*25*Nt-lskq13O0wMphptWXue3) zS2aCtMxHksddzz0c@WC7PS3l$I?R#eOo#VB1>bVK8tefS#gww$z)ho9n(+*3LwwA5MRN_nAFSV7+8%3Oed zAOqUTLh6R>Xp8ch&()TekNDo|GNXqB;?=EzMW>%{xwbb2+(mq`Lx>g?ZIEjzSePB{ za!VwAoNdGQWm!XqC7P;L*?epprwByB9^LJgHjLE;0UCdqOWtmAUi{Y3QB@6`3mR4-9Az>-KHCZCVJ<>MautB@m=wzE4XN7T~8 z48ztrGWj1$3x-mfL=6pRJ8S)vIkWA}W~aZa&VO4RS{-L=f!{l-~KQA zE-ZX`H-a@~{npo_1tr6^!TKn^Qm>DeUnOK^GEi6TxknG=8t{ggh~i!VTb?G)sI@j6S4B`-hhLDrE?l2kY-k8sgE&6ti!4%F zG)NW7l#}u(D15hF|W=8gwwmb}RugWH(wS(>oKPC2HtPVt_Cg?_b&)YLL4Dsr$YZ+jd&l3&{6e8WN( zX&cGYBDmw4uQmtyh2mlGl0|O`R=wk%6vg_r_Ui>6Dz-coIXN{*O1WWjA24TNq(eV4 zgl&n7oxp`}VAzzY&-*`~d{{mv)BoGHkmeP84G)#F);Zta)86H=_jQ;6SlnP1!?aC`}EFLxn zd#6ctuy}&dN6cc;!SI1W-9R7SVGq4NuoZxlY}O_BxpQ%yN>EXU6S_$Q-+1Z;f6_0| zCZ~q~eq(csZ(l*?U~1AYZR|G*!<0>@$XGdXW}x3KkpdPqRA`d*L9_+D$6*1J@{>c< z$=suyix%t!F0@vE&wV0b<5@U$pFr#-VQ61Lgh+=AUJB#gZ_!7>yyqb>p01HN`QE|$ z7u$1Y9Fz{b@2h)mxsmX``g7qtH;Bi|ONFK25rn&P3rV_oIiC-&*yTVfI+e^%SQ$Z1 z0qvbL{2h7aw51$C@^Q4R3S`;dq-+HQMGS-Y$H@V$C{FJs4}_t z@pe}U96*wvynZC)8XpJ< zKR_?6zU;2UZK1-#wjn3Zs+T8!;; z?*=YjF)?u>ufWwU5`4=1j~Tu`3g!Gdnc-HN@_95nJn$dqc>!n2W*IOX&nN2n$|Ctv z^yS!;ZjCBGHaGDUy~Viu1cBOO1d!7q%YaBzuTDd%*1}?V`MvAKmv|3!dduY&gvPr-jsnH3qqtgwOW3Bu4nS&!OmS=i}nI4?&11^SqBoWNLMx@2qG+sFA zI|rTnBAOr`tk!mVa9hs^Mo-jIE{-drmzT%>e*JvV6Moe1wC2!jC>}5Z!^5~W@C935 zaNVZLkarlo37}zXro&3nSG3S+yKLuW<0iy?2`~0NSZO(#+e$Y$xNYTgN;avOR}QYlJsbEcsbN^;^c>2RQauSa~SDxRFMF^rkyB#*NfQ~ z=IoNH-&JXsGs&NQ_{*2@>o}Ol_ayXY+bQ)!7b=O&*5ny*Iv)7!(W{qJ@M$LeEty3@UlA|O5=r}$VcW< zVVSfzcXZnV)Uy@7&^#!00qQ-Ze18{0><(sGO*v3i$p^rX`EZ*cST&OYGPJXV{L*l& zsC`rh_EA#pP8kmGQe}e2ol*<91|LSXKsOA-d#7`BZI^zW@nI1Dz9po>pG3?W7uT!9 z^7G2?#8G~@E`BmMhBINkxdWgdGU7sBPS9_G0 z@NjZ_(SYQjgn~|=E9Oo+u!XG`v>1X5zSfbv8*;^oMlV|?^N)E<4J*Jj- z)|O~@kDY51WX(%5(g_QuYwJ7)ue~1Y-A>N#ipTjv%jchDrJv(q96qkYfvZmx%V9A(D;CjySYWQFoG`A*@Y1t9`?V@ zp(Cwvro)XBd2=K0UuERMqS)m`)bkjpol3Da49{l_P$Bdlbf%=kQnO`a%aY;x)A(}; z50gMv+eA%|utpSj^*c`tOxf(MnhdJy%2%%H@P`=0pmql@1bn8Yqei~Z!`J(DOG3AP zr3S=aSI!2rAT&JQU4*~uzz&D$ZkZeZiH#CESu>Rzgb`Zk1=UyuKU%}ivaH&N&2959wz+U_O|M!&u2`Prrls(kwfgDS? zcmL^2HsH~_J8+Hk((HP<+@eL>0+jEBje6MxX1Zy~fV{s@SUMS;dkqc4{M{ z{NsAR;dRgPWxr}KwM?tFAu~0pKGwPJn4c~Ey~$t^e=P1~M)&0~$#Xc&oe%NPtv>DJ z=2>lBFG=~)i2{@acH9FAb6dw`%14TE?XpLpLraIOR+3;x0fD^)o&$u?g&T#Aa3lJR zx~jg3KR{Qusx$_=4GMG9gLHYDae|kYW;ObKitls=EfjC@-7J-869*JfxA`V?kNe~A zjt>UihCGLmn~RREhcb6Ty_;2JO5qRY9HnheXxG;W2>Fnbo4)(6{Kqh@Ea=r%T`y8d z4Y>hsWY^_J+2pR9-aJ3|{nuc^p?0+XIf^Dq?H3j6dj`=tlS@CD_eW4upXzt#KLK<6 zzWpk3$Ie?iu~5+#KV@Dyifipfa9*+KUItxKxN%Y8ep+Ld{4|xT7R!U#p+ZwCPd37x zG{-GpyaLAaXUH;dD<}H1vP25Vb2V&AXhRErNhT;#l8c2)294^*YJ8-9`CJtFL$GAY zdpO&t&8;v&r2;P*zCbrx;APsDIO>8&Lvl*8C)**VIziB|S<||`KBzN`{RJs)0QC8w z1CMaruw(_(XMy*_OGUw-Z{WiLe*V?tbP18s%C{xrG4u%l+)~`mtBD7*!p|`#WO1(x zUSqEE4JAwTboNT%5nUo6Fq|Y!!(P+7EuwW*!r8hOF2rf4t(Co7TPAfpxHgurZ>IcA zJ^IiG@u{PPb<~#%CWUvs?~d~5Da5wz!h5!;d)Ws#qTk&s@g3x|*_5(7;$qZhqATVr)F!4u$@*x1|?M#EMu6lA?b2A`U5qyxZ12 zG#rRRJ1>H8qYkji6E-5=-g^0!sZxFmJqm~fVdfk%%!L1HX-IWfawje^FEAmK)8=!0 zUc&Aq)n~MxPuLXV+Oqg=18fqwWcl!H>PUHf)(Dt^x?4T8L34rps&XRSgG7k)%vz7$ zUjZf9Kkvy{+h<_AiFyF$+UwH-$kiJ#V1%P!7b(vu8i+HfCfH>pdAz)hgrFT;7Y%sh ziN2-BIMuE2as^o*k&~T`%fhNjN7Ls(h9|xqhYI&{a86Y{PXECNYB!-T=ua-KF~wK&@&49Bbubw) zzw~y;%~vt^)ph^4@R`dYR7kb|`g!26Z{8uz41d#6g8s&N&%xesjy%j3iBc5Cg&_k9 zWltSP+wnfc-onkN*OkUuQsguXIerBk7LFkRyhA;%D7_-M;+Ud;gVr z`=r#`UcZ@QD&Da4ll@$8)Hrsc%OKfBr}sPJwue1P14`UV&5ao?Z%A?x|1n&47RUJ; zVCjdr4p9(ub>ms_$r~;=niM(Ha&7$vxhA_QLzLVY*{KirHleX zxWjXzS)=%NQrnl8hO;51okyO5kuD+_^cgzAeke3$l9fERe31-iUZ8H1ghhGwAxlX! zwoozCj}3pa-?1>=UwiZ9AB}Qm4=JBeStEr?S+b5^nTA1b1Fe2ce=e1fD#GMd$M7r( zi6Na^Vm_zu2<5v>Y#lTzFWZ~gtrD-$4jlIsB~sAe8e%6XnH$OE_5Z%tnqS8#(7Vab z4f6lQ$rx=s-it*50_xk`%@?wSS+EgXv+^7#pI!6;kyKDoz?5(c={J7`_@naK`+m{g zL48j|OPp@ooAi4O4WXRekr3pLyz@zg1K8)Z^r{5lRFAvUjNBKl?xxKYse>1~T2TJU zeSn^)u=Cn|U|~PI z{WV(rH`(bQy#@X)Cm%zikFuQki%&Z|Mu;L%(0Qn0XnwKJmX0|sgf7Ts#?dlEHvM&k zK5-^?tEv=?`)^ek!Z3$kcKxWLU8&B%jJK_0~`)id)lp-p(0@6kRfQ94SV1*(uFgB$-ZZk zaT_?F9x;$xCObb%5i+}lM+y*qRSw)h@ll z-J#@5jB+G|>hV-t70P}Z719P@M~P}Z)RcHt%mGRJj41+JmW+1O(yH!FIY+j2chq6d zcBYkI$T@=NwM@${biw_r1--4de*9Y!-nmKwn5%m~iCv|4IM-Fb{HoW?OZHdN18N+LSf8R#7WA`1Lo@-S=c~Q9ym}9P3%nHAkWM(x-9{ zMBFSb^-h#Omg8Gm)MF^x_Uc@c28*zd6`neZ7)pP{w-&#^NTCl#;+}^6wtf9K9X$Zx zc+b+0q7U9b6|aG*qW33v((zM+ft7#$UDiSYr#U^2f^|dg_mt3v_@EX6duJg@L@5%!qEm*+;a zk@^v5X43k6&Bn`aIOgxoBz{3&o}%Rh{}Y6~eKyr<#~G!Vmq$$DZY>=^VdnxO!7pPd zgn`o8TPjl(4U*L;jKP0L)OWYJFZ>K-$DeJayfzD5y57yv&98>v>R=Fj?>^$9^--%R z+!|Wl-iL%XQDquC!4-55g%u5-e7$JVhGG#q-4r=zZ+Tq>e z-LlPyKkb7ASI+UPL04=;&N9(eyq46|MkdS-qrqX7ahK*;8ixBB=NHqP65qq0c@Tpm zlZr?9X#`7)z)@z@`*pe48ip?37W@FP9HO4iK(;@kV;Y@IdH>W|5ZIsaXNEOXEIGOB zd?dunv#LF@McpDxAC$0p2Wc7|Q`Y^NK|zqTGdbRLkuea<=n&Hv?pc^d3wwem=1hlX+4 z@dM<6sLbY$+qrz0Iu*aKo^S1cr5Ol^wg~|^3X=h3zxaxZWvBo}BmUEzl<)(`4+xRo zvvP{I;6F97j<2kJ0y!D{H)IG4)Uep~ukF`g2Q9|^v`cnt7fl^4ujB4Klqmr?%1731 zi))6Qhv-qEKX&FyrfC`iJYl_fKu`ZlK`yCvDRf4+o2`x=lR+3;c|Pz<)w0nM3CEX31{4t^E@4@W@==pcj#1I;A=`{8A9eB$Im zW<^D)PR2nxQUN}%kJ&T#Cra%1;82K-{tLA5Xup`)#I3dBf?b&Jvdqxy&)R)Xd%L?# zG?h#~H~)F|@}^&2ZeW+iFv+xJ{*XLf<~Fc;>ALY|tit-Z)d&uVT*}LN;DF=hc6EV5 zgB+(Yq?x~moiyk!1FmS}$~d-Y^Nd@XCZFbm%F^OjH)3<3Al(=vu%ypZrvUk+A81iU z!OhMzR$bbz+gM8t-dd-g;}R&Y)mC$c#IHs!Q2q^Zf}3fh|579il@{WNze z^HUya-1_N#W9XMl>0gD1TWr^ap8W+kJ^&arTemRWmYwGKmsBp(C-H3gj?BTA)=Kxr z$GXny4{SnTj$9i!yW~8AI3MHZ;i=>( z<__5ZoX}U#c1gjcmps10kzC38`1sZW7xPBRXb9r`@zinX#oUQNh=J~| z1O3CW;GJ)UxjhH(FxEg>@w30M#1DvLXL5_A?nMtQ(-B3Bg=Q;#|3qKd;nwKIX=}&u zre3Ma$eoqrt(N&LwP|s~>ZYrfeehrlJU-yrT@RGFh66j;3E@!$X5CZOr95q+#!(ec zKKfR2py(q@Tv$%{Oe8k?WG?QcnWq8Rf@H|;%` za5=it1``bQ&NFPuz#p=Uj2IS9o%it&Bz(BucRjaX5u=wO3cqn^Dez%Tt7-c;NbMb@ zmKkUI1}PYAc>Flv%M95e00}IWG3Z-n$A~>ZxGO*6kY+nOJ9~R^`hBSXCVH~Jtc(SG z6E}sN4?jcyfJ8!Sqm&ew%M#=^cAl+1J1&1rp_19CByOOsIQ)WFyU7kSiO1a?hD&REi;vYZPti;wX{AevNTJ&g9pH+q_RO`jrP2rk(4+o>i%AUM z7!(K5*tN2bVVb~>_-%)RlBM+gv0&K}hIvyJZ2iGANWHO&I~f2EXFD)VMOj0Di;B(9 z4zJBx{A7vIRAcW;PE%Gb*!a%d|NCc)6}n0Ar@WsjiB?>_?^UTyo95<*hoO{pwF|%e zb3v>@ZiK9{EI!O6sBJFSzfU)K?eb32xaiL&Wq_SCbVV8Cdi`kuLO>e_Wp zwN=;O(jnccDwk-F6it57OuC07ZO&XNv;WA9{$7ETb9(ZEjvG_Iwc=L9^pKL{Zx8yuCh-Ad z1z-Ft%d2$I18uq_cv!FIgA`!7%n2RzwGvi`=>h3RB(h;vtvAEqKRzw*#mz?)aV_sE zOaSt$@TjNCX$?3H^k^6AEVj+nmzhq~;nQ!S6JpQbfGGvmhpM@8rTyP1#N?Myau~q8j zKYO3Djcge{J*@}el9B*86d14p&m#W`Ux<>pdEb44uM67HS_J@U1aYgfwz^wOUWp)d ze9prDl>&T2hU=})i>)+^OEcqO8x@t>M|)hf;SqRVd#Ok_56q$beLZXg5iuml&QK2U zf*QWsQy#BKMAgFzs+X#VEU(^a~Cfwv+Anbv7oCC1x*hYyPcp6R(r9lY~ zd)#j_CoX~c8~Sstdp=G2-;W89j_fi3i0O4ykPHbP(J+5PMvbYKlH-yI0Ld~cL!e51 z4U`9ex2R0rLNrof(7UZOxmryqXG8&#{00qNn?z!OmTm z1d8lk#kk7Qdphq=QrbFiW$9zBOs=!vnty=Jz0W0y$RcN)P9tRIwqW4Ye3b23p|@aK zsIqtbB3OS5J^U`n8*;mn=6!e=mIW+g6zjVqZE0L+_L#D&<)iM%q!Fx{JYoDlY`tY% zRBg~Wtb&B7ARr(ujihvk(#X`jjvO5az}J*A*PPoXA2+Z5MV*&EeZ(jOp0sv zECPh(BWoiui8q&>7k?&Kra5&@kZ@O2MYWJyT|4<@%_0hIkmu$d(S38`w3g>qvlcB8 z?mr1f@)U7$(Lo)goSa+%*8gVwxEQbn{&^e=D_nRj!;D>Bc%(&o0FDi@U`tE(?|_TO zzQO)iv6B!&56MsFSUtI4t1L5lN0eEeapq49nD-{hI+#QXH=GZGq8uB*yHP}50X+JH zyJi|0vO^FMnQ0N8`tejQ-{~YmGk=>HX(FBWAiF6!l~P$(RQ0a~W&oB}Rlrq&n(<`D zuJw!+0wo6Nip`EZVJ`ZDw;s;Xd@tCkku>Iet!NVW*=J)HME1bM(bhomoFla8epbxU zpMmM#0-|tsU8Fl4YLiYy?XqSz3_1OFY_82_P2$;dJiGDhr+Rn2bSkfE_AQ3373m$0 zVWR;x(SK8u#D_RDd>vf8)V(Ew|8n0P8Y>sCHq*DXs$VgXwd*t<9X3R17NlpAsAqjg zYH~LnYhR$Y9^B7Qc2z}X`|}_J6R~f1jb7Y1L-H4Y0et$)H6=~Hx2;NQtRh>KB&Ksq ztb{pZIt3Z!yAjZxtN5VXi2RgPrstS{Ww(H_aM)0*?tUEv-Er<&z?(O}70>poAO(Ui zmoKoWQpTKKwa$eZeLVak*FY5hWoo&2_GSWQK&$TT9#1Y+%-rl5kcQas;_XVziJH8kwQQA0UsUMU zz3bNgGIYTk)cvo&<704WoXC{ldkhl(trNXy@1+))Am7?=8Cjs2q%sY0$x|cJx!|~EIDVFe<%Q4f6&#>*#D;RPbsxC%*fVGNFg9=G z4h4#KQN%uvZSnc@(fYQKisI3~LDn@MYu5dA9)^qF`=yDfspGSR70-Irdso(!@DQ_v z(a)iG`3u#tke@Mn`T7eS|JikyqZUE=*Z0PWDWHYOt|(Gb)Th*>@?bn)%V;$+JJgWQ zM75!0+68LCyr6~PeDs$d&!#rfJ70NDjh;%XV&@9ZU1j++7u9YrFs-#zs!8U%GYMWS z9M4HlUNrOmBeO!+s-|^IU_Y1aW)FdkbN86jJf5LCLH*r*u73q?ZltyA?%l2qtJWqC zEyYb;eXkeFGAGk5D=AZimhKT={b#E(m6QfBpUd7qn)azz&mZ`Xn8?OSM4J{yr-H7V z-C|0QHlGkPZi-G$YRPR7b-3xrvy_*DUl%rgX0E5O3i{8pVxn*ErnKz4n|bBv4I?JV zcWcjYX0|QOS37@Cs0#M^!bl$8g(C)tNhgza&E&An+I(a3dqc418JqXYJ`%)g@=@q`Gv2WT~bnp>VCGD zA8)$sZQ5R%clXM-^Qtto@vO5TnM4366AUZ0{n?A##gEm`nhJtii_Bfl1b$up0LVZx zT4o4n=*k;DA8fWY2-JS!G%JDjh?2rd;K#we1Rw~+^Y^O>-wf(be@UJUql75@Ww2CE zS6743UEcb2t{&jufi#VPM3BjhH{lXEJpX$WtQD4JcRT5A&rs`Gr2Bu(-Mg%}-0jw^okklcO-KnsSN~E=q_>FCrVExZctG05@5qIAC{(&zaKcHBCmobgnOcEXjo~L%Fr#f3}sr#$-H@g{Df3tNvUO`+o@8FuSnG$3E zm%-7-G+UvuSrp{H2P8x>fxU2-6O&I561x~k#PR5UtRzUq%-zz1tEG0_#y?`6v?XIM zk57++Y&WL!x4|)G2-0l_xe0@3f8wDi^Yr21_dfJ~$6Zrbg*WJMd?ryN>gP_^M?~=8 zez|?hM|R-UO;xW|cgNeDRn?0!`K(26tSsh9Y*`>=b1mw|m0Y3g#h#u2Rq^O* zL*&+Pv2M7X+jDD-tB>}}{{&b;PH)*Ca*ld)a3USF!u6Ia#^#bLs!f8g_;KY^b5!JG zB#>$d`riBy#xqDyd@9H?5T3ZO!Lb(4VwlI+OPllyZG<50)xUxcT6>kPmc3uWo2IP* ztbmomzP@DgTOe*{?B=y_MP8FkSD4Y8?=0-p|B5fi%>rLZt?QDZ@8Zu{A=~%LipW@>rm?;_`f+jb*K?Uw2h5 z?|#LW7RE*OO4~eT=c@cxHl#kM7B{im zf5Z&k8H8?_31WwX_0c434l?I;mcs7Gdnj)_L5DF-GZ%t9qh_)eklxt?eD zn%0KPP^Kuwahh&-vRvICtew5WDXNdWP=nnDx8u2$o};nj>@K_`eU;3i69T!4v)jzY zWzI6Pr!eoGtjGob`fN^O?0-{NyR2UdcxIcyYlV;$$}^QbVq z%B1_dNWTI89D;)_xkK-D&MpP5SiyKJKhD(o3rp_&xJCSzZ1WRd9jSF+T+O<)1bv!@ z54ISur8>ETDyAnd1qp9{19G2jx+thh+y5*oX)6?8nuB-S63}GZySt;!k=XXx)=&<+ zPh5W#)(?D_@$yzJal}%P^sKXjwurl%O|d}DPrL9ChlhwkABPm2wd0)7*cxZ44NmxQ zx27(k7Bl*@eH6TykQA|4Cm~w}S~a~?+=!B-PbF_R<_O);;!^hrJ!ckq;lJtgk$qu>6o^1@ovSw1M+#*U0 zlQx}nld~AJ;{2HHe#$tmV?=X`?t)Fm=(?AUE#pmH0(MURW~lvA7cZ_`x<3tU!uz@C z6fdWDlU95qjZL65jJFS;f<`4tNBa0DZLQecc{R`H2O5P0L6*;uiaI9=?fdqOlAaA! z28xc2vwX48?rc{VMLUUZy3JL#JEMb7S?PUa?024s>XL$Q1?BZ~c@qU1b#=W>yw}f+ zmQYb=UgIb$JyupD$kzVxR2*MPg5Z@T&MWaav>8;~4?UT2(afm$-d2U1v1v3oxs^4^ zjA;plx~Dz?iO$2l$C;_`{5*&G>m0w<-n_%t6*bX_jwyQ4&-bD{W`SUv8LJR@SKDFA zBX70#Er1Miecbps?zK8U^gWZ#@HL(TJ8zY5SK|-Oh)dicM}s*q;geg@XL>e<6n85) zl4?nGZ-0vSm}Byh3XtCk@$u`vG|U)bp~i7yfCNKSBRbo(7bJF*DLK|XB+kCCF2f($W`Hoi3{NS=`JF)HX-IemlB%eAx|sMUvjdGZ3YjuldKqYR$$E)5uKda!)HE zmy@y5RO!18dtu(1$$8?pxF+A)>Z7NMH0dg76L_cLq?Y=sQXZiKY!$^<1yL8U8VDVi#TzU=Gvx;@>bteiBG$-fbO_UMt{^+naM1A#>0P`=!wmQ zRW_CNf9?iu)qlUJ!nI!&4*qdQgd$czf>J!t?F4q3WZi27`sLrSf5APg%6kz4OJg@` z`-+K2_Z-4Y0SJ1VoOqm{qH3CCHTM{Z+0r}8~!S#`ibBDQN+uP(o9#A^_pv?*_k z&xQQYJ$0ILr(>ZLx9yJZ@Q zk&vNt)b;eM5)m=Hz*3b&cMszy<)12v8qz`mtcAUI?K|AfrXe%ATWgE`8J_GLWvUk+I)N%Tw(*5)DvwNWzY!%Zl<@ZQJcGPth>&Y& zGu?33!>U76)S1eW2NOrfwTy^}MHXd_)j*sd{X=TpNO6kvWj2q<<826WN86DS>UYEK zk(kZvY4gCF3M)H^_cG>RoGx32v!ve%Q9KGoO~xw543L<7MrTJ#h4W~*&ChHXGohk_ zQx+6T$NX_)6n`Ybvzah!*CYAWa((``G@mVqf;o~>dy6Urzlq~H{>6&M#@^8qpW&?# z8){iRWOzp7+s{LqH#b(3mMSis@7Lme_=ZWUc@wCuIvNWvV6mGTD_r%4Iqv4EGAw=u znh9RaX^(TKqQ%WWMT zEVIC79|Ej3ra*b3Z0u*yzPg!=-$%kJj&L$arZoMOkosiOK=tBVxo$9ZNf3)uYlmAcLxEsPi26Pm$y7zNQ)>roYm)dKDdROFDNtzIq6!@qVAic2fF0jj#T;qQVslAotQ$ zPtxtkX(M-EHrim&zBe{IFpF<|IsHtH&yzPDEgQ1014C;E{)a5Q5Lkv?1+QRHe=VOL zY1sAmS7+gh{2A-R7&RJ|U%B6-)*!Z=Qx^3^m%h9Yf(n8aVoIi%*SN&^bwte54S)SK zzd#WlzV5Uqe!;tRDRr1dsi`AJC8BqLWATo%TIx<~hNQ7!If((g4%4C}L3>mBT~0XV8)spmf7JBE8>_${kUK$le;?iuX<}2LFO|R2c^U9Pqva7KLjJ6&bIPZXwEwoxmm4R zQ6kDUKn6?rw^SghQUJO@AV^3%(Z)#xOQoFC39s zNl{N~(wpz1Vv0Xk7zKx}mq}Fx+nOgUTkX#NA9QFt&N5y&%zXC)0>(AS#-@wX=`uJM<4F*i`u**4E zvNnoV$)PpQFXwRV6@`6iPw|yev^k2y)z+=_8AT7D81|C(6>Pt_>HfKh15RvYr zz4TRGO89I=X^P>D7*)}+7kM?k7Q_iXhTX*cj>PDrs)8H1!KV4F&)(J+V?y2lW7R4Y zoj)>qWVq{S8A0?Wk~$nTAcqD8dvfBc&mteK{m~GA^pX}~tw8b|^8TMh@`(sw&jE5X z*Z@zlQ-ZnK2H6@X!qE$dg5z3~cO#Rc!`+@lF=Tm}r2oXIoUoM%XQ9hoGe79DM!|(8 zm^sSwF3h#--e5pVu&Lf66L^~MEhmfNZl}96Nf&|*%bA&h!NQ&INk6wE?tKC(e7&!S zdhq7%ng$CG{O;{`GfL+0=ZId5o(4M-+}tjbJ-ZR!jzj-+rA9VWoom~<))V%e^4;r@i`6M%S0H0@`>t=r`D4Pq%qEMoy6ZiLC0o@QC`IYsYLuuc1_#t~uT%;T zN%so|=ku4y6>|*%gJZFbe||p}pF6kU4!g(&mh zYzi45C~Ec5%hJ9Eaa)eTH>#g7rM6Zr^Z9gU71*O#pCwD+R~7NR=J|ljk_eT^;9<#; zR0~6^!uay#`&=8P<*UkVlM`Zb9IkECF+Oae{&u6G`vJlB*l(HR2Z`a8KkDmM4>tz{ z*LSqa31vIxW~tr~FpukUd5>CpD|*Y@AT7p~-Hgj(nirc{dM!0fvJSiqCy;8=p+L|O z$tg=o5uHwdOLD3&3){7d##LSAx~lBqC&$syoVJ?ar=?h_E$_6>j4%5dm}0;YKN1c* z{el&4;RWZ3ngHwr;%y26PtU8heiG`#;JKE!=@?|6Zi?gOvkN09lGi4Z#iOH2^b?_l zL?wlMnpIby%uWwS&I0pIxy}ri^2=_PW9DD|Jy0C5+9BunzFiC|ut>x{FqEg*56?ha zBvC>(9wKEs={#uJ{=4V_K7?s|IE6$$`Tx0iRF@8mC0A_mY?UFrp zw6ZxEbPJANX_>e1&9OhPHf^UM{JLvpbvNKXa+*9o?+bWSsA8fSb<#lFM!nx}_wWzF-oQ<>lX zff!#Xk-w!!*xtE-r4JBV+x-j2^gP*GUHG%dvurLpDhi_fM8Oc{L37AzZ;Vt1Xjiqq zVqk9(D`$FcXkt=V7z{am4^So8zl_c8xzfW%b$vrqEd?#eDl|{4Woqa55GVB zXbmuZvX1t)kaI3l`>vz8VH?&Mzte!s1J!%RiP^Iiv#S+-+|E_qIL^iB^vDTq>b9J$ z2QSTDHN7S4w@)@GxY~})!;*Qr6O;<@|CPnXTnWLoe|3FKE!Y^%>8f2j`Z^xhv++bltNm->YX@;- zpj)3-g6QF>8m5v3l%c@>wFaXrA^BXBOwAa)#^=ka8iDSYAKwhT9yvAi1iECo4{&;w z-g1i!>aLO z8ygmoBO~4D@haXiAO}~FhcNT&BH{D2+{miEWB}G`08_8gA~m7+54>}W7tZ-dGjAsx zv+-_&hY_z><}|bRt@_mVdu9b>Km}v*>oGLFPt+{RNG_Wbh2}e2 zA-~J>EL+GA1ci1`c?*Z&=565o4ucERFT<eEZ%nBdJ2`LA#Sp z&<89Xocg=hc!}X%q4sXFY~8*4@ZT&h>FyI1;~hOH#^DW*cPp!(n^=3~Q|u>V zRBj0%1N1Tvmg6~hCDr^E&|gR{seC;0iwPUnc8ap&%?Zvw^4Rct<=@60p`Eh(LP;^X zJs4o!&tUSNf`&dK(W59*%9@E$40=FXz@Ee+)z*t`V3}D*#Px=Hl`M=*9iD@oh*%!i) z=I}>XZ0^|w%?cl^e`1(`{hGOO>RVVRc#Ap@CJ^Wn!AwD^ za|z!W+RIUm!3Msi=$#G4l>gF|MTN3sldpKx&w4u8?35DA^(*J1yHUPidUEQq^Qqfn zqDURHUhHfmwsO~Iykk?K6B8GQQ=eda8^!A`9E0Q>xMnQ#v6 zl?C9#2PYo4BmudvL?T8bjn-Rq5Ze`pV3)EJ18#tYIVpQr;&y<2CepR*0IcLw zovnDOFy(pAzu+S*_zkw_FRJUeIO$)VW(E4*>@3#!;1;kJ4`W(to~o%nXKd64P+%WQ zSt@b}^Hg|qoQ>jJsLZvdWU(fn3vEVx6b?m*@PHu)LP$0^E9%`3ukL_I?b-NiY(fc~ zEZSM~KyDO|9~jqPwut1Bl@Ettaj<_p6UiT09#p(>FmNwiP2hTc=2+2CgNJ9=dbdT? zN6+iT<89;g;U#`I{gVeyNeO8XfgGCz=WR^XL>?3!R}BQu|B<=dENQSOSX%J^^J|So z-&&~cuUS|RoOKA4Dm!y^7N^d2~L06uJ}S$JG!uF1q}S86}r?kwm`G#D$%Le`}W2o+ZwUrG2k zorNTre1B1PqW|5MYJ)Rd)cwqu=p@gs?%YZpIEP#YjP2It2537#LAy{HEGD1&2U^upNu8hNED=g+3vMlUHHs@mQ2sz^` z>KkpOFU@9#4r$!Z{6rIyTYY%oHIfXLDg((`*<5KPNA6(|OTz3@W;35K$|zD@6e{8m z|KJH^DF8O_Tz4qsAVdPXxqY!xDFkXz4(Y=+OKtyPe>^huJ5X_*0f1Ndw5~?M*`lM5 zGH{XOUH<@FUj1_aT_3iI14`Y-`=QA4fP$8ao{!Gf9s!DjxfnL5&M#4X5fan*tOofF z2Ddj0Rl|~}y8*U=xdvS~Dh9XhMlthwNY7qDEOv8|Pfgm=#;NOJ^etq5j+KOY6n;t; z^lAM0a#uk-Sf49L#dSFrm<6BPQ|C-*qq3)2oxAnaW-N7q1^8c|FVsCJcxIxfBvJP9 zI8i0j%b)TNe^KkLTa<#_&EAGEOZX~HCyv8mtYUwREqpQ&hDvTvl6 zD;GLSk;;G=aAz*8qB%AZIi_6kC9QZlu)x5i#{*TSH#I!x%8g@Y#u~)lp5^p2t|MRp zBLjSf7rz>`t6MIqGqBkD-Vw+HGQyt`of^wePQ5>3hKnRL=_g#oxXZ4tneS!h(r9Ji z(q>8|lsV**s!Nk+P5ORw%goi^cBE7({;2W$^8~FM-)!V4cHRnI2>sl)?P?O&r25#O zxcn(pSBsS`zvOf}H)G?_@rCr5=YPILI^T{uiofc7*{LY!xpN!nMDaOR`e>4IL1F9h zro~>O<37{`SMeD@^|tsaS_WzzwdDV^ymvZ$x3H{RQu-YKleMgYR1)j@-_DKx=hvd2 zQx*|=D$Ah}FdG|7IkGy>ox%F@C*z}Z60L2Phs%{b{5j)Q+_GrtRk4xi<*+)=vV{{H z#uuP2hOL9N2hW?pEK=#B@;YgB4k0xo{N z+tkn^tP=~bNM*K^^*eG{!ORr~L=}RalpQ2n1{+IxeDW!rd|0DfgmBc$L6XC_{G3eJ zUU}HL5{vewgsrn{%oB@3TtU*kPDJQle1^GpI@oq^F-ZjLGy4Jd?zoF>Xh`dTV{0S~ zUzBkJevU2zLTge-T__6)N@>7@C;NVnt})t)5u`wF4F32#mUAECP}2MZhb!xqO8#p9 z!+yitLq-4FYtg&gVgK8$VgJjyQvYLJFN1W$>!8R}0+2aGYwZYrDVB+8xmD*t{G3;I z|G7P$MCvPnSzL5LP^&;-D2d;R`wR--3+#Lz7nSFJLVSY+N@i6>>3}D(DU<5s(43#= znzD&7Ya2nGorNF!j*%*CFG2ZT-);o4k4DZbOnHk*FrI_B+!aVoUHw8*zfE>WvV1Vk(m}3YtB9g|se{1Gn@7@=CB+5K6yPpg?#y*=iPfR&knGAb&4FXm zy$_Knl7<8j$I2RDI6(E1dMmWHxBU4c-P6T0e>zYFLwLOcy+j3I{m+N)hXS~7f$N(?0>d%Q zL>t?G!IH2cyqH~&E(J@#T^p^Rv-C8mUxGH%5QA;i0wS)yjHj!=fmWb!)BfNZ*`Yz^ zrQRLOV=Nkpjy9lkMl!?jSfxrz`+NnhP80bs;CRi2#D$J&3VZE20c})lHFRXixw8K} z5*Fb*l+#bE$m5#_R48?VdmrbS+Deb%S6LT-aLMeAMld<+A^lX|`1#!;Ds8fqE1~%_ z{V-~|$*%6&nCq#Nw9bsTI5WVv{~!?d1Y+IPPz5VSRF+{x5)N#~7b8-K0sRxfj)?lV zu6rlvVX!1G$6~O#IYd1QFatd3xa^%gh(@M$LmMLRHa}_OPj~&$+iC@HqC2Zt5X--4 z{|!8w1^R)YPq~f*)_Bk=|01J3Xw^_q!R<`08l|e?`ATrD+(FjqEc}XFt|2@U;n*64 ze0A$40VMKH6qbzu<0xs@^_+5skyD1g@^sMsv#CJ%=IalURj~i%v`+z3Ui5=>}#2{aLB?%<^P&vPveQR-aTQ8{yWb-3wmPhpj5KZ!8n_^@Ji|rY*D> zD-gG{MJa|fI7Kr9pk{}=ewXzyE5sH#RRlsI)>)TlflE|DO`8~DLNp-Y6cqMZJ0DerLDg@3O#fWb#dVP)b1g;X@+kwhW>=Gi7COg+b(2HN_cpbHHlAY z65w1(SVB?A_vD*J@08&T-5;(!j%$cjeKJ{-mAf-pj^#w|9%7J~5MLRgbn_$0Vv(Y7 z;^-2%XxlFaM&PXvr=32{^+m&`&hxWfA5PvJS`>rN8A#=S7hQ8H->tIt*TLRHG`$k^ z{@?eKfbX{DcE3`(nJFW5zs)0%*39Lam379kUYR_L?X%P3r*zYtvlPDwsC-SoV4pPutqv_9LJdzA1InTZtW zHCBd@&H~93+CNS$6s{IKgopJ@UZxt=#;lEfeu3p!KtmA_o#s;pS4etC7tl*cckk@8 zZ?e(-OcW2W=&R{Z+icc)OWuVfyJvZkAL7SW za|kKzZY1#S#Uu|=L~-y-yXmDs9lh^ysKbsa{KCE+qqX%8{7&R_2ACb$cC#LEA?#PZ z-=72-nICL6v)wyh$S^{8=&_50UVG1$4Rl-V)x+9v%jn-8*Y|}S=Y+dLUSz?Pmi}C^ zd(2?BzZjAxqZe4?05lMKao2f_7izof5GJM0tgFr)WrA5fbvL}m92Vdb&_5Bb4^p^e zI^PST8R3t?dX2adJ}3xc?g`5}UR8H%#!oM!-^p|}6kktqZu)tApY1!WRjP>22gjv9 zfl#NbXs)9hyM@UkjkYKOc#y{w2L>~Un&>V~cAt5z0S&^JLWrnjoHY z6!cG&TKdCOU^fzdWWQmsf1!~dIo1bCaHzX1P3F=4i&cX(?^_5xj767*NOxPf7RWyR zpMtY^k>@402xg=qiWFqx$CG5FlOR%YhP0TI$MdJ-z615grac?~fA0$@*D3iN@84d$ zh(~%dJ(R`p0=<;ZamMPO;B`9Kqu4cqf2+$QyqvltX;Uw*d2sDQLSPImOT=Hhx1ao=WMH28azSNZ-qokLQZJ(9j= zb`IzrW`6vn_-ud;swQe#!n?=; zNR`{4j?Q?dHu(-du*~mmQJvfP;^{IkkQ5R3R3_l7FQ(~RF`jNr_3Nb**TTgUapa$| zP(*$33|%16sE^tBhlwv?xHDh1j}g3`{`X@r+9Fzfx_L#6s0ClKt4kj1?{cKx@&UfX z40RxvMdJrWFwvGAr1$@$v`VyFyNWZ*O9~3(sGua;-M}+h0R@46_WrhDb?IM;|18On z6_pS#1a6;bQSce{MSRK2*ib5Idiz>MFfwkl)2@ao!87;deW%WbWW8l`MYgZp9qxP@ zt;^_LW1{Yl(;#NY>vAo(XDSv5>7)(Ml;XWis%DVH&ebKvONZ`H zU5VlSN%giqGmII21Z*sIA@QbByxLqqOkS7t60c{ty^nADda-7iO#^9L|E~|x8w#`A z+u=RYz9-+xhac1;?y3 zUlLfv1VZu09j5n3BKEYJJ-#L_8p|JpXst6@750MibGjG?7GCoEX=Kp3%!qI84rIkP zDGeKk>R(KeAq;;$LPq0`7H*&vAIA^Z2pD^Nl$Vr0)WY#`8NRfJ-PF+g`Kr!FBD{Ya7%{6`L2ynMe=BhJdu3RR_ zy-EDD@SYVcb;XsBMMlvCAP*j>=%`@z%6Cz|ndi6s@s(DRgTQg~n`^Mlv$tb;nc@2P zI_?>XVf?l}eVqOa@Dq4F-L^dOFt=KKi&N&rnns{bBA;&NTE?+UDeSx$J63UTlCfE2 zK6BOZNsUeVKo$&G(ki{e*_sE?{POD)O)zy$h(TLzm;Ze_r5l=p0yX!I&rBQf|E1~j zlK`_~)jcSvR=e$o+bEKsuEauNw9py>;!|ZvEbE|Pe_FRFSPG3-T(@OJw1wWFJGEX{ z9dX+%pl`6$XUIL=;NsQiI{RDm0I*ahA|%2>q>yN;D95>@vq}PhSVutReyqCGKP!%+ z-b~;AIpgGxK%HY4JfeyelBc~dcX#gxJ~1NdKKMa}a}y!eNkiv{U->E>jKB$~*a^CWl*IXM|t<2Hp-QVqcV~O}{MqG#kNrn#$>VgSUC4z9*PV8##-BSo& z^c8)qzH;*j=biY8us!QCz#sBXpC$3RHn(@k=F?a8&Y&Os*on-n(2+_Z<%r+Nh-vQD z>7jwm(*BcI3V2k1B3za*G3d2(6`Qkfw^ zzM~!Rb7Dv&6FG!}9CGbzm-8@o6(;Y}ug^e&O`nFri$ZK&dnO*7*T9V(TaY@G6$eta z$UMLA=Wp8%?C!Z#uOJ@IHg@|Z$MiYt#P#gXb8P^A?qha96JNnDYhqD$`QE-uenuqR zu%*y*C=r>RwY{^%)ob5HNBPEMzcK=LDAfs+wVvi=iyFHG&1ksE9srt)b7^MsC5Nt4 zvg_G5P`(5>KQKQr5FJ3XC-gF^FnFV$JFOH?`*Fk7ZOSl0)GLGUWPh&9%!>C+eOofx^*WfD$Pz^FZHV?q2ry|=4w`aNc7pSsd6iyXe8?h;8Q?0 z{+IduAM;N~-bCsK|7+$ii|zm-n)L;m6B?x`C6x=KpYJmI(WsS`2w6NB57XBXis$9flry z;`b%tCxf~ZQvZDr05TUCbRn)8aGPClYp)*~*3LN|u_DrQLzaA~e2Pu(x7B{>9q4bvB?`rWZwT9RhoLWVgyXf)yv{)|1C+V{Yxm{zbU zM1Aj$PK$5tc#~k}f->XfCYpxUIdjKfwOO?H*`v&q@j3KkDaa~j4WCyFq(`?afck`9 zW_9?b`x^VVPrFw$Qn|4FPo{aa z&7}spSAJ)+Wsq4ID zwgAhTdrSoSv#Q2g%dWQ=szQQc)3EoFv|(HY8jvt&e0Q*2uf?_m6_(x$ydSd}wcBIB zl12-H$;0$D`rvWkH;d}5SPaRX@rJPBywNTGO7Sicljc*L5CoZ`hQMX8dK6la)1;Gj zc915RyuO>tAfuL8HR}=rzD|lY0MIyb|3BkDQ5*$!PSZ@a)X<#~Siuj*ff0Mc{Y72p z{xTDyhbo&H5?7hkjbTBNB+){AJ2eWU)nmA9MX!K@iZuW%VFBCxS4BiyS6HU3C-|Tcx)V=?M424KKOPN1hu_p-T@naiGo=$8QU`fRs zb9m45T!8X`b(Apce{*vpGlWmT7bk$l%C4rxtgC#buCuehik6xC-w_R8-pNyU8s#H* zgqwlUI;$<+LSky(B%M)Z?=mB;{lR{NkW_#-kX&p1atUa9YjQB}m57nUcc-vk?o#)( zPaP-j+T1ZP7~Dxm4C|^?Z2l${&D}*ek+vQB-XKSW@;k1_=)w93{P$SgTf3RBbocA$ zzifsQR=%b#W%65Wn=x9An$X%(V^E8WbKGIC0_5g8v>ER z3;)6hykrdC-K}WYEP&^5vo4{`P0$$K1k)pIYV6aAl8KbbZidDXZdWE1zVTvw*TCwc zx%3Oru>Rx2Z1RuK6*adzmB>Y(5oDQ!Keh9$`QWcPAHDc+=#PZ&FwIf81wCY~cj@x$ zuhwDsT|2e3>lHFLc>Ac!{j$R@o%a-HtG4|gI|J%LJBA((QhL}L0oUMt>@tbxch z6@+jXowr{6n~?W*{01W!Q^02W#*eRKPXWgnk&AGj&u)geC85y-*4t?BXMONzt`^~P zP8O4bUwVBYNHa(w5-cbr8by!9-5RzY0sQ- zr8L+m#pHu3Jx=@^hxHXK%;KbXKXuFL2QA!U z>0cQ->@yjxGMbT|MQ&HdLZUY1HPJ#C%S)4#Ufe8(BJSym_!e6LHaHht$90yhc|va} zwxDB};!x`7G{q=gm}gyxPJYrEf<+8moBwa6q45A@I*AdAC{HsFVXib0bBp-Qfmo^Qp*&TKTxrTGO)P&5Xl>In|zn4Lgyu|1?pILF3*pf46#ncF8~6t&#p> z*sGlpGSTS1n9a|Y0z@&_m&L7oeRMXkC}=z@dt2#Q3=`-Ck5p4s;fMp|9u;EV14PMb z_(BHmVnus;;C7<7JDIZmD?tO24YF$~sJC-5Fw1-xH6as(R*yDecL{n^)+iwlu(v(v zGEBMqQ&#*Ql3UbGUCvAm;9y}P!=7vt1_JrT|Cf7Cg6`DFeJ>c;Q()b!e|S9I1f>3l zu}*>v477`a8&JdxwE4mP-T)30SN;N*vzunY|EXTW32}zfr%Fjn+KNCpXF$*2)unFV)N|W#^dc-FkZQH?FHgx{uZ1`*v;RO`@ zxy7qy*r%b*aeVq3`#9@dR^$MAY^dWjOVgmc;J=Lh{3W-Tfk)3`@_KCy>1alptm6o? z)w@AFS=k>Q{Jpb8Xg(fGPvf&gfDpYS|I}Gf~MDewICXf8KQYKM# z-EQt@^JUH+$GUQ`+e)MP)Nnvey<-~`J%*%VTnW1<8rz#(eJPgj&N_YRS)MG_=V2Z0 zlbP8+X!qo2SD(C=W3z38BlZ|@)i@oGb?5ZEb3ROmq{pvHhXftl=a zS}8H2eDhU-H{+$c+$ z6{SU+sbp%F&~3Mbmw@wBobKrgH>O-t@V-q`&xq5;bGLAr**2Fi3=vNbD&H{Kr-p%p zSHAMye%%KyB#J~U`Yb|Y?Y^${aHoQKnZ9u7f#=x)#-w^Do(=Jy<&7Ovo~re8#KhVD z+u;GC-xmh^T-wI>-&bJmWOpx}YF$SI1r2Zn-0vg5KKfmEE1V}>Vd4+L{89{WhW5Te z%YoUsHu4z|@pWrUqiss2>m2S9W_1|!NUo`0V(+F_Z5;9##@p}Z?{r`E;|YgRdry3! z4-NqdtBQm0=U&$%4eDQRb~?vqR5`i{d6$I$xfyeNO!~xA;skp$1aFzA=N<_8fW-lf0tx{{Cr-W%vsSx*uy&i<;{k1S|A^ROq&Hzkcx z#3kJ-387)g1(O&OlC9+X%bZZqMNH$E+OBn^+6d9+T)IobK7NpUwTx}aE5tIJKohrQ zSXQy~?5S74Ve!IMm#rRxrSxK7H!v*MjkDmQbp)x1Hgp0%N-vV&7g-*~xqF zvo-5S4;l3TudnZnYI2F%MLEbpX-e-RO#wma1R>HvBuK9TmEK#ZL9kFn1QY}WlAux| zAiYTmp!8lsuhI!El+a1I0X*MY_q%J|{L0EZ@6ODgJ!S9N&oioG``uZ07jx6={mBRo zM`!^P@1Z!G3`%KZmyiK;uaSL=Ld|VkOzD6nv=5{D+EdR)8XAP7k>{nM{<_;wKak0h zoMJ*tUcaHtt`v|#iM#hewEErqdBO?%;W78+ldo&91E~HK-W0uxBPG!P$s-YX2_&J` z8luvmxy#T()l31VxwfU#L{(;QWM=;~D6vQXrUI^};kC&=p4PlW93(7LmDh04k&~o7;iiO<Mj zz{1J>iYtY(imHKMwhVeazda$47LZ%5XO5YfF1p!s9YxPf4U#X%yjrFr{r!*z1tx#B zqermCwx1AL`67OSL%cB)yCsp|*H2oR@1*_7=yfY+Rlckt_FD%FN;W@`xN&WkWaPzL z<(sij+!vl(^G=gyIyD57Un2ApXFrimuGXl__(Gt>6bX!E>mSd5hpEDo?1bL4%?p5O zo}HjQo%8wuq<>y<>wGmOmdR- zGk`rEY+!Ua@^UTtMaJbjtmG%6am|f5>amf+#5Zkyh5QW#Nmz!_XM_G!QZP=;pBvfB)okzYQaCJ%i<$@4o^o!ug(3 zpuAe6asZeN9&U{EdqGj-XNZmj@-#j~W@fV-9={TtAZ}pWexR+PQ-iqPNgP={SJt4T z8O@ipS25qFR-D%NlHO!Nq>NwWT4`42y#kdSTb-_y0>pUSyc8^!4_b0JEG+>vyzJ3~u-1!P?60h- z<4(Lz#AniKMHi3sEvC1VP)$?YuP4_Z3S&=8W}+_{uMu+N>Gp6GA)~2h3sc?TpyTf% zt{7lX+myzJb{EOCAD|8Yf)Cd1#SYZgWRMx}Xi&|t*WWp#*HShgLe=_PtHV^*&Gye)q5@EpE2W67; zD)+ljxu^UA1T4NbFZl{Gsf zquKuO0okl_mz>4E)3SQ(Qqo)+1u-xG{xH{rcLY3p9FE#K){J|TmlhQ830a4!1SGn1&BJs#Vv;m4#ul2z6;UQy!(Kan`Z7X!`zKc#`nhaxXDef9?h? zN8cMdxy&Sp;ab{^uk3n)d3YW|n1#qu(YsFr=ScH?Ho#pr~R*FcAaU zC>OI?7fDLyg8C4X(|dBGGQWUJHtw2y)T3Hul%RKOzi|mMq)(aZx&4bPlHottEW2!t zFQ42gC2B3y8}N5hr+N$gL-Vph%!jmJS+mTOG?-?fL0zU(=ZX~UUV-?-6PBG%F*Nd6 zkICGxTnwF}EkHT~=JmAc+0UsQMCmwCHzB>^zWtiMk}pFDfY1Nt#a*-g1{kZv zXkv)kp81WzdG}#}1Pn28V|7}Ld%EOT2w@XV1g%8CK=D|c`H;2AE_Zq#B2m_9Q!06; zSx$BH4W27kwaxAsYW}39h6TxZ+)~jhe01&A^KX$fOy3+O9F*=c>J}0Q4C&+zE&?~O z$Y$cP<7nx%QBA8hq14goAOS}x)ZQ*Q0ZoF_03GYsg9^)YM?-XYH~|Mv>loyT_Ct`n zB+_~J7fttLi)zBo?|yu%Vy)el=RD*npmwy~-m6OHJn!GD>bdNTwS&m&=!LdyOkaaF zp0w<&93CtqAQd(KdNQHg=e&kL;;qdB{EFg4tiY(8ymvBF2vG0 zGURnMWWl2Y0qQlM#Xip{@<8?-MIV)|Q;8IIIV7htuX-Xi3OohBQ)_L+1&Yk|SjMo|E!MX!s5 zOeg?3H?l=nvBY|3WmgszywG;e73fukJk!WTIs%r$DgR&y_{2PFX_jgwsQp|$caiBo zjJP_X1Ea|<&oIH)dzz4c8&TyF4@X@y{dk7N@Twa2KI-C87X=es_7>eM!R)fF!@|Du zNI8M?`a`gbxT)cAyIMTAbZBZ+UQb<(*j8WIMW>0Kd!rxFr_dK`twG*=Y;REbjW+w^ zqj8-DXGU*9QkM90v@N?{loIJ9m%fvD|cnKAsxht&Zc;DKzlh3dXGXd(Mv4~T>&`d=Fnn^Td%*0`V z3@0*1z8T)@H+S*_9*f0YznSV5tK-M8D16Obd`Afd;5+C&c#~#4Yk?JRX z5vU|%!lQfc1X}+qV&}2+d5CnyEoc$J>p^l}Yd1{3qWDM$2LHS^B!uROIrp_?C(A-U z8RpVR5#_&W!N(TwQBj=|M349O`wf^|JWs4l$Cc=qBo@nIz{TKYsS5b+nuaCBN=ZB2 zz(x_w3;vh8E7(MOQbig}res!}U1FpdqMs(FW^grtTE7`uBT~B*z}Y=WKLsm+>&!T~ z+!XuH9e?-dXEZAz^5QVTYfKl^kvr_p3eEfIzr73_y2OEg*46E8rd?|W6-+hXbrxsH zYaL8xOU1I8ed<6PTeM;7{Qip*fLk#(f+gc8+rHLXzk)_OrpEfu-d~cHmyyUZ@9ORn z%D9$z_(I4^!fYUsx-oq2;;7*R8YzKn8bL#kt^88b3XI+GTXIfdCtY*E7i%+51bQcG zP~CZTy<}0T79#bVla$w8x z=uXmplCjDgk+Sc3d)|&q#A%?@_XUu?_PeYk!g;McF=sz9$4`=-zN}nfAhjtj&$KbX zm`k^>+k4EL+YoWIRkWjw;;h|&Nk^#vZpeBK;kl&%u9yBm*tOzkTHo4|KO5z^JMEY~ zofNKog(?3Ee9^A9#Ij!0*o_J=GbiWO5@{xh+hc?MtSYK`_5-Yf-WAR|mJZ9zOR}!~ z)&kf26+&*_px%QXS}PB4UZLZ2=KbThr7+pR_K4*yqVfUF(doFR%74x+RGH+P|~qXfyN20P_of4hv)H|qrsXoAOm3bERaGW z{P%>P2dJ8-<+w=C-c~&N{XAcNgz)EMJkwWs`hP&ZiO-VVB+C0RVgQwvu+W3-kgL=b z%Q#UJBt?=?T7$5F;LbfQ3-Ckj$rd*wm_@_(_L??4v8xbpm)u5g#XqvuYlZbjtchYp ztZlI#iy8Z<%%`^Q544LJ7^!?~Xk+hoP$(i+W;Cg??#ZTPuu>MM77 zobFun6cZytZe}r%f-1=YP&s6n&$-$oX~SqdEO$S?!$O-nOzv6g=Qm+YOoI!CyuX}6 zm!k|@t6nF2blA&SCA0bsVw4$#lXPXcv}@2~Uj1sw)cmI&T}%C42%(d7(A+P;o!IhD z-}MF_`QfPdg~3N_J~503Mg@S0{F>XKom*Q;q_xH{>e?S0V4ts@<3%*`i+l;5F{*~f zjz7y#JwB{G6Ez|{#|`|kwyPhStBqp>otq8j{naLI-7yk8qe#JG6#{%~ZKMr7_%amb zs>RTKpO0Ej4Ayn9Clt4yLH!t!xWt1CQuEcTvL0wOHqtChE8;jFT7ft`au^!UYtcJ-%cVDa8j3e12wBoTvli?B3gsS{lKwL(q~{l|Nt-d+B34Sjm{VY6 zz{NzG#WKe@YGj{!Cm$>+A!m?=DfPI&f#T!oygDbTdv$x%?XF9ylx6mW?Rznw-|)W9 zKZ2beZp%^l&~dy7_#t@0blw4RE2v3$Qkf0$ChL%64h+UD11 z&Ly%)N}VA=<&K@}fdv1bL^1g{J0sDqFf6iPV665!Jio`#Fwqv#L~s%;gnRz6b3Y4D z6Vo~rk=V2VwWFR;>?whCYiY4-ny|4yeL9!cbzFC}>t6e+1npzUsH$t^lMq(UkAhrF zlWTat$cXXv(3Nq8FUGt}xj#E=aCpU!gU3YI$Kst0CD13zMO>)>xtR4!2<|pZFSpL9 zZq}>~6S?bxyR*BP{)=SCbv_ut(Y>kqDDr#Zib%+uox8-uVl<)*(B-ir$iK>F8UJ;y zfdfCJRbaf{6Iu=(g^&T+Bh+H6^jEy3MP0c9J3Df6ahY-er`16k3Fe&mwuz_?m)xt^ zRSBefNq_2xk6dWXuMY;%Hg8$sSxt_=;usc_k=AAijwd@UbAv@Sh=zQ+VfyK6K@;9* ziu@WYzk70e^`_pF+Mgd+6Ys#i0^gXYZ33RGP7gh|?RMQ(hCIezIhlzKAvpO|Q9mYo zM!`LCwG9Belc<(!fMd`Q}K#-Tvs&OJ?RAl=@j-<2DDS0_3zJh zP#qJwmfeR1a(&|UBhY0DIWxpN2l?yQ7ys$n-f@wh0saK8?+!7L|4oePb?n865O9-L zr#sW9*K3bEra8eWLHamp58sr0Lsw(p+;1^RU&Fqj@71c_5l#ggxTl^unhN{i+2O)( z8*%#rF0mzOFd+Kp@cbO<|TdF~=;x~!vZ0wXOQ?u{HY zbt__*>}b*}TA2*HwK=ioY3}g2d?v%Pvr|^okF+R}6wcUJ3N#yqUgw*Zz^r_l*w9bR zPt1dKS7xQDy z5}$$GI|XU7rT8KXcXpQn|NN6*LWgF(F{ulsE&I~!#V*^%w=8m)_+C$Y$pG-qA#bmaa@c?~!A#`&ysmb#;WMK6hNLrTM4&UpEWpR=MY zET5yU=v)#0^&Y=qA<0 zow-}?3q*)Kg)Cmg>uC9>5#`zL@(+9-S1=u77Ke6ELwCJ7tK9#F8qPz=7l>!+o4&h+ z!K1C>CGO`K{+F;|RAzkAASyi@Gx|W>el87l=AONTiNv#E3~((y^2s}$Fzv;IBmC0D zZ@u*&oGe~zUtkKd@~6iI6#;3smVdn+BhauYo4$BS(|vC#mMVo^7dNcc2>W~xBzp8Ol+U?c2p(|W%H)Q0goYtTaw(e+FC z+Be>;ytNKlbSdUa`}`Ofa=6EHR!r;6%Oh4@a0UNnZomtXToU=KU1Mh*l6Zc2DT|*E zakp}k?Rg&Gli}w}lNKTexBmNR5`c^f*+W=a)K=9-QOergIjhSDw5=>hQLQc*Jl;-w zAz~8Wnvc)mrEk2%4vg*<3}w8o@(%?^RtB)Q_=|FDZXu(t3av_1ahU%l2HZchzo9ZW zmz$esMr09fpg!f(Be{fjj0Gm? zf^osc2ZZHfA1V$!YX6Hk?TCs)U#PMJ1Dlm2Au$Oj=$mnXCL%MM}XO2q2XN>`uH z|F6$0eSTlS-|G*E+CGP=o-y+iJ+8Cd5;7l1(Do*70<%Znpj(ZuMpbiF`1t`{C+*qm zPgB4%-a*}4a#GDTW1-BHp*H`pzkl_ag!&TQ6rEg6BbIrbnXaz!VO&=G5NMs>Cq*K= z+MzldEbSOSmIpp6cVwy_5s_N=rD2~-VJ-Qfi(IemS6`Ox!5+eS4nw2dV~QvkX1VWT zh`8G@PqRVU2jY{a4f^OUlTg)_>LFUBJ*?IzqH)^ zBx_ig*D>+qPmJ(xiZh=9(_AXx3Ok&tJ-*=JovVM|j&hZfBg?_$$}LEmZRUf_LMnQH+? zl`?r^(dAHuI@$xjOB{MT%by=V$;qVb$zFx9CNNuN3ce{f>tduSj3CPsbS^XNQbaQM zI8r)gT6m2-v#lD6%xg8MFjF1xh>7H_QA?M;{ZDP@RPk+%V`U$0v?9%h+~r?(W9C>GloC26i*T3IZ5>UT3d;$eWA|nTy?gF>>+~R+}N+W zsadEgS_FP>w*xa8_Mz9trWPg*%4v}u7F&=CL*97D)d8oj!bt7gTa>IqUst}Q&JM77yqv5>#t$Pru@Ooc+a)gT zF=WagRcxT5#ZU9>nl4$z0)QCy-EgV$-)-cnQ>**|1YQe#{bQL57Oi{%?^gj()cX{r zIggvLpiht7j`2Gj?OxU@Zp%CW(Cr7+j;K9H`(($++F?g|SCn9GtN{ts0bseo9tLFO z+6hETX6!eN*{yOfEc#0c$QnGcy=$;z3sU8nBa&^RvnQ;s@-5lD*%~+g6pDqr z-3tq2N9y>PJE}RbBBn>Q8{sEu4QrJlL5X-VhzodSp zjJBfg!|ePyd^w2G#;kNrnHxxP8YA`YEy}^Jz3S@R zJpjevMCC^hr`ig|N0L_i_)N$jvunzdJQDBS!r@CaE8EtOR%cO#+M>Ph2cCL^EQX5t zplznSPWM6P57PVsPP_jFG$a@x_qh_~LIPrB?&_At7GQ-fauk-A64w~5wZ<=bTSu>W zB#ntBq2vZ`Ka*f$#EOnn#`Wtjw3%h~6P)?O6P3)<6Q@R1zFUblyH{=nip`fox)$aj zl|qQ^@^ZQK82N2m4plVd7~Le`;h1v0_*)biYw|a*6qK=*sZ<7 zhTRdvL*FHib3~_N4%K)0KK@+$+$&j`Y{tI!!?4ZqF|Mo`127K{Ua2w?M)ahpTe(;7 z>T-j;ozjV{eV{uD!-u+x%p{f1}$IlWotcH3p&uBqyeR{8rbS zn6hN;`4>|3T_P;!D1itEa6B}|62gP~kumZ?Co1j^H)A=0hnwj-SG6S68m0?n?q)3n zZ9IA}vEZHLb60v&_bs|9K6IGIRRzo;%vS{3_XW^hZNf(cAwC9T`ZR+2nFX9VJ*HyR z^r50j$gp}yXRPnZPPJ)^`Hz@biEo-acQ1os`1F%}=A>R?H?$i&%IMW$xai0$I7_ z8~#a<&%Gb3-PKNP?#WqmF}^N&RK_KT0a?N9?t2&GynP&cAf~#2Od}l5-%sI&(r|_( z&6XaF1QhE&>bgbjCYv9j26+T>+##^+nMo8bN$a^;{zPaA*5Lm3O zYd8`Ig>|pF92mDid)iD>WN#pILu1%dV}oMs!tX?*GmRe{=;e>(@8?7YgpWUxoOVb9 zPiwnnK2Pn7yfiF0wRY|+kTC2I$zd5=Fk6w?6(NVtMOm+jm;Xvo8Q%gKbLRGDCtPha zKE5nqeDBot{q>x|!9f`AC+dJ{Z_UKgtLpCMg8YwL4=e%aity)7dB4=C@4-t`jaE0`6;8B#V&dExL7_lkGUS^v+<|9`#^eVGf9 k|L>Q2X_^22uRi;P97D@Cbj|4a;HFMkL0|#9dr~m)} literal 0 HcmV?d00001 diff --git a/doc/ides/images/jetbrains_notification_profiles.png b/doc/ides/images/jetbrains_notification_profiles.png new file mode 100644 index 0000000000000000000000000000000000000000..8aeb1307385ff5f7f27d3d220da2054e2756a884 GIT binary patch literal 4977 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 literal 0 HcmV?d00001 diff --git a/doc/ides/images/jetbrains_open_project_wizard_profiles.png b/doc/ides/images/jetbrains_open_project_wizard_profiles.png new file mode 100644 index 0000000000000000000000000000000000000000..dafc2c0c43be6d3e4e7fcd5095acb7647a97481a GIT binary patch literal 67392 zcmagF1yo(Vw=YU@DefD0cXx*tcX!>myA`K6rC6b8vEuITzA5hR?hX(7egAXbJNMpq zGZZsG9UUOjbBjne>H{n4nDpH`o^%urD=BT;FIqRy0dSH}z{A z0dle?RYr5%d&}D?EWbmev}(@veJ~7qt33#Xz~2bH4?X!KnKrvUxE4~>y8HH$luT|} za+`*@V<;Cq0GU63{(N?EbUa_Qs`c`8b-kXwzUFvqRk8aJowW-8Fq(DPgq`)V>rF!KT`U;??{V> zhy>qzJhQ(}2IPGo-!vM@{rWlTfG%3EoUP9h&2}jkxvA{2Q3&&0_H&YTAqLVkr~S{s zP4d2GT}%8Df<9(SmPkgzeyZnKPY<6rB14_s-QD&iWx<&KiC%h2mZ)x9nCJ&y6M!v$ zIMw;6Q=`xR`CDgSY_AtZX|@kN(2#SYEq(JUhqD|p&J3!jC!oEzxSbcjVDIqU+sQNiwLQN6IxlRD5A(Gg4)vsSye&5 z5R74oc?JH5wh`JkIs_}?9J=PL{Eswg_$L(qaLFD~_Cgj~t23+^3#pK=`Jd}R8w?r7 z7D?+z9axW@M=V-m;~F<0DEt#VDHF|qzG&o`2tHxf19-zv35=6oP#D>(KNx6cy=cE~a*NEeS$kwq zWh)jQJe{BKR!7Cgszq6_E-KTn3_d;S_TFu61vd&M{(x7sW*#*CGsVou*WC;b)^RFq zB79xUIGwG1Bv0iE1yB!5u)|aiI!Kp;0b9G3dhQgN@u^u^!7{a=BAcDn*lALwl_rh9 z!w%@1naNk8UP?Kvu1*&xjn5K^@w~BdTZJb%sBddCx56VN{LPyw&y;fC+w0Q%(%#yd za;|&wYvUz4di+{aN=k5!Z+3?hG|R@|;0)+b#NQpzQNf`s1EENQu~5mO zwj!av>TcOJtVS%+&cecqLz18>!Y%|Z+0e@6VSH>i(!C@UdoLJg)CO zo*}?yX@2THqs`}Bg2o<fG!v^}s$_edRzn4h>xm zl!(Mq-o4tNq?)UH=nlJ0 zt+_L`>WyGR5iUU_M4HT3lYH!7Sz4M}Klk<+E7jo=L&oa)TBK6Yl(N3&8t>cg?O49J znE6X0he3xEW<2wiz-GBAWD|@24$@m|UU5h&)(@Ra9a(Cyt1Q`~(Rnu}tFkiSa=Fzz zYbVkDLAPk%a(^ABC@>+7%}2$&m~ z@r>FqbXZEx3Bi*k%bF$BU4bR=?CjjY&}f*&t`LqDs^>XR+{H3IJ~2N3Qi4Xh7$Tt` zg0sIqZ=VNBUfi`R^xzM(iLRnk%625I?~EE0F1C7WR9H<9%^mW1-+Z*Clu!RO{~*Z? z;JeW~qKq=+G755%lao8r93CDPiwAc*n#;%_m(Sp**;}l)KU?c;U0Z5A4HOGQ3T@xF zK3?F!Pdm)`$$QOUd)&4tz=svV^w;`YO0qh=DRUWD1EE)9DKDA}*Lh0sJXlNz;$B5T zdo&_qZ}vC9#%As_fUu19Sb2T9DJO>HSKXFwy5U8NKc2SY?`-!M84B%xB&(UD0r8&0}N91w@?@}nOK^FAb@OuGB-76gMF zLty<~E{*fgOmK7w@9j;+C2_i8WV?l%P}szHt9SdO0D^$SU+)p9N&)~ESh)* z-@S=EFf;`aJZx|qvf@$4LKW@3&pGQB)cVFg8jXh>IyXlrpQlhodG~@S zr3^e4H2LuQS88l$O0?$G%k}eKao;#4fO1@cijLwvk`(OkmV&$-J9EBtH17EF;pe_e zVCb7SpTm=|cS6FoO{GbRnF}m_U63JCh~XB=^?!ZAw_j{guKSgIvfeTK0vk@mf?z4i z#7hcH=XOlzq%2!wh_3-<3V3t&YuUlqH%dX4< zu)KJUjIQ$oB!+B%*<@Y9?w%SU)D2I(}+(YCuj?MP#(kn>ga^E(22frE}5BLWlHps6MCRK%06Khkz0p3jD=82e5 zIHZyP-A2TX0zf~5eBc3&TY8`dzh`aL4zR~#p4@*{-w-%`1bV~cJoy)B8SQ-o7R|F5 zJTAs2*qb=SZ*p~Yb<8XgHJ!yBdOR9sx~NCX%~ae@zXad>$pc)znXqqh=0;<$=nIV4 zQ{~`hyT^H0MFwOR)>t6Q);fD_ZDyWCmh0h66Lnl~I?oni!1l0E?96=`DJ&i%esA=2 zi!g9A10RpGB&^X{h}5WXeo)OgC-gRz9N3X3vwujoU$o2y2M~o-(j=tvLie(?@Bs=@S*icegq0i0MX z0odUb<3rV10sRnsF-z_KTtLhF1Qft!)qtbT#@%Omt4798vQ|1dtb)k5%;ktJ?6RK> zWfoFE=%4bWeHMA048;%WwWx3CH2g+9f665JNH@za>6nlHV6wC(#Wbl(Cm=v&hgGPo z(+ygN=(3>~mwe|i9Gils$Xo*_*Xi$8V-Lo{!wye0tQU|R6q2`h5C6Ud|D6emNG|i4 zp3K1^^5NgFIo1e(xc_K(td(H$RR$GjF>>qi4>7wCoON1-DAD$adr%x z`IC^q7IoW;0GW9@S1n*+V$b;{OXTm!@AG_<*?S=d{{8uv@i!?2WxWdv>`kD>#yQJQ zx62xr*;P43_9An&Zhd1;#D6P^fx&p}HgQ0~79d%#)tpw&Kb=8FI|PM3CxsxQ$3sYP z)WsBa62Nzr)v~$GtU!mo=QLnofs!5~-LI*iO%5A4$yXQJgDR|#qh2r3k={brw*nb` z78puI4Oq&?BGXw3mr~ED@TF zpBjlC2JAb%JVrKM*raHRPI4gtqMN5@)L+`651;TC9vw}UADfrA3CUKtOPX44&UtT{ zMw0w~ru>|(PL{y@W^48P6c@=sE~MerV4Q6B&0Zj*U9s2~CS_>-pGiJ*+LYk4bKdM9 zT`FidxT|3^Z70$#{aNaQNzBj(bk+snj zny*coq0FIv3SvziF=a)zK)VEP)IixP725TDi8z7e(q6q2hQ!of`ZZ-NLQN{VFAe{J zqckd7LMC5zGNNqRbzQX6k2Nn~-RYR-=Qmad>{auzR*N?0ad%`3BH+avC+Ey%`3?jB8>nh*6w^U?HXlGZ{WEVZD?4Ng$NTALmt z$uxOl$!aWAw8h@ez+VqrlL3-p@P+x$$vbYg)5WtI@xsJO_i2Y6=}}#iS;8ned9UwT zk~chL%~Y?QCfCs8FTx|zvjg2e95_>fb# z#4GDASg^LpVC3LVvrkz3OG|bQadAnxTAXta#`I@odHvMWTvp~D>0bVzq5Lfn(Y^qn zZu(tFG~(LsgTZ(El2gB|_>r}#J61q!D5nr>4Veb>yk@6zHC%aZVEuTxQHNq9Vg;uI zW`eW98WZPvr+v4#E0LI6LD8ReeUH$2h2gW6(aq;?vGWtv4qF}hUEFR6Dl4?wL}_PO zyUZ_!;McE+>{pj}{VR!n9Pwu7LId=wv?3TadP?ddSRWSMj8t`TZOxoND7_{4Jy1$O zCFoz4kP^_Qa5AD_;+BK;Y`x9LQd5zgYb^3(oz1Uie+CO{6%0f}@`njtuit)HQEqo% z_2s%<(KuBLQ45Q#d|HbVad6_iKGQB%2)6nYTi$ti!8;`xcLlMgxt1iRFDhBa9EmeLv zcq{l_|LQjZS0tfW?riS(1^7({>o31f6fe0m?I*XG3bvO86wErz+mR6|4ThKy+J>dC zuJjmZ)!{TpVJ8df0~Tp%THE=*j>euAWWAm;FT9x_A210%p^}P+qmYQo0_jaf$*4CG zt-MxAj*}kCqAH&1?{?dT)}CPuUu|b!chW&|^oudpzPW{;U%}BN(8_@Hg@RacA=T-K z{S(@FUhwE7yD*|QXz;ae{JfY5Gg^H%J)N}ZSMImOiaR!VVI@N_+yvMkzgj0dGM#)x zT$9F+q!=n!WWB?$O;s{Xw@ploo8*r8oZvuA6Ui97pc`QO@jSw}oKQn0b}gWhpn#J) zpP+$(LJWj)+VKKoq7oz6Y44RzAL#x)N97e~enJHf_D#i|n?1204T4^6D*t(>9*mmK z-)&KaU<&u-+KiMWAoO&Us_hYo-hMUxLdTJ-Udc6&R)PZ=$GCF>Z$h6P)^oJdfxsLGYQ z?#Kqn{5@geo-sbwBgqMN!N8SuNY{H)YhCG-h-ZzY);;BkYIG6%lUhU1m*yKr3xTK( zrg9vpBg$wy`02qPJQcRkCr`D>NE+3}1CFxMc2|vJ|KT%saF)-L4|hf-0|* zEc?!pq{SL55iCdpE7CUiyw|xn#<0>KpAl;g(;P@C2UIe@#XFpFbA}J6X|2fHAdgAX z=DWjG^H`L_%{%P|Y;DpKwS+LhBT=QIU4Vtc6Z`J6E__wVX6qE4l3)n6JHL`p%JX9jC+DE4M@=qfjH6w^Z=aQ6dnEI54GEf zAdX$K5i*6>>-hSNoj+kH$Rq+56|@DIF+aK$<1R(cw6EO8ihrKZhAJMWYxlc|1_^Nm zFTo1FM9;PdLkrkHUDNT#{NpddDWE%DDS6uIQ!p@p|4TwJ=8NQ46VIJA1qC3m59DwT zrtDr|q@YjmaYp;dptE5pwFVFsB@mXc5oU)p6}?+Qvt~jSF|PyREkiit3JZq8||C990_C`@I5=@|v<5gl@ zFNUaqYK$!{G>H_z&}yI$n-z0*;h`>rex1KIjoC1z?d?h1d3 zQk5Ew%S-AvhI+(a3+Wk)bl-^>Rv70sR%fG?MmwBgH@NH1=M=8px53b((esD#i1x`o z6Nl>s_&i1^7@0xePC1yPEH3 zQD=f+$Gn1){hg`U{9aCB$aP-^RXA_SsH&m|YyeWH(-_Q?%}x~ICXk*J&5BBF&59RH zr-lQH(Gb?EljFP-tS3&e+n>26%G;w&-SQdIyok(C#<;OB6Zu36q-^(3mxsovX@Z?-B8ci>W4zWVZ0CC}l zZAQKnf0d4L_C`spBixdr4o-%`Yp zV#woBd0%J~dZl8Mot;6zuP#bbF4hfwkYLO ztYbq_ZZrm{2qi_bO-73F(RQ!H+s`aV7a>Kr3#r z7wY(cgow8=*5Al8-u!O!g2=qtBKsVj))Vh5ILgHhTee8~=9ke15L-Z9mo2QhSOv*7 z9)DF!SBDK8qNv?3Ka@?qj$mjOd;OX6yFrcr+Kqj7%F{9e1JM>*l;1qCAPLXb>JEJLG%?=lA3&9oW`2oq<}Dt^h_yVM^Y>_V`ibvUtL} z(rE9V+~xot9~R-N%2{S0X*P7DThd~4(cpfu)O_zacdv9vl zMk75vwn9l+c?Ny3;DyL7$w=BK;9KBy`4%|IKOlJDPA-U}ggHLtT_KV;pV-yS%gJ)d z6$0VJdu6Prw5h470sLR*s-Y^W)muC<;zPA%-co9QUR=#v2P!%S z;OOz>JPPHyFxrJs>rqxr4o~?blPj%AtDNU0E7jNUgk#7TKxpy9TFct1+}5|Sen+KN z-7r zwz?a?!r$Ew-z$lZP3Csj+XxG+{O4CKmS#tu@x3c^xYc{J-=?9|b?B;B|8&3Ih5B}? z8|_bwZ!;W>wlX^Y!*YphwFxK8()uB#&HN*$#Qf3i7+*1YvdiIo#*L7`tQn)^@|{5G zDIxG`BOr?h7`?@&*Y6^lj0e2U^|w{V+5J|X%sjfa215)ko!@Dl(e;4k(2 zsoHz_B5XpcpO2t1%ieHapvF)a*nU$CU48M4wu}2y#lNV$CoLZtCOcgl@;8(IY~rPy zn$tMcvcZv_rSrlN+Or7s*ZAPMQCc_V=6Te}xz4#kxs%81T|I=YJ(>}IMKxv2)q4j) zS(e-r!DYE39X4g`Wh?kDGs3S)O2=c&KkS+DZ{lNN)@#e-3FFHYnA2^u4^^fjKYwc1 zyj9eaq99S(ZHRpq;xV7U@gkMmvnHUqA0I_js6q4Md6J~gH{gsq8yQ@eZX_Hf`7xB;T6_36=M8}E5&pACSI2= zL9H&@I4#NgBshZti>+vNqvTCXLjD|g98VNad6t2m~ku&u@Q;5ILy8Uet_^c_X{ zI-sTAsxG2xG+5=5e+a}Bo)AT-ongBegazM+W<|wCW+$WJMrwbymW#Q zYf&)g)NKIsBQo!|962gAF~(#EjGs1w^g_S0@gt%MGk%+iV}uc1CK2U+(v~1fgaE(Z znJZY^;iig1F?>ug zK27wgyR+B|eQa?YQek*idX_P;7zDa0F`8zDf5*w$6CGso()B3f1etQb?gDuyedJ?p zS3qn3DTvYjI%1n5QMp$56SCAED8|&IvA_aw;&}Yu)%GAA336i+j}T(zXEO63ELLsQ zkP{@CXE?ugpCCcO79-vR(#&BJ9513w=KcC?vk2-dq&ZpZoh6uWk|w@gg-RlP4!Rdy_G|Z2L;ZU~G!~IzPArbc zeVd!T?DNv3=OgmF5$8>5zhqij8>hR0*SMJflN^tCw0d&?8b`mJ^78EY6JOlRVGyy@ zC5gqFl$_T%{5Jw!ds$BinIu7zqA9m)_WsBfk3gNLSkQk87rWqYe8Q<_IdV!lBlAP} z<5)XFC8K#C+qXQZomTvC6Yp9z@=+`XNE?O0;YczoB`2C+7N5`;N1unmaf_MHw%z}?=bL?@h--lh&okOx~Hz+Q{kb^OZx!k?d+VVJs1ePNv zWtwO`yAS7*I)owf=M$TAx9tk|_I&3}xyf?v(!jp`o;gM{BE25YeJ`)m##R(fcRqrO zh1*BH|6%9jO=2dI((JIGE7$4;j8suh+cq3@g;mP1d5qm+p3k9FOl||V&-uK59MN;z znn)QPQ|-7Wni5>UfSYW7>D@ta8^!{U3Jpz3GhrL(^d6q{u5MV&#y1``KBV5kn!A$o zbYmPb4(RO;LJ@9h?0j$t6a-|I2y{s?9nf5ALA6*j9$jj-EZGI2NIKpabFz%=^gT!^ z?XVz}W2vss+^J?${sxZdv)Jd6tSo}&V$Xn)xFTsrd;>vSJM2QN+zoU$mYmC|-hRKJ zshnrUE84__h$MfvOKmENlsI=)pcopA^HN^c@SUchCKQ&|t3#||`STu|ZL`sFE$tv~ z7UC|p2b%o6OK67+yl$O-s@SP=aooZVqH>E`0W#@WAb0O>T+i(wA|m};RzP=CQu2F? zn;KLj8ccgu0mWRWqT2oD$|0mhXTBZCB3H|kAm&U_yfhl8bJ+0M^h{I0QE2GwcfHWl zGSUp1$Wjxlz-WD_`#5xAb*_46Hjj#ZaZy+bI^f7^mPXE*;*)6Fsagi}!h8g`viK+H>!$4$K z@)3`gL5mDwf&?|>2@m8*OT>td7?#-PQ0Tv+%FTYWtToMD*N4TWQOB-dXmwodI8EFa z!@gphlHT*Y2p=;MG)&Tnc-H@4OQ@chnB*_dj+}%5rl}57HW6jPRZStmb|dAs78^yz zoO-=%oTTx?Pw;tmFtHcDh=?L-KcFBQ2au-S=BnL*npL+ts<}gX9>qBpA5m^ABRuly zZy3>N!p3gG-8i9%&c&voQ(99N8|V3hX@T_-af}j<6?NUe69Q&sh<~$CfBWg*=QC*_ z#lGR^A&$8o5imMPzqagTlsvo^l08}5pge?QbDLsYS4Z|iXr>fffL!&yq~N=-pG#Lx zV(6J55@$A%4L`hJ@Nc@3wD@(|cy?77wwhl3wZok^ zJJj7lEPfZR^G_O+dJ&486q9moNqg(Qc3(y@Y;`{#4`VKNyPD)8w&C5H|6z(Hr=UB$ z$4w0zWUQ65lKb^&H|AwcizABQ13sK(dY_$Uj)fHbHY2v*!dS4sRIxWl@uIeo`c8tC z1RyqA%d^kdB6ABfE3|F1m~L@yu$c?_*y&N?#9_!(Zaxez6EZM-M0VBySHaSesurfs zWts1s!)LZiR>ILVK*_p1BbJOOz^~M`XJL`o1c?1obzW`z05HpPdBQ+#{aVze|R*ak*g@Cdp!Ff57D9P#o1 zwEgfib#m}Nn)LDZKGK>^@$K(g*d`TM)b;7Fv0D7xIWrslovXq>m5fZqIBG|spp}** zQ>$^E)n3zc{NhdYWL)Y}C2o8*RZ1QNtb0ur7l+!qig*C7g<(+YwzHSbWqJC4M`ods zp?wyi#9~*dbCEJ4NF7vY4eg8pg~7zA`!B9l@FI8DRgj-+1$uAD*j^&+B-;XDz?tBL zznLvsu&V%Vum*;9QUq3!ltPKouyq?YLrN38c>=e>lJg6QI}3ud6-_Je9Q?qi>6A8< znT%#?&T_D>~=7gPh{W zCu7<^WNiw~->NkUL1MMxImAe`I8v?{OPD<(^ntl+jUUY*)_PFkNb#ghf;1w-Kz!n%>4QmU+~CYqGD|$EclwgKcv>`t~8~d7_($A zV;3mfP<>Xw#YTz1`Z&TAx5}Qm*{IuB`j8>1--R^qN z_FFn3LH@D%${?}$#O3k(`*TaiI(hSKZFx(_voHd0Z%fvgP3{+%#>r|6ot%A}7OdenG76RFkRWkgcIf(&GOVkxil?N&3+=x7ioI>cq}H&eK3adQPs4 z%|skdsuT2rqDw+LIPCm-B>To<)BM~esYPJm^qFgmnpz4HjG4DHK1@(wwiX8_A-&Yn zq_O&DadE+)<0;aJ@V|u`h>yx%!pJD+L-oS?MSzHC2R4?es;#!Opv`%%1ZgA*Mz+uY zo4&!OwO88o?Q@Mf&UdZl8bk>#`DfBJeG5w#(X(HdhCSRE>?1pmvVE*JrKOb*3}P+^ zByaankIq1I1SEBhgIzx9;!Yv;dWdtiQKbJAcmRb+_k`@JPfYBxAN`4Zel?Fd47i6) z^Xe#Aw#N@-0SgxQjA$=AS=+djy}9T9Em>g5_IhOR+1YN^`o}Z=BvZ<{U^b!VfW>%> z2}SFdlDrM1cp?P;-%Ly6z?R_wAhus~lF~mS+!8_tXzfrwcrGpWC%obP@IXlt;Fe}O?q+YXk!UuGK>fn~=*sddr&_Ng{%g>TzPEOh ztF_E*6El{|C}*&=F>4(dz)TRa`U57$8I7r97P^JF?37mOAewQ$5t=z&IMR~XA$KjUJU07ZT`muwt&p1F*?(vDE9w5p%Uzz2)H^|Vwae#`d}?06G@@Ry;RCP3j5CB7a<75Q{2?L zsD+$$xuR=XYsn&US&^_zoe8(RKgSWlK+$)qw|V^ic>`Jg3uZ9{{c{QBzdrmQaTgRI z%D*>Qz!Aa!1K|AASor@0A&!?@&WKJA7_FTtO!Af?uIN4ivKu3_z-Q2iCK@P&iK z-$lIq+LeybO{LrO_~vzG{|TR+8&n*8KB2uC)H*Zlee^77f12=cO7h4(yPtV)CX}&@ z03Iy~+ghj~bIY!Ar=0ooS=3~6)$+HCMkE_iQNhVqT=o8QA=Q`TC?~z25mM3MUj==c zS@IzWB;_VVo+~bjLzVk%k6m5sj3J;iA2l^zeuDI?=WDwU05m|>RWiB;%iA~0kGKR3 z+ZONQGd>J(sz(ztoqA7t$_sJ+I92gEv>~s1Is1a`9N#`Y_9#&Da>;2b$L;;%t@oq2 z&?h&md%QnA#<9}v(f8EF59z`4Bqqs1s+P+9sbc2Nu>AV`GXAn*ZiWflcjJce!PnQ- zzx9S?)3dwa)qgIQN|qP|oXgjk(ePEEPl;cCFg+>D4`qedRN|o8U{%+ga8dtRl;j$z zKt5fhYV&N27h!gFvHQAr`ZY+JA|WDJ)XGb@-*RKts5!F<$W#P%E$s&UG%2n0u(TGo2m=%u zF$ydSY+Q;kv5V#7;xk{ybLZ5z6FhGGes-U=?#FbfsNf2imO2W4g z)8dBMhOm!3j08nAaB&pUNgk!&QbKBeaToT`i^&fNA9$IJPZk>a z$faH-h=Ope>91OT$X!zDAp?E37#gMo!s@=U@4?y(cw`;@Nw2rgn?d6g@9Af+^Ku3^ z4%(aoO6ck;>Pk<9$HZbEvos(oU1>lxK zv{UW&L5V3uYqE^*KE@;(JRQ!$01hFdg?zJ6&AMQ2eQ*kg!u(i1+ZGo4LM40Sk2BgD zkVBYV;LSo*i0Vab)!F0CBG{*a7psqZJCWSkkn`-b-A4hJ zmgr5RZGt)71PAWt6o7Y3Botm2!aNS`oH7rj)HUIB49HR3sJD81R8Zx(klsHkz!4jN zv-<3YD~p=XoTmkH8SY*@3bk@{$g=I&cnrosq2EqbnndUjL%(aFnr$cgB+Q2B)$NvVE7}ZJBY@K9n}KTpVf+L z8s@lpH|Y66o-`9zS8Ohi#D4ZuDTPh=msm^r??wU>(Bsw0{hL(Q!dEh?oUc;;+ zL`hNNb@0B(>WxGQ%~+_+8K2$Z<85cxZ5IS-5&@rzAP~DLb@zUV={VB5Abdto5$o5u z4^Q&rE8EI->WHM;trOL{(;4fS=yT*gB~Thi1*Bp|bUspazBj3nY)-bM(~keba^D)t0nO4#wmO>es^T>trT4@#Mx3UE=5 zD9e0{<42@33DZj}Zl0~*NOTx^aN=3k4Vo_{eg3LO%k@3BY?pcIjj#|9sM zMB-}QAZf+MV$Q?41~;ApsoA<04U!J6jl-?3L|H(Tqo^RRiK)#b&vE}|)$?4fH`7!t z%>37(0~>5-eO(uRl4o72bOs6!g_ZCGh6E2cPMANt{4@}@h@DMC@I1ko4;NTjf?jIs67NrmI1_6CTW{w7aZBAk7lt)Fbxwlq?2d+A=-f z;y$v@IgCXZrR6#fHb$SQDK@J)k<(2G`MJ@W?<6>%MUnf6utZUzMU312)EX1kSwn?u zReT=n*;U9lDqn#5dMbAI$eUCe>~q1r-mP{)6o8uzQBZ(#Wn|bpp~9y=b+7F!wUeCm z?;=xy#13Au+}v`N`?GJiISc#Z(=lB2j^-OS1w_~W90_%Y z?z6@odsuFe6zwhTgp3-WL^a`%jhT#bI&7wh5r1`mG>65K(I5OwvA}1eI2&8Y3k->=d)qV|OBwCA8K@8O z#V()Kxfh1xY3@W+UO<8ymm@~(iUmR^ zAtKJsMWs*-nosg0a6au0`Z*(3(A!;!2;tb)Wsty`MajC)`^~UWOap5Dwoxf5(ZLRq-_4w z@4LTa(YdQgZ`2wBfC&6QZVRuG@tV_R`yT~{_%_}T?LyKC+Fj&6>q18b6Z~kKkuZx``{IabGfxk*1A$#D-fBeW-XM2Y+dV+;tJ+vhpWI$)yllkzW?KCp$?2!Ve z(U+iGo%dvLGZ^{vOD;>U>(&)Upy0<6O)snd2yTPw)551ePR|d*CcatOwv~7UESBL0 z^-i@~>TAOLXi01?R4=50ASoo3Hczn-~?kv1Fu$km(L7!}rgh0fmECUHOdYS%s zVq<)BrS!h}_zzgD2Q`^+SvAt~N#ok2TG8O8c&djZ{#|bT%<{n@bgxc>?L4?3$PfaUYhL|ER9;`p~(-oo(KA zv@T+58E42*;Z{?bE+7TsO|^mv&46SUiQWTnt9Dyr$ zT-HIpO|j_gm2)}<9^c;WO7ET`0le{6Z%-^kVc&2^`#l7rC{_@^_o}PlvI&RDex7?G3zQ?BUlXZW)7QCoN&(gUe z0H}Cqw-M|}j5sMS9rQ+C=!(O6px&Y2z*b5Oc^BvkuMJ%pG}uyHM|&WeOwkw_*tMa# zu7ou~OoacTWA6Dy{AhT;5h>|yeM2Tm>M%RDd_V@|6nZ%M*%cGzVl%=dvMfX7BN`;r zhd7w&B9O(SvZ59AQY4?g0wk|6azHdn4sFpRt4#{O9k@@K8LmLrfH7accFQ4`AW*ky z75rGf8e32>nT75tyv9_et_XoxhJ63-WzvGa=|Ujb_o!O|=0!NDkM^UN6#)=jh|B6Bcs^t7-ei|A^iAJzH5_E_<2`HOenHYp}+qY|ICBRkSHTKw-oEP+5E4r z!`^glmQtVITOag6wvlT6$6o*OWjzS>fuR$UkbO*vrr*)s5Xrgvm|9-{f@|{hN;r2l zV9{*)Zelygne+>d1>M-G*QJEAD(&u20cX|7>pc-UBN#noz-f)7QutD&H}Rr@a$#%Y4AC z@{T9JbMfo-7Qr_1HW}r(ro%5s=(>=6XC7~^pM61XmLALK4qnfD_Vf}lpXb85!nVlX z;vxQ`p9s{tJTD0K?@3j;Q4}x5(eS9o#_WRu%j>W|%j=~-Gj>Wqbphsd!38Sa1WWPH zX0?0XL%Z}cZ=}TzYW>>jjXNo|jrQ~0&Cnf%CPGj;4aIt{{~hzq8JQ+I>wkqvsoFAg zC_Hc8om#P(kHEH^9G|Tr#CB8^#0Cnf#@hxxXTN7$SLyFDIec!9_s2wMHNHvXf1d&ddj_q zH}65Ilo*&CfPyTuS?;!_%d`Jwjnj{f3Q`Mt-;yk1LX;lb7c-`@`_DH+4UoHHqi>dW zcWt+yN?J~}>fZA7$FKFp|7cE#NlXd9 zf8AZwxZux>VQw}_kw08eA1I~7n03$Y8!YEOrNl25)#i$?9}r1e(lf8M0o`&njpJuW zT&vFG4ubbM*jSK=#1zj&pX&y?a53FCo+?pmX__hBu3LXmX?*qC6NB#D^Cmajh>Ad{Y*UK)+)U~I){d4-C-Ql680dPE>+4NG zb$v{Yc?A9s4*x&U{J((OKPW%RUtsJnSpPqW{x8P>|3UL#VWQ(F-mhR8-oXNBF>w=H zU{2nULS__7r~7skcK(gO|HlSR2c%~xzb1P-T=?7Y{ixu5&@Wjez`x_Zois>Fi)@xX zGG7S|$QI#akEVI}J%_I>&N4Bzjsx{;jqUs<%+TIYz%N8MCz-WhnUU7+@T^R9;E>d9 z9(Dm?#&XgjxuE}tvA2MVqiMH=A$V{LFt`LK*o5FPIKf?m2L=fQ2=49>+}$O(d(hwm z1b2tv?(h$JzxUj8&%Jl8zt@^sJ=4`y)m8PB?7g4rZ0^S33GX*z457KkAI7EsP_)BL zzGLwXj;(1?`SMfAd2@!r!WXGcvEmVpym9gTA_jF2)Xp!B1ss)v1b=umyX)mtPL;am zy`H>Eb;>7mGIe|x=Q`}@#<1n0CpPS0``2|3Bnm0YasT|@!RZ>Rx(%&L;&e5nQn!dB z;-q5qpS@CGd@0kH2eMsCZ>ycvCIgZgvlsq85BIjso%(n_Vo6ya1bAOA|K1nX=LLRz z3A*JcaoSI2|B-KOFL`dmTZy=Z5SzNk@gS_qFwJ-|NR8s9v|e}^Ga$GpF}3dyYe5tR zpfw%teQE{{_F$*|hqm#R2m$3@qkAbKcs}OU-WPYiju6QP{o~^ZkIn>?uN4u6{^BS2 zr|4Cm`GA5RPs+&Q0RF{c5$Zx2QGyh4G^(d<#exZls!CFs zI}h`Y-f_X=d~u(Aal3TAaOcl;J&T|Ey|ffXKdBJjn})lh(9(vdlpUO;Sfb}!E#5AOj zwkYz6=^sPp7lYcaS9VNGqqMGM6*H_*QtPGJN)&^P?KbE=9+@!oOoPOsotx7YJ1fAzil&NDv@R+q#IsGIK< zv5dBovlX4-Vsq|LLY_IY%XAiH_$S?f-@DNQ$JZW6K>$_*Ln)mTXV@FzdJfFX%mNDS zA7b7W$keass2K$CCqGlo+@6upzA!t8=!oc4h!M{w5N@SAT$t65OG3I0*Vv?gsx0kT z$A3-|c>2I%xr9=jX^7CG`xf;>*l}|60RD(KhGLkY?rw$GP43KM86F7eCR9USsT5}$9m z$i47BXaF<-^y?85aL(D$?}yYmbA2RQzrM_gh(5D>!cd=|CRvsL>T{%JQClMsdoCaA zACK&`8qB6CdCky|O6odMi$5l@R_A!?N$-^%DQDFEI4p4iC8Nf2R>%?;SiqjZ{9laL zVqZSLWp;?5=f5Ef_sm(|nfeOLAWf%e)Q~;wiGL}sm6S`m#c`~gSBT%KZrR^dXX^a= z*ibjHEQa>?0;7_W7k+}uKK34KYY;1K!JC|6TU?e)E?oxhz<}4}Gd#^a`sK?Me@ivO zhXfeBOXSzL>vX&>H6E2zb>31x^C!>kX1fKc@fk*HcB*~kF>Bl(x0IHe(bOmi1zF|B zNiry5{|3nOx?LoX&|GAqxAd1*zE4o4K1pP+7E&b@f7am*_S5OFqFMu$#zv2W7#(nH zlJ7}93nN2N0bpq zUHQg%v03-`wvGRCi@g_<9hX>lg}quZH-O>oC3o8*eAS6|p2#|gudJoeiG;eZG0raT z%L|925#1=H?@57!4L$r2HNaT50TN>%(W*1DqZOnhk7t;aJR3vvCps7dub)v}PfrLD zF~mjh+Wk>^nn>TRcPiuZ^Re!xo>l6OH=PNiJqF^a9;KZ93TPo*H+Wym9u{qG4pMKm z>7Qlcjv<72q$Q&4zfPOwoI1`HUd6A2`>ai@p34US%Cj1ghzh7ACP6-5O9@+AL8)!V>4lbs?#{e9SCGLE9$viul4UEMO zzHl9qR)30u@vb;pMuGt@-x53!I2<1Du+SX|Hcle_snal!iSpCX$@*?Q*r)QVfzOcT zo$%E-HMiLp@oBV`$g0w+FReqf8K5Nl5(K(!BxC=f*&HHC$gFPAdJc!KZFFYa8;Rp( zn*c4Ez1MHbkFRH25pr&*i^jANiVKIhbGO%uzUo#M9cQgwxx0Ac{!^@eh}eD-6(DMZ z(@(n3)dHpuCzEhuRuAg4Pr81AGyZ6*>ZE;TiYt)@*7nKpI2( zTE7INU|rpVnoejKzudEZzMwWn9c!R>T~^lx24oQ1e-K>hZtX}|RFm!HUt(6>p-CB*xHH!c@Mz{O0~F`E}#GJ+UDzHaTY zNCoUD12s0^KmNG_-7 z>wjl4^P#)FYbk@!Gj>as*01BH;!gj01JoQd_^hfp=Cvi(LFjgtUQ}9@+gdOb(&;@}r(KBX4c43?q#d~D zpVwMD3-s5oVheA*I(G*6Q8P7}S5m`Kj0u5%*>OUk-eT7H^&hl+B2F_dN;P&A-sBX5 zxrZpmy&k@$47KV8vIU&=F~Js9T1Pf?t0rhwYavT`V=%S~t?SX~LXNfEnBD z(Q$7~L(Lx3(9HH?Z~t|>kZ+)_xx_cw_v`E0m0gVh7at$CUrU<#*|MD&Lk@AK$C7tP z%m>R}fSr%2tF;-uDcrGN!`+PIW+{BXEq!lVyzL#vDnpm&@7OL&Cb#n;k~=ZN8Ifi z#{3eyPi6QU6?SuNBb+@J+wiw&2?yB}zV$Cloc1K|gqt(d;_RrMNvsd#ToY+Ke!^`eTT&j=TAs z;>_}oqM@aPvBSH18i~3f%%zWM7R}-BO}}UmCa0)}gf1Nmk6&q{dOW;ID{e+Kb9i53 zQO2F-isf=8nv`!!V*vR(#y|~&@H)dQC_Cbcq2vXD_)llt#SB7?%D)8t61!l8`1I|c)lJ6CjY+>wkjSVBM2YGXoRt5br~x8+hZ6BB9|xw7 zkN9`#F%jCRK)nKMopdGE*lexhPV1>j=;jj4JofmBTynmywp=OYTv@Hdp*RG+nO5Ry z)%IveSHsgz&#hEAP?WN_X)5~rB{onsta@#D!52JZX4R9QUG%@D*ohEfw$P7HhK zecC5{6s?*1?O@6N1V^4YA)4cV;4(*%nB5CQf73Ev027o6Pts#$t0Ac4GQ z*p6+N5A~~Mre(Ro>Sr0DpR0j!HJtJ{<#J5mv$gL22F5=uWyNvarlND;QvC$lc#{%( zWt1Qt{6->N;g0YsWh2RK*S!AeGH-~*Nub#ZO9jbrm{)F zv;LRd&iUBrebk2KRkR}w~R|4Aj( z(p-e8Tci`~@OHOw4yG&5?;$epBPw~c$4!PngTtfv#M*>ACacPiUvxWcaj(*VG{+Tx zJn47GObX_lpM~F@u{h%GkaV8CC2H*IuSy=*tQOIZvH3>J@t;uxhmXCNi^bXuM@zo9SQfJm{sy&h~(1^^domjnjydX_+ya z{%xMbZ~1=WJk@Zi0S54ri0c}W>|gEF?}!y_A|N4<>}@n7(m_Bal?Ckdu|N>2OK)SG zJy~+s;+kfx?=$C;m1q6AkbHd08E-J5m4V zVp?GXAx?29!Pv9uN~B&&=sLIev#4u>{Zd=Y!qQ^-LSwyV991kEo@ux?{Wp!ud@XAV zEh`4}8nQpxWO+duH13v!A5B7XE)?4?qTKg8S2;fFDmCw2ufc7kt!#B2>vHdS&gDB& zXUg2Vb3j%LUKRvBHP~YkXh1?R35i9LA*%ZDb{Md(*Npbe#mb)u4cKV@GG98ix`29y zV>9_!jsDD2u?i@QpQ(IC14hz7R%J-A;n1z;Uuv75_)-08-T_|cIZtPL&{AHPRc)m6 zWS2W_56AR~RqCvPB-Z{VB`gPntZYrGo#WNPTbSmQZTN6XqySl*rGw)t>-i(zcsBb6 zH1j)kSg6}N+mw*5WubRif~(T6 zN?z^Np0Sq&jEcRQE}$cHS&*14Tl#OR)qy^UP!0+M71%18yZ(V_$V+fxA3X3qNsv-ZOsThEJ}Y>&4b)6m8sEo6zuV1rlFGU!1yH#!9IQZ`Wt>8 z=kEorI(lmcNvy}zPcKH^IZhuLDh3kH!|QqKi3?KA(+<kg8Jem_(F)&iX0r zS2X!5!se#7Eh>U8(U3j}0U&r|EPavOd>@4&?nlN!iRY8COoKB{5hr2C()AduL(AW> zs3We>BLOVR4cnj5&Z=q;+G5Y-D@Y=wuP`*r4Xkz4v`K}Fuoeguqk5g)cz(p&Y#jO~ zFnh?2Yli5QCZ})AHj3Pn69#i85}I>)nJh{PrG>j-NorUpS<;?^KW@yQU&;9degIQr zM~Pel|7Z(pltRkz4t`-?jXpAjGNE>QeP7t3@1@?1y6H5sH%F&tY*jQ-xG5n^qFp$gm>vrva?ILYKb?II6 zn7jSaA!J2WNT-Zz%HuS2IC!ZtU@?ayG9f;;nJ-qj<|rP(s1bpkO(`OZl@Ja2Dyw+? za|KLDSny;(16zR)P+vc*7#BSFIkzOUvI~DWg;bf^`sNNcv2_0HMH69~YhQPXdo|O- z-kzK9Bxl&U1nfBK2Rn};TL5Wnc=Hl>Ki%G4bNCNRR74$_tj0Y-2bWX$LDBm|5mD^8 zPP$LAK+^@Z?P<42%5pW`Y>%)0f-`suNJqA`EWX~IGmj}*H9vAP+y7i37-nOqd`sD; zs|m+!bm90d^Vr11|8*w1VBk560@;@WnMoXMV)xm^d#L45rnk#TZtCaOpp9*#?nUH7 zkC+B&@-kqkvfR3`{0iGISJd5!3ndm5an1GniI4~>lDV(`L`snVk^Q5I)kmpLjFgbNE0YA|6b>EbSk9lN$zVx2}H`=kh3w?Kc-#aM{4bK=19 zC8Y{dd9;;d8EzBGm0is3%=DH;Wf&s_f&~x4f;S9c;bCa?v+NpKnz!FPS`J(~^AZuA z>LQpxR*MnQ8LMiEQ53pi#ek`toNF(TObwd)+idBxJl&~hCc zufH3iRt}&R5%mwGVE&MeJ@$?-L+9T6L?S zfe}6I=i(@81W}6pLHp_UVVc`!wjgJZA~rGmXUg_G7`_ZSJ{R63g_5 z_Dh;;O(F)Oz+!2yJ(7a2XUD@|?{~Fdq4@_UObv@InCIv(8ym|Yq{C4GUuq5H>=h8m&TEM? z*tm9eGPU+KVOkYnD891MlpJ+r_UQ*(dFCSlAcgGXXqEC6=^!=4>K}_;#>~!!sFduI zeA!SI9m|2R9XE%%Wra{Hsk_IlCwGV#|74r_POtCnMUah6sPb`0jKGXb2WoKF1#vp$ zWp52mHE}X|_;r`ayBGx$E9m&>KNYe#dgVPD`+M31^+j5c_@N(EwO7Wm| z=79|eRjl`z?!7qAJarC`=u=yKbjS-BJ+3g6DJ?MGUhzbYP;+I(k7Tz_w5FQ{u26p0 zwvC#$F9vsPO2p`@%8zoj9AsB*^12i)5Qt=H(a{4_BJPLG50ckyNA&6hCk?FIL^gP0 zF)e4!HD(z78@s&L$y)o`rfB22E*^?u`D+DqHJ#S{w`Y{%=;&g-rtd5|6MgprAl~hq|t;zH>7bMRLoo zXOVA|Q^dd@@^G1{f$@tAvdwXKok=etIEfuW`_}s#&*DJ~GRDV0;dH7%$SU&ouBuu#~ zxzz>HzI1easeeml>cJA?XXzYRSpG)vBZ~faNkDyghrQPYg3!pw+Ecyng!WEX4DK+meYM)Fg zfDgr`5tjVV`U@qq88M^LzBo3_6<_ePDUpX1z&BBWruveqd8Fsit-5MhLW3)(Wc0@@ z>aplC4}}b@J>WIFkfWTt9e`TnKh$68eHgXqHhS$^hfw{D!zTuQ>7sp4=zc_0+eMZ{ z`a6wkG+Bh(mLte$GVdE1Bac5NHP1KpangF)zj|xN-RL{n|Cv_y*$IVxo#`EJAm;fT8fzdGr}!+ zxYO~jFM4cbW_XInwCIIG{7WSnfZ z!k_%5pZ>2`JbV6SWB$*({)y;?7WvCo{Z~ZLm|y-nCC7``27VZ#=UbYcpF@E0gr20u z^Kc$w{1cLmDdh(Tm-Zn&W7sI1x65~IqxIva*6Iw-$y?vQ5&SHp=ljGC2Lm#0P2O{V zA#8*p27w8JJ22jJrl$Xd@yW-8ZpnCKay*=x%6%sI#0!X&N77)oGS zXx@TjLyVE5pe%H%Nwb`V?-FE=>#pfkagjonP9yX~=Rzd@>fKpGqce8S+a4t`^DEIr z*U(Asl01`9;riQibKwx#b)joBiWnFx8$|K)5%Cj-N|@oPfXMK#umRBjk)Q3dK~uRN zGknhboh>e&*DW8|A^QOdbGk0k??SIf(}cUIdNKL}c8Rqb8_|!Z;J=gZfds_+O7Dcs z>iYLa9^IltYelF%2C9~u2!6T-@>{<0fhO^iTrlJ%x!G+I(e-Z051Gam>k2m;(Hq?E z>&&zTo?R0f$2lQO{t$@7*OT9d7pa5?tJOFA8>O@P`d(ekj_%$zI|`JI<;>AJw#V@A zTK5pepY}?5kd4S2IK|qX8w+?41U;HeD*caSok{)sE~wMOfB6Hs{1r?? z&GC?$)Zqeg1{KpVKDwNGQhL=v=y#ap90}t%6xI9@S!{%FW=X6w!$B2Hza{N?i{ckQ z`gB=jsWVWN2Q!Up8kWy^zo}sTsM&^(SVxj`$O4>>#Lf4)U$UtL+LIjW9tvu_Mz2~# zu9;KhDX;5uU1DDXF=auDgkx7gFIltF$^Pj*&O5$&4B}1^mXyTeYQtHdan&U(O2ckM zJ17_mCgK6#Z5fAba@9IwQoRR;s&Zg7XrZ%bmS1GK8DQQm$TnFbD!U2cz_nnr)riP? zAfK)&iRoGdGpeW6({g|(*m^BV(>(aFD}S9o6VJI_8yV~f`m9kl?kA;`YU;%H+BG>3 zhzV(jmx##DBP%c>6qaZreF0o!AROsfL0~xF7V6achQ+uhi3f*K)+pdX?V97e+j3vP!q~~l5x+I-VoxD? zM&I@RYjh(1pmM|QpRCSTU|1pf3<+JP0FL)Q`2(11^d01-$SJtGR+3vLRR# zOHEa-;Wf5uQ-Y`ti`_fMbhaLb*qrtXCzDI8r}Bzp9hH_*bxA&5ZZHH#{9{pj>23`t zbJ?+t3FIngd{R*3@jqk$CIw<#m@1#}ltC;mAX@IoZ;{qM5YW|Dyj9+X+pBK$W02R9zS=BsO(TrE45RWRwZ*d}^T^9aQ7Crl? z{`sX0^U3Fluf_T9{DN(P5Ue|j9DGGSH|(hU>~emgFHI$OEoPwNX zVm@vQvn^qkrZMzePM=-NP%3eC5ibN}sa2hFQZoUehKp4n1#$M3Iqt>Y+Y@!NqQh_( z3>i4TH%RQmWe?gt|Ogm(o>c16tA zK$nNFTqzVx`J9)P+$@TFBwt-UiZm*k6F5b*D#*(vs5P6ZBFo6OO@$Vhxn}=NWeqx{ zP-T3@OuLG z(OfHpe##uABT-cITI7C0nmdkZqw31Dr$zU>G8*0p!^QnbmURVxVdVEd$POq;g-}2_@ilm+Q{cE}ghinj1pRL;!w|h8NSV&r5SC?r3 zV$`cOm9T&T*Q1m}LBCsa!9W$|@cRo4LAx3eQeBe5#DbGZmDkOW+@{tMxkF&F^lV|# zZGI@B2n#5kJwBudwg&Ck?tjIaDDij~amlGGW$AzRjS1tiGJGuOEX7z3so&P@YIczQ z@D2?X^<{v9e`RR7zBW1b94clCcHqtI+iA1Qrc}O%;9iT${fL!h-wLT?2YN48Vw%># zMLgl%x$)oP!%~$Kr6(JH3wo^7`k`2&{;B385WZ#?#l?_g0rfNW$m)%AILeVG;-Nh8 zMIWv6JtD+(Gt>bitAz=zB_De`f&MOLiOqJHcAg`++~D0~!S?(PdA z)3q`9j&NmVsTHys4d2Jk`6yY-#`rB$uWJS=4J&a&uRFulR89YA11yyg^?b7@D!Y>O zGYVf;&xK$lW=0we{@!{5udz}K(k>nAI>#PV;YE8mBzlK215!0EOC}U5KvIv`BcwgX zc^Tu8{eOfY#TQ_hje00CyuZo31qvR=qoP+)hC4fP=BeWEWt&R^)lYd~@_pZPpmj*A ze@C7pBTRN*p0|`Ec6Du2RLrl|=5|AkwU6$KC^w-)n^D z8>Cyx0@ubS9p-{V{E*yL7p!mF<&~ghElHYU!4lw^M#hZcT$o!Dnx!A%AvB zrU)vw&trxv1u%Myz9=eN2|D)V`xF^Hha5oYIl3R7umeS!ogW`5sy*^O-j-CBj9PJp zm6TE_sM-iecW0cU`8&xmX%v8vO&+k-{R;_QQ_YfLCR3KYc)r z`+247pT{i}eGRw1OEC^c`@iDBdmL<;G8`3_t!Wu2wU3wB>t zu>s58Oz$4snx7(A&&-SZ9d@qGeb#juLQn8AXK?2}Sy4DWSnlhupT}J4^b4ad(up4H zy8Q{33q!I8ON3m(KQzTfYky@1nQTzO5T_Uk&z zBZMjF0YOKO2*PEDwfAPEeoj=BknRmm0Cr@CAGg~%iYGt7$G2pBry6pD5Y-_RD*S}N z800YHW#qYL|BdPP0bo2*yLz~5d@n=fYIx=g?O-tskh`90`PJDcI}ESU!#YzE=<8Et71qKVc>nj4pRcD9sBa} zh6l@_+S^eh~_p!vQ)tb zL?f9nF@V*9$Fx0vy=q5HbhmFGn$uENpl4Mi3FS*CsWl^^IqRJl#|Z z)4sCUtg<#_tQF>qGcR`d;M$P(}wU=i2coR91{Kb@-`m>c+C^oce;J(#79Ay0Q; z?GwA(o$y;T3wAzq?>Mu$0E#7Cnn?Bmy=JjP&W+BPHDL->CnMZd0On2nD-t*y4-ZTxT~640f!4(k;c7M)d^KD^e)Rz1!JmnCEcCB83yS?8DU_R5&*v%g z{T+MeWwV^K*@3>5qNuj*XdNp*+2-YRFY8sY0emL%@RKOq?*kkaEijuAC0(+s4EcIW zGgLm^vo)t#1eXZKqAT!3t7X`AO<@6)`F!W{*nSqe6tj81A+5QJi@jqrDwf|RGPFA? zO$5ox$h2-U&@t!nVs<4nr<_lSp~#pJG%ukWfZuS)z&|n=B18la_8OFdhw|1YDzPTf z#NtJ$P2ffva^ZVb`L;Iose#jioOXjKFH`t>EI?||dZ*puo2SOA)j#2i)x)$Kzd9M? znP~ie*}-TWr;Ao7OO?ja8OcZSE=lb4a%QQaLo8{?Q zS=LqKSBMA%?bkkdz5Qn)x^OY8?m6XM{x6d98JC75U+4msbn?XxrszX|4M}r0;gtS2s&yy++6IqyQk-zC~1;jZ2H7rT-tzcYX`gcJmrAl|ijTn1&3 z+u0-9j_aB0xX7U?a>i|{y$jrjKS+~>AJd>K!!lUwFD1_x=n;EK?6Q->l%zgR^}5Yz zUDn;ch7nm#DZj*gYxeUf*6YT}_xF<>55D>a?lGr6I4wkqkKWNLWY+@JXt)lzzrlix zZ@G-2Odti+;H#cC*{Tn*Gu-cdY!t#2^E(jH^)Av0-Uf7UWOBO-ikZ{-soHv*>3go- z?*rhYKvY!PlUZwul=lzcH@6h*>8W6(pT?fwfSmCi)|{l#yXfHgC*1LrC~wo)3poh1 zI+-3E6~872-8=V|HV+#+NUsS1-6l0ONv;*;f>zh~zqoJtL!}0w4b_lWWUVei1X0?- zAx)p#amd;|!q4y#BhK^0EC@SXqoVrtZ7*)&^vI^M@%f*EgTZRPeRuZ0h}Wbk-B~}g zKs{L?sH*TqT*xG|BnE6kptN71{73VMzAjGwSI_dV8VoWgeY}}nl-S4u<)lCxoA$H~ ztS@pGs2&UPN9ds#psL5l@!<1YAGYv~6Lf^nN{VF2d^_Rpj)Wm^uvr*+A?4yC@JddQ zdKr%mKQLvCKRnggx)uY+ug?1&jbH#&$8zs(iaIY6r|FQD-LzF@76@{W*=0Y`xozG zX>Mzeehss+m1@rSPu^azHDAPk;4ml#2Zhmm|G~~)6zDIH_wo^X6Pilc+S+%H>{p6c zC!P62y{~swaBvHy{X?JbIn}bD(7LOeZM~lab?@z@j_&2WXl5PtQhWb~%ZKSF4`J(G z$73KyWC!V0?(VmosfUf@+E6JoCtH_$EV|xpld<2kVUM}FvtXiY82y7J8B1IxJnv^k zvzP&6obB-eS-Rv}^D>~h_I+@%fko;~PwlG2SGro|01*Z%`U^}veVGr#1X!;H8%#^8TMmMiz5Q=R6ywKC*kc)(?!hVvWptk-#R&x&TyJ+ho|>x z?S7X=`|d=N{Q2Vy*_t*=lMOcD1yA?y8wbZ)S}iUZ)eK!N6AQL`4qp9ymME04gVf}+ zR^30L%#cR>JkKGj1}x4u$xO~X*<`c3M~-ONxKyrBG7-(UkUQHJM&33cNm-^9$3wk- z!(_*}l9GiKjGIJT>UzYM_iG}OV8vNM)mM~fk97CNj0W_7S< zksuom$6G-jl^6c+g>@3iwE>n*^|vmx`23I%3YDho94HXkA3p^vVUMz`YZ1i-w>Dyb zzqY3_Qu0tXk?1>-ecah7D;Q_Nl@oaQkxc~q6dX~IA)9U3_*q`f0|h4+4kti&S`oa}PE{`j{lo}E9YX(__SMU{QP}o~k>Cxjt({;2{<5)~xLvjY+4Oa* zB09O->>)M)yYBNURFsaDJ)Q`eHFLSgjYr`mQITX=d{~S)lthjIvggVw&tU%8>-k13 zKCU>Cn}>$<78oRUjGl}8p`}W-WI;$q_Jg9U>AUxQ5*$Cs85ra8#-RJqT>dC#TG{Y| z_zUB_OF@kh_D1%hfE8)n7QuOESL{olpIh{UJ||~-yk1=ggO{L;G|G4?V>*<|s4dGI z?{k4;q)Oq{#kcLW>rClc?CQc|;=-{F&iTvrd;Yom`vuF2hh>u=3Z9lMDw7`WEF)lQ z59McjVy;@=xkkC4iGHtLtjL;we=hRGDvUofV9QO|0r3R=$=^}Q9}qSh6wSrQ5`#g9 z1^WBLLllPuJ{b~7vW~F1JOVW6%!r6R{gm4z2BJ`b-w=qoc{#cd$NGuVlh)2oXXs^J zaGWwe+zXkQxTg&@Y$rV&V6`J)#fZocVUI4Q86B)|oz%pp5f^o5CA3lB-BMp&r5l+X zFy=PwByle&+{TjYjWF^C$=et4B>E@50h_2?4dLo$B&~MQY23IQrCsw5u}N%jF6s~h z($}so_rn!NzwX_rFD7rBAp*|sUE+Q>sF;tXb#Q5ZQVrYyr0P-ro{-O)7FTbEr?XpP z@lWA1w{{5-rk64-(0auj;Q3IxfHKt(_~`g>LJ$XkMu;cd6-v8TVy z0&rPCUu9`FSmM!y$_qjAges%m%^B%Q!r&Z)HURL>sd%MPbk>DPe)z`S#;Z<}tWS;=9{E z!tdn#J`j5G372S(Cw3!#;dV5!U{_C+yg#AIym*rv*|T(yyW`j>ts*&JHSW3oj{5O) z(T;K09{-(S1wOH2K!w^Jh0P~oW67$cAmNQ=^4p?@@Hi>H0r88p^sV8(c$`itMw5{i zy4i*A5r5ty0PfPP1CRAq9SY$ICWc(OoxyMzD%@P%W1U+lo|A{B0hD%!Ir}}N5{RvY zUaui+Ndv{YGM({3%O-`jhUin`Tqu=Eb_^#Ht?n16-aYQJWRWSn>QJ%nLa3T5uudt( z*b^~}^1LSomy(h)n?N{dg-?EA9{t%}$c}YgKr+iVv&;7~?x+vU-QF#NvQnNGHg6M^x7u z31wW!&`!@ow!54jB307Ue#kU9NLsYBP#s(ML|F&=aM};#O+e`AT+nRUrBhRa+`r$N z8U+)(XwH+b+E`L44J5*$7YnsT85yBgI(o>-eIhEk<$oZb@<{==+-mASA}SOqGhkLb z(Qk9+qt3MN&&m!ct9|UFC6ObgDeYYp{?~?)nX0UWayL4<@*@}whmW^57W{5v;@ycL z-;FBBvSf*M29srtl<~xd9|)G_QUab#tZssfjYq&+LZiDAUpC{N?3@qCB>foJkAr6- zG4AVSboq8EPj{*}dnN{38m#S&bWroTZewtAcfR!XVc;?{Q;oeV&e~{LY^_YinN)xS zs9!U|FFB$$)xi_S(ZPW_TMmRFw;@L0W(Vl@v>yrE=F+QKPBQw zT72X16hI8!l`2i`TEU?Kt!c%7!CHx=-o-WUSMB&ojsPe*pmuJ+G@8&Ef0vkNpOIs| z4^#Xn9~ND0RH>B@iJfMkRM7%a&?G=&AQ2Xwq5k;sYni)l;`gG6SR|I_L3ZEt!y^1M ztaFSlcMD>zy8E$43t}lbIehz9ut0bS77mWECE9_!E^Mw{1w!?E9|Z;Df>$3xBT*)kWCjNCU>zMcvI?GOl4`YqpEDJ&EiknmN=U#(_EMN>g{Cw5 zrBM z$3t2zx3Ayi)>obdo_?79s#$SJ*gtfAfhqo<}V`YlB0bcirUKJ)?=a{LuJ+N}P!e@4*=#z!${Urw*oJp;O9LfdPc_=<-5>)pA09uN(5;j~Yu)1>gF>0A7KS zQBbn56Q+A3qZ#C_nG=v(t>3bUerUQ=CoK;3Y!v_&+*bBW54u|k?h7VuT1BP9zs*v@ zM+9hKtjUMnXeYEFaeEy{jbQ+P6U8}RKsMQ0o!!8M!?P{Vo#`P$C(UC%40f!!$oeZh z)=MaGs4ehBXrn9-M~m2iFB5f57DAb$Y1vG#C^`NR5~gqZjZ7s);AEYkyZ3lqO;xQY zdj}%4jq`hzNSS7&mIPwF!Vgf++`fir%E+C53?Elfw9`d*{&+~3fd~Y&ZRW|e52*Wf z{NQ{A%pr~}@7C9oYGy#S_k3SBk}mWb$ehmtbRE((#KVRE+pVKn{4(r2M*>U;J&o4< zzVr&{u)*@9We|kSvimF1MM%e39V>dQ9uEqO?#+qO)sI9Ke{n-^8<6-oDiPYiPt{l% zB!Pdm9Ms5|KF%0~Pv}cvdtV}TQ)R5<;e>4E{AF#E+nhX7ve8l(_5Q0#xtn)IU9=d2 z&=VQJE5BpB2AGf}+Us5-o64{N6muiM7|KYe~==9HY) z$m7-3*ZUKFlaBTCfHVfWO_ZJ687;MO1EIhz)AH?&eh~yRRee0qOx~dve%A{Fup>e} zxyUdvxr43YtdU)Lj#;4Ywz}HP)emOKS?erZOlt1lorQNg(p`Hz`h#E)y*A(DmMO={ zV;feATeEm`!}cR`*sKVmsW6m+er{pCpCzaoWf@PZBAfP8TSB}jy>I)ye@ZtyET+pF z``=L=n=~EG@qG#J;fcG?+=`Gt;sdZ(YO4V@_BU-6-;|Fla*C>1mSbYUb-}Trfeyfi z_v@9xD8A7C=R0@3!+@}8^K_tQ-Trg&X(Dr)1(m*?QTHO3sKGHDzlT@ff`HU%!dq1^ zy#Vo*TJh9ot!`ekQ{;SQV$&arsj9*>6RW(NOG)@^_YkMCh8s7;0XC_}NV@oTzxCx~ zbMCLnR&9>i=sUzK{tGuYf37^j%pT=0CWE^LG?^ zGSw%D1^YX$DKifBv1d60YFcSt8^7Yd>&Wff5xKmutP+C`S}#Vg_bn@%86D7u;1Q}m zpl_s;oRsi3*L7NuuuPK!jpixOCoI9Az-Aysw~v|Q@a?8n%?%QtQ{}uE9)iJ5K+Nnw zZ8T7NlI9DY+<~(n8!Q8|FIABn=xcN$j(%6+*DVXMC?(8Pa1wlCCnT^HO#U(_6tOn~9!nr~XH0^LH&@RiXX(LCO6F!1`fj<7%iu zs$yB((?n3@FGTR-01krt2TN%5`cNGD6Ktx8;O*5Y9X|&PbtZ;`N52xUl492{APg@E zRD+o{aKMvLZyM*Vz z;E{h(k$*gS|Azzm|NEXmax^GfVT)YdO3+Iso?Jq&VF?4{%wJq$HqLyE@)x>zNW6pP z&@a$Gm>rXewS|Fhph&MZ`cQRcW$jkqRJ`C`a4)_#tscj{)a%EB%~VSos#%L6jz>nr zyh}mamE|w2j^nHq_dHB@2ixrerj^aVUUQhczI~dtzZGo#+$fj+p%v;>E-absV;CU8 z?28D3qX4gd%~AgtxG?tlHPrFh7`IOO<%O+K;nZ5sy7@aLXmFBJO@28Dw1!^fQ7^Yc z&!HzTcSC>u=lrii>hCLE)0VTw1&YQW(yUd;QLX)$aT`K;*?G_7&41h;p%|ncM zCn?de0=&4K)Dfsqb2uJv=5o5euyv3J-Ag}?2>{+z?DwuWT0QGPzG@A!E{>}%S`l%6 z9#I8mMh(pQgpCrlm)z!SiGTVcNKU`IJ&*L|`1fTk(5{vfL+|&&nZxIY=Vl2-(Nok= zv|G&ZrfvqU=#%4f7nZlxF5(aE9Tj^KOv!C|O0iKC|2~a320VHYM;+;*HM8Zgp^XH_ zSX{>M>)I-|vhsxQUq^9cX68DcopdtqJ_Q~?<&mS`UwJkh)NecApS%6u31i$OQnZ(^ zKzyL)h}cf6c`Tr{%vAGomm|9#b3wX3;dXT>h!Bwf1R-QDYF>iW>v{O%K_mYLi?(YV+Cd*XjM(+_Z3N zXUf_9rj>65TJzYs9*~V!9gcWL0T`zw4c8Gcv?kDcRyu8elGAg6c@hx;yqYqlF7f)|10HsMG5qWV2OxQ+-w9$=<%-@(HeLtTt&U-wps zvEuXkUO#PUx>&>C7$^Ttl>|l7hJ$;jmxtGw`69LSStr! z;qO9&-}@^#07ef$_I!5QFOl4g&_BF`cFaZfIDprrA0uFU3oOOmUul2(y4bH%mF>!y zjw078&rgE#q6VRWmJl*JB@o_sMFlKY$i)tv0Yw5utimH$`?jmd6;O~dv~V&#_t(r- zXk#?04G!tZxJ{|c>^z(MY5t5JfEOJUL-)OjnRG8k7k>}eTNMN$yyUo>Y z`1k2mk6y8@IdXCU2RIwTbS|~%{Z3O3WM)a3oJ~!?vXrxVHuxN{G^m5ecew1v>PFXx znai7`c{`4bdRT!}Q=H0oBk-dEiDfQ3grtXS755HLr;nG88N;k=0H^f}XO{_WKk3`R zO602<`^w&cnmW8KVv%lP`N<8%!{~PwxOEG700)!8eEXt5gh(TOCnBQp^44;1i&G$V zhr$T?@pW;e#f8Kyq8=&vCn9;|1n-P?3 zO8-FNlQmRyX$e&0Q6>{9KjU%6vyQur`<3haJ!`%|(3z#~G(HLLkIlWyqW7@hrW{Q( zb!nAw;t-e=xN9^3Wh~w9E@^wEw^BLv$sPQ^^wFLk5Kv|4cCgDrcjnLejWy~-t9eU!~aLwTZYBabbo_H@t{ElclW_%g1ZwWSn$E!E!f}?AOr{w!7T)L z65QS0-CYKDNbdW2pWS``D_^*V?w+3N>gulg$vLMQRri%fUekGsAYRB6(W=OjFaOPZIDk|%e(n(t*z{nZ6N{vCd9EKc+FvIi%s$z9$? z&Afk}Sp*@k%xu^BJPA=`5^VyB{fEiZ&%k zLAT=>x3}CHfllD991PQ0q|ahajy`aqG50O59KgK}PA<8l1%3g$k})kDJ+PGQ(GMQG zH_*;Apv#oQl!gWq>XeaLdS+(PN@rtmTAyT(oR=eV2zv`rUPZu9HRPU6xI2Y;Bb#+g zb~W28H5}*VZx&m2USP)NtZ?3z;Sfz0f2ZMW`9bocV}Tmoy=lvcBbYz>3^no1gi2bv zuvhzAz{LKPt!7p1dr^0V*}3vCr3e)xk@UolU%hdJ?BOLef1W~wMKlRjq}0p9NR>9% z1A?=EM$L~GgTtRS!FYO^4TH7jed2^)S!hn2-eX*O1c@W7o7eJT1a&rS-8sC`HM&ho zmC@Dt$T-`+8?Gc^LTY`&CWT*Q5W-02BVAidz0EAAp|*C*ZXv62(eE7JiY zW@WGGuQdA7d{n>Vd->e+!1FD@&(oP+>upz0MdGk66HsI-^MGz82*U0srj6e*;rvB| zkPOrkcwZQ|j*v5YlhQQV+JJ+ z17lS1@>&g|TdEQ6wXU-wR!$!+!%7OG7he;MN{Os3oI#}=O4$TwBra_VS{7+$2JUMw zRh7W)pT}9lwiXe`Mz>g5_OHfP@uF>#;II$s*YJpK#ftx(KFkfkw@ap zWRt(po%e$QhTR}sK;fu%fB)i|TOr%^9Y-BcEe<+h*5j}g#c?DHb(4U${?K4I8Ux_- zC08IUTFrs9X7dpTaQgjuZ;`B&yEl3fqHHKS;O{ti5GcU-g1@Sy^z=?nB?F4I8_-(j zdOAd2r285iNedob)00=uDsz3DrULSpvMe z7BWJl)I~K8K$V-0>!narea$t!s3izK5W*K-);Z-qucF{jjc+;emblip^Q}5~yW8s9 z$CU@(*onuA;o~W`=_nQNeeCxw!Nd8Cq{2iYs#KfGYT7KY_w194+aDxragDCeivgSc zr`<&;#2Cxz1;ZA~t%xYZPeY^JHi)yx$M3vPbEMu%I00V8+MSJ*cVF(4c{oPt1Pd^m zCePUzhTO7Nrvxe;QnZvOl;`Mc>S~#oL#firZJ0OnNPUTnGFOugOq-B-QfzQ=sO!9` zud03wPY*io?O=uZ|gEJrz}Qbh;X-5}0_Y|VySHYgpMj6EY|3&izEoMkBD z)qidNlVA#lZJrkTf5FlZ3#bFO^g!h+I5=j-7Q(*(CPltMwbP|0;=8H4B5b=}MHSY8 z0^Og82|F&!J%S2&Ddngvy!q#FpTbq6Opmf)+x|2OhNA2a{smS~NbCP0$Jak;zy5>S z{{7m2@Yw&f=iX$>NB>*MAGgPr%hJHY{N^{u`aZC6tc{JUv?w(q+8w&meXZ&quHw`W`h;1V|69Y(RhFR|D@6;${On_wTF zgy<(=_2;8-K*qB_s0o(L>JRvVAt8MoYwx@-si#gSUJ4ZOI!Pp3vAe_r=8Y^&MWfRn z8(z~m0BoYwTB3Wv%ljehnX@U93_=+N$r_v~Z;thz3JyH|lv1B?n?pU2ttGdnNVcAH zZ|8TvprB7(>6Ystt#z}$k*me+46b!?e<6nw-y*HUedZ(=XivFXT)pZpNW$rAAxi=) z_w^=IM)m%Sfz-XvwwBC*yZDAf^B*05e&ZfPGDb9fmso(__vC00(L_O!exF6Cv{AA` zo2K$^J=Vj4rP6o5depjfw|el-b3Ka5a%ge#v?-6JB-f~Kc53duiT;OMlt(>XR=O7J z(=PR1)uVI&*eJ3;(6O@&xoA#%q9{nZiaSwcJ>+t)ckJrhQ2ozSVIx%%q2`fDijkk< zMqUQGdj#q9FC#sIR2#+#9gU$tFkTXbTD&dOI$V==rI79eo`&$uMJVJp)(pAFJfi2? zYKBdtq-VNXeBubWx7=T-UW!VGF{G~n+;n|d>K4eHE%M0(JRICL z+DpiU{Y??1kw%7B{I0M2iwE;S!3eVuGm)*a+2P)gt6Kt3v&(U{v{%cZ{J7+KD|B@# z1jO#tN%F-uvN#cmJm04 z3D2;1m7Q>Z*zlF`W5)`JR!=vA{avaL=Y2XGdfj2Vac+=X^@M4}CU89K&s6^oArj=* z9I>|c0!Y1g+>#h*YMWdAJIq4@JP%kk-g+z1z=ckIleiH`?Ez7A+`s=4Jv@x1?d;Ex zjaKKm$Ms?jONUoUE`OZ`X#D#?a+?U2>7||2Rp{W9$>~y_T?p8|03SCmP#5_y(yILFl|X*B4j_FCfcV{YfAjFoAm{Ss@(kUcY2|2CT$f0X0~OIp0Q8^ zn{(nkM!CxpTl*UtmXXaysig%^l-e{lpSq-dcQ#{EpE0dl3CX(2v7zN@BECXK;sWwK zCC6Q}sNyJ5x)BC+-3v5yrFrCgQvw z;$3H|*aoCUZtPbph`>u2BtG^G(FQ%t;@r4-12lXsgxNm0Uv!T*b?`~Ud zzoaDf<(2`wVG}59b+$Z)1sHXpY0B>ENdRGP;Q#_Er>9O2w%=EGS!}WZlB`9~t8i2v z4zS9GhjUfNZkYI0L>K15KC48%Sun9JoRq`^BQjZS%`PuZHwu6!H_ggxY$H_nu!d{T zH1mb_R&zOCOx*2`ny&1pFW(*XfEQv(qk7{gP>30RvC=4}J@hX3SjchQ(FA?8uKn$H zM2xwB`-(|@i|F;6^(>E?0OOdObjJd^u`{Q4333Y?boG`@v)$_3zWK-M zeDS%EqNd@M_ij&$s1Py0mz@ITeiUWM{Q_8>j4+zh1J$tTeb>z563EWqw9HcFZiysl zdzkR?H&J|;pEOsu+9PMSelZeNgw5L!%D($q{ypXRgM!bE1i4{9ZBy0DLHFfX?t3kr zUw+2aI>&wSs+&_ysD5cVMrqDa7%EjrQMW=@AdNwCU!wB!H0Uq=Q5giGZ;EVAn!)o6 zdOd)jxlRl?L4qu^^Fhka-g~ijG>aG!GtDA!oXl_%w%mOgRDl+=Zs+<8Oz99ccB@A-f>CjmRn6-2Bb6~yzbj;q(q*C8&dniOknTfE)R*La zx#5?XfbhP@Yrve*`VCaxYTsS8{9`w&0NY5ZLu5zkSqd5zz zPc+`5-IN;0fQs3QY&V$=itTU;{Eh!C zJ1soiXy%gpdtRXXdK>qeiw~YIfvO~?e8#S zPMt20aWo0X?X}&cq!@dW$p>4+xyv$5a%<5bGx|D9Mr};J>1Zr!?F6he!#9VKyq|sXN$xy;BV3`aI(i?B~J{C>ypTSRDWf zF0b!0x;QoPDC0mx>Xg9wr%~)qkJxXOZ{3R;t2}!dfW-QFlbF9x>x&|;h{(HW@0`Gu zG4#Ga7F&q`p$|lWEAyV=8-!39K3(pDaJ1=63|ilhnJeLZD2^M5wp#5-KOsm;sUt z(QiD*h~JQamj&yuL`3ngG^{**$vRU4i$^?G3A_S3{e%n?-hZ_Al9s4w^F6=Jy#1V)aY)*5@7DTT%6_Cco39qQNF-g~;z=+7&m4Tjz;5uP$%qR1 z9UrbBjn5gvqPtOoD2E2d|L}Z%%c`(y>k%W9zgopW!9^ShQ$4hm%HJnaRxh&&_A7V2 z`GNB%WJUFOO`uMw=YesGin5!zTG4uq*pP0-Nq}?n73;;XOEyA&MRa_e^&ZP^ zDwi{4B!+z{WImt(CRO$2+n~s@?w(W(OynTQmr?nXNuut0cgW!OIWoIE( zpn-VAw?~?BqwBSue8i}$YlI*2fz!F%4KnJ`D|)wAOx!d(i}8oVjRo?>nb}ulXq^(Z z9G8JBeFtB}_JlMWHm&bZ2;u|?NzB$qux^JADFs%$7(&IRSme@ zO6tuyCGYZ*(r%OFm4DiL`<=oc4u8&~wKyz^1bXC0zO}z<*r%IY3nPd|yT! z^FQ2c|Wpk{pcI1C)7C%a32K8Q;^>k-}fBEF!(Gu7fQ6w zr>|PsC$8Lw>>wlad8wjbqMDGr>*lrg8g|rIdzI0W#mwvh4XF=;a>`>ARwZRUOxnC4 z60xuw63?r$WHr3kcGqD%@PjL)zQu(3nmPdMEX}q_puIC&f%F8Jb+RO&qDW)yaFx>y zzRW<0KK`A#sLUzKkjV1Ji#Lcj8^}uuczi+OtHXT?I+Y4b&8M77 zRgJFXjbLP%)D|`3F08WZfrd#DEX;g(3`NsY3wwAp`(7edaofw0(3hpM6j?m-;nGtN zCRZ-2zd~IKN~+6P0WrYAP3PY%U?2&{n zo_|wN!!%VmxLRig1yFm}(3WqL5C2nJV0%B_ZIp9(;Iq8BA}?>=D;c^lfC@b_@VYth zRFzMSHGm?xk3jO-f6dqB_*LC~P`B$0f{7}#@oZi9W{c^ZEnx0*{?it zV!p!!&ki^vjI+i_&~Roo~=M^o>V@-0lf_pZkb zRy7vQ%1P&i7T*}`JPO{d;IC;E1D$SU*hL@BF#tH32BB0qnVHC25v!$HncO#iD{f&D zY^Y^+t7rFAhZ`h#sJ*q7wlCsnvNQD?AqUGsETwx45tR$xk3q)<0)?QAD)!IUEC5`g zPikB6ooDHILz8$YuW7$$qc2{uK~td z-WJ9NA9)jqAa$H5Mi@hdj2>mbl;(IZ$vOTsi%lGrz)nNiG)& zGxo)$6l!lq*yq-?6#@Kl2vV%gN`|kl#F$3+0RZzEk!{8Gx0)fIhrI9&lFj=y#`Em0 zvXfimD0R-@3`)Y%#|g6A+gs4LjIRxTeF|Oh_1*vyz|v6YXRxS=<-pY~TFKJR?>h`8 zl;L!na^eEc!jH=*&oB!iq>zAc7Z+ zQRhxx2$@g^oA}%EAS}Ba)aWa>AU9UgkR_&P++n`5t<<-bJ*G%JFv{2sjYss7pEeW9 ztiE51zV5=`mK@Mub6&(3$zl)q&=AmV3tcJXku3WL(Qx_=^N^}*Wx4FZOLoHl{L}km zL04omI$250a97X=(Y-6gm3!XGF7{aBtvN!BW?fMAW%x352&~W?GMsV8ZM%fupUy%G z+`ey(K)+eRU6x!&TT%cI8^6BGg@}VS10hsj@6)jWUg+i~p&}9S%QG*yrNmf1^2ZNv z4=ORbkS5qJv!Vk!vF??x5D(F45*fzz^pvbDw=Fm-8KRF7;osxK@7Jj-5N~%%+kdyb zMO^!qO3L@p35m4co=Ej>Bq_MW+z1R`aZQrodkgXQeLmB<5a7S7i$3B&k~D`|50BXJ zsrBro!y!(=B!dj%Rm{VyS{qNiZeF*8RA=5tQXl^G5TqSrjeQd{$-G^GbAEVo9EjPhf?@)YI+soUntFabFe2|9rTQZ zIm3cN?I$A7jp$>(fM6!+P^LW2wi?OG(jB~@5Z4lsLn?3O4yH^|gq%s(qAUtm9|(c> z1vKR5u3@j*?yYx;zwF)7r0=ul{g4|F21&@Jv=`V*5qIz|a8vUJHM3IWf;Tw!=kzUF zI12ukHx%F~FCIT!qbKK8h;oFtuf4%h^X zKgl$eeWHg&2`G-HPaZk#qXs^YWGMkfqAh@|Nql2wic11cP80e^3O4L^zHg!xEUkEG zGLY-;WKX1L=K*6-cut~wZbY#FPIn?d!&mONVvyL#L8SW>Cv5Q~Dr=Ifl3Qqx*@2yV zxB7fZ2G9g7*rV2t89vN|E)*EN$Z)EBvMuNo)Ja&khka@RZ{H&Y{-KIZHsW_V_o)h^ zQa|%rNfP}kRTL+6E?tLjWhjbu8Vj)hvRu2?ZylYhXDqu>go23ld0Y}IXzZ6Av>TEi za4n8gRqg+Q2JP`Ze(zmiDw<894iX!#S2V@BEaGv?o_?c*4Qre17i)xveTWp?QPf&F zzp&8ZFVK<*nAK|l3s9_cms|A?%3NDA*e%{la-i?ORf_z+h-^LMm=n`R4l;r5HY&vB zyJ3i_(C{J!PolnJI_)#Z<8;dBJF%66+t?MksAxUjia;H?UmNHj9d#)f%Blk)8zg}# zem>*(oO)YLwY|}qoFt?tkc%ZAA7d}l*}UV-lN_iei~r(fF1y%AU-+|hc(+&2xagva ze;S^MU$vYP%53IOBnZEdj)iB40+9W>bU*Mh_IBi? z4~IUp$X2Y2A;Q!Woc(HCfApxiaR7vvxrzey!13v+EBGWcfva@NcKrPz(8m-J6;oft z7m1|M7t>b)uJWhG+{3<4`zgC#tdHIJedhUGSr-;cJSKC8b;_xr1;L};>E_PCC^b3PKNs)l)i4jlPM2~oFgmkx!b*fc~AFRR`lW0dV8Vc8YmocBJ zMr8qL3TL-LwJSHO+zro}9aPUx+aZy#a&uAZ$8O;wp4k zPqKQSP)dAyaEeuvvn2YPSMT-|x6Qr|XB_98)0|}9mMNbHcUE8qsL2j_F`1%IZ4G08K)1>$LXOpm`S9K9LOlrhABH(D$;fc z**^W1uAIp^nobF9KQLFhLo|pTCY7C>Iit)Zdl&XrSvgnfb&FVP7D%t+lbOiFI^1McA22>L0|$_fRuXPwm{IYbWrB6z$;rt z50|AuR!X_}FC_kBFNB={X*9>2g1+S4Zzl1;xAWdu)_y*`ukK>DE|6$T9E`pW|F{qM z%rP9D*aF!r#Uu7C+rOj6&J^zWcWMfZ9v>!9LpM%#qHkt>R5f#JHoR;YrLUZ2+NUIY zcg>PXg5MINq0o&atKusHst9}?#FF#RTh1zta?}KWU=d~43H7}>`3ly~jp&Z#CP(hy zVU88}I*CN-65~2?5UuAV`y?ANvb(0m0W*r~VoDCyZ?MiQZzl&TJVIU@9ar5F5yCp| zVWsRi10{w9wK-}SU6nSf)9p>euLHb$LZjoBJA!9UznRH&d^*7Ozdtt4ciUaQkZ+tnWJU^ zqO7$cga=?1yB)lBu-87dyyN!Nn2X~<4}3%)ubdz!2B5#CLLm3gcpHe-CY$+6c}V|J z2ZyIq^pN=5EoPigA`7LJnVDIAg`#WP44;_2x&vk>e4H(AOXK%`9`o$HH??B{Y%9Yb z?_Ln4PX|xCLSwOz%{~2H=%|DSu5d$J;JATO_n6S`hYq;NR&HR3kMJ{8Q~g{}1)6X3 z-Xkp@r~Cvlc93Cu>kXMcsQKX@ z(z*2%zWK8?xQNQ|O{JK)pC=_c)MA_Tb7ZQll(3&zjNkGvA5CARLMw zX`*c5zFmh^8H5dtK;x=8!fqiP{>;qK)WL+Y<6yXj!666!x~dt&Ok?az3ZonCT0_G5 zGUiGH#!{)0N1)<7#D`^$AqCen0_aijNC=;=scui8;V%Hcg_Tq6l@g^>fW(2K0o#h_ zSZwUHS7{@bX}>;HFv$CxtVIB4e8&=a~Mz;f$Lwa`)=<=v4}N8U#leNHu`G%v~Ei7WAT92_qwLi z9l>7A8DvbF`T_U`J<>?N z!y4*s-%;o}kfF@U$hAl9f%%5;+_2`kExYxbu23WE9>TtqEJ;>{ zGQUs%oIwrzRFi|1S^>O>!5EdZ*|CJlkwPS9;pu$ zX!7`+v*RboqkUn3*X5k*u_9IL{*CT*PQR}c$LIWrk%I4S*Q9$_sOM)dz}=|f^NnL+ zmozIHCxV*ou1&GffyJiEN{paqcn_k#+C@Y6GG-;#7umI-dEe-IgUv!s}=>z{U06NmUdG z@p;b$EHLE``pq*T4y|NwPrJA|$?SgSdRBOi{M#cZxDwUq;>>2BXo%PcR{WWpz}Ls; z^lB*D_V}S|;SF`3Not7sexz^LEtoeQ)N_5BaRNLut1N%(n`|yO$h?*@6Md9ySq?nf z(|}e@&kKdMRZL(=cE0(-(hGc+dC4OqrY5D)r>HoC={O8yIzwIFd&;K;yE|U*EbHy4Ss2J z;FQUh>XHCeW}dyL?{Jepjqvp*Zks7^;3;mcy1{`p=uekD3J+i7Dz^#dcrH|z9`sR` zY9YPhblT_-8Ku)$nc-z>yesA|!<{(v85KDTe15j2V>U!32xi)AQ;|G4{E3quVOd7- zxZg6mKadBSZm7x2Ev1cG^ZopJ-7Xd(KndAxWwk_Dc--aFhomqQnJRKUKE3Vy?@>%u zkxJ+iuf|bZ1Gy9=qkv_xpe`S3m+e|cQkXRX)hV5-E=#}O$nN~$|YUfjNIpcQk zlPhA25Y4Sp+H~JFDUpQs@|Z~i-?rs9Sp#OCM$mYLZs5FY_Y#Wueu`btu^>;7;c)h1 z7kXr=vgy{An@>ry~{GPP_BS{sBV`VXtrK|qzd@E3hLqZeSK>9*yt^<=?w_s z&ov+dDPbb&^GK#wfX!@FH!n1v!dHOM{QRW4b&|5ojvA3Ue1gp^csG5hoU%;6BQ=9* zUuWOLDT2loeX=S}gQY&^&b^4|%9KT(j#q4|{HBfWWZj!h37a^}C|`li^)^x?SE3K3 zSzm?7SwATHb^PcWK#Y{-m1)c-hg5lFl#7^|@Ffo=MJsUwGVA7v8x&Td(+XpFC?n2L z)l>F;%AUt}f7&UP0qbmZhxPS^s1(W#ueNY0ZZlQXG>)Upa~qb!Is~5~T5m-@_e1@h zlHU-O5oxz&zM0&|vtM#Sy811or{9q{XR;se63V z3iEF7C4iV$T6T%)&tMQE#{|Qg+0J!On}37OJ>PepvUFA!`ix(ENpa0N-Uu`!_Axi0 zLW>K^J?6uVb}y{m(HRJ7iNWNgrc_ob*P0OXgXr;3d=i}zY3XGT^EoF@p zde;}WV8)ZVND-?ℜ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℞ERJfkVFF9%yLCElWaJ3;gEc=Nw-POan` zpY4HVZ?A&Cdyfk6utK|-`}d*a>=^gMIss5!;^Z@d#zYoJEtLOl${FG1{GlC&hbkqO z)`Lk;tOfyE4tKP;a&&4Vd7>mMI?L!c^bm%;8Ae&ck&DgRay)ihS#8lSE0hgl=c22c z?eKTk>(3+(xx!zJif*3Sq%R*zK<=K&JfwFOzn^SbcdqX5$*ENP$8b<*_j%)p#jctJ zBJe|}aAz;gN?=$_}c~WANRh0Tmb+1Ctqsy z(R1=CG9fQ_5Q1Cu>5au-scB*`M-o=A!Q`g!+P)NM(QYp5KyH{s`+hdG;MZsT21jj znFl>@jo{V)V4i1oruc&`HFPZ4ltGdIqb9Gl!E4G*KkQA>VI~OjvS|1cDktKCM3b9a z#^#NWzu@de@>=ovW_J+I{6C)Ta8 z7pZ!#n{ABw2uu$865~I%z*y&-=;||L1`EH`GfI%^SmnB7!$G0)K?m|oV$zSiHFdFa zliVxN;-IVZV)XM>9reeX0@hoHqCMTLi)XVEE?!i;dd2oRqk0-C`YOfAqRBHh6qW}N z4o=%zHp;WOKTAcqNh35?)$YZNxoMa5m!l1#6VWKl#}bbL-q`h^+#>9+6CZ1|yo6HyVq$KsC>>bid-0i037u4Z4%4MjKIBBVpO3j}5t zH4{2m2n4?3R9P(@YY+*ieQ>i*@f1{p?K}Sc7GJ2NpaJ^u0C;XPXMrHI)8J6ZPZd>9 zWJ6f3W1xrbsbKM!tc0;OtxaQfdjIL;D~_0G`SFo;jq`yNVCDj_5_E9R=Dwj@t-$3& zIl)zHdR;tO#2(bWf&w_%ncx`e^rxL(94*Xu6C-zTHgWhA>pz#gMFx8~o53TCz}e7& z%@1&P$m7gI*$pVLiZ*>@lkI1IvMX9j9J87UgIyk2Lc)I8T~ zgnCl0C>U?|kshmC-ViK*y$gS)kOzYlvYfPdPp6yQSx;a8>^U)lrcj*rPf(7?s$csG zW<}}_LA8HoEyI{g_5DteX?JW-X5!7LyVv&TuMgyw8_!REK8+u^B4Mz3O}J}^Y?N8X z7BzfW?x~jb}fCO-SPHP z3P(S+hVyeNv#w!ifx{G&*7~`3nN!$f5GFX|7;20ijsBF9sgejo`?^i97=>z?B-88y zAh{(Da*RGE^Wni=v;cyz2n#Y(`;3kPRC~HMt#g%w>GAFWK1xvZ;680k$hY480;LR$ z!tJs6>u&Zp(>0}j2WmqZmsutmY+I3F(PwY!?pRl3NOcTYZoGr- zhrvQEvdV`9WtJ3*r za`EzV*?n$E`DigKA?)aMR_a!WqU++~W&{^ROb${l8jHIAysID|2}?}SwVKZK+)O@p zY{z3B#z6sN_@Gy3$sOPp&R9?JPXV+!=_&+R>$`g zOg$&@uvR(h!`z;7t6Yo7+;!(Y4f*1b43`mFfu`$`{Q?4c#dmWkEjm zhz>8>4Z@HJ1wLIWmTxcYz?qCwju<}=a&D3 z`DcKr%suJAx7^#0SDSwFyhe&ycZ$;fDgaI+$8l?M%vL6NoQalu{=jVfk_Uy)0yFDFYeEE^#I}euz0%Ru`62fVx5B2{;JP7daP3jGPY9y zPuLcn6oMZkH=+3>7+32 zT*{-k{;Dro<5pG-F+Qjwm=v$3(yV(Y7$sf-o-w3=MxKaod_+?1shHIVl%0a?NK&E@ z=Ij8`nxDbl!nyJR2$9U$(YSc5Ki>ZJEJbxyJn2LgX*^`4qFA!&_q~1+efjs%d;Hgp{&?oL-`Yl&_e357JIs-0@%ivzhDan>f8wdRl80e~7 literal 0 HcmV?d00001 diff --git a/doc/ides/images/jetbrains_open_project_wizard_windows_cmake.png b/doc/ides/images/jetbrains_open_project_wizard_windows_cmake.png new file mode 100644 index 0000000000000000000000000000000000000000..2366908ec8cf313a2cfde3e99e5a07be27e3ba0a GIT binary patch literal 64227 zcmb@tWo%qc&@MQRV`hq(nIUH8F+A4RrOO{XTs!UfN;=Q&;S4cPFzg*7XSbn1OR+Hg8cR+;jbJV1OQ%2#DxVE zU02RlJ1tQ3a0aheZ}==hDrQA2x||Z zNZ1ZAYc%OF*V;}e58dJRWJ3>wI19;|Emj`WRFY?A9$@nED}=dVZn!U*#VDPS9l4*Q zb50pr;BzcK)s0c4n>MHWuM4S7I9Kt@1CGxaTE0eIZ_>XvS9oCUxoxP$9)Q6m745Q@ zD@#hKRW6nakyIG*QLcF-B2AlK7n1Bz5t9@&oFHNb6Xw^^jN0Mk&pngCJrtd`NEFKy z?)t1Pq>atoNZd@cFE_h=J+7|iThQ^Pl#dlO;mUc#FY~K1MeN9F<61SE#9uIv&8S~f z$VN8*_UnsFa6CP>v$`_9c=)i@W0UpQ%b$*v=er2O1G|$=k6*IR_LrIK(np6n)=pfHH{Iv=>+P3m5id*8 zv6`zB)-+^}-d~D#o#8uAkQRi|!gk5CU1&Ag>1oOoIG7gDL&{Mw`?m$^UkmW(Qzbu< zKtBzE2~7P!`(8)&?bp4so~sDbZ-0Fci)`BbhC2Z>G<=Q9>DI>yxdPNxD$S9ey}zCj zKYb=AKQq<5IO2P44s%Zz%4bjSMROS`DaDC?*VWgTYqdI)@`hK{Od^q`2{?TwwN=-9 z^qpZ@QOopol{_Cszf?b(=R60uscp<$tFl>ntH*O8>GMjh~ndr4H!^qCi<8C=lU?<1EOCSa%ueJFn}_GPerXtf`x!qex5L~K*Go#?= zw>xb0qefddFrKxDUoG%b{X6nd(LFn|G7lfo&A`f1eqJ=B`;Cdc(YvanMI$%ukE2&z zwC3c#Z{`a{P@Y$?Juj6CX{CbNS;OHBPKmI4ir`PHG=`JHHO?Hev_tA_P82x7d;Y=n z1*rJp<7p=)lWP8GOI4GU^y<{PR0tq(_YPatXG6cEk}%k`m&>LpcJ$)$_&AtjX|%>O zjL7dIrk))Jk7~EIOR`fJ!1w;fbj6OUCfXOM+q%>K@f=gs@75v26&FlRg^}RxZf*Pe z)OYg#!9Y-&oi~~wzXE)HeNDc|JzcEKOiw>QJzcHR0}a+AB_*|3C?oW$%gWNdJs3}8 zwNlTBA)6~&>%#}oKo2OvxwjyIRw{b^J))2%I5ag(x(l)rPL2HbeY8YL_A#*#80%m> za*X#{M3a`(|5#Gmx~BwNxT&byH1-gJtFk}h!=A5F zg6L4~9z2&De|^?+hNU3a*PUZxq9-&c$6HPQxe;yv*k7$ExpOmtXGWLl_l>3?(zc@` z;oG-{+E%l%X$Lag*wgFtL|IL||79S`K=EX%T;{w8x=~^tXg?49EX(6#psP|%MZ1lwHEAL{Mia~%kXv> zUaT4jD&FOq2m$hM%HMUtALX$wiU%h$m{?ot&0P;J1aLrt&L5KZzjYDrwij}kBI2W= zKFXY^->`oKj~HIhHXP~@YLu?x7U6&Flu9Y`(G~)ixeKb^+_yMTzUTs)C&sb+cgEb&9RI!M9Au+G z`lYeHoZ-l;KqdWVfXq!O{p{?z-5=8Y9idqfYChjJ!MjhgbrUYZ){|85fxkZ_-ybml z8Wuxztu%=%A+D%aS^G$VO+fL;zD*``jXN=IK*tT=OnQj&qpjz#rSgOt29lKiIH6Jel!9p zYws~OtrZvfvS+_o4t3boY^7-~gIfcBEnlK>yRO+nEmu@0gdg-ToPI}E_RKu?!oTo= z!Ozz{i1TJ~ZggbJ9Peuyc2n|lA2c-zpG4yO<3Sad0uV58W-&bzVhKw?4W}!`ZAT+w zWr?A;^nPw*MHdoa_d*?wE5?1F*Z%<>8#;&F!{qIt(eyJVIS6EhFPF_9T$z{>PNR;NtC_0VU4qW4+z3xpJ;;dGvlkDbJ|KF$TNgm` zIFXk~5uSIk3)~+8@^pKmhE^VHzRxeuIkW2@Z;iqJU*Urw7i2L_c@WqM5t;Zz;m>4n zW6s6mTzL2%IcSBWoG}r3=hD~mxzwDQOt#DJyZbpgRbGwflq)|gTr6ZNH%P!a-_4W@ zj|bZVy_}6J6*etUDilKJhnZhzPIM7s%}@vS;PZXE#}k~0y9b`$6*n{Fus8Y8k4?w5 zT4kZw%?}jkD!ESa<8#vwfWkY7g=~X9c`@btal_z zWk^=y3>UU_FjLkOIj6Z?J6ui2l=#2jKp4xw3U(-D< zIFfI7BDnvNc)-vRj`{F+9~m=H%X>zs|R58NlGcMqgF+Om;P3_#c{2q zoR?>8PJ-1ow;^lS#`59tbsl02^n-o<(B5!|9=&mYgSNlbd0d5J( zR42a+1=$@a2J;nJPqeCz&1&7X@RW4iccAhS4AmwmrfIDeB_q~jPT_5i6u2{Z#YF<4 zP-y#=N5@vT1XwAD!=%WbUcAw>_wyw{JL6a3MfT}8W#a_AHC5at4eD)x3ddsXbfeiS zCZBi7EEy3oqo(a)px&e zzEt#=%B*{No{oUUjzBavFu$BevWf$zDX>IxO;MwA=h0$niVt+lhNH=J@Z* zRekQydZY-jli72Sit!nbm&(u6k;ph-I1iB4JuY!G5TTR2Ty53kNad!O>}RqE$qPjM ztisH-I*kOwL94C{Pvu&#ABHyobq`0q?+QC(el-opw=z5%>lbh{M(XX?7t6!j1N;qo zu8TEpMiYH3yJt9B6IxBb=&dSSLg3fzl`|i)X9M9LqTD9TtDuk4ECDBWelHr`ON*!e(POTy3p{w5mYFj3(l! zW$7v_?U<$(nXYMcyzV#3qk;-@&!^>?!$toW3=P0;&GMm?LV8-6xez;0a{Sf~ZY3ao zD|-$Pw6d+8ygyx=mW95lDP$7Q!Z8%u-5IaDi&r9@{>bXo93=xyy+v>0Aaw+yX!db< zhTHZ{4^6E5`cjICja8;P@`0&D2^&oKNQHK%m8E$$obKkEPh4m(`^V>6w3p#l!d$sM z*@PK~_f#=U8oZ^~G>asq9c_IusYs3poC1&mN(4HQ{PT6J6iNZ&0P#n>U z{0(IC;bl9+WODueW##qOv(Hn}2->c2BE+5%KdPg1nHO9CXnfi|P%HJ0sgUc1_5NmZ z$BpL~^CSGJb}B6^pMZ(lrXuak4KEhCJ|Fu+p^8#^lvZP3(^$tWH(O?Jo#>RNeKv&W zzl<6HxRTHA3;uhzVX2UvaN1$(X9tFSj85_k0}`uZOYDs|{)+yw={xXGp1i>tB%)yg z`;~Rsv%cD(*tNoEyVE_IfOm_>gXShzd!0CryrtzLFVB?b_JNVpcn|{;=KRS-N_U>Y zsU<7(;=PT+f}M#J*IM>a`r;rB=+geM)j{89S$!1xY~7_iC|2FCAkVvT-d=n1L15oR z!=XmoQLbew+3fP%f+97t=iP=$_$Uc+NVYAAz5v`;miO=TR3xXbbKR8d zPbV&>!1CP(!d5LkN1g5@*9(7FQMaaDSqxv}uKR{py`&kPi+s(_UWvF|Y`4rALbmx; zDk_G@jCCK&oc}pGZ@>7HElstVaQN9&-wd($6t4**>B>9GSbQ)P7T9<$wE<*djWbw7 zkw~aL-*B1Ei3vXRNN6cXP3CZ2r5}{1Q$P4K|N5M#oO3qoNNt3CCI6qAUBDn@q4WX7 zeXnV)C}Jsht2JrSAM?L(!tNv=fKh}iHb=$7=P}Nm@HGMf5_7=UWR$^*Z1;_CrC8uJ zndnxtYq~J=~6A2%dD8y{|WIZEYd+m$w~l`&CGR z7AWY*I&2UojXn>in@{#l8h<-@Y)RQT+new{E~a}_8%XWjd~UpWG}CZL>V#Wt+KUz? zZyz00=ymW@)L{dn4u#IY_W7w?58|+&y)n@B=J~%?o)kSj3+d6gY~L;~PaPC&EKU<8 z+)6v{4D;BKxLn&i4hP1}oh%%T@)bQrYx!(%P#J7EWa| z&#i9ENot#iRqz*2q6pT?6Mv8ePd19^^KIa3W#X-Qgw!L^#F%u-dRbW?E3qzHQBld4ANH{3!>AwaS&rrgAt)=hXDIt9-(&7n07T zin$c7?np;pZf+|cRvV%y^r0MQR6OiuPtvAJn6#{@e{O^JixGJdc`;*g+@V%07Q7l# z&8+!N6Q`$0aa!l&eLX(_ko9%e^#9f$$@4%1e7|@b{}+|`|I%-D9#4DnUJ3%Pg4SO= z%%~!Fzn1@R1D;4sOLlQBgCp|~luPjAoO@8r=9xV8Tzx4G3=zpie^EV#Vz*h5a>(Fp z0ud;qsiaVJtrS5vk*Xv+QLa-AJ8t-WxXLshkBN@WTTd5ud_YmC;1AM&^v3r;0(A35 z-2nVwdVX!W|Kbb5|2-7@?>O!M9QuzX5yY66+G{4hb`E{a!=bXGHrU;LePQX1o9b0X z@mQf38i??9O?-1ylJwrqqVr~PZSY(lS3J+d?8$LAn!nZDp`Gm1xRjM-=!}Q(oL(;| z+Z#9U_2S<+PV%u3Gk<{zsTrmn6DW&&Qp6Od9}glM;_i@GO3!@epcZ+gya|UFCKc(X z(a>^H04q|16D_sIJ6&L{U0)&8Ur?q9<+c*EP=2m?60lpJ~-K1pT&BJ0z9p=83pxExxC;ETcVSCC?!6hic`65%1V{t zjcbZDrN#max|4UbjQW=Fm#l_moG23*-ch3+yhA@8`(XuLKot3???Gy*Fk;)WR4mq&z>tru5kcNL} zKymyog!26O!rmPGFM6*>fsMV?uWO-_7p3%?j6sLXOG~!HL9f2a2eWzN3sQ8BZ8^<3 z4e)^jGDu_t1K%mc&u6<~aU8PfL7R=gNkcuh!jmhpDTJXyalSN%Om>i?D)ds}wG|Tr!`yEUji6dlSXEDyVV-oc%*?-z3`K0m2tSc{p^f5BOi~+W z1YyR3ovirfM^8>x=ex3(Dzhb@1 z$4kzp=~g0ZgTa!ur(@9@dF@0(UI%FCBDzhrHL)zHmwnyt-rin3epUo6V+Odjyh{NA zyd&IG$A2tnw_d1FTqKN)I53DWcjn&8H{@Tt!d%T&jkTue)-GBega6nfR#Y~ML$v5% zgp@Q~`Axl5ng>rvUdpCO$CD*XyR`14VNiLx-4^v*dr(pP#_?|ps^)E&v7}1LG=6A% z!6VR{Djw5TCYO^%JuGCq&XHo6?&-gKm2a>&*6Azh{JxqFibar<7dw>qp?F8O&ied-<_ZO2ZoblO1yqe`yf=)|;*Q zy6skdFn2|hPL#XQXVHdnjbg_&@Op%>@(im~%Jh>1DLB?=95QME& zrg`tdgbEv5R4d%Z4#(EMqrNC-lTp6WEdBn%G>uI&5$A-MT$4a^WVO%hTz&Vd*mBYp zRPgpNmwMpC0Go#m?wX^9F-<+GgEvr{$6}fOiTNsvM6O8}ORf2kc>?Iv%nduJZ;vgi zxyw|2sd$^H%Woq}Zy74(O!7}4P zNDHOf&2}P*%o}X**5**s*mCejzXvT4IWY^JUSJyoJ9o~n0_uq>j)h1W__YinZwNPP z-|v2UNlL)b`k)dv9W5T_`c$}ew!B=wS6bw>WZqAFBHNsJ4DcepAU+*o@Ah30?^oFi zbG2`|lF{GCR;`UHEVLR4>Xmd6`-gkKih0G+&l16o;jXIfy#o)x%F9h2581qMwC#-_ zDrUo<8`~l5L_d@Qu0=uatt~z`f4rqg$duyA6QNZ2wzjTwF8>Nevc8h=KzD@0d=jwl z0qeVWr>1V@)pJ)Sj=o<3WIv;zDmdU_!oSK@I^@uew{G92D*i$s@f$%7+@GGTw=viu z!HJ1-07N6BiYbt5F4|bJq}W^tEqvnzh(JvM1>{8i)@lhwtySj?qB^J`wsXgNlmir& zJt?>-!Bg*I;dC$8bv%`=8zT=N}G3ijw1fDu~OqH7lQ z;NzN`%uTB$wC-q|rsN!}{lmcv) z5`F_@e}o#+m!=)KXIyh zruZvfmwEguuWp+CY#Qi!C!2V=_IVXMz4=kwP=9Um3;*QR9lW90eQ$pcEzCrCUn?cU zY_!+*h^s0C2HD{d{sj13c5sis->CJIGo(bgI+JznF5qM~kI?z2i@2CUThrvlq+(U% ztofqYIU6t5h0JrAc1QB?(5(!+zR>aPKG(j6Y$>`2g1aae%l5Zj_cpI-f}IGX$@3TZ z;lgXEzyth)w|&8z%C#G=h~1f}Bi*l!YIo+;_)jQrE^N#S!*_ zhtWdA!8XQ@q#oW2%KzK7Jb_A)7juU46Eg@0R9cLn*0^rcee-=yq|FILuwE#ItX!Vh&*UaafK6lblMN7+kdix=-)X1(_h3T=u^b>&Rt9a!$izXOZ} z<5AtAhjTmFlbNjIs-2(XL!wc;c3!+Oay6YCB~?<5=XxlUacK_9BI4ZDKe+^m@LfE} zr9VjDXLet>V;K2#SZmsFj*Reu_Ff~5^G=o?_OKAUd1x8GFwS69uAK%;yAe^wXO&;vmIb&j(hITH zF8u0So7-@HYwd@Yilu=_>&lL8jB@_TIR{Kvo6bJs3>)i-;eA_W_|v*4+ISNA zhe7mAErlu3tBmYDUR&{VF57CUC+aIe&}>9P;n zsx{f=a=AX~>^OXrbEAS)RB313LVVM)Va;*E4vf+W8OFrRL?gKcWiei<)8L!#jI~D4 zKFNxxB3xc);)%5UB#`#NeL^4~Y0d5NVBt?J(z+1~anhZG8gL}qOKu)xhBfFc$0pUI zyd%OKbzED-5H-`?CV0sQt@Mdouo}wKTL_T`Jj4GMANNB^!0y;x`!gP`;je4vY2>@jS*3M!@I8aap(AA;-UU0$A&dXA=dvK!T*t_va`=&fBBd48DB>VRg$s!ZNz0s`vLE^w)`aEZ~yL)d!BHX`RTsp$m@ym*uBZZiBcELlzdv})h#B5h7`VgVChcw7`m2vfk%^~JrEHP9-&faf|ygP)!iZ?&` zBiiUussKvGiwo@mCqieNmbLRjw?N9ZjLb8vTX{8e^KtXF5nolhvV%Nh3-(hdE)lT1#~GKqaJr$(niZR_&*)@~k^>Qh0J( z?97w_J1gLuZ;n4(ypIm*_6qveGg*8NeLk6$G#UmDA5#@lQ7?PT)t#Sc*I4!DLK>ql zF{p6d^E6G(5K+WfOu=I=s@8O4p`KFKk^U+NW_TA`n%3+4^i+bqU(RTxFk5mZ4l zzMIM1E=GVojtt6b4x=C8CDZ28ZRI?OW;oN|Sp1zZfFW{oJR6Yg1**ilu(1yszYV>a zBmUuho4ffu-I$KOv?@7P>gYN^AO#A+iX4=6*Dfm}Zc*+PaD})te82tOSlk8Y3(o?DYr9 zB53d7z)cRGjoN|D^GAov9`%Xo?)S>m`J8nM1FXWE+{wxJ8>4uogHGjjBGNRODvHlx zDga@SDHrYUeG z`WiHXs(ogMCD+$2+9f&a{RX$dmG*YFN;B0}*IMHtb1waQq#9_!CvomB*J1N)L>(VjmQzh{#Idwr zR^C_%%W==#i+xvWuHe;&Ho;2JO0N)=q z1GCFjqENcIP4EJaThru|u>as8jywCm%&M5LlKi!acPaC1eO4H)vm+z1f4h3nf#ZP~ z103}7jNZGBeHnb!xqeLVp>4z0I{~#3UNT)^D_?7Epc|}vA@5)UwKA>wAWn0}m!cvA z^N_gQj_tlQOf{h%&ECcZVi*Y@`$Z%t$P*)B5Og}N9$Mg4t}8(8 z!?8)qq5bE5!|ui{1{`w>GTzH|mA4rt!HD9Q4>>6f|u7C8Vayvjq{1x?!-uws~1 z-F>;fw>qPaN;}arJ$lOd~lV#Db`>31$-M82rY`#~hCaLIi# zuu%zdC>1H9J-u~q8RL`T$4>kPy8CbLphUk{{W;h0`2xSnknb}$uJ=-9;NQ1RuGiw; zZntk*_B)#<#G1}L=($W>wRQ?YVjKRmq%)vE#%_wRqR9lz zH@}qG!)^f-K1qQnUEG1GZf|BcW81F~h5f&WsF&$Sp{tXZMFu4hG^tgOvZ_6v^vl;Y zvv`3&lu>2x5v@}kfy*OLdY^3@;1e5}&LfcdP54Gs<@52ak4Y1H`YTuOrWls}brZa% z9**x_axjp7|6r^qvk<+2P0Fa{Q#UAoz9!WHfFYyZRynsPzV29;Uug`)8FykzL^2YD zpw-_$+<9L`&pwzUdk>`Ji78_8e~)r>@b_GIzg+~~o~#TSKA)$0s(AUP8TDKOUKBe}Ca0h+PuBqLWetE#}jgmT1 zlhgbx(P)P7pfSFxM8tv)$fH~IDLAr_-wEpHKfK|&a1k6%_}SJ`dqa+?5(yGK`Q?eB=>=OTiuwp@(nM24*1zwM zL$*MkiXsNkMP?CDm8c-=h4)pF1P9|kt({eUa;#pyEq9~|oxFpIdJsTVq{{CEW9}p4DbjM_djiR zNW4dVb|7#-{wyDqg_DtSo>Q??P~ujd=YX)Qc)eS`{_qq$2gw2=G-VYq&TeS1+ttV& zNez08L@P4M;5{6REPI+*)VTAgzY{{pD@X!W3V0L6gy)wP(GZlqCIr_0@~xzD5{ZKR zD|H=xk@J838##|UT{7HPS<6eFF8Tg{-u?M1cm4Mc(EnQxQ40(Z9^E>-FLmJ4YId}9 zZ(2eW_23dR-|AVvDZOWlh(9Laao)XdKlVfo>@RLk(YM+Q1_2yRuY1(lY~Mf5@At3f z_s`74o1Efr@^&tuoM#g|BC;HyF&_O%N3?9}JY)J;6OB}VH-W*&`POi{A;mO(D&Mk8&dSPkF%^p8&Y>{i~ zpv1JcoVyCss-AYzBV2cTqG#ox;Xwy)MEzCITjykYxtd)O{4|zJvaV=*z53{X>q2>t9~XWdJGaxXnuf;K%OCr^H*2rr2HcoENxXq%c`yxmG!5WH8sI3 z??S!sb)m(pnHGCR=a&mBrMe~Go#d0>BK_orn={`Uy-mjMw)a~F6Q`hNeZqKNxX$ws zt_JyUvR60lrDdu;WKGMnA?@Igh1?Rn!P!8Hyjkpib0%(Yf6^PT`2r0XPqnupBYU+8 zzM}rze`P-Y7^Z|CSC@AUM@3?Y{fC-Ee{I@B;_7kxol>aMa57X5cUqb}|3fwP7Q=hP znUC7m(a$q^39}R#MFSaPA(ZfQJP$Q(hvmGKlWj3t7K@|?bG0XoXB+jsGv zPp2owI%b%e58$iQp)PQfFRy25I-Q)Po8MdQ9m-Ij_GWl!QqFA(#z?@x+^WEVr)%fn zLnkMbwVDc2%D5>cHob-ym%<^SecesBGt)#p^YJUuj9GP>-KaDfsx*3bSjj)spjO~) z;Z=;Hv+Tq(v)Ra<>5*U@z7FGwpo@RwOK+cms77d(plwIBf525{Ucu+FmHYY+008jW z_j%RPQ6Uhc-sbsnjcMd%>e$%AXWrVG0>8AE0w*tQ;4IA4Q|kb`u?YG#eT#c`(_PWb zj?F1El5i$%40xWUH)X(5(bK5CK{XP zqk*bN=tI`W)r2xYQO}I!@fStK(Tg-tNSM-?8_~w~WCgYReHXbGC(_q+M0Y8`Y?Aue zYQ+5G&~)f_;dbHU;!$#qzW{F4x}uN`7k~Vq$%-bH^OfR$dG^1;@4xo1Ec~zi%gz7G z&;Jen*NXl}(#1NS+pjNA2mpkNRI4U@)sFvV?f=@p67qj#F?uL7@=l@-(#77uqN1&>*p6rIIYBU${Cng z|5y~Q->PUo%gK_%Cu>dGE|&1zC3G=xCbrBA0G9kDS^*zb)s6 z+@uifxDf@5pY^i7p<#ErmUI?c0TC~QN(%jOv`$uJUq@O|w0;Rac;Btt43>4u*u<*vJ;l5ckpkaiFkbxv5G{fveuYB5c z*P=S92AiV>DK#x8x1eS%vOW`qlN?@D)P?sZFbL#>qqz+*7gr@yy%fdjX*p2Va1KdS z^+1K4eP7BXR@!;fdh-RywyXFU#ubwMz1<5YD~XzLsN29{1^Jq$gx)ZFsMy9(`YHi3 zGDeEso>^)tnGi_b#7$Nek(xZA5N5)Fq6wmAPVEUSVOcmwM6zH!kX&|03J&e>PZ%4h zRiHSe6g0eTZgId~JWeh;U1UYO@T|1#Pl;V-=oDA-Yj8<&iZp@=btM`u{A;9|uqMEF zsyby?A^_iP)o6@o=nZrw8y#H2LWKBRGbV#f}(lPqj( zyK42$DmZ9B^G7o@+VQq|gWHz^+FKa-h-|s2jWRQpRx(ZRU3Cb^z|E({R`*tM z5*||yiChWWpT@7_iewYx6j-jqJw$6X(9#^{V;2iZFIb*fK&99jGcbIdZ=iJK<2`cA4coM^iUc z>7aAI9kk$dQA!C4z=Da~c%vHn&S;Cn71r*wm{}b;AJNGar$T?1B4yNIGG>wX(Ee-G z6HV|8`J<@1z&y8~^-^{DPMr%H#Z`RdGq@i)_>6xyD-Q<|-Z$;)$I|Jpg{L<&l@MS3 z^rKf#((UCYtM-(bkow+6?Gw25q1ckC%)3eB=*LsrA7dgaq*Sp4iq?g;6`c$uTulm@ z&gs+ivSF}S`Qy$=kk@vahR2wRmf4M8e-W3h4-z|1;6$}i$3u4{qo`+HW~aR!4emam z)NK84G$X7Wo}8hp7=-O--;ONHLXi$xH8yO%ivBt@y`gbb5TG!R4*WNkNSW978)Lfu zyK!R@v+981)pJh}IME>taQuD0pX-{q79+oZqSA^}F>(Ijni#_P*hg?il%>fl$$fn# zA!}+S(%`G+u8<890;>VVBgG-{MfJm5Z}#_(VJ}JA8uEdDodf{~#Z6b|IABgJHanM& zzjW$HN;P&ywTX57Rs*&DSHVOZvuN!13@q(vG1j&0NiC6QU;vI22!}8%es>sv0&qIa zQDYIP2k)VYeC-#ddVt?+%O?Ru?|_U{&KD{RM2}a9IqS&4=S_-UEl{d+1QbH;aVbXV zb{;J`l-HJ89g@Z>{HXgUSEQ+@WSI0f8MBK@5#b7#fiIvd4_3%vi57`?a1)H ztDJ^^NZ)cfg~hd5zZf1c*pUOg%V!MT7jqz({FRjrZwdikXe4n>Tv2qK2|kt*m9xtC z3M$N;<4zYhA;xH;IkgRU=$CZH6~}q~u2oI7CAq!B-JJ=U@X0C|OI(Qw*kq<<&-8?$ zf&J}SC~4I2;q-7w`k1Sb-KpTTi;bJif1|iT+`4;ReN~EvuI(Dzw!VNqM6jm0lNho= z`f!q2z1CaS;!;{}@|?`KB*0xA`#b1G)0+E5;&4RKhzWx#7I%vRN$-05U2mF+mHP$> z0R|1@MsvxbD-*Zha2?kLGxKpW>SR`UOlB7@PDYG1`!D*cjIbRzrWrGF^c7M=Sc@+X zQE<$ZFaQm3Ck$`F1__Gu&_=QYihU1;-}{sd1MU4F#fBH63PndY2y1z~MS_-oX-k|B z$}UZ?>8Bp3%sn`F@=|kDVQ8(r6SAa4q`b|ruY*CI68ofyNuC9gB$3+~KPrRCqMz$vwR~IK1bu}i( z+FxVqE4UusN-0#rN+ZBU-LvqaW=;$LTy5(;__g?QwBBc%Bhhi`_W9C+qvN?8M2%P4 zZwzlxMTXK2XlmEL9npB*q%He?Aj&y%8~?k}vSe}|BgBfX)oI4ilY_bOdDLi15z*|$ z<-#Ors%Mg1;qLgyp?)tLEP5F>J)Pek@%)fw6_HzOI(?QvQ*$3R!r$V`d;(1+v`qv{ ztjhiyI-jZlSA9ZagGS_@)ym#>9LkyFMb@e@V9s^(^D)q2?Z`*a3k@9Rk?##YXlO)duSn7`$fI@$LPJ?=7w9m zr+MYuAyNzUe~048bMz$#JNWVs#Lf?#CYR?=daaJUQwR6ghy+;AwJx1@tI&X#7IBm8!-pm({RFbOvYwq34TeX_qY*l3a% z3?VQeNWpHD?7)JKc6Lpdi;@}d>r;&J6LW`(j+O!Zg2V>SoUkT zt@Kv&&Ih<1mWfMUX_Mb?$te457OBDqP1A?RAeMpa(s1Zt&Jd)fO=8n{3&xBQXSZPs zjyXt;z(cv@ApLxtJ^px$85~Y^0=;y%#)Z>ir4uxQe?*X>Iy5xXySv6Ler;14k0ve^ z7_;3D;NlMk?SY1~c`GQQd^#&SijSJ@_^2Q(%%@D#bH@4#NS?J&uJ-S4ActhM%6&s5 zdKR%dwiSdSq1(jlNCaRE?iWOQe|o(Wh1sh(M*R&iWFR=T_!^j;xX|=T+x{5xRJHi< zOfn|_!1NM32FRj}yEH8cdduHs*Zk%yOuH3fK0>wqff3C8MHOI}B+P8`gPjd~C#T&D zor(|DxkXHl#ur-5a~Bm0vVMCfi~@`J{oX0Ge|7vrRu=*Xf&`4C>z(50+cA)jx+CWd ztJX#?nH=r}&FiUUnq}3VHC6W7$o^!kqaz##Q-cBp2LJQAfK}&vyLP7XhRHFVvLuB% zjzh!vMEfW^EY;_5&Z?_8QXX#k7@HKh>wWH&;2KE4-m(p0RXN;=y&B7&+sp5Jr~g4c zr-ip{mSIR>D>XQG~GXphfv+%>D{|45EH%$}(3$7lgWa5-If^2pU z7jKjEfbDGi#n;3Ksnbw_6PdiJzD+3#7TVZl6EG8nMs6?FU9L_8U_76#n8qk>C*J;SFy}#?n-t%;54w+2n zi(}8B6D{q_0u8;5$?&p|Uc}`>j}H%~#shmgeDa{a`&+v5H=hn|hwJUC`QeUESEuC# zJKCR2(7EtvE=%ne9m|*@%S%tL316N;+lS8}rJba8oi={yKu6122-k?0okFC}irPlV!x@}1v!WqHY1hjI!cRY7x<2}7pT7=sg6^i|e zK7Z?9!jos|8rRWm)3_pL@{0a*mVfK?OTG{;x=YpR$K=&Pt=adiS`^&r7&r0}g+?5y z+4gI-l4uF%A775MWSL_+{6*Bs?l4#FIu>}3HMG_3E1t_ctMTWBy@agiVjrjE%wB3bd8-$b`B|i}6zKnNgMX(Q0_5`~Vr^VE z=}|H`T}~|sz1%4rzFu*4_TmvfL!we-;rB~YZ3PEG1vqcnS@MP30@ec?o?ahv+uPeA zYS=`85xe!*OxN%WOe1@)n4MZV`n&PWOu-=Rp`_O#%o0!o@NKq@Lke{yWQ-z|NHF(c ztQ0>+r);WLn{Vn=6N}^-hZYMY@_iv)A+Gy+%!uPi&5=>dxUyn;a07$#F`&}MJa8tT z#D3t}upeLHP;kCq)Zy?b@{?$zsBHbVD^ItYkw zG?=(?kI{b{yF%c2BukEpdEyZF^{|{fM|cFdzusb^uor_1!f7F`;rhGA{3;!f{Qg>I zu{Quyb=Ijc7{u@$a*ar%p|bLU8OVOT*5R|&aVur(egoiBjX7YHJF`A| zW*W#Ge?tIjC_DF;W^cff#sbotNO*n1N!%l+OOc(YIUpNHGAZ^5Srbo2%1 zUfXVLYg_TY3I6|ps?2jJ`{)GVZ z>Pkr}Ja15+NNv=|tGBu}X(u2tJfGgN)Oz&x=S|ImzyA*UD;JXh{BWn^tqW$ILy`VdQ05^jMW^i-Uk&*s zV7uB9`;akLUpkDKe08`e$>!9y+Q#unp|8kQEmUW_A6Nr~=+gI>@swXCn@OTll|EJLZrMQiN{r~KJ8saF@bA)sG zBVoS?cJmQ7lpS1)!JVPOi?_`fvEd1F%1uHWn%d2~U!VbVOKb?GJG3#5S>p3BbJHv= z;$}pcQf>N;S1)^?+H~cOlK!FP%e?w?=C~q4#(DOsA6@B3#GSi$sdWzC581df{@bU;$808rWy-?g!8L4rP7#>Y4 zb(xedFO(Pas{jnyWM)|PNu@SCY=uJ2UU4pO6yC3UZD~};P|GQ9z7t8Kuod-g>6&$ zg#Ht|=csr$a^WkC+3E8Ef18thzRlOKhXSxDX@!nSLuL%HtGwK2gss>1JTA0-tTtU) zqt-mRNj#1#9z6}I@2rHpEp|Jb`c9m^jzHQ6GoHTZu+^(NH@1D8wU^KSGF)AZJ7HXFWG(SJ#@!^m8vd$z#{_*1F^Hqa*6RGAG7KZ@4tJHEK~WE-Pbe zPH9XZslM^8?#^^Ln>oWDA*$Q0N!*ytA1m!ml-7UTyOk``D6$<{U~+GUTLF&zvSG@H z_O(@BJtK5+DS@4Ip`0%dN|Yp1s&TFm5FYh${)vb|_)mn)|9usO?7}S#|6j4h0<+K! z#~CrWDdJ7G)U)xy2L^3&J$ixQpT~M$@cYPX$m_uMB?7Ef9#c31u6Yz#gu)G2k8+To za8=|#HRlEJpc+I3&+`vlRf$gr^bm5v6YJZJt=!4oQ|a-sshS<4a>tu-avnH+=bs=U z6MNxj3VA5ydTqFI&b@-9BO8I&JSQvU!;I1z&tX8qvvb0p4$arCmB(hg!F~1&Z?Jp| z%MKJCjF0rR(!x3rFrTu%tyfdy$Hi&WCC8|hhJVTqkSV3m5}TudyGnl&k4P-js;&)q zN8|#v)v2aJ5)-SS&{gkBES?QuC7|@BCo^OX%$S!u4KZ`rH0Iwxo_z6 zKZ7f0{mFAd#oD?|q_TkW^kmtFflJF=G1Z-Oe+SV&KPY8AJx(K@>4X!?FKpRD+%T|c zd7+_0GO@50i}{B0n?g#7Q{j}IJjQbf_b4=D2D;gu%2`kaikFojsXo62&m_qHd#m&i zTy?hZ!`6EVn|1M>xV@8qUZo!(evw=KEzl15-&S`Ux8NJ_*yW`;tH|CFM)GBWbwBm9 zS!f$p#wWtq7QeJeo`~#oyWhZFfEk2otcv{aTbfqy&DE4@WibBSq<{pr;=37dR+tW@Z zxJbCYdHTKR`_;|+6}@Cf&kIQ^#z4QLi%XgiWbxKYau+L(&QW33Po*G}50E@SRH!#= zm0Phe6rEUFXx+7_(!=d|^v59^|HK#OWe&jNyU~$(U41`$4$^S<#M?W1P1yq;=i&SLcK0l*;zgNR0AuEJi#MnV0-- z4mZZPSQi^x{G^kjV}LorjlaBZA0v}vvRWP1R)>d)y{az@D(t#DIzO;x@jIRFmKqh4 z++$VD*0Ap* zYxqaWrTDIPZG^g&rHgnQ8|4cAd>b)^1yvaPpK#&>aOEsL)I5knWe!Sc0!RYf#&SGh zd^?yqKUqlj^Lu(2Nkv=5vxF>aPo)wNp-8P{s{c}7h6?+{<_X+-s>^aI3eCW_u?caC z^;^iN=OP|q|g(5^~T9{$7PZChVoOAUj8ty~gL%}f8 z@hMqpM3Hymh3qRO0WVWeq1{pD7ayXM9R3p^9w9vkg|0!NVU}x9+Y7+cyFqKL056uZpdp5uS5HICTAoL=hK@=CzWr`Z&Ro5kh|(i z8qVEiv2%oP&0Vrz$@Bi&+Ah@$m$7MIW}quC8Fq7VaYm%f|M_Xkt#)>2+o&2eauvEQ z(SYpn(U#oO@hh?8k40QYY|h=)W_n@bsk);-HBxANbI_o9b^$Aq|3-6H3k%GWlS!$i zm+RLbq5OIa|9JH&JRbT0#f}Ah4QCf&Cdqmkpz^^Hgz!B9YM0U(ht<;^Bb2%l&5DD; z_*{g`VV;6QW~JpU*SxvUQ??iC3VyRiGi9X~jC~4&FJ(f5s3pZan1qm!#7$k2KwmD{_ z?SpN*S1ajGPg~yvlD&o@PW5yu`4BC(e8V!{ml(eG1*eEYtluZOc?|xZTX-!9m4)l| zfSMxs52b4}-*HdcX`kZ!C}6l?U~Ny#C9ED48ZukHE4Uhg`ys=5kUt~^u_T_Sv3 zd~D)>`_TFHz|p$tr`)%DXFm4kp=;bQ_Q`u;G=jAb(rVLZG77of5$WgUgS2UL?n~dFyxs01uc<~Cxw8O-;2p^-7#m-iKBk>P9RVnxAM`c zNep3)?L7O`(>@^Br{_I!XNDmBY+wEj68Ql!e3VqjLlj31AUs7|aUX*K>xq$XGJ0 z&KK*hnFfm4abMLiRlb@q`D{Frv`EID>MY$L6RkyecbFtg zJpAz*81eM>OVrDMN%{^aiTV(z{JRF#4bTXeqpyN*ap@;0C?O3|gShn8tdQsxHUY!I zZxjNGRjdxwum&MT3fSEy@T)u;9ukZ}&#>7SnRwxOu8(0j`%rT->_NMsfWzV4TYCOo zLC-@!uVgTRf5+d`PGtQYq~~_68&I9;JpHC0O;zZ?U8n2VF~*~*T&~~GM=`>0I`4@{ zOq<0xr7c-8^ru??e=Nla$5(q$@Z6!vG1qU@_J0~8AOIe|19vq^hvv-$-%r2$J%Y@D z1bCikxK;Sv=+3-geR#3uk`ZixEBH5(1=r)11zoD*#1PowRBrRJlH%(GzQ49q3OUXb z+P~iDdL^Jv?3vE7kT|K}pE=?JOS+kzQGHHk91btEna}PiTAArj`1G0Ihmc}M^wFH< zEJK&!=O?^e=Rx9bZVU`wa>T)MsGU6LeOLr#@CPwg&03~Fx^kXHzS|c=olX{>T7fED zCKG9Tn)|7Ld(5*Z4xs(Fp=>=RS`P^>@~SN&eyu-sc%Z^0HKS_Yffm=&(OhKT9g3x% zW`ztVqN5l1#xLjZsj<8X{*zuo{{sB9yr+#c8y2=wUteK!0&yW7MulkO$Nf?PY>-V) z35~m4@^R$cJGZrzHYW7P;|2Fd&eD0Ysy7z#Sqm4rS0wz2M~iXU!BQHRQV{~spey!; z=4&Y?DR0g)FdhAxYX|*jVUINd8=EM|MGf=5+4k&ImHN!|B-q&GYiC&6;{5l_N5EZL z1&eXsbm~LjV@sWkc#SSdR=%XoLF1dj_~FkUbKdtqii0b;w3@_2j-YRGT%b1LtS@K) z-iC<(U8>)oeG>lE%jdgPXb898=O=vY5cTENBIOAVk`J$Anr9=v0#KAuk z%(vzp^3NVo0L2@(gXqd-a?7sgJMv>tOi&zr2x`WCLPwmskF5Xx!_%HuXgMAzx4!Ok zrtmIhUd-%6tb4<72c_?yYr^&%Wj+SNGl|N@HhA{Gtp=L{@3e&ad=up*l971U&)qPU z%sWNbA~9N;)4iivtVsrp6R*^L3}EV_5c^__^0cCsO2<@d11iOa#QJw%k<4Q`^}w{gGb0iQZL-ZY;4iXx>tgRm$tTKaNZ9wlW_ zosB+vb-x`lwo-Uy#H#yCsc^5Lyoc~Fh^+RpHbEvKDZIE5D}>HMJOQ2)%icV7eYK!D zth2~IlM<1VzUO6RRQuNATyb`hmbK4?oz^|JaIvc6fVMyrSxs<~Z}$RQ>7E*&iW-5N|ym^ok{ zj9WD)?aadQyfXpB!~i(=`qKOg@K6A}jkbSECX(QNupO7~&5O3P1a|dBOQ8gzPLz$hS8}$y7ETwn)sX!&hQy; z>9rp3tvc>ejh_l$c26envi(g2$Qu|<>rjH@d`^D_UB*gV8%WBZ^i|DW8CQ5@69>nL zq9sTLZ^tR!(YMC^bm22(w}z(>dp=LQ7@)-m&=Z$(QrCqcHRMfJf=^dU^GO~4^@sqe zmJf5qDmrocHuaO28df|P^iCabkM``(a*dz!oA}qPLVc0sWc2pJWUpix%7MjdF52=L zH86rf9Ki`8D663X03;1j%QSVk{>YXs?u_>8It-$MI4zLeU(jjuhMjr2lGvV`* z`{^9E{?T(Sk*v%DJF-|E^2ZIl0Ne$k5eA)_Q%_Cr`8XO&g?YRXv0|K;$B~TuVf%K0 zDk5Q%2*AjyBtIwBRiH>C^@1MLo8Z_0kkK0Jwd@tBnbNJf4&4^RBeV51Nax)>N1j`c z9MEJAI6LY#+bYi5m6kWXbKXjcBIDVmeE1HJ63%B_9}#h!2BJ(fiaEqq8qdwQXmw^Eo{fZR$Sdf`iuM#0zj&98uL}lV@ z`sD#&L=+>%+VHW)fG_W*88E_z#lkl%C|u^Zj973{_*YfKt@m5oE!AB9LEzQhJ5$n^ zSY9vLqq^AqtN}NyglF%&CoX5n6D8wdXR^2e5A&U}@U$n**qb`QK-Ep_J`~Aulcni4 zh`0@3`Aul)o;dSMiT5P>d1DYpw^0ruz(r>q+fE4!=sm{-Hcw;8f+@2yq}_QG4~c zmT$b>d*$zeV8%|+#5v}Sff>oGA8_9ByVqv|^16QSwqQk!GSr`CraXU?4;N;USX5D0 zcO?jDcr0SKejFQSGRphdH|(r2d8TPXxJl%7l31uo``X^a=@@&VPN>)rpM7bu+%4X- zTgV=WTaLe3Jje@hw%d?VyqY6%UA;38(8>QyUGE90{%8R|XrD(i={@n1fiHLYe!(?* z^5>DIQ0Ab9#_2u_(U)Nau}>rC4-$XGPUw1*X>_J0v$)n2P1#hXS5m?uCP*CZ!Nkw;Xb( z)bdQ%8FnqABYBMe$*Q_<5V*^U!gn5@S-lSstDjFmZvqRS#rGg9N{r_rXu#pj;dKCj zw5S}rw2)p7I?dJk%n-84Zk5mJp3m`w4Sq=qE3)24q zL7z9F8v8qXR#q0w8UADQ^4$Y5vjli@_;Ve^4!?C(|9@8s#R(APOcP9};Do#|r*hZmG%Vy6*!)!4A426V?T_($2fB1LaC{OoW#?Nt z!e_k+J2Yc)sg&8hw+|dg8)oc0U31AmjvQvUA@kKq>2mEs$(wQ`F*_Jn9}2|3aU8k3 zKvI{pSR3DZDZw^p_lI-O^BTVsQ1Fi-s3<6%68N3<36o0_K5hrb*bDSY=_ z*g97@n^SFHA!YI!49|E?_5ow}8K>R;oKuiesvLnpt}~XuiW)e19O7#WZ$*unM>eHy zkK{CU+{e9nU(PBO_zbOpjk8NcyPzLq(^QmM5|oC;T>PG1lKw6E%)V!-oo7CX#ddM&ZeM$*@yyubo_STOZ&lMS)a4?$td9=fqj!RybdUGJ>o$&hGBD;B0ZDhp%_M5v=wBN{u$B0hx zk?|edmObh~9UMCT{*O#WhjmS#B5I6+>}uceAinZN^7>4q!tF`~JoN&h*RLOC`HP8- zwN2?*uJ2XLQD~soVg1$`c711)N>|@^9QsxKAOn-P3R^lX$(pjj^$5%>~FgTl9s#^8gLX(ZR(_(yY5EpJm|E)$*a!Oy03ae$~R7#lWdagmcXn zq;?`pK+~=0(0OfyY+*^w?i6*BYb?}L&BTAUuxd8IE!Asxah9|e(p)cR16QT)LlA!Z zeuVV6Kx{v3x7Ez-X2cZ4ZE`X;s@LtsP0g*1%$n5|pKnjbd+{cgEmiwYoz#}iauzF# z5WD7pdI3Kicdf0(oqQ+TX8~GXw<8&4g^cHxCW&vi!M{Wh5$;%oN!o-N`(lFOR=8ZB z(z<3grcLb+nNCfDRo=-jzYg%vC}#Nnyt<2hS)SQ?JMI-6QQ|3dtqdu`ROXL+0JynH z>P2O?UH27LXTNnXL=o+I08vwLE8 zv1P1}gN)NOk1yhxZ`3WF@^d;kf)V4K6PS2#Q|FxX$RF9u@-T8dJ3!y#&)N89O6{9x?BI z{;?^X%t8wxwz58~=WgMTNhJ+svLrHc&2b5%8GKFeDYIz|+OsF>rZgU-Bx7#9uU6gd zFJA2C56fve<@MUUHf(--?I-+fKoBMw5xz}m=Qb;6xVbVOiZ&i%OU-G`>00ax8V;H{ zs4}Y=PMs05k{%yKAu$tj=FZI#cOc=q_85PbtH@hkA_hMvI)e`w{a;JPzg7`BNWj0< z&jBvay7qso|I_B2{@?2Vw1GeBT#38msZT+GzO^Nlg!mmOzT&MX3|xC0Mm$k?u0H_nNNQkv*aavx0jj^ z%9pod|MY$IbP*+muzGbefc>n2e#chV5=;$Edy^Uw9Jy0Es~s`=K;mBYIY0%BUVclhywXG3f)gRt4? z$?^H+@E*>$Wymkc7zpb`CdIIWh!C&zs$_-mdc1TQhK7MQ6%ahhhSN&UpIlQ zMm_byjPhlZD6G}wGuD7+J8Ryi8TR{6ns#;CLzTv1LnY(r253mlDgA9`-_|h$1-FN| zEloJWJ)I|Z8Onb8EV=RQ58;b%%#rVayO+_B<~x~6YL}tOpEODe0YsrM^nD!<8(l(G6{ z8y9{~3TCY{mYDf#(wf~KvyuCTtrSx;XJw36w-{VgjKKsikN!mtA|(WHXx zcMq|*|F7s^qqJWJZM`wlMAGC=lApdA7Cmn4dK_NqT@|NZjtDPsN;v!XU*t3OYbRj6 zFKkNox}?VEh?naip4%rd`Ica0C`lU(Wk-@c$jCKU^Zh{(k6!Jsf|8vrxG`x1;F`mS zAaSI8vsp#hliS+mu#h^iB*uv6Ktb=HdBq|b!z=!+h@>h@-prd9^Tl!P__}+-_`Baw zp#CDXN8{ETtKJd_&jV(aCRO@GXaPLL?VnO=+<(7K+ffM`t4;L|dB(yx`L=Z%= z)wH3L7SCD(E>_4CEwCnXWsD?)zsdbd4Rs-Vxz)(2yP}S1!{=#rmbMp~&c_o@tF1;t zXyoKhfGFo=n5?RrFYq;w3w98j#1R?VKWAgwVe^{(0wO#q)3OdJ*Tk*2I}l178U z?ee?5&4C2^>ly<>$fW5weXbBf@&Y(q(e6rXP&znFwm(yb+2ZJzX9*z`v(O=~`jwjo znXL}XXD*@H@43806@NFvuRwveTxF{->nR7~U4EP}nSp=HYnO0OYe`{3T<->36J)=7 zb-1>aXKlh9Lu2gA@$1|;m+N0W`pW|jE_S-APr5y-Lo%sZElD7pHW^HF(eQA5sJy(8 zm}}~5gh-a1pm$J=J7ak*bSfgZ0)Fz1bJGk!Mq2anS_6jB6G@vuo%_amlj*sX`-F$J z?MZ#{Wj6oqP(POk0cs~*1Q$xW^ShX6)+IjUlGn!>FJmI!Q~!_#=sbyHNL;?JOByj| zmtRm<(SAJN-4K>TXtQ0~vqJZ7bv*l(W;4AJkkuz!j9UU&(75{<-?Ch`qT(S|)4ZZO zKnihOouDRva9p1=7P!g@_RWb_SOZV|O*!p*p;L!I%q~LdU(_-qJVlQ~zBy4d zS#L=!{YBeCT@sw`zc>y@ZMDMEZB4AM_yKk=aW1-&M9E zKwSOmfR8vl1=7Dr(`QmZZ%-cd7aM#A(IvxGaeA&uiel+p>(ti}wzB62KrQn3C`?@A zRjNF?vt}N;9t4P$nP(8c-Os9pk}nybm3;%s4a|68jeB&ww1v|w%fpd&0xe+P}ORoi3*7QCV?%r^3{qqFi$ zM7WW&LL2Mz`z68EGS}Cpei}`@1qQ1lew*f~*Pf^Jqc{pe^^d+Nd%xZ$FZFt^U-W8j zBEE&>F8`I_8hFCQU~~mlXAJ&+M zVrb;$@3e`HN_BVSO$4EZm4+$4$?6C%)C^)I#~YM?hZcXFB7GZlpz=8ux5tQEs)F_> zVgx#}#r{r?BI-+KojtS{lJCA;g5FhfpVH4cXAVIv^j$Dr$P;Hdb5LF)NMnXO|B*2( zX*lD_HWn!G-gy91h$cveMVY_8c3B3?d^z55}pwy>OO&#y0 z{Xh$>7X4jmcY87@L!QiZaiYY=Dr_hsvMa=Wx0fr7jq6WY;k12vd+Yi;2xmd#=_-@A z-u8o^x0ivd?M;WEqw*ST31bUcP>x9|a_jhfH?c)aw71TW`_sL}XiU=VZr4fi)69{X zdV%_t_Fa|w4$~Gm&yz~c#1)L!ydTFOW#{yNvHKeg$8A9vR_kh-~uC&et%;W?U5TWy=YX@ z7iBYD1Iv(6OAt~9pck#4F>${+5<$L;~ zy-k%oZuP{jTZ`L-h2m95 z546@_1}-h>Xk}{8>SU4(Ehpx_h8^8Hu0G0T|EjO%^f}=RM@ylh;A^huKgqs$b4IV) zGPFcqUOM06>aqW&hZ>1}?pHA@M&}WEvx-IrlQjVm>%#&x=UI?=Mex`jn?+|gtl#2J z70<`l{VYr~-38LV!q>O?ct|xH^kjDA z?HiB$oQ(ckosae$HYF-kf?CRuy>)j*?l0`14)i%PiQME4>fT5?G8$PL9V@iHPBZAzo_n_jYu(Tb}3RPaq%o&(h z?gz3+n~9mxVtXN2@$VFyITah~NwdDbNEawLrK4vF2TjItc%l9c^y{tXq+@C>Qm&a7 zkg>~Az1T$OA|}zf&}u6Vk*%*$_iV&C2eL@fY>x>_xxJmkMFa#x0xAxONLC3nlO9K` za(@+pXdKo_!D`3?4tW4F(pER)r!D`T(}+}g$!Q>zyGU0x07E65?mYc6(m+WN6?e^& zQxWaKtfRqRZHLZgXL1CPA z!0?RvmJQrh;!WO5+pw%saU+U$x8qDvIg75WG8MGRw=6t`Egj4Rd**Jcr@rgf(V}oR zr}zs(OH}#DWZOh!GRcjuoe8M~BQBJb4QXH0s_svQSAnM=p*8AL4wg=Z^pyE`^x)DE zqYwy6@J{z|zcux>qfi`!+h(GrZVAU}OJ7dWwnEIV9 zfUX>?WTr)xVW+hV(g}k35&Q=A%7js4WGtB7wPBNQxjT-5D*SxdKi{?}g`%+4m8NUj z1DYRi@9xg1ZdYE~S6rIyev?Eg|~|TCe{KKJ9>RIQB#UWS~-}$_$y~dBZ7(E1;&h z;9742xEsOq@7{n|p27kO90VOj08b_D80s?g`N0hKR9o{~1-*n=bOhEENJQ*(b>2If z_i)o3{0+_>qfCysjRyAyZI4>1=xYcuZCT*VN0}l1Ip+wc1{Y)=78)5eOe~nB3_O=y zu3CVJ%5F3fIu>RJE8Fozj?yQC!yOgqraJG5?n z*yhS~9&GImNE9who^K~MRduf%ihTZAG29w@W+Yr_es?@Dw%fw)a4kk0eH8;t+qY0u zE9;Fb{m=s#VQKTJx;}pt&0{z@0_7aZzX3T`bhVr9;qZAwq!n{lg)t6htT#M$-JG7p zKQg+G^(w5N*BC#ocXNqdG$m491ZJ!q#bJG=ECUrbkYhFG>9MgUviRILT=`fP-s|wf z-l@$&+s!Tftf3qlRU3rsW)>b*4Uz_Up#d~(A;>P#r<-Qee zm-@0MR-{jUI={bm-)Sy?eb4Ou;*F$tdvG&aE4OBRiV;teiCo@yQ;npznwsF*bJNd5 zsidmPL0w31$`ZL2Qg$}}>Rt_>&k6gJUQC>Ln%%djSN%QfTwHqCH#_PDkOTLtAeeWo zDPqoZNDw^f>5yadvXV!miVO5)#F7by{`QV>z$6LGl-^c;sMA{a5U>vJ*PGmN;K0|o zqW7iu1oxM*hQYIVfza-lM9NW^?!>Q(#PEJK#*L-e%ONJmR6DpZJN(2}aS#fXM^umL zfgu^94gUC|Zs5Pljh6LhuaZw$C<9aS9XF?_;cvqV1Pg0%@(oeJrc8yJw@Gg_4>}uK z8>x}@MPp?c-yHuaz(!Zyh;XL=+Z{ujLmR&QrX+3aH<5t~v>*AoEaL^3?WIt5kQgWv*GNTfKV82`ErQIxnH>h~Sw$7JahA3Ll| zDMvjCh?xB;6cHiM$QIac*xP9B&qDD&@f)Mz@t2&hVSK|Jv4cNP$IQ7sNmD+WoXF;E z1jw)C!8G>^0yyx5DO=BT9n9YHG|%@l6mlcKT#JHnbcL67e{q0XWkVfA5d-PO*UB?a zXB*-RV9H3fctUd1`hlRG!Z(*IGiHZqQ_l7i4w8K@j-4?fVz$sdZGJbUy~gJ$Adce2N?g!m4~ z$Qv595m#7(Gy)E%hr&mz=Y$VkX5O(%x;rPfQEV;e#x|H`_MMoCnJEQI%af+!)#ftG zrpCOtWrjTFFu(h7B#8OLpPnz*rlGjt9%}`b%XX|;cb2x4Mv_T1y|`hwBu4WBH_oa5 zJaE=i^4`3Ne+2r%eK<@J_k#&f>j&*8Tl3V<+pJcp6!KUIqolj=?2E)o?;JvF*u;8n>fPov6K_F3TemDbFJ zIpHCpx|*Q@o%y?TNi>2N8N0pLYkYc`!>FGeEFf zNh^w|iVKR$E;LRlIMIF_Gm(C!b73GR_u&}*-YF?9<}&y4(oW9SkMTozlc&efe4XsudnI`FXi%R|gojudF-u0ie|lm64d@ z#H%-gp?4T=ewgxE%b275CaTg=8{;zF zAq$pqOsYOqh~Zro9gr*YEob?o)Bkf?6fJUQEOp5x*fO1YRQb9=^{Q|-fb)AywXA)h zq1UTu$@IAA4fl5w7KLxL7^5Rs!emA2YdRH&@chQEb|>D@C`Xn+^ie7(q`3Rz&-%@~ z<;n8CZ7ph&mvOmU09PTOnHn2@0#kLo_<=t@Gp+Pp3_;hk3`i(JE3}d2=Vf80M zjHbt~_Y$8=!X=7mTt082`8f(vgq_f9p)u?`t2;`lpB<%~(#PN6iBW_^v7yUFI%ISc zMg6t4Eh&i05zeEB&^qsGA9glNl6-l7s%{@HF?a%nun>gyDETvwy2&^OKF6uL6{_ zxkn8Z_T}lXmUrnTsjveH-CbHFK*>{%3921S3$JdWF|9C$sXx%Un8T*ENTN0^?BJ9{ z>l?Td-yZ*T8!tqUbt^D63G8QvcF%wsabB}!3-L6SIQ%*Q8g#UeJ_GAn!c!i%jep<} z@UH81SZZ-MxneXUr6uf>2YEF+5y?sIZNSRO&LpBa*+bGAYUzl{yZKOY=5@B0*r?nS zewBvj5ECz$lfPOGB5T~#BY*Y76eq2iS#69H(i4k+o#*TFz!cb>b~mK!(g`v__HRh; zWB$%@z})SUw8T(kkqu4MbeAUD087?AlvE8`f(1wW_0mAG454Wj=Jd;=mpgP+B>u(F z*WrgRo_uJFJWJ{;(N|zRsy#zw9HQ2;OCm->H|@#`?`7k@g`GVgoO7!}MdoiWGF4R# z7u2lINeMvuKEPe!@^Jod<~qFbF#}Nk{Q0k5cX2Z?jK@&qHZ}5%Kkq{CTLdu_`CD9t z0k;H5MiKHpKc)_vR1n2T^}1`mRe@``8%wQh(eR_UXwB|$Od^cxzz!Y>?^zc0n?ANn zNKyZ}w~us16BtR7l$sAdozq>6%$v5o5hUdzCZD9(aenh-XFZ)7{!brs9WoJLVbNP) z6_wR&H%1d%l_Bk1M9GjO4>iX=cSkkS5wTf=Iuaf7ZkLdeT`k+*UUW988U1=^pr_uF zdu$+%4LyXUjg!TO7+7~pdhYz6;d$N*;J6@&q9=asgXiYhTf$S3kvPt6zK zLPo%YQoFcs2CIb8OG81pX7o*!zM<(=;qCOa-Hj~ZZXWwDQ;R1u0Av*PD6-44%2tC+(J$LQ_8(yPc`Ks& zEx>&QlcS>)SC@pYe+~{Q70f6&6Q#1DaQb7d%T1*QdZB(|J~kPU)P_d9A}6D;J65}L zN?*psz*BLXVfz{DgI&ider|#b=8aiirsus==K{(G^A{{Klisk- z!(q^)nNpVLR>1qu*f}tS`>l&`!Tqun{x+w~JhG`-I?+{eJL+Wk$5|9_Hi`D%@o88~nNnF(OjkOo$0Iko ztRwbz+H{XM=6nG=*U=SU=(&&1+AxRtnyeJ;6NtvIW&w(K5)z@t^%#`26BGRDVo&1? zN2+FbE0>{J&H6Znmve16gV%Y@k2e5PLY|Xfne3}i!VhTV(NJquZoi4tq}394y)4A! zS3|Y>7Z)QniDajp^+FL_q<>RFy2?R3Ac>n#*ao+6S#EuO?tvadBb#fHJ)2oIpFSf| zJaDD0t20{ay{T>J+TTuihc>_Ll>k`FMv7+={o&n-wl`2W7 zYr!1?`2_?!oM9*i&ZvZ6DT1nA?;=O4B*y)D{r7QT@u7+rx90_^pUBGCdcFHB_d~L} zW5f6BEMDizm#g$CPrx@AGiO$UcRLl7Z$FW2 zoR&XUdEZVaOK%vaCays#pQ_gAh`85sI2%=gh05tHBW1&b2{AE))L^yS6+5=?yWo9& zyC+XkNQ?w!{|L4S4sWd`Q*yBtPSaJJPJ@STL^d4UdJ>F6*HmTfjO%b%Z2;K3OCiPY zDsX&8q0j$dwhcFB!*OWi!gH&CK=VVJXEkrzY9Xd-f!6qcsxY`uztwcii!W5Y%~SBH znfVjQ@h=oci;U+UkPtiA6SQz zgZ;li9Znr5xa^f%qdC8jEiJi;mwzFOn+(=&>|AF1`@Y8={ME{Q~d>RsBFU_;-M>X3%%=pj#urOfCj5RtQ2=bj+TVHPR);YA_H-<77vzmSrsW{M{HVs8+=am z_hDRq&E~CaU5J*S=Ymj97K1h}X&t5!pQ=#$n5mn9{|2hPClRBHy-7eKN23)0?wM zw}pbmc*1)o7W~dK5B`Qt7PN!EnRCqAjpfh=re0>l%3W5YEX8f+M(aLN^X;jv_8gVf z_JfZdK9$^Tp0B@o#aB$CgWZ{=#xTjj1eT@7lSftbpvsodQ$ZlG|1j@2usSmSDl>%dD?7pw3e^jFk ze6xQ1Ehyg~=eU_P$SoExJWL3+T=Uc^Sc)?u%U(}Xs+(E;wVwfy&dTrHHY&y5e+%>~ zJ0Xg2UE+kG@T>#M<2Jt{J07a1n+j*6C{|-1V<%g4C)rNsF5lN zQM}&ad+tjTVubZ6q}v`;;Ga58j1xNfd1TE0bRibBz2y8%fi`b zXBL>+?tn`OiBVHUMcFstWVmR`qE2s*DaA)Dfy9KS-A7zOy1R8~5!-BPKdO%AdzjN^Lmv2E4x$76!mr8OW(tDDU-(&)w2yt@7QWL%D+6npEtrt_gYL5Pf; zUfDNJ^MDq1aD~JCRz|f91_m0XjBMHUip_DN0u!PGq~61uhjWSsQZv1@fT2)RDSOio z+*(xR>w%8SR`oOHCdjV9G{Pvl(8I%IxwLyAVNvxt&0*OTVx}d*zPVAtr)S`huf6x| zHTy!Ah2O-)YF8bdph*ELqB3DGGIhjJ(FBS}6=?r-0t$s9xK@j5rAzVNf8owm_m4!4 zAiSrEh{Y34C*_y+U1rH%3gcqNW_(MNha4L&AHRu-xUS5kT>S zP6AqTgGfP(#(8kea5s3X6PNqg+JpdI>kXFz|Nf$pXNL3@kCu9%=L&g1wFk(m)aG

L0&U=0AHWWLidD*T}(j|TxuWgy7jxOEWg~Q5eJiAJX13DvoFi z7bFP*f)g~jYk=Sc_r~2dxVt+9_uvkJ#t9JIJ-EBOHg1i}G|9d1&0Dk9%$u3w57a77 zojS7Z?EQV+&`(kPs_#y_)A{D`JRvq|m)_9R&oNag3T}#Ef3yf$;-a>#YknUdBClCG zt~L!ioAy-)o|F~-O$9#5;N5(OrxZB94%(f(cL;kKoRmI{< ziYA^kmZ6e2noRE_ba7Dyi^j9j>2UU9LmWi1HQ*Bk9f#6L06&>a){lE9+n75s$Nk{U zu(HYiL*l|7RDJtLVs-&F{a0q-FMHx3FTuz+pHN}w&KDnikMP3W)fbwQb(yAThU?^zW=O2hoT=}1}5*p-8M8)*sP8^r3rjUv+Wm1{3-nLMq!3L z=R;fPzo2)-6$nRLz&iO((~)lxuy_H69xb=WlrfS*Ar;zqq+q!3;cTZ-{7tT1$YJs; zydIYN7i9I{+u2hTaJhNwuTN2y*?78m095QHe#9Dc-&6dR>It9hJ`)R%%kq5T)X2P> zUK(Cit+{r?5S)2#3zeY|s+l!n`ZnWdo~=l96z18)4y*HoR)`h3i8wkf&|hK{sM6r@ zp&o*xekwp}CTzL>eTX0x0~9;6X&EIrnF}+;nL3l5KkTpw@r@l+->ze9IC%~$2Cg)) zz1O0?PV=uGkTNcV$M8CFP;hUKLzA(U!+xT4HZ7~CHLRIOa#JRsRY&jf6AglDCeaRE ztgYc!Bh`Ir`&}wUE7llpdKK@g_l+6?wO2HpCM$-o7`PvfC4_}(y|D<~b5B44yl<7|@%Xm>!0$ubrm;7H9K?c2 zrWB^cK_o{|ac*{zjb}MjFXd{QcE8b3`6ehHwg-uN8owEQ=9Te0p8FEbVU)9Fi0(S% zSnu1)=W^G#mgSwOe?|?*=8j>RC2+joe9I?0joveXN0^JE4C#L}MhE-zcPKR~hYJ!~vuKv`z~^0Ih$pM& zUG=e$<*a=l@7AZ@F`gX7NVP* zV`u=fEsE2Ex^bFSErsMKSS`9~I)H54m{a=Dw51Y@ zm{NV(A5V7A?p^Vi5(?eK`0uXY2Y=rjKz1k|IVWsqj3r)4!P!|Q!y>d+{{Hi~Ri%{{ zp)siO3qIzRkZ6$;cKdUASl$)vJzx=%@c(n}fG_^}Oc}g~;-vNYfu|5#RZNN7Ji5k* z?_tT@@*R9mgIkU03qd*K?wpT@)+MEQ;o}+Q_QXT?8_otDo>TKt0Fv-GzWe0OiO2a~ z;0$-Ysg#UNhQs+~(c{x$``RsDXE)HS@aAgeVh1|BWHnVilQUP0I41m0@O0r8hY8VY zyVm{r!Ti|eSR2i(`xR*MDL-iN3JrTy53l;USneuaEY|ZDnOMBMw!;_hwtBh1f%=cPmCj@OQd|IvoA>)`JMc#8G zN}R}G=68g`BYlJD=3fK+iX-IT`M|VCkRxhg@*EAWn-kzq%vuK8}>*7OwEQ#kg zpC2rVLwf)PFD&~jJxoN_E50E&!(H8#MpQ^ZPt?=13Y88Z@o4WK60e(+dxhyOmP8`t zg`0_?n|+VI!Ro>Q&n%Q$bZ0{R;WUwoZ7(OIlV^7#1kE>uyy*rJTReO^nLNA8@LJoc zu+^rM;-)`Gfuw&RgUF4_!`J#OBPjDAKp2(bd$!*j!v6Mi4Pm;)#eSw>dKZhbW@ak} znS9n_3YgIcGoICpe4$cB;0L~C=dd%#QnAWBxfqiZ*}Z1O%=)ZonP9*L&t6&Nwx54w zqvad&9BoYR3Jfyg$dZao60@h{C-mTOWq%R&nHkQ)BGy=EJM&7tS=s3T?9$sz`vz_uS5*Pth1-kC07vO5C|GeG}H3U+WhonWv%y8i@fTyWIqq}`_=HAfeF)iw8L)I5DONEtYEW#uZM z!zH=F{L+x`f67Nb?de*NrKZyjMMPZg>`a1VF(x|MUMHXrujFWd`M|^Ydi8O5zBqOO zCgvnzZ|qsIT$ySu=@=#ley@PG)Y#*9y>8QE8>qvkPX2l0?qGzhK+O+OnoKSI*}TLj ztIFY697@=X`Y4wnfl{vbFxjh2*p=PJ>z64vRd_GMu*xn&E8f(mdf_kz7b~9{4S}=% z$Q`v?-xN6U?xJfmSpoIUg_)I;y*p1~-eD}wKsCz@Zj2BqhSq(b8g{$y8nM`drC~IC zcXu@&dcd1c$7Cqp%y(|@P{|)Ozwx_U8g7c<0Jqk2a*7YGK5z`Ln$#S-W5k9r)po5T z8F>I@y{R}!%_T4b*sZrR42nXoaDJ$|Vc@VZB`Xhy>pczp7N*SE>i>Y9NV!t0Cx|Jj zm?tKQrx3=4I`Sqof&1-13f4R9CN>gg*aAPR-ZC0>;q$pj?&@=8cFamCbvFg$aB%OV zOOSS0@v%+S__{)#i_@g-gkLcAbk@ml9jZ;6(Yj}z9+vxs#WsO+ljH#cW!g;vv@TQ{ zu_N4XMhj#om%!F2Npeku1?;-cOJF2qS|=Pg^jm)>hvhl{-CUOaK>23mZ<8F{^Dk*= zK}P%!CG1Zm(5~XjoRjA*-lxxHj0`K)YC_`Ir#lF>%>e~_kjcq@dfxVxQR!R5B%`+Q zeb~G@xR{&_2q~?nWY`^1Hw-&3n*i-dIv)u04yAicpDU@^sLhth&6d9Cq&0dZutITf zE1*e6@p-A#l5d{BZAM+{(;RFOSRbt#3gW^F1hhy1%tgiHb}ydJ(6~3(#jm zq?R-oAyrLA6eVq&Wo@j3aCEAjrlCcXoIFbukbd#Svw6bJ-O2CZ>wG4WxY8ZEQv-koxlEqy3sOdiD= zQ0BwE;Kmb{30h1HcgEgKHAu@{7E6TFXY0by&bhV38sLrfYOhBywM8poxl`OwX8-sB zC?Kr8IG;E;^YWlX(k_V$!7XLcbi*P05|xPVe7|6I%x%yw~*aN8DmpMo_hR# z?mB&qc`(>TZ0xrK~~jP zc-unuXTOH7Zz=}rL+#0kGwROJ&qBGijAxpXpU!=+Ps;O)zd)_u-k=$V8#L7sWX6NM zxRWk1^Vs?jJ<*ijhPCVqB?RUoL^Oqy{-JIHkv_f^!dMaE2*URHk^5bxA)h3D1I0&e z$=w$@-jDJuX)0!I@QKxgsox`&x^kH{GFxdkDYwNsHfthhMju8zkU}FYR04A zmG(%dmeo?kI$)cQQTg%~6DNkk!Y(Q7?9dT;fjkK9w2|9y1`U<(kR_--hBP2a z&Mohy{Y{BDcmPiy;PLnEx+&^x+=}b-@yD4j`4R?SebXo5Pn9Y3Y=-IWjfm(8d!9A> z78$*rmhOVV`x?&iNU*Ss0%woNI8%K$bmZ~gQ0DWr`BHQiZv6#Sc62*D`7$WA(hBF# zTSF(-Iqk^*<~;n(y-<$vz*MG?X39$|8-Fe@0{jlz7cLTL*znV;G#pmS54c?y@~<%H3~! zFK;09iu~@s?QfoE(^0Zrvvj4h!l!3UPmhkg-EZtzFSIr?vpmnuT~EOyMhi${yY(*h z$p$m~U2QvRjJC$UC-va2gRQ943I|;4nCRTUC`dmAG`WJ4BpyhaFTqY%`fRe5zV0!N z(R>;H1)^h>qxEKb%0oT<;WHZ0MZwKnyF>_4F}=pzRhTsbT)hnU z_uuxuN{54%ENqMTA)r2xH;_V5l|s9^+8^e@E9l>c^@D;`CV!#da3pa1EdgUrmYW-? zr`}4mO0A4~)}MY~0>TNR)2aEoYO&N@Q>zbl%nuICE@Ko-&QA~Y^~_3RTfXs^-#WH3 zk0duR(YG}%Dy^^uZEP4IFwR|#5X776a`H2djjoQWs}bcP4ilS2M~6W&AK(4(x2h-) z3n*=l&p9!3bis&v{qCZGnyZWNlx=d^UVjFlRhiRW0dks_Kp=|gPgb$kWD40PGz>k9 zowN;{c3O|kh;dXDJ5VMOE7(-^!AXf6ta;7y(d0>*zW+a;k4&N0)ivC)dR285H9hwU z^67N|0NTQ_!phYh2ZJ;+hZt5(l;!>~%H#xmsvSi5Qh1iz7F7khT0h-KYwJ{gJ0|<) zi7~l*NvqNJaCeU_o=!XDzL#W_-^z2vwJ6Kh?^t$V)rvo6e+x`nsB z{?OFq#)a(2ZxADC$n!;OGW`m32$)Ged?BF>{Fa0R7o6}Te|S6+!<<$n{!V>$GanA! zSt);4q8(+naWBI&Hf#Je^Wk$c3Mn7_yja^nx_*1Xm-!UL?Gy;38J2O*JF+cA5~@PR zmFLt+H<*BP@xXnHo|I)XIe}2GVbV%Y_2NAv)AHh9$IIkL)cvzjM$M}!c)gQISbm!1 zqI>924w_#pXv?;%BXeiTv1-x8X|b@q(-aoT5)=*B)mgvxE}EDIuB`xKv=gos($*#c zq692*-ue9>EX0Qa9QU79wzzLJC&g{MBLndK+f$CRL%cBtY%&^7n9p&b{FM&8no%;s zi?9pSD;R#s=qbln#Aw#P3?xUTpgM1lEHe@i_VKgZG; z76nD=rvUeZ?;tr{&1wc&;96hR)cq~<5-A9XS}igK9#mhTpZ=;c67pU&c)k^fG-g9l zfOZp=_x z&sVxmmb|dKFw_b;UdD_n_M}u$o8yTB*K2t!YSU)4Kj%3!m!{d`Z9n6BL_h5Zxa`EW-> z(A_`>kL+u`9ELjyF-^1~CuM8q)8vX@Q=d@kDQPJ_`lABW*$A6vvT#>9($F9Kp_Bxv zpDwzt&ED+99*&cYMDXigE1WSy60ia+0PFprc^?nCzB&(}U9`i$i(udBB@syUB04 zSVe{5OCA$vSTx^l0z6eHHx7MgeMp&V z-8)#KsCJkRWRFtW`VU1rH`J8l6!K&OF1)?CF$1d5mt4Wh@_CA9* zbH2auy)*?a);OB!I>F;s)$qWkMDMox2RD2D0iq4DLR|R=F-Mb(?{=tN7VU06)xk3C z8Lb@%eS$8h7qfmO$UU$#13`?;$nz;|OA9`q8f2*h{#g@e;LHhO$-<-?Xvgfyw8wnMQmTmG+BaF0j z+i7Nybq4E&LA|(}wQ8Z-EApG?^dPSO#~YA1QILS<)N!k2^sl-`D#M-IOrdE2n5g`C zwS`!Y9DYjEl#I`#U3hf1;UqYTj~T5B0R)qz2ICCnKJ=TN(;#w~O=X42aU|+_*$#KN z;o7K8Lga40q<%dGGH~c?fNlb}G_JLSU3HT9YXw|f+_Y>IXpG70xq^dR@VhA6$BJYcgrYGz3+Nj6*hp)+d`O5<)b0Pl z>9y3^J`;um=p2@M&KxL^#ySc59=Fk4ze0IC?}ahza_?$WV@id;^Cgk3ZDdD~1zrB# z+&Y-@Q@8-J=&1(W3JCRGpwnP0$c5zlpL??mJ_uk{9EU7JBm{d0dR*jkrsG)|^Z8n|fuf#8@4lDF0h3(E?Ym`#{+3lc$Bd$D!}Y z4m*^A7e>Fab;$d2TO>-5g3lj-ze!VG8Fn|zSM7&O=0QL)z2DEd?S%w{53YYkO`R(S zpz$9$N|{?ts6R!VD;#BvRs+cyZr;?B%~g}gklZ7-GHKS_N_;6@d?K=j$@<}N?c64V zeF(H4lsi+PzVo(DWO<`z9jSCaX~7ts@y5hpHoPP>GH#qt^0Mzn0q!S_`$<^P_{nob z#f=`!9+jrF7bziFAyk94%6+{(K_cnT@|+X_(0_c6dkAIjk#Fow`d7@yAgk(|&Z$qX z$mqmHx%Y!d*IVE0j$yA)ZXkqp5a$F!ShwQX*OCrD*a;)y)52+TgT&%eFWGAdV7x7g)w)Gjx2NDCN!+4k;5(Z$-Bp0lS_c}E$5!6o!OjX zHJ7%ti}3myn@0CR2xx3(>&@eefsGGId(-2_Mvqd-XL5=J4QeCX2RH6F9lDK%LE=1= z+zwOwLx7OYxUaf70E&erR1Y*s+pBzzQR6me6vVmWu5-_)UF!{B^yX~vLDvE>4vrA- zj!=dH7I0kNX?4D&wD<8Iv><+99$)FMb(nG>TONd7y0IPpBHeLETwf=9ljjqNu16N% zI9e`yH^);=HOd&=r|L0f>HpkN$0qVM4J1(pV2lYnrm9^!W9Fr;_kcY<6JcPjB$VMf zXU9-uq%dcKRD?gq%ps_(sKzhV#!L}3^Bk*gts!GX0eHlAKCrD!?J`~Q%l)pvrB`(f zsJgtAnnS8~Gu?5gjZg59D>a7OP@l`c04PEgF-W)+n?&S>@n-AJ!YkFPwBudu?P|D_~e5>lZ4@4nzGrp9ZQ z`l|y2Qos)-JjJ zQET)~PS&qw?(mOBr`oRZJxx$v{6^igaHL3C6yWJuRSdMP970qyc}=;s7~8m1{`khY z8)n!Z_=>W5vqE>%syM2s6&vi*h&Y}FDhM{29}CZnQAkBp>gdkP%R_c_90Y+A0$e6c zT+Xws&iKM|Tw5+@v4atG_Dml>Cb2lKyj```MEuQer@tpq3voMRliSr z!&r3MR`_2=68U`!@s~mduMrvdSe!ZE$J!iTkKHY8c|j{RX7O2HlCQb~i56K}``3lN zV7t?9mcZn#$95_NqkEtkqAX&_hl~-|HzDgW07oHW0Rr_HSy3~{LlCtFH5!nAZtXPH9q^uc64g=?I3Ma#PZ99Mg!mB2P;XUEWS0uIt-x~iwgjCWE$s!Cpnq+Xv zylq^`?ocGB>^BnCV!s?_pxiB)6`@gtd$=^^%vttKy{1D8WGoNhf9)oepWY;Z_mi_O z1axH`n0|m*s9xrWwPzIp}g_&m&93?GWhll4CJg@1$MzM^Xvgs8eHL z26ixSC^F>_m0L3prgS9dyEVJXY9^M0JsALx5)<)i=j(-?YDIfrc@ey|^W3dFclR~S zW6dftz|eoYM=@1$Mzk&W=Z}AD?KRzjT?UoEyC81NaK25-U_7eW?Rfj0e0h|vljW*) zLW<+oAhc}aVv-4c@QQ^(Rp#y46=dn~dE`enSxP{lY|bX++;xuzm^FL+>}xY)kR6M6 zf70+4w<>FXYPP9Yn^zGI<*X_S2LW%Pu?}_>+l(`M>AW!}764$CwkX|GV5f7#FyMn^ z5uOVhD73iX6h@H$Orc=0x>o2mFXkgPm{UW>lrQ0<(CyU|yuFIlp;R$mmdq*#B|?4& zVRW=*&;N|QV0Pf*gY1u`srR>#jRXNQs8oO1?WaDffb9UhkPs1k z$kDcLN~@7lu44NN0cWvQ*ChF_&;&#&^+93TEd_a5r=4_3j$w=}+>g{_)qY0qpNe47 z!n>~0Ll=U3$>j^TJDQm0c2MywGv1GxrMCkG8%G{~?kYT3uL;TZN&pK^`QJpeD~jvN>j9Vgk(wc6=~-zd#z)et7z!R_;}sco~y%M zZXMW>d`I0m0~6B8UYag+t4O30Bp*7&0skRbycQe~Lz%W!S(9GKKzAAI*Lr*WAi}C} zIBFX6I98xOLf}P*4}YP+h>W*4iK$VK*HyvJcOV#eny}D2UDtazTBz3R#gd75!4Zpa zV0Z76UmA?JW^Cwk((k%eY}Lc6)gwS8!((Pi(>&&nt=~on0lR`=44x^hBX}nmnAx__ zi)CIY-jobT6M=Avzxj-rHB>i{#;3SNIWf*pZ>eEehLx~eO=Rl)@- zC%*wPKGpNmv_@MloUV;wEcefz(K3ZHGn`>VEM1NKP zFtf}_trUSqy%UeCLt4UvMc3;cTg0f;0G!PUCm$K2!Hi5~Mzv&htbJZ>rU z%(83KZf!WCvFbwZMJw{aimxcXhAEd>+F={@mK!31Sw%^%qZ4 zEyP{Ty+XS&EEXq=FO^UE>URHukY#}t+9RLSy{`IVh#+^o^!7dB+L)Y@r95~{#S!nk zP*yG8P>RKJlC+i>z-RG6cIksqfcF*gE1d1^zHb+xn0iYV==mF?>&ICAAoYm#oamoZ zyeU!PpBtceP*8B|_59Bc3DZ3qQV`fvznGJk>KyG3Oun>Y-1K9w)~}XfcW_yO{b#%{ zHAZJ=j)+z$yjYI}#ye-b*--;QR9NSa-ZZod6VBn?XTB zx(i#7TDfZo5DnjIey04ydEY#_B*QlG-~yDE}(%!##LXB`tz%e_0a&Z8c~}SUvpfKKCC$o zLxt~>de;03q4nphEJOw)QC^Rd_Pg_Eko=dD|D*uU0wo$3wOSv#UE*pmR7`B!?;e|c zHwA8;3meb&kLQx5j@J+-OxBW)mHJnt% z^;%Jj*+bOUwQ0b{VLp$<%~u`8H-F1$g1Y^8nCCj2Q(I- zY_I_TI{qH448Ep&UcQq}U62HvWqi(MsN7$0xp`5tum(ltfhx%|d%HC*P9o$+ z2S`dKCE+Z54eDVSv%_elt&@5kM1J(>@#-S?2t5-MtY{IIwEy_QZf}0r!2J=~#zt(j zZH;S)f|JC3w9{sA3jvx85ILt?xE)8O&t6kmS%(gmt@9Qrz}1zV!Z>VHyATuTnTbj1 zu#@SX?4vb5)Iwxx9-~i|qreH9>h9y?vZ8S*RO6?ph zCSI;&|8m210ze0G^#-RUz`01gLV^EPNfSqRW_LdsS#5+;IQ+!Js+1oTU~9cO&)-vC z-OSYys%8@UN{OUMi)S_KT$;#F-O;iyd=?0^o1Z9}z$p4UJrX}GWXdPWJMwai=6FyF2@&5V6yR0QW(4 zJVS)*tr`2vX&7ttuS!cp4nAc`2@x!n)q=;!($+}wNR+xRW$&y|0n)~a-Eb-%xuW`} zo82ZP#lYC(O+#+p7>AxKM^5RwgWe29c;0u*78oXq9g2q*9eXHatFcrjED5_e z6IXVV0APh!ML&~2c@Kx_{(6H%h8LWztw^Q&BFjSFNA%DH75=L}({$s1BK}qCLpnx+ zTKq+WfROUzizTCvKr{Za7$yfp!?e*2i|5h3=C}+}B!fd%-<_qzP)fwg%4)%l!Kz-P zC5VU51HbLLqp?|ds&hQr)aA-CiqjD#t$FmxyRQ~uSuJbQi!TjJ_TH?+Wp5QB?R<)f z{mYy};>#@U>MC4bc`N^^cPnkbn5*|SRvGFE%j?Shw_HwPa-=gE(F==8)a@ncu?uN_ zg(_<+?-WE{e%}#=2i-AcO}@}Y_CLOL<`cWs1B<~qU~)$3sBVBmans^uT6qoa~GdEJKdZu8JVg`H;-;|74g$Sc3jXVfX5P)YV@Gp#QID zf&cH**IUIHrJkVdsv-I1S8X>%Ay6GiuSb)Hn$t?vTv` zG(Pa+<~I!{YC2y{XKz#xd0wX4EqFbIoaZkv_MqlVWdnQ@9CF-=^#~u?qh$)oD6x<`9Y!8RImdcN-<)fkRVfD9s4@x7~Kqs zlSxjuAIPa)5hsS%qqQ_qEa6*gAq+fU5ZqEFp@n5yiQ#(4ozo&@SeE!fUE`bo6NA zN`#7O5{0J5qiwfX%mW*o5}F-?$h-+ zYQ)r(cz-QrD5?;#wRELo747?T1=lLsoP)_wMrHq)x2E%e)bK(E-!aq1CYsYrk4UwC zfu=0Xb-EdrEVubXd)Qe(4P{fz?$EXqi;GskL_eO64s(p!8eT2BV2mk!%W=k0$;J;P z7I4`QTSG&-jWmTh2ZhYlUHFqeAv~A9F?!L^VeylwuXSX672No)GL{%YMogyA?bbC^*n+2kH*WgdPE08do(R(}Pz~?a@+AXK;^}D!3_*jDnT! zRj$`b1e0$-SL-mN~_$mFm=_Xti=;*fVbtW1V06DNHDX z8?62eoGUP+f6*~9W}cBFh^;Kev4#%jXd%y78Qf-X`by}rq!mTt<_p1|grHYIcq?Qd z+<5_$#$ja#Mtx@!6_f_E0HM!Qf{R;ukQXAhF6sNw9XT}|VPLglaHkkgg;k}S-cfsF zJs%&fo)SD}pppN&$HqGpW{zB9tuvlc?)0F1ZTZ#0;B+RUKy%XEQgTgIKNRW8FO{Ba zXSLk}LvRN@I<&_msT8I}5_W-i%vUxc?qe*Xs^|x+d;8d_d^m+gej$Zk)0Qo1qtn;T zZyEW=jBw&krk@m=Vwh8~A6ER^?w7Xo(6{}faUc_Ibn!qP$6tru?lRzR zBsS05a?vY4CTUZ>4f7B!S}Hxul%;4Y_1ThQz$@s)`Us^KE`N=8X6ZwOro^a$nzJxUsNW62!5JB2Y8)xfp&~nd1FuTSETE$ysLfNnX9g$m9BJcX<7uz@yQnWM9 zLfAzH{7{_)_rMcrW{oh&r_lYe_uNse(^%b!tbX)LMv)0*Ass1kIRah9vhpOr3lZCC>W^ zR5FKT4GS_BZ!v;8loM_~nc#t>r)=xC~&L?fu+%>2qmpSq(O6zBIPIQM%NI z^WN(#=P|Fxk|cq5U47c0rV={sxVj&mD_e~9bSkKP(s#c;O^4Kd!SIX48}bgKUhl6S z-u?FFasWOZKlJXP_S{uY6*{Co9WFek4tOzq$1sbE@faL1 z&dF?C%8z@ba*ewW2vn=rqYnQO79_=Wd+NVdBQic$Mm{1&LqpipE{(^m}kv9VP+O=D{a_i%O zjS#CpOnb-)|K1{bj{VO+0-=LIg~Z@Eh1)hl&H=uAYf$lHugqPUer8W=;UzWG3ZonDyITmxd5dQvuC~t88>W zZ^rM>jg>|qdtUz5UMYNNYffip+%fO8{Q>@dLVM#60e+&Hc<)h;rd$_`^#cntv?>Ri z|4G){HS}9*dBz*Ship^*iLK}TC!xT(wdMAX;vPT`W9Zp%$0^6d+!`dptIW9*vq+BA z^TU2MlmcHZoKQkGe7{A3DS2_)MGZV*=vh~@Y_Mue39Nv*d^a3nLR!mHyk2j={`@ys z^GZ1Kh;n+E?zTk-s_+m*;`3Naz2@VK8b|V)k`0PU8vEV5yrz~o5)_Bz%$-TLY3iRM zsyG8Ey-HZR{Pg5vrK-wVAIip;Z@oRO%vlSCj4UsZfA(jHQRthgXzG%2n4+Qi!;o*9SXnpWDCyjmwi4)-OdAlyN z`*u7sMMwfO0&#f1cy9vD#3cgPbCoqwhMDKkg@sZEa|x+&c1iIaYcT5LUG`n{7hWbT z_!}bQc^4Je&OBK8yxNWi+^ScOZ4ylK=;D)3*`}si^O;848*XUEb&n>>#z)9_Et>n< z08)6GUN@v#_m6%7kjT>Xh1_Y(a)$^6hME3)fB9ztoQypyyIw0B`be3alZ1oX7Z;pt zyaO|f6MV0>UqtiQMf<6eM-`Dej)OuDH#|PsBhO1fNmsjW^-qU)yju-C_!Io?N3Iy$ z7k~rn^*CDc1;;0i&(-WcvRFX{!{yQLLB9zts{85y`Oy=ul@z|ElmiytFIJPuSiWPWB}19M z6E<=^r@5U|C(a-Azp5!2y&l~`sx@|Sc=dU4kwZWV8Et!WTAm4ebMK%=zfXj8dc|RF zS5WZM@TS>9j_wNU0f~?qlY?FUcZm1zZ168IwIrk^fmiTsh^Bcx1sn&y>=Z&YBirj# zLcBXRvHv1#|33n5`&PPANQRSYU3n75#-Y+OYy=Boybq^4@ox^LW>Dc(x)f!}zC&fx z3d#eOfUs36ST;pqW0!|LPg+wG3pE1&!}7eZ@HY3q^wFV&6dnnF`wHK)q2K39v%)}; z7yqT3_DJ~qRC}O@q2htnI4VevY@HMl7UUY2-b#q1IBkobxoYmqad_Hk_$Zd9bk`}V_G2emxa|g@7v9Qv+#eS{ zE4HM6y?0eqbev>>(?cXtaEA|#>iz+416b3}C-2mb;nY_DyS}U{VE{vFr_;>y2tLeG z{dp#Sj5uhap(vxWkLvJGlG#0W8IiJKAk;VCg%Baj9&!8>&<&FA&^YD+dz4( z8w{cQ6k>WWfr*~#AbNOKIi)xGYq_Mpi8*^ylAh@pcRFu=r%Aj(nUTue?W8B)D1U)o ziU+|!B1d7yBm74`W77H6RIzAckQp>7KzkF^xLP_#OQVMBC{$<)Ifq#((YE9IuCDM4 zd=Mo@lN{NwMGVER<`ioF6h2&yCjN$!#+lK3NJ!IcEjr*lx~nB}n7Y)s8dXTS@~(Vl z+fbd)hoJ?3R3Pa*Fpu@2)`9}PI~vrHj&2)KQIW1#aF-r6mF`08aI#82WDqH)Ohiv+ zHM3xI`C+2?TUohDMNMTaDQ_CJB-p1_mytYI7I+#d6lJE=7qlJ}^5SXcNf%lQo>ONd z%FZWN(`l`dVw&zspe%J}{P0t2cEzE!!uSVM`~-4vEx=>?@V>6fsp(hA<^ox8K%sOv zP5`Rf`T8X<+UW2_D27Y(-*V)i047m9)!tyh2IYLU73p-2 zpFgJBQV{i#{gP2cwA-CToRqlgV3GB_xC(lAHoeLy$?D{JEEZ6HwY-m0F}2V#R}$s` z;p@Tu+V^?%#-%!kGBkX7g#mzxrQNjO?SJvB1lY<}`W1RUQaQ6ViY;|4pGMwF^TDTV zIAS!#g%|s2G=qVCjuGghgF3T##96vS-SMYtuI*0x^V5|#KJD%yK~U;r8{HS;WfQ1x z7*IGJeen_~u+vWl=G;ja@t88@u14XS?_OJY1NvFk`=%DHS*IRbmqo5 z$t&tN6To&%>N@7%rlK&4u?CLK3Ln*YoAe}@Za8Oj=RMdr2?5X}&va&{yub4Hi(`{^ z>n6hAK^Enbduow6?LwMX0OGNWy_fZoHM~!N?Rhx+rF8Xs|^Pf+E^}+r0r9ix@CI4@wV8 zn@s7aHKx|D=XJMz=?9;W6i(U_p6jcX7QHw|vzr5?dv0eLHf+6!IN)7ph?>EU!*dS- zgx3Fp;1AJ3n-|baWo?Z~=B6TN>EMeVtlF!g6vtt|v%(3kWaK!V%>Yhz^AI|s#QWz%o zAwlQ;&)}h}8`rY+PFBs(0Xc-X01 zH-!;TIARM--OGKwz(!kOhHkiTAWwf^M+|)?$Y6(ma$WY6&@Kr}&B)K=b~onfj^#L4&K`3>JX@puwm7MT_lNvI{a&c9q+uT|hT)UfbUrS7rimaGcMl zz$opIA~3c#+lDF4>GWKmbtqu^(=dE3%e^v$p>zfl{Q zF$_QcUv73b0b>U99xO4b@tnNjpZ+i!Rq>+nN(x-<+rEP*-|0Ebn=512sfkLQk>TYj z?>n3=>DD1Kj;B0HB&;hr?Y$eKc+=>5dp;_8PG+ecQDwgikL%od`pXn5MfzN2#EpQ* zac#h~DcCvz;VV{Fbh5w4RmdtoG|%4^vvFSp#t+yp*^s2uH@MpVGqrhms62QGhas+p z_z0f(ZbPqC8DU(*`lzJV@Dn@WB#3}dkzAWW?K{Dji^)`4G}WFlY9~~Tw{N3g9?Zyw zP-~yBN_V%aSxM`Tt-qePz9&~6@%DWz0m#iE$feB?WV}NKDf~Kol9c=)b2;uQsi@D% z*_f$ag`s^Ov#)@tWxLDd_^}XB8Q>;NIR^+Mq#*XVa)t7En3=6+^>AV?P3sdo@u?5~ zUbj%2U4@9g_os8Xb?V@Rm(iN!1F5#API(N8i>laxl{5t=Z>DjNh2D_R(P-Lfi>zhp zr7f-bip6kiOw#N%U0G;; zbb4FT(#y3+(SkZY1tI??>2PD{CG~)8rrfB{!qfMMWoc*C7$mwH=@EkdhMArELPeu5 z>NRe5r2s+2R3ejJh2;2X-br9*xnK!XU715T(xzW{^GZ?blty8)&4D}az3L;G2L@zA z-E|ma>c)5oQ??_D{u4&;$3+`l&aduZ3*@SY(jJ}DtMVaes)MuPs(xrffaxu-XruTj_h*k~`za+43pS|LnGaD=GJ?7yLbpSC z6+WhT9cJcV7TPi(Bv6tsnARCSpo9Hcd;R%dlTzMH<*EvbZq?Q<24cl4AMe^K?Ubdv zUE|q`KV)C*#B$!fpi=+%Vd`u4sPnbuaSedih^%zT*ksW)rZ{KFqmm_iY+G{c`+@;Wz2)_nS?i7|8T@> zyb8Vo&`J_N=8pQh)u=nk_NiUHxo9b)+u9d}L~~=apKINNO}0kONIq+%iA8UI#(M{K zABiQ^0f)$hy!z0GmW7h_ZZV(K>U$1}kF=GnJi;h;2`-*9o^T!lS5hq05Xua$K(znf z7Agxo(cAaoQDr1mMP$J*0P`)&C$JjQD%mR3&h$!i^WiV|JNPCVt%c(v zVITkY$sb#pTuT>UOtXx%`DfnVO0n8EF{>f`DtwCP68b9W{yf7%JFnn|+bNp2DXH|Ax+~=7nFfpP zj^6dv2#1*8w?YE#nD_cWiRx+&Dflw!cX~9fTl7Y72Rh1_*1QQyK{u^%FZRzx+?cOM z>m!>1#p&~HKI8>oyL|6JYGXy;02}7e*s+5ss!c-e^?x>6ed9!Bo*U5q&9tDpytJTV zkDHKU+LLB&T$s+{n{>LtvZmRJejYske&$#jwHjoi0M2&|6m%mt8k-uZQt(kFvr2)V zy`L-$?m^SJgF@5CT^};D&g*cTOC35Z&vD!`dczIM)NENL8;zhJ-XF8wZ&SYKw0|r~ zRvN8LhI*fpH{FU#jqtexS|#29`#DvyHKXP3%_ZzJ$Kd)=kycU<4wAil+cBYkKUy7k zvYSWw%|WeXc2At~zB!Q7VK~z*HaD!8vGOv|5-H{YG5ND2Wh?G;eEIf;<`%+FT?qYsHd+ z`X9PWJ}7-ZJLE`NGW^ib0%3c3wcI69uLs9Rvmyci!#?|w8!-YND$>Cgsb%LLwP-Be zS*`rlKNdzk`i%qEDSuC_&nyg!qjrL8_4iA_*>`^4pI$#1BtPsLoG zc$UoBQcB9hmUepy^z3Ec?W((?re4ii5itKpWnUc@W%sVDBB6jNAPoabBO%?TFcN}8 zcXxLTB`qOCcgfHx-6h=)-9vXs4$T?-zWv+#oNNDcUbtp4GwWTfUibUN{p`Xx9*I=U zkCeJSW5W+OE_-^M^l9xqJw(CX0`C1~GhO7-OR48Atr@$?^z_oMhJz$pPp{T5O9ied z30kh@054)qmC5%@)S1D!nlxJC^pP)5J3OaxJ1a&tWv32$7`>#IE?* zo#VuqTW`T}@`3)gnsApV7xo>_d7e#%3_sQo^OXi&M`m*4`J{C?*d!vFZ1qprp8FLM z2=6DQ>$~7?nkQMX7VA9Eq(4x_#;bYSzRM$6JBJ%-TJfyqE5+^gVe3C9mf{v{wLD#5 zmpKUFnWDlm{1{IrwaohK%5*Z%^gwA-v{ped7jH2OoH_O{9^6th5n#pR`b?wgXc_rq z;UMC8ZaQvl;nqgP-y;uJXfq;RT*RKlOYTc`nlDh?=!lNjh8j2ly|_M8l1@RM|CMhe z3y9#eH1~qniz(f%@ZQw9-oghM$wo<~R9N{FSl`R1CRQmA81FWomiZrDap2u-SdhwW zUa+b%nUGQ|&@GVribkYhXsUUfCRIr%o|oim6eFGa@q097X3tqM6)uFu&Um85!KO2W zM6!t1>6zb@t((o7w@^FbC^UlL?{hOu9tuP#-Ze0YyVq~a;Ongf7S1MZ2Q{RIxG*A6ExJ?_Xg(Fey$FG!JHF?%`^|CI=(5ILm7?^PFAl2M=I?@UTciNX)F z?`5C;Due6U{Id!k&IAFZuhpkE8q&r%l`MAc4(|RyGECx4w{4J#8p@5gWZFT>RF+k0 zyz#7Erp{{S^qj(S%Nd#|cY&4J0Y zs4+A1i@Z|1zfGz;=zTkTfr2Z3)UbZ+x_l8zec(drV{QqYQ$k%Cyy@4F*|(T`?=|{w}@f(aJP~IY)oCH_`Ld^ zVA_zYPF{7mLU=wFBy5)J$UKQA`i4JAy5SVD^*$ z$zi4z3y>)qI6DGC-rQl}$MT=6eD0Am?4Izo?jt)LIT!z;h;u?PdOy z1=;Yw19|YC?laEVx;~{Yen|%64-H1S_Xo!hGzWsWH1()wN!W{+RXOF8fYoNl^N?v}^6d#iJHvvhD z@kr8RG`JpCyOHXI7>-jd^9I92!-h~sY)tq!Jv*`wv&?HN>!aoF6K!wwDuvjjv7na`=w=JF~`7J{~$Q4h1pL+oSOs^~~?#Jv*QvxAi_Xb~)X$W3#f=PNTr~;Jk|Xh#+#eSns*pUEB9>H)~ave=9@+ zx4oobZbdpSCEj>`@EUzkyu@CQgh?4T>!e^OgRPB)uv<8kjsvEZzfUar%yU{)`{6x_ zGpdHE_2FgnTbH2{e6diB%T5~ili8R8+~rr*CsUgn4X?9~bfM$2G{OdXNM$K&eqI!> z@LVC9J56!5HQn6p5L?lHP|Iph63o^oM|lMB*_D-R?F%FcR{J*Tj^;Vi*pNO4JkA<^ zl3W@1_~D%+dqW>?3!2gxb|9_~k5`H&dH$Ji}e^8;#c#OY%R40nm zgzYFIg}(nfp+8yTdC*^7Ar;8~xU}Pcy&su8AKsqpTiY807zbD2ozf3XK;w2yHrMtR zu8HYJ%9O`i8?@K^+k;s%u1k=rDAD=tl)mn>6bWGEhnZ*KSmRyTc~2RHBLaUB6vvJ!kpE0Vi4O z>LxuAvCpSXdo4?BEy%CS86BJ!_(*f>;q_wWRK)XA77tS11NyrOpj2hYVRMcZ5U^iq zZ8LcK*ShwR^{?VFF9W33n%7@gxROypBDAUM707HrjOz*Uu9l9DE#VSE7xLq z``$O;8J>7&#;-;v4Vsz!H`LN4g)zM4H;AJb)zaPTfw<1L5HkDFLBjL1)P(2n#~WTc zJ87yYNWtq;n<@UWO?^;zq*4GRaHfg`!p>ikOJ%oAQ~(l6Wgr}**FWAg=XZTszzp^Z zya*-kNV2=ICp3@`Y{p!crMhp|H((D~+{u%<{3w{r1oW$v#N}ez+@$EaHe*jIign%W z2})g7Td{2L#K+c9-jjKjc80Ay*l7@4KiWRKCIKoEk8xy;zC032$=<46P5bm!+Zz|_ z`k>%%jM}gF2T(kD(?l+uN~At&xB{Xg?sBq387%eIl4ub$?X2uarDfv%s=!+QjAA6D z(O2B*0M~%Su&j~+Au2R@ZRR}XsK`)?9)99uNslwq#0-A zg6l5ZX&%J8!uf6K{GS`P{l*}k8h*kw1de{bI-dZ%))E;Cp|uP-*&g-WXRH?G@zO}D zXrSLtzgfT`k)ETVkyK(L=nQ>rCgX1bGf^v1`BL?b!s-;IT9m1r)^TUU%$Y=yP zu0Y6ZhO?k4|HJLeWs1HOf%T$APx7XEwZuR9yF$L5vsJpK*zX3(6!C*AyZKJtE{*k4 z?nFy~m$yj6s|oPm%+XCDT(^D;7U=8T$`&&p)7M4~n8W}&5VnB~>i-u2veITtK(auyrn z=$+jt4^B!*@1+&^9!Tmm!qj1P5sDL!YntW&rEu1j-^>;4KI^zu9lwUA9KsGrlORzx zKOC$1h4@AI9@IQ;I`WXL9FNlj{V>pEA}M})A;*ok@a+#ak5X;UJrGnN_T3OFcrM^w z5`R#}8b_4a*x_mZyfdFVn0wZ^1l#^1%T_0_Su)Lc7JMer#!%!~0}#X$KA06pmVF_D zClngL11pPTg>$aoG=I1X%!`gKcgtaSG_Z6onSwH&v?#jjB*dSDVdlse05`R3W;Ig| zU;q^P#ZcDQj`db8iOJt7K<~cqs_&6Cr1d8Bmq(FT7;R+?#;+1;BQ-K#uc4>(chFmL z8h!S;*Nqc5=%)SqjeC>2t&b{aEI2T@0+}VL9^#)qXl$;hDs_>!+=|bzB3-hO3Z*u9 z&fWnLF*e$VDQ+dNBJh(3MGdml%%lf3PbSjvZmAH&V@m41n@afK<&bO{4_` zZ5A>-$rEG>{hR(1uJo_V*Ka6%Mu3n48LtcLJ{>}tke}Jv7^$L!uc@TrPpG5;YH}K} z8EL)$xX00;&0c(9RGB25?$rCEFrOd)4|hH#h9B}5jDJRtd=Y#3RP5zA6k`X3OrMG& zNk1qgzChmmn?+10^l$DR(BCX;t!NTG^=^K->Z#BF>o)38x0TUkAv2~y>|1Is6AfQ< z^a3T1y$HYRmtUM(Oq?CDuH~(h`-tN`?BZ2?JHydp zO1Z9UxEzB!#JDoD%+EQRB{T_29SH^&BwEvboObTKmTE57+nTX?KDa49R>YQ|{keXp z*HpKWLxjFE-|kZj$jWfcYDBR<9}pnEN0kr?&7_bx2n=QD-bmSHHcCguKHdPZ#y#cz z)TN;CzGt+>@MvBBBt(QIvK@#QRx6pJf4p&RGd;f2>`+Za#%Rpg14F`fRt5VkuMNBT z-^G*&*-b;?9i9pCa3P#@t>{t3M)T9%!f3slS8xf%r$+^aDd3{0JN$Oe3?_1aQdaEh z-X66-Z~9-Kg4M5LUnrDM3?IMSBDInj94D15qkMWP8voJnTTbX*)-G5qT-S=94MUoZ zOF5!PxbkysY=Y#f!Il;ROY8Ba+Jz6JcC7N!JM8uLm{ntA;`Pu^WJ^yD+u39k_ND{@R-{h^ zpZ&y~pL=Y_VMiDOORCV3fB%k{S`l0Fg*!c{rUehBV&FK-VX!90a^|V4rOF{M*{5hd zW>^YYy0w`u%Ht0U&rI?vkLtXb4WuHq~+ee z=N>nZ%epXO`vmJ-8L%-S4%I?xr6Lvx{H{gkO1Q|z3zNBL>s^?QHJb2ux;CbS1F@?aL&bzMjVZ;>D?>_@#~r+Q7wRmo*5stw zYR`X1#wE|f-${Ot>3=8E*qETwoL}V;k+-?>(TKVt<0NvVlfKR~S}l}5+sLDW;!{H0 z=sT|~4{2K+%Xhk;X$bI#mB5RZCG{AmW*6h{73MUW3!^irz6?wdNFc$ewrs-PYjOu) z1f`jKHQ|bAI;0v~iX#2#Z&WKoT)HM=IW+l&g{Hxm#L@)fZ-A4~#sxPs@=9wg!Kv-g zrQ$_r-bm_pjVvDbwh;&~%j0bF9g5pj6k}51%PDI#^0>}p%L%1i9NA;S%H7Y@HsoAT zu~r44h9>#_q#1LDoq_4OT1Ph;@xKdDsGkQdU3*21Q3bqMY-wOOyFb(nVsX)k=nLA^ z<@FWI0+wY=#fULl57w*-I<^px*|j(i^hBx~SwF}0=u z<4VKSt?S1|?>=3*^Ncy(EiuCvENR+xHc-^j{0@62m?jn*yw-g_lIHMpxJL5DEy=@X zEwdxqQ{qh%+~MgP(X!#FfH~iFU0TV^WB+xABvJrfVuJg`X@rj#N=t*(Y&W?>XS{ zKmuLy&I|IQc4-NB#gsqY-6N?bXGLL+xr!!=a~w#DE* zPuPm_K&!aF|1vl>KCg5AQ}?ud3b9X#sqwIV|4524%|S*&OknP0C38;-D||8{>gj1d z-0l&^r{Nox(MkJ4|C}RuFyz@~cf5oO&F#4@BCDa+)MpUYdy1 zpjm(n%+w%$>;4iT2E&@Y9Pv~7j&*P_=&b}y!TJFHMgNHqiNf8%k{h6G15XO4FwlyA zdYh}lXP{j!SgTD4LnGQ?SrA}D8ZAgf=Ja?jvC}t&Dw-njEpQ4tHDi(@Q%3cx5`4e> zXcyKxwFZjdWz(zkICXqML{=50L~( zkf+tzmMykK-uUHp=N?anyi=NL?V-qbMhzQELQm3yI(%1BM44g9f>&1lk2@YV!px;o|$=mF5QXq9JWo%>gWs#rElP^sU zrg4<0h%S*62aVaE^(QO%csuMxH2*EP#i!1|6~^YX3YWsU)7e3cps^Xa*bhhR9}&9BZ+#xYr{o`(FAa;+=Pk89G{xSj+y+ancJmPgV6ar zcyUy-QWMjKLNfv>{2{2 z=a6krIKor!IpToh*yA`!y2|hYuJBB@g?oWjb+>PRA7S<9j^4Lukf%{SNdR&`p772; zzFoC2<$ITBeNqX0FF}%>pe_ctp<|-G3|#%5lyBDcRj80_jh1|ZtxOLi?b7C%2=hIz zZrymEB9hqV#JX}0TfpMakS@}L!A!pLY?7t?9SjQU-;67$Bw2T_i{Ax^T36BSURqc> zPe}j4Hf}>*^k0s*ONZ1T_kOGwjbE*0SK5X|9sT$nT>uAQ%Si#R3wI&1%TuY}h|Lu* z1n`Ht*OuKQ%aGYMm?`OVqJtMB6ucN{t;SN6*4<-!QwMKMY6~|CG1>cm&YaP<7=8N} zJ=PO45^S#a5e@nI|HK^Pz_QymhOoTvU7rn?`eyZQrlVy!o|DOL&zkjaSBcu4!t)uP zmiM^$w9V_Jnf@faeJ`g+d~VxPSe2FG zeJpgWLBu#g(YX3V<{f|rY!^B_RcsTX?>l%jW`ac5$l}JKy=8B0@9cNa=wMVLy@c+} zSg&QIy2v~d?mVb@FYBcPjeQ4^>-q2hK|&c&=B1X@{#Lil2j=5;&8PL^{|)~()UyUF zfCF;6%+g(!LX5paW0#4LtIyST;PTH3U<;n_-rCBzSoW}=gxL+mP6-8&NvpGId{k?0vtq7FvQV|%aq~f47*};aYzVus)q=7*V3bIhbYVHyV>$N@x84V zShx!tWnA)>agv5h%yb$zH)!V%cx86Q&+@jAz_@$$kC}5 z+4W20a6drvQ$%1E9}5+G`%vrXbXxVL=mhy>yFeO)4(R~_`+&ms;ymfPL0)^Jl^fhv zs4Q=^+e%oIBD!=3f1VZIjbNjO0#+P<7%dCavts*Cbv1 zl!G?APvx|`U@8i^+9cHt+dTsrz@VKy|DhP-1#p*n0q{|ghIou@kfgA=Ho zt(z8%T3)DGiGVBapNfp74)I)% z{HF%GTgOI1mOWnDuSbY9SwBJ?3b(DRoNjoDWL)!tTX$fdhhDS&7c5peu2r!&)&HD) zKbI(HB3oT5C)vI2_!ny8Rnqmvgr3upX+uWEjTn*IAff2Y z=-}MLW%?F$b-y59- z?Z+sVByb-c9+;Zinfig{F3fD4*ylp4xbo7%#W`Ka#zKgMqA7)C@-i=m_gg=2nhtz$ zqv*1N*W;FbTBlH0%?sr01fsZ&CoHP$qr^Bkgk53t9KfWn%~U)`y7%*!Unpf9X4Kz$ z-KL!GzGJOezuIB{tWoWp=A$(~Z#|pkmhr;BQ4tIc+kL0&-8XgV$-g5xS8K?V_ZElx zL5M6orTVGEM#MtmJ8dn$J)Qaf4aYDq*1Z-tqp3>50+*V|EM`akGPcOC7n~WyADdmO zu8Zqtt1ReZcn{E8(fu7d!?OS5dH&!wL9qoxieZp)k&BVP$$y;VAEYP`J`MkiQ+%87 zInp3p=?=+!D)8vheJZk;2SF54!t%eS4>IWovK^$5>v>zu>i~NnHbGBk&UCVlxAcIT zJ3XQR`;N_%B|C@I^z^3YX^(aG0p#v@^40BkpEq^NoY9FR>wNqG1L4^Up@8Ys9QW() zqeDY-Bx`2~{2O^s#Ac`cD7X1lgfS2ZmAg;}{_A zZ4tV}iL|l=xvA7}XQNk~r5#_T_AOu=8ym@Iuvm)=v0A`sHb?0oCu2?~hR-q)O}6>}7V&bSa#ssP4rlf$b+W&bQQknOX~~?2lps8R(=VSY6X0zp`g{pv0S^RK@fS zGn6Q%R47uOHppqO5xk39#}QiP!}|NokGjv{@KFF_qQ5ORhPr)`Kl=RA>_GJWX0YJ? z?E7cLpTsEu+E2wICokz)o{V}&UvGJ95}(3cF6j4M{R}2|&Fzkk2Y;<>X1wz{nw*;o z)gQ^FitN!=K%UH}pr>B=$e&Z6KggEg|C07J9j{n`5D2FT`m~>*yd0~<+l;qa?7wW! z)m9)1JdA!gS0Qh*U38gsF`!ajH&5nX%7>JD%{=uxVq#p z`j?s~7}=cjNg$v2Wo(*#SUSLn+5gz7I)B?H{#g}^)MuV|#JHbF73HpTX@Rc`*DGqI zo-LReC)!BjkAgZ(LT34iqpE7CfQc5@$y%zhV@T(?Mmmd>k%pq_h+OknYBdY7+UIfU zv+bT5(%vN(%-)G!v2SOh2zB_KH-zaR00D5F>Z*AqLP><(eSZ{`;1Uk0I1;e+^f(Mg zJ~h-k*(9Z1tWGY(0SLVnYu*ocKR(|(NIexP46^JMmhTq-~h{H$x5^3u{dJI_PWm?4%r>e){d1M*P6g#yaHOho@6|v5Y zuITIi{f&d^EM1sP!QDYYmIT^>fs|(d3BmQTqoARVa`8hwb(#|E-d94Zq(i&T5^u@& z&#E>&@n<8H`+um?Uv^p4bC1GROE!!W5{j>TLUNEubdqE55^MsTin94KOt=&)oEdoLD(_Z^!4y~ zYwH1-P0v4CMETvRM3K6lw0}Fdn8eFtpY}MlwWo5_qOqv50a9vHQk55>Q4xb5 z&He20lk8_)YnfzHl#!j(oUyzteR|Tq(LqcMPYtmuo|V4m=A`PO=HSbHPvpp}H(ndv z93NHujhkrZQm|D&z7~F6bx6Dl_dMKZ+5ZU8b`+lFDbCQ^$R@yM7IE4S+$i@~9{PaO z;&@i_c1p*~vX=$O_&9JW?0bo-W`xfDfJhKPO*sEzt3c?3W39F|pM&+7K!Q*hn&mjq zTsI&)JkF4pDr*1W`^WlG#eCL%e;N;@Y}{iDYEl+^OdRsOi(?+&L}ECXCbs+L@uo|s z8UryN$D0wg{Lw)I{&c79V_^#{%qI9=2&V8rom-AMig46RSF~(SUCQ(*1*zH>z**_( z#QfM-0ojqCfX&=wq`TrCZd4$EJ*lzCZEn8L#=2+8b}fuf+ztZgDLL!J#Qv5r62S2dX+O{jArkJtfC7K zc(r((QmT)>N25=gBR968+9~=ZDXg_iqn0h5+MxSrbhnHgATfWx(L=r)$nPmOXBwP5 zMZU8y;-Oca`W=he>tsvRR&^?*<%E1L0f;ygN%b$;JzhnmFAd3yoyD4S1-aOHbmuJ=8G=`@49K7I@ z?ycGmhqAH=ZEjYwVhO-mu)5X={M}9%dzHRAHT1=pk`Wub^S2;j^7wp1scc+(GVJNmFm4YpvKfDc~d0;?e5uW^yD|1GIVHa zZ_i$(xl*ITS$$vd<*m(+!0vB~nhg49!iADat2QN`1Y=-N9f5TD^_5X7AXUjQNhzcO zTjT&rWP@cylu#<}+QrHgUR41h;E{8_t$v`@QuVp$Dy6AuV)u5j>p+ovKBDVrwGTUK zO!-B^De`eCr1cD--qE+e)=_dhQ!U%hzG)-mctxWx&a-QG5P0s}TBaw3MSe{&x0tfv z3GiK3yrQqR>j~V$1gZb#f|=IZB-?iUAvuPH@w`BUdwyB+*Khd5gNa^mTQ=fh5vk#@ zeKrF6b5`H=SYD@ZvER%6qJm(115nn)!&Y(CRbE+tSSr$lMXswQWXHtAUSS0Kd}a3S z6{FxNH&H}dZ+bdn>Fw+Em;V_L40PB+kEA9+jNM!Y{%kF_f+H zVXd6qVBB9dO65?dysBK>Rf2{oq6!RP_PIrj14Tr}-@r$zb}l#K{Y{6-W+-t{jRoh| z+IoIXC&;IodyKEz^BSjO)*udjh8Yi$&n$DJ@NzkB>>HjE(}-q##?jC9sw43MU_q&^ zuJcOfr02qg(cC5@``QQ$!->h2I~Wtsh$k3YE9x?>y8_jvS{Urfd$BOME}2Zh`drP) zy+_aHw0{U^uB>!cP^oV9u13dKh{vIxbT@va?P@D`s-{@;-eQ?(=Yb=NYZn^YV}_Of zHSs+8dBrGYh!h2Jwu<5~5H$rY<0S*tIxOLxC|>#}zxPYSdT&fr2I7<)dGV@1-FgjQ z!ka$Fv6b+=33;);%GSRWA$ft{6SmBT_lo2;er~^Tm<*}PCNZuom>eP4Hav63sHMKJ zo}gChgqbFfn_+R&UjrxjdB~$KJ_dyaS%aB_mK|n4hOV!GP`hQ6&q4KpKR=TAyHxyN zoqO(`i!+zZ(=y+rzmQC~X_}hMF0*%b7FJh?OC>d$yJNo8=(E z%Zcw8mgO>5xE!BOs;fQpqpY2^l+_D7svcmviPH<5DkDmDzXm373E_nt_v`B4BeA&X zP|1|#AVF}6TfxGXe9wGPQ}L%4p_y8K^Z`y5O`X8kk0ticMa4_G;(G%Y`*H-+zxCEl zTteFRQ(yiWGx#O-Sz4{mJ!50{1GjeC3i=gg#*!626<|PO(m}Wo)2^3Lc~#QVBy=E2 zc?0BGH5))=Zv1@rS~_gFkNatHXExhwi5X}r{`-78z7nF}@jEicfSe|*WHpEWgHUM| z5(X0l-KyEIxIO4LuLx1!rpAVoscXQ;_YyNAZ!X!%4_ERAuIdmLvEV<)M ze7d0hPoMBCWxzuAbJR$F4P3*j++t?;7z6Re$9y{PCH&%Eb!E!ENIzwWw-X}#SrGMv zKonq#ailOF0IjUk0EFlCU*O%D;$I_| zPEfMgfvGUA_R_MSe2u7*w$;W4%U)_SivljP!)6X z;x6kv9lP)vP{OG&xU@#1-s;~e^$Q=Phg8}t)=5esb}~P=;Vu~gO7*(iYAanYR=F$X z+pre))U%cr5^nxc-P0&gX?lLw9`N}bzB}pM8MxZ_#h)ZMMrLZHCU$M)5WrlZWkLigf5CJPT3pvN zIKrnhe#&By+fUaMu1^(bXC4>l<)31U|Ac#w8!&-huVlC~g=SX^h%Ds1Pw& zsFEV%46O8VlIAimvhSWb2XqYNc2e7pEv=WfPI}>sJter?zOD2U63PQ#&mwH@N`g}{ z&WpkKWAbwp90}(%Pr_<$95Y)Hx_Rh#3wOhz`8G|V^B4W?i>xf3?7xqu;=)qmu6NTM zbr+{#V;MqRc6U~$1FPXADa^tRe+=r%Q85s=% zCxR|fW2D4ksnoy~)=GKChSTQcch@(g?G%T@darIh{lQ=2Ln)&|J(W3uFB+GZaN!{D zKIFG|x0Pd5>|?sB{8zUj5g<$ppsD@{a>Hy}=+>*4PU95Y`62yWR{j1@)+laF`{U{WR-U=DS59uN(owGIQsEalxYM_0_zighdM8Ew(RwzK=qBR1^mG@a%?jU3%9wy=d&obpe&CPEK|O(gT^zc~a-U)@?o z!46?|zccR$0fF~D4}>a6$d~c_FF_6RPd&*?dN3M;EVAT>%T8XD~2@aHu)Ua$4VAh{{wwYIvJmb*>p0E4XJ zcB7!A2b(hF^};WQrwG6ad#-TEY22pcXxMpm!-JdqQ%{zP~4s3?(SCL@c#btIhW_+ z%+2hR$>go0|?l@t?J z@mM}x>9)o)BpSS0xfV2WwpFgOBM?(QE--PVD@q~PsxeTkDGov$H-N{89;OWPCad2^ zpyb}iuhU`1U+p-VJn%xXD7Cej`aK-p7Z|?;h;WMH<_jA7uF0Zb7C|B`M1$TlI3)oR|;cecLT56oFr9;+o~u zw>NEp|KJkYuU44lY?Of1Hgxpzeh|>v{X?bb8nr41%8#Hw9)}suQT|MYps)TX{GaF| zHK6~f=~6`nPxeIr(NcZE>yN4}=y6C;PyuWginfP;As=J|@Wnt&twiUo8oY#-DNnkV zG0Sp!GJ=Z>16+DG?Xs*v(X>i;6X(dAcPLQn<`el)DnYyND<*6?}ANSki zT0AzIClq2(#r?MQhDI`JU^(}1!<_iUDRv06<)skOGyj_pFvCgQ)^ai!h!`mJ4IQ46 z$mi*rN*TM)*=Pq7=>JCZ4~Pi$?JRL{=V|5O{az8))NIb`O!Q7uXET=72yNZ(?40TA z#Ouh`m2o;U6nSC-rZFov<3#^b&|DuLYH-}k0)#P80S_c6@>trgDpCS<#dXH$$iAE! zEnZRt@8L(Kgh}q`bgmC$9-tubI2c+BuqSfZL9&z0&;k$4v;yN#Pj-}}I`Q)MA0t7` z_3MxKJs5Ehw=VwCqS`F?U&ZlsNc7R$g@_0r`3%JoIGP3=4hIcZ3~M0G#RJX`hY)ml zSeQVe-XDh3Th;pA2~q^K5LH~O-U~=!WWxQh0c-CLBu3OWZg_xbRd9$`eUFEOul5f0l-*5i^@z5?pZ1BQG)EEJJDp~370 zgv8;aX;)SATA?@_4fFKu+Kjo3FCdDZZSI=)rhXSyvCq@K?i&__af?Uep`$(Y}5eHOJyxPz{Rjc@B9n#8vJFqZGxec`y%ZP!29sQ*$|Bze)igz{} zIU;!}Wz0ux%DE902N+C?_#X0z!3ms z>7{Fy22LAJbK|lq3Y8lklCVyjHr_ZEMTEj6G5x}K0ZVZVx0Lo+#2IxQZbGUc3_%y%C`WewvC;%_sw){#q63?du5=OXl1_7{=llhpw(9 z7?`H|cFVD8XKLd3ldH32c^#7fb3xQVlBx6%&cbNsQBYZrS-k+stZlis@mP#&T&U71$fIMVZ(_=xCTM^W|kzgV2e8vviMtw1GJ?&up z#SnUP;?9{d=1$q;b}qem+GR0X<*b~4d5OW3C_y~Cvw=ow>4UM?4Ju0L5H5*r|!Z0Cfn&O;k7^~{fhk{ zcdT{I^A%r^TPgfPb6tIdY$givyL5?w<;O@a|;ISh-k{zSf;VuQ$m) zH#b(Y9{LumXI~Fcdx~bCo?dkZ!CSqewW=V@7kMQ4^+~sHpd>l?P>I|N^@kS)LCwEJ zg2}H`CyAvbRWz&XA7}}`(LV5OQOjMS(dkT1Abs-wlwL=6Ql24$5=lK0$aG6&w2vnf8yqX z;VuG!8n%-VEJZWQ)L3udIob`vZ7P!?PWxR~sg*=Q3PpOh=hFbyY1&-8)Z*8}5qNng z@3Cp!gqY`Dr^QNygWs*TI#zPTb#PaTzqD`GblMmcN*hE;L;rK9u$XE-xktXF=l)2f zMF#r`eq3(NF5J1}eJ#VDs=i)>7N${?7=mfu^a<%u!p3e~7N??IkxAH5%;m(LU&+67 zfmv+)o;tqciVE}iVvi=25x>qG;ULCG%whJj`#J0Pi9}8b7!+tVmcJLI#XxLoUH)=7 zv8}C)jn!L0!&e3H+ouuBfq(ItcC}s3)XeUD>#l)B`(PWwN4K9odxMOi;>LRnpeP?E z3X^H03eR^ydm|u$9v|$8>LZ=k`K38G9;2hpG35X2KE6Sq7cg``X_Bw&tc%@9+fdyiZP5){?kF6lcZCL@g8szwyoYutQKu zklPTK@`+_4r-jQ!!x!bQCU2QbHfk*q#guv>w9!@ zNcUS+s;re)c4oA=+~*mwZ1`U}nz!q$SJ+i|=XP^sMLGC4yhfDu%g1^(*3DZ~u?Ucy zys)6~PfT@P^^8u&W=j>VuD-VUTyFSIPVM$uHP$w>uN^VIK30lUHnQ=jSfKF&wO6aZ z+6{tl5AS@+8sBdgFZ|bK;A+>W+e4K)mmaTtozKqdpOO{8Z(n038U0smlNhx3jQpOT zevy2JlB1Cjm0YfTff!!Bt+sw!s>Rq5w!8Fc9jW;Y}PvHAj>EibT?B z?dy>y7h(p#!Q*83&9$&2QTU~BCgMP*xDFDWKIfQP^z2NLt}GNYi(+dN74cae}Y&F}9MNo#FR=3Vi;{b`XC2IzfgK(wlZEbqCUL>8y% z|4<-Qz=u>n#_b_W&VeKPe;*yFJaW1x1f=-ewBY+Ek^iH-dN+uZMhqx%0lN-6bx_&s zH2oPHyS++!Y^Dk=t;$JeOJ?g!?V~!|C{5wHS?ev_Jl0Ul9i=E}8aV%Q)OMKWOzY?{ zlFr-Pjh`gBx*La^bH`Ft(%O7%Ed*YO-{A1sF6L>!DRCzQb zv|%OGYG0dEb2=*!H1IBbyC_*GKD9ai)Nrg8G%WMa+TbO2v>8_}64WliC5>O;eb*T; z{Wxf%8VAQv^*i>5vuU#=yUONW@T6*Yo%sg@u9NkU{zgf`G zt!O8xF^`nl2&HkqOpGyBQ8DvRaEqRGf*_lu8)w?U{y!UZ8kfV2hYxv~J`FkK^4o3Dg%kSZi#G>1?2rRO9W42p+dodYZ{#A54=Rf3p zsVYgeNGhLZeBG(q%XgGKL9=zz!e3(Jyl(%MCb8JdcrpIER)?i)27fIowbD;02>+5G zf(|Q;4ohO|kz|Gcv*zh6_Rxrl3$-7+z`Ue=s`iGOTssWwZ{KxNbUCVSZ<~ahKJs-6 zNRsW`euu6}G#)LY>3gqAuG{OD)xK-rhL*rSjHAu=L0~sP3XyF?Gj4m8BR%S_I!hu+ zAnE&9ZwI&-W9_Td5X^-q~_uD)C|fF^r-shcWhPeN^>3Ntfh6jZy|Pd z9$(>y`0zUw<>OSs=pGGQ({JMaJThyRFRshY3heY{-uK1_vSM1?r-OE!$b-u~>uMXM zuY4EfX#9QPzQ0*EAg+`p{+FQMox_6Ls=V~wvtW)xw^;~C-IH9qFMd!{EI-hErMT+N zWW&Zg0r3ydo9~1iKffsT==c*W)}oIAv*hSLdHpy!Rj>vbi-GANW^nOgSUN8eb(nN< zNH*Nn<`x;=C%NFf%*|j}qTnWg)J65#!+zU+_r%icdGpJ9|KCL%(v6?+?*~QRQu#8q znt=*^ClGaY5xR2ZgNNzq;;UHBdO*|;EVasC4Ku$01qYy(d_phk@D$J94;)*>2~am) z(#ados|r2eFmiRsENN0|zwX*s<}!E946AT;?+*+4NGU zjdwX7Y$U52+RWJZFzGBm4gw#%WE3IkFHN;;t1k6Rs8Q55 zVxTMhZ>h>ZDC$%Q4DBV{)=9AO-t-RS>HKwyrF?wma<72ZdGblnn~@ovm!1%#6IEV= z1%su=k?G+o+Ptr6lIC+IVxLO;4n>O&bA;J=?}lVb6t*D6oj*C?(tkWf-L9qyHIclL ztY;CQU?;TXy@ah}nEG}vwSOR-KTsbCM^YHHlx7@?n%&YTD@jY&E1EjvC#2vpTp*hI z4a~(t9EJB9*cCj`3($#VC zcyxLGI=!3~r(IvmIg zUx9*J~_(3J0oJnLn#jpCD6u?8Kd7Ns9tJwuF`GywLZ}}_{<+~Vr2{y{#a^3My?~bN&6T}o_Y3sN*8F0EQN4+~xl6r#FO-J+ zSwz-!b-0IP+!tAJ)t8OyHNQh|O{?0Ep(~f5drFyf<77JJa=yfxzcn&{*-aD-_ho=u zFr`~XP3^ihJ^aZ1#k--)_;v=e7u!GA>hKMl*Ofn;DRn_jurk@{=4kpYf`Jg~vU^JZ z^Gaf8Eo3~7Tkc06WZXxM16%g~PVCcN`Z!WZyVuJLKgIi9b5W!R=nwDr^!lIYfnpU) z#D8i{7TFd#Kf)07N92b2|1?iQ7Cs`E0OJ2b8vl15fp$5(#E6E1I#m1sOnIRH(1;iI z$MXL?|JJ{=piX>J5BKJHzt77ye7Ne1PObK@v2PNP3^gQr2bzQI(0482Y31?d_R5Hy zH8f*Y*rQC0>E-mWzZW+~EqMJLONM0CHa6jKW!@EFCMK zebv)>aNWpqzH+%*L7_=yvGaS>q1LCZEL*#AhMi3UF^UMgSoaW1EeZ>CR2W1$afH*S&&>3Zf9g3WS(x$-(RwCLiJCkXEVdCX9+;iz;|5Q?qRf*aDknp^gfF-RkGzy)jaT#@eXaE@IR`UZ$<%WQp zior9gYS>uVbB7T19i-j7Pt`mw6rPOVuZmlX96ccD zXT}d$ArEs3ew%-z^}z;4{jXnz0xmPYlaB92$KEMM&r|;#Vem}~OR|9`-Fsx#rvz@z zs8DmQ{}|f{m(SMlUEsvb6tLcSZ8-Vz=LR}FXf{g{(7|nI)R>!U?Hr?vz>95TpOgCQ zc~ZXLext3G2bXAugZdw~*2ZqdMqjp0Iw(7L20hQ?CEwvfZbY`^{ub|Pg_NES&lB%Uk*TQivqiYQ?`0t_P;2m2(YRUlvL`s7m280JPR&W7{0#m-r7h6| zpF1zFq|e_KKT_jOY+C?qao zKuexc&*5d>aI&WQLQuir;K!^8Y-Ze&mc#{peVk1jL&OPOsAKwX0WcWRpV{OXSwv!B zy3tyUP$$qdhQ*0QA`E@}*nnMF$% zM3L^D3;f22piNXN{2>CDPyGjC@BTiGCKe&gF-(dm@y-Znb8z#Y)?Z6p2s%V3>4zfm z-|Q8b3>rLgnw(g(ri*}z-_2P;+0UGqj+ET!%{HdCm7T1yUhS;eZAktx^o|cG+ttZk zgoq1qyYT9B|r4R{b7=Np(I|v^o1$_~r43hR-a*wmT z5PBafE#F9>(E_JgqEP%vv1#j?x-6}-HM*`4#$zwK&Cm-!*!tIJq5u9wPF6?=A6fmY z4bZ{L%YN#Dmi_cAdv`j|2hiHLEXcn{;3&t;W1~<1CrwsaA_lMX6Uk>f?wEZyU^V-n z?Igr+d-X-GM}z)s^RIb=Y`MRh6;~w>zC=;+WvRW6d5o1<`{XDqQ3S-pZpf=vib}TW z*`A38?={vQ>%gWmmY$AtG-H&Xd?#r=u(Wdo4u}l@QG!WOM;W~diyfEeghSEnP_p#1 z`=vd{-8q+>1OLY|TG`L(T*z)szBnb7K-n;4x~%#oJby2_s{!-NrL8#wXPXPk`-HvD zOon}5#mnZxQQXZ_sP`x0TlDm9L2$vRE5VTmj?SA*guIZ-Z*-p_{?sJ4lc9#iHxxS? zXK_Ko07TWj4LvsN0!^qo3TYdTPxA@_3UH}xM z)4AoYCF{LmA}BVyUK;h{i$&Vu+5|K=H*Gh`rY%0C8Kw!x@`3J*p)q&!D(q`Rbnx~4 ziqC{GB2hX~|IfG-f$0D^EX!vRcRf=7q3?(Io$Gq-^0@?Zas~n}GiXUmoQIuC8OYFm z4xXlqSu7DC#XP{mMW-VViawkBY3HtE(@|@Hu+oQOY$HKz->`|j(rp!tzKQ%h>YiJQ zEC*_5D!nj@W+VO{d9H{26Neto=JXq?`b9te4?rO;yiX}D=$m0Xra{r6u32pJJs=>J zrP{Z0VW9fa;T<&I)ra?EAG??fD3ws~Egs}QkA2$9`xX)-c2l3dbX1W%Oc^CjauR)` zD5IWrt8eX|`3|67#XCUL27@V-6Qzp+>K_TP*vmLyU|L zaZ1$0E(es3%LaMqgW#bbuMDoHN~6P7@>d^R7bNa3foyibeC{uryTbFRKY+>3RzBN> z%cK{jZS5vZ0UBDfn|;mwN|rGm;@iHd#rEpr&@I`En#i;#MCGI7cq_FiTw^(W2cM0y-;3fQ*Ev|SC*=LZnF=WBXhmx z!WzzY@l%kM46nLs;t?76S_QAZ#;!_oP(i*E!bnu5xR#LLV&E$8axt3);L^NooAwDq zGhhX`?euQi_sjq7E!**!rn8-$hkxEK6eK1^7!H)V4$D|--$?Mk z+J-vwBkMlMXWV^r>a*84pwt@I_1QS29M6g=M96XM+;(>BM3_}Zb=~W%=P<_pv8ho9 zYqVgAbWKSA&`Y+jw7VvDJckQ|a8Ge=B)wC_MfNwEZ?nhEx7;no|8)g< z`OltGs9cX8=nE&!V=BAE*u;RvhZBM`zl#>K(q?%n-De(q08vhi@=jy*T2f!fgP{6u zOEAJLch-&kkxg)&!CIdKBG%uXQ}p79=y`X4O(Fb_idhKMV79#yBK-vC`3fo2@MlaU zegZ3@a`BPjwYAxeBG=r-`07Bk23x<>`v&sQ@7;g@eUvrFkm8X9Y1@!}qUjO^!c z;ZOyP@e$I>Jgb5i)#8^9@dOfk!cK%8#v|2|{z?F{`B3{b`f+_woqLb#TZ!x8vAAb& zr8Hdb2fu!1$1yD+clHI%{+r@8ntqmD4pd-RBhF}C>g_<9B?DwEA3Ef!DtF5{5!VZj zL|{Oeu4&=6x9Rmwzp6&eNd53lP`IzROGW*Jo|rW|zd~N>tKf;-iY9gPC0U$za57wx zdT$ns1a58^l$z*Hr0_j<0AVZBL&fL2hkrxzD(M;kY-iLI0dhu)rS;%QAkPo@5I89( z*eIX`V3(_}qqnCxepHK~9fDFz@?=B{bg@DC89@Gs^L8DH(Gzp$KPY~gJpI!*BS>b}6Df4a_iGxZ+qdb})! zRW?pNAnWamy$!{bz;=`KtX46LglC3>Y`bzT0+emNdf(}`p0rhs;1y-~(N*U8_uI;o z08ngQBR<}Kz!1<<8JF++IsLp849!*EILBPVA489%k6^96H60OFk&>?gEF_9|=?|~~ zqp&scmEDUM+tDBiA*<7eR2Pru*$;710iVPP245Qa3M{ zGgj{ha9`6(Co8-%Dwj}4m-M9o7ryQVGF={0n@R7=9gtT2S-R}pxHx*gI~j{kn||3= z90|1C_l~wcxT_OTJGjkqx0sqYx3)N0LI)S5W!AjAT+9`&oL$S@yEmRCY@0ONM2{}M zBpa<%)nkOW9pHwK(Ovpq(xPW(*e$Ki%B8xyjTpXaOi(WZ^Q)fX%!ivd8};9=^Fjd< z8$$XpRC8(F#2S}R`+)VL(6YdZD4C!NWXhL`)jJPg0aNnjz>n7fQT9YgdJm6j-oKUH zuI*UC37xZ zGJPNi*FOsjzkC4|hD{L@y9^LFo3@NbhSnWVb!Z!Lk2i*qVi*Yj6?FqQ3YwXO4l8^o zvjrz?Py(}O{@Vmxd@C3sn^jGtP)tS$&ju4f(v{q00CgbVKT7#-dhw=}fw%(|*PVMs zl)IQWH{HMD~J~ z-aR6vmkLqi@;BSF0u~?Uri0TZ-jRP-;ytql7ImAl$jxO4nx|qPEjqt8R@vWzb7VYZolKN%ni-+HTFphA{?fLA;s8K;4- z34U30);EJPkSl-@kO~Sw`{axRm6n(X5(cxILqm;8dSTzvlEUpXLb1{agM&y+CtyC7 zZ)pz|SHipnmb~X~T{P4foxEJk+)Ryk$_q$JORN-$mnT)#Jdc09pREn+ZX2-r)RxRP zH=p%3$c0dl`LF4>mbI-!W$}BjGEjbGv`~lB+v6rp%}oQ_(f9lN%`hJuD6u^1-n$|* zQJ&2Y>nXoA=&f)!rZgOce-dd!MH%ydv;k+&NWrD6k&@)-Xf$nf>&kbg<#u?@s2ANM zx?hO(>P3aQJ-Ed&u0+naueH-ykz`3zmOK+62G_mbOVjW!+v4ceyg%EJ^SVi-L@D~e zyU@Q}&EnGd%|6gz93|kAP$zIKGTybcv0?L>c{Vjir)WnX-Cfm4{wEKwhQr*W;2sS? z*H?(GE!O9`0=`1MykY%oITvyLgDe@hs_v`7JBr}R+4QV>Ok0c@D$&$|5()CyBB}Gs zJ({wL8#~+Q=~s)>E9Di(EN(P%?PN{CnnM${Fc_y7m%yH)Ec$+R_cpih3p$=D6BTh9 zb(^V>gYJ6!DqUF}>yPAK6AKP!Q325@{&PgEIIV(#B@(S-kYV6vS%62G6pV(9Qw)qa zbM2NIwk$8KD6AqDd}@e8x{z5t!b`;s*F+BTu7iP`^hRU(q*ga>;ya+;e6oGRD~A{f zw`NV+@UJn8m%-q!cygL_;`}xA7MK{Y#@Bdj#Xq3`d#e!<0$&CF}IPGERE221Qa_(Y->nfup z2FtOOQ&&~%h8^c>X9{k6wRNQWSY|{$S5D-bzHmOtC1%4Tr~Jf7owoVa`lPF*2#LEf z@@H<|5Sxd@qY;?~s{qjGWS7z3_v8t0<%oYXNnSO%rMv%@_uUfpR#bf6ULr+-1SKjP zK97Bu!A)(ONq@Ukt@zq(iA3a_wFk4Wk*DqInHRn{WxP#af!E+<)8F*LCxH3~*ob z-h`CQvFJeeRx-D92U&!B&&SK^*01h5J-JO!G`$3mKC|i%_~@-4t@rj_=wgm|G}nqb zwxD^nd?lDba+%!{48`=7(TA^ubJxs7Jwli!9k$jpGU z!?d42Z=bbe(%mwCJ??>WA|S}ElzgUja{D>}8YZtN?N}3K(ES|q7n+KK>&l3Q19)d+ zmh!_*iBmdphLm^rd$##sz$?2Gev;+GhLOLyWfG(A$!`yZLB-fr!D|*{Z+eZ6VR8p!< zjF7Y3RV2*kjCLPE!v9>-`s4llY>aXHQFr?o0dJ+S=93wvcBrUT9p25aw`Diacig~@ zZfh&Ohg(J&*{7SE7c=uG=L5e&(*)r0cN}wpEYY_stIrJ5c6Ha@%I=dor~8z?b4Bl3 zKKD8=Fum1Pio}4$D~t;iOCu4aV$L_^v^!<={Tw|5OFUJs<3Wyb%ib+TdG+*|VOw_$ zuMp0vb6Kb*J4#~WAbpRfxPK{5inP&u>JAR2TD9F<3Cw8JL13IToU8Mg{=aX*f!7vO z`0(SiR4VKuKU4f0-vX!SxVKixhrAe=w;x_;6RX{^{2G{p3_r?I|D!5W0VxRyq`T?l zWPdJB85wzdWl~qZ+7;qLARq7WR~*y`pwx%yxlFq#rZ|m81z|OFz_P8q6@CB7baEyA zY@x9;VIfEksLBTtsPab*h2M8f1x=SAF|eqs=yCCQoZ7u6d==>*cf=-YT>^x;jU8+B z@U(xAKQr`w$8kEGiNn`B-w_WPn6o+5OzsDB_ttxBPVC5&krqAStlv$v?*l`g=B~zT z-z;PlGlM}qEnT}=bzKciQjfG}%Q?S6-M&pyEpPOP|M}970i^Fa7^Xjlh>9|}z2K8M3_SHw_BS;x6Cm*YRsB}m2ui* z*~^EbvtMNxQl3Hn)zF_p5Mu3F)93vVKKj%C8;_;1PHJRvr~ljeC6SaBF4|~z<6?#Q z7AIIdD{}?N9YT8HlC)lM5EwtY%g*Px8}R(~c3Cqz%9A*3JX%;}E;o+kIJV;G5KwNx z{&74>pw6$`s2rKLc$IPjZB<<=uZKmcr@5r2Gu`#q|E@!-s_Jxn+aF$0xAk ztd;AU^#1&;#F94Pqv$v5Vtv5u0*k0Ik42!$8+BJNTFL2@}C%@wCx0K@MOmoN7c<1!G zu)OjQKyRsgGub3wWRJ7l|BGzo}=*XZ_t}OTybr@vjBWdFzRPOt$B6J9$;h6;=)21hHP#S!?u?ZhUKY<-R|OQNi~& z+^+`z<{!|?b=}Ux(f-?b;WSFE{Nc%G@l&9xFk^(O;^W3$^e(>7t>w#AOEosZ4sYlE z+vq?@&&|n)`FkcYf1(lnN&7`dHaw_DVdbX-EL6Y@i953py!5%&oD zA4d!-6jYKW30KCy03i~p4=YV`9D;lUv-3egDKLA^amxP3wEGbeqY^-}vy|WdIWMmb*aPn^F-%FkJOWXsrR+^XN|O>%j0Y#)9`#?PHkBx!^8d#MAwh&p|-(l zaJ&8PXIo4{*hHj0$i&UdY12fbK3VBuznOz+xokLU_bglApVffYn~%G~+xXXDZhO%( zAGUOs{aw_aOYBSA%lT0M%YO7Yc(`6KdOYU5e6}Q`{z1{6R2n%BP-8yHa7=wF_7!%+}V4kk3Xq zSy(wpJy19IolcIq#UHKFY=vP0neW@_NUturl6eiQs_GtjHY_ay{kA3FYm4H$Gx5}U zJyjxg^zseG}8_hc1cgOk<>ADAAWbEArWZ?GA11dqp$Bs zS?<@)Ajq&ZD@afMQI9RHERE&MiJ*v01l@zA0(|Qp^v2Hmg14V%e{oZlQoEp8#p``P zwqE}L%-Y~Hk9WSNRpPtXb>zn5eXyuV_tDSw*z!yg!&Mz|R#Ya(qj1E6-w zSq=d04E;Kjb&Ct|zv91nfPm*^9AY&8=>vf`Z$Yj_EZof0Nr6Jg>3+s@g+HlzkQtOT z?m#C>C`f_ruQz|trwGe!M9imTMhX+Yr~53Kq}gLAk*$l!UTUe|vTzVxpYR52RM^Sd z9jOl%o>j7#FUB4Z(!G7>Ri#Xyg(wTyCd9c*{b}RmCaMCb?u_?*S!0>0!82;Tyk5Fk z_;r#g+Dl$rMXTx4d>p86aCtEbfs(k0EFoa;US2+HaAX;NwR^b}b~mrgJr2^rJ+nZj zW?_$qiz9RTC-x&hyq;Hv{=<(iBFHgz`mZ*3O3pf~R9l{pA*`jpqShPOhYFJI)+&j_U*A$EHN z^2q*s9$AqS6|JJZ&N}YIq~e(Oq^o}bk|s_gdO8J_Nd|Td<`FHFK`{w}Stp={M?r#6 zz!&C7c6kO5oJ}NkQ(-hly{0dtvj?@ta>x49l6GIB3ywjD_w{&ic1o9Dw1yl-P$Fh? zhVO4Pt)i>_zyljSKW?I%DBNni+E!5ZEyPx6uM#qe$6a8l0Sy%aF^y-#AxUz0$!CCX zG*dJQJ7Yaxh}R7#P0`c?D3RLZWz6fAwjOuec42hz2!8)2qL>vG4Hm-pPE}+v_+(wc zoZz8&92{;d6xTSiEa8Uz4s(4->JLxG8pVa1y*qQCQYRgUNiFSCnSWwe6+p+B7>BkY zx%CDBuq~xt0Lmt}9Ci09EQHr5s-NqBOf}Yt2@bV;2T; z7$+&YSxWB&QIXre?l~BKYx!QKRCUN=q|ZrGq(jVVFCgm{loKDLC8-EVr|ct17u)Az zI$9Qk$2i#Q0w^@nEQv7LDj&-7=TZ&F%RJF=$JuMdWD*j%_~yMm6`)XLv3#j_x~=>v zc!$#|5q*C4zLZUBvzUGo?=y3VEKzRSb$483)*t3Z*hplx+e|SGp9RwW3(xnS($>|VAKxhvZaMr5GdWkmW^Z7bP0?3cdCKQ|U`)jd28yiZ5+B(tdRxTzY;EY7YIIist_5x9-n+bj{Z<8~^=+hsdW!<~ z8ND@__b~4hox@4ZVBJHZ5T58t=o!jIlGWM5+fT!*^uLow#Z7zDB>Z0)w;PlnPqAp+&%Gq#sv)z_J)5ty@h{>@4OEmy&?r` zjta-U9y#i#`fT|j+o$tm(Hz+Wiy{Vw-(T{ha_G8AD}mjZ0pEZ>T{+a&c3=Tutqm>{ zbCbW?Haz3RV9b=jl?+GR*s~JeBFH|AE71!ed%d&r2opEX01$;Q<BxcyN=F2$Yi zO8TrV<-8q?-dEY&7dFGUICgQdj8talwQ)QKx!g3scX1kKQU0~=tL+oC(*e>S6J0Wa zCB#VhIvPzB@`IFoa7-AE*k$PV=sE-@3xxI+tc?FnQ+R3Z7_#(2G?>=DAEd`)vd{o; zyXWXSRdYu<9BtQ44DOxZ^rD z$hNld&zQ7Sztm`@XcznMhU|(-dj49fMo&z!fnu7sQx_`4=tp^pQ{mja1U~2JVj&hhEAbJVVJ&$6%WesG#3G{iX&L{kyUH#EeOQrKsEb!2_dr2m zzBgT1wIkAdpU2Y8&UZsg$)f?6y}gO0`n{{3R|!$czFTlP^_m-8cY^JYf~ydDOXf62 zWHKdOcT{#NK>yp*r6|rel?FJU&*EeVkSg9oPE)m&+{9~TH+g&(+tekxyFT>vxb5G( zYz)E#AsoVNRrf>GF-cS-1p|Y%>Hgrwq!f2-J77BPWWswU^)vXGE+-_{XsjkywYWaL zsQ4C8h{3dsrHomOd7qSP)3)&`jLgvB zb)QCKFJFd<{vzwiE{hmJM*dPO*MB+j(kAene(Q;Jm9C(YmDP2;W4zj{QV5W{+|uRl zcZi*BMnwi1vAR*K9tqVRn244{!a!H6zx^V2~GB9Bf& zSaLWTzZn`i8Mcq$Q=B?UdAQa4s8ueOrj+d21MP@VaCwg6htk&$)x61_Mi9fRuixum zy)^JQtp|7s@WAA|EvWNNrwJ^WIzZcuDpax^&$+uLVSeY;RsK(kKz{Htn~EA#PQI$? z`FQab(Vt9e5u(XR3IihvF|SS^Yx_<59Y#PD5_VVow;so_Z~HNeD?Ocx>sc|Nm>IgR z{^S%>Bu0Qdk5H5eK0RA%XeoP9-S6$3)iGu#|!xvHKVq9}?2PmSTS#UgwLEaJcj= z(L9unRs%kjJt{()fS9ZYi;u4QcFnGY%*Zx!t_(6SQCY=G-g1qr z)|F#P$XvU&umZb1D4RCCM~ch6_6iO{YS@1-#z=Frl6;n1@B?~iXFVD?+8;`M zpB_vzF>Ik>rfKVk{TVAL^RyZu<#3fUg+4^UM+>~e875@o{fbG+BZm(-f}?Ks6Yl;o z3qwTuy;;86KSiR^oq}*54VdzoLhKox*9;YSNb6`v{N_nQC6!`M(yu--Y{a`v2A2ti zl#H?7ugC)S+Ej#3*fzLT-=01!H3@f@NanqgW&CiWI%$iQON3o?P=ol{1dtB)sJ$`B zDPiaGYirK0tPo>j9%`cse5R2JI;&jve_U?3gAml%Y>nGIcOy67$rwL;OXl7BmJhen z(Ie|}NYDiKm+so>g{VJstxzn=v!!~LflLV)uCGhweQu&Wi^(G8Bz|=Lb87YL3dunfV9FBUY4rlu6^+&5&if|4o{MZv*WYOq?e;g=sSf7mj(eb_DK7)i`M(6v|uXKNk~jkKJm>B}zPxQ?NWyVh8b{{@qOQ(H?#?%|MfQK$K4+D7J&%fp$=TFzD zOy0@GEYCSrvNpr#7m?0@lUF~1WcTuS`FhFjPSbVuu2#~6aG_n)4Gc5Avnez*j zW=Kjhg6*p0-kMKsjj@dFMp&(qa|4^b5|N*$0LtB2#29(mD}QUhUse6v89J zXEK(Wt-2fRx9J?SJ+2 zRjHLRUgoZ@wp8CG?u`1-JHqI1+wo0E;1%ug!$`o}P*ZRCj^dcY(q^5fxi{fjbLl#X z|Ic?uM+&D8H1-qZcz2pZ7Q|qN+Ep!WR9l}jC}(hA@A;ZPZ=@E>og#gdV`raVJ~L7!ULq0%JIEW{#l5!xu@`(J zwN|@1A$lQf_?KCy`dVc==u-onPB`>uq@1BiXip19<|7aH&jDSr0r}*%N>B@pCGayQNh#thc7Y=oA!Q2U!s}N#Ujh!OgCDpWpe4I&O>i#Iqr_?Jb!IV z$|xnU*W{vzhnPx%`X5>|z3W7AE?bC;t0HgVbuNqym5v9~h-C*G|#0gqoh z6;`Tq6h!}$A=B%`*;7gh{h&+?)TR;%(jY_Tu8=T&R6OOtLNWe{^mJkid>d2nCs^ng zWRFak52mO86Q4VY-7gpsI6iyS+1ALMUl|=O$V3^1FZOa^1Vi`f$A``w-_jgcwF3h!yI&9Qr52!7m)I20rQ5o+i9$U|Fz{u-vJv$?1)O2G3fN94zJRbeI%(? zwE7WQ+}I+144~&@L|bV0X;f6Ul^Rr1xBpGqgg0)Lu$v$jr}~RKT+3Q_6iaxsht^&G zC-@7=eRg^edUyXH_v$!HMDd@|REEU?hD(onDx8?YQ z?^yieZ)R+Tl2lCl|HIi^N5#>6-NFP21PckSgS)#!LV&^DHMm1?mxKU8C%8jkaJRu- zgS)%CyWin?e)qliezxBA%^$N?uj=lSuCCtaoPBm#rRU2waGkNKDZ6s)Eln=o7Dc=) zm*ECP)fzZSQ^%+y_jQM)oUeWM#EK|BGW*luZ0u~tRuiuo!89axa%ojp@rLP-%m!ha zfjb7kWGL(68U?#7@QHz@(g2z}?B?DS-S>F%d5&CaisEhG;k!Dk=PmQAuVEa!7Y$yp^qhJdYTMnex=(7Z9+7#Od{ZYnEFUBYS zO^r|9KXLlqu^`Xb_i#eeP>*fh#whp?@XqC*{57He&>U#sXwhL;X%w9HrKXW<9NynPMb4`T=+zb5s)RtI%Xzt-??ozSvRP_6+qiR1r9DOeXP@M>W3 zMBQm{fmT@kn_2C|-gWpYuY>|+^<~83nAkv1gq59pmelwc8~66d@V41Ez0_ZmsgO~~ zKXnXn^$5bkSK4BTVu*hFj`jDPv+I2#P*kciMb)Ztq0|*zqDc3o80Wj+%&j{oud5`k zN*{h18P=oN|%VV!di4xr1x;R7RfAFpMRZ#!rH$^^1y5tritJe1#y~o z&WGmc?zlP(BM-;`^VC6-zkrXktn*qU?)TK?^9e@E-k;KhBDH0{88 zR7+Xwr725VYU*{C1L75(#TRaar*_pTz?Ujh+FtV#r*l%miy;=<8SnE?fWP)wt`WUa zw5w^>RufHQcXCQ6`VJdRNP(h0oZ99vmY=W1KJVC7geBFfD5?Xzk&`xu?G_+)FQ#@c z%H{w*WD-+dtk(yzn-LHjM&J?=c{aSuPJrZJFcfw$?2Pqmqll)~6BWzfx$fPHWK4< zBBqcTZaWY;{JuKE6IWxU=^QOJj*PU@Is6)1N;G~(Gb!=YA7{!Apy7Tmz@Ro>%n(*+ zCi;W3QO9g8-yJR)bn+oMMSEkNn_!DkIrUfgG?1qFzx$oH8D~G_%0P%hT;HtE z7YGx}aL~fhSbL>S#-a4}IXEW^w&M@zIo6(zl$KEur$YX2O4qj1pGl?m&ijZ?7Jodoh((T4)LT`F2XfX%Z%9LL zWuCr7hY4?cbr9%Dh4yFkaj!@`7(wwKrIdhJ#1Rq-Wm9z5G-jYD40KpAy6FQZVQ9+RRxooghWp9Vc#YjS!PL0%}cf-S}3gdB3AS%r&5_4gIE` zJ;W}0@_onpT;!BhxCUn=|<(X3Hj;dF_rY>U!=(cf9Pb2 zZ{@K1WbGPZleum|mT|_e^1PZI`OkIJnV3EG9;1XfNlG|Ku^hUq5O#E`j2UjqR1jv; zg*VdHf3{*9k#v70>6Rwo9FxS4c<&z_8k{18;1bh2^G8%R$Zu`zAmn{Y$s|En_Eugs zLPSiXVi`lh(`~%D>2eO*9LbhOcx#G`bbyh>kF)33_0h~giVt51 z753qmpJPx(&(n)3HP4U`2`ADlZ_n&*dCrdyBvi`7jfH4}zhIJP9tPd0=r)`5=Xe_( zdKtxF&a>lUGSD+{n@50e!es^~!a`Q~%Yq4RFb!~P`TFJFD4uvW%nvV_6O=`E-z2f( z4LaP6p`xKly}^$m8{QppIKgA0e)o~IQdy_QNe&@8L`u$RN=IdGE}-ZV!Q~(_$4do2 z0G8%_MYF+=b(10K_5*bBzG|Z%kX@Sx~)7t!lCWLo{w>{Yo}NK^z~08_9SXF-%idY8+n_`*He;xD>+4+^`!#& zsK%!v?*%8Es-B1HpV=*EeW-o4x8Oi4H}2D|f){&QZx@b)gb6gBc87E!f`uSh+6r|0 zrP_hI%u%`@gSlgIa%FBlOB|ZHT_;{0Us-#Xfj82nFGdF|RjyCH?jHIjGY%*8WZ7H| zxwbMpeqjx6;kaq!^uJ%?(gZv$$=}2Llj3}{PIj)yBmqyee4g$~zVIhRLVsdoNDn$eZ?JW-8@M-=JW9s;n%>6-1nMZK4)qYIu|MFNAp}e-`;K z3^RxL|BhRrbd~?WoPROTpF04AF%16AD-OC2G}ZbS==>MzL4x(XY^6dIFQeWtPPVX( zUl&OWf6&YFUBs-vI{jR1P+svM6aDZbWD;UBwm6uh`PZJd(J6%@K>tgWxC=;nc)1-eSEeMXh)&e`GVsLDJwsrF$)`l$ z?L?VGqbHKOU$`H zKj$MEU@Ww^fpB0de@@I{MjO;6rd#F?kHG^dj;A9oW0%kx6Z@PB4{J2 z$WYf$7gE$Ba(S@tE7WvjcB?v4J@T`lOcrr6M&}*q+@CkKiIDK)V6w$#prLYE{j?4d zUDU6&IZA4=;1ErYPwlD6WG2pa_IMw~dV0h<7^1P#|NOCc0&_f7(-cay3g}_J;iXRk z6Cj<9Es;_FZro_4K1ax-@dcsk<^Zreo$Lx&59Ilr}5^TIFu$x+_ zt$kL{@$eE0>GutYshGEs=MmD*AP+iByo*eTjnaqkqu0vK!}jsX%UP%99U*#!4}B%_ z#zFw4@gCv~Bn%Nxd>hhRQ!*#r+0}*5+&n76apAsk$&xwsFAg2QiT%x!0My#LIZ2vV zR`}5emvu|?CF78}ajv1I>Oo4y9pz8dh{i@1l5Ng?rs)WmgkD)X@SDe0^Pg1Rh)dyWF)}cJlPg^A{UkD^n`}$j zzXZT^^{E+O));r!Syd5Cy{(O^MP7`WgiL|(Ef408$*M-F z88gLC^fJ`?1V5;A?@v$lq^*rT)nAk37`dCAZ^;>9j(VvV|NWVUsF?O z^(WOF<9r#KOb&hANcX2Wpxi?595!)@hWV7vn(656RiV_rMU*tq4*u)n$nqw-A2D}I z1h}&Mq(m+S*-Xm+oqA3yTLQq>FqL`-bN5lYzkd9;zu6GW$51n{aiKah?DC$2X{W2x z4Z0{ylvp`5)567$mIN7Fuji>Vu2v*a^e?D1#dSe@^HkadKD%IuaGvq+bCy8i+Z}g@ zQ*qNEgwKyCTiiSYV#_t3uA3B8=--@D-r`D(gYEm?!Tu&m&&xzNw>BP`X7#l){LlPY z>O%35KElgb`o8R{h_t0?6$wWpcvjE9k7v4>W-z^M`k|K^3+2ymV&i-YpEQ%FXzN?H zH>8N5O8LYnjOg%afYiOayN(74($`Nr32RE1{PhWS?I#E5m-FBYSu{DTBce{mnmnY; zl?TsE&FI9`r_}jdZpEKwOrJ1jypAtGJJdCJwbgtu&w(fcV-jDcHwKokvE=Lv_ zoLZo)VI%#?igUf(3~N4uj|;)4#ew~+W%m4;bX;I?7`+4(O%d?e+OQo;>8P`GP+szq z1j#c4&3kLJzt`#zTCsweu$#Gu|^kd%G1u-B&&LUi3tJT)+1Qsv8t z0cz0<%f&=-r&=_y;kWq@vvh(jL~by6?gS^se>H$rmX_ylbu1%QVMfS4ItY|SCFdNO zO{^pCKLH2UzOp4lV5BB_#NO<3h+`W+aBQ{HZlrU0km^*mOuL?Vr4&ziJvWdG?sqEM zvcXrH#Xu8koE?(xNU%`kN_++CZJZjI_-$R(Q60<%sVMNvw9PzGPGgY&GfS-p8Q5A6 z1U==%l^p1b*r$G8SesLSf;yTTjh@hNg)!fCivAEw*c?-Hi5v;ZIw%qcf7<=s$;Y2S zi|;s52)~l}*h2ghIKuKBeWBfj*s-|j2b zS7vA|aeAT-b>zR8 z?)+DhLg$!y-q9vei4A?|OK>^z&+>O*yDsI=p@$t}NBzhskgg(RI2J5*J{j)m`LA_M zwE<4Sq56`&dd03a9J(5|zH3h4RFPA!bXx;K_NS+89W37oXmW?8w?t@klLe9PJF{AI zpF&2HDIVNWH}ePiad1!GUX))C#W@o>0tq5yXz%mYZDJTtf{33c9(yEWSi>868izyUh2Pl72yQ z7tXgGxh1OnZ6kz9Sg?Cxs52#`&A~w4S90@I|BVt~tI6n5(<3D*c=YNk$E9m^H*=r# z#Hp3+3W4>#Q+PKelUFq>+j8A&B7=qAUeb$W9pBdD10hcLY1q%}{UGoqi;(oq?RN=?(L9sD><}3jt8^*cG44W$BX4CW601ePo{| zowRS4z#%#3BL^jRWL&y>p$Ff!prp~qrWPk>q4++lv9hz(p7ry{G4OU@iYKSYS|f{y zWs<6+!9b9{$Yr{aaPB;yBomvDr{3pMh=0Za54^?Af&TeAT<|IwGYRT?mIY4tBFdIU zhQ@H-SQr{LIlOuG)2PTr+lDrRyDiJk8O4>tT98%i<`)aEx=Tjl2vmc?>q{l}WJq2V zBWD98kxNT^4s#{yL!LC%jcPgl3sWPZiM2^w1?_uwSNZEJ)O`Si|CY1n{|b7bdPx7I zZ2aTsdG+m#5l{{D&I@>XA$t2P%iP-IqnI$pKxHynCg9x3CZKMi138^usDGzKN(m6L zvC@kSkF4nM5#0h}{!vW3LXd%+1~Rp`JW{FFh}+2Hb>`a>k?&l~Brx5?(I~`G;K>Xt zh}0({+z0wlj0>=$95SNwtBkqF}UzGOT~E z?FPef%c^P$+02$hzu-Y0ScKb3f**_l-TQ`}L2&pNwA_PWcjw!}?p@sqb(1$uM4|Q4 zI_eN*ebP5gu4X7Xxyd147`dnmN6Pe36=M|(Y5+P+R2l0=Fw4$x#i};m!3JFPBFqKi zQ79v$NoEl8v9k6!CT?lQSc0G^<24?!UGxeI;$QBWv!f}F9KB9Ta@2Rq^>?$%0#m_^ z;vK6df_h3tLv8d^;fcAwHtq(f-Y$_34wHryipSr6F9}l$j9GODKr<7eU93?U1S8q* zUr)dw)ZP8jN^v-;C`An(HZ0s;A-6=O4DC#-eahN`E+&8{q)BjCOwNuQME*<4P*aKi zFTXZAY9;obVLQmPaI016T3rDB8+py1IO>t*9>(#V=)R7wv?A2OnZ_kXlvXm))>iR@ zPua1VjeFMGdP&2A3Vn_w4jD{CzdIywBk%%_*DblMX{7Mrj3zwO-+M9*DK=Jphv+21 z(R~Ipw&w)I3$4JKHK>R%-u0g4J7W=D&~7W)K%eAO{ur4q{5pJmCQC4<2`}s?7)(Z= z>o=@@jzsQ=37ptF__zEEnU2b~euXB#+R#q-N0TJ6ym=Z~f`~Dp_ZZ9g)Ql&oYR0nR zx$pdU+SV<16sG0kBY?)~i}W^u`eqqH_uIa6R2Nx;V7#=@szHIB*p^cSOY&8v%5gn= z*@rNF-b_5Kt0mcworh&e?2#QY?+$Q2W-491VU=%}VUEtRK!CxUj-gzgLtCN(pr2rr zbmHfRfuHi}QbV&y%xVg{%d8Wv=Lo637}zmQn_E-4p{22rr>m>edj0UXXZhH~emR{c zdTzBu@F_BDcc$_8Vu85ff>w}3u#%6%()cKib{jhJ_r9x_F0kL@8KT4o=$MF9iI3D_ zX%NUQ20*lR4xYpqlwo>)NxND14+ZYo91R&x(r0hwSlSm4T^K1E|HzET(iZZvUufC8 zJqmJIQGO$1d@T#Z&d<&vuIBm&t$&sc)}SO^dLA7l{4(hy+{+b?AU5($kB=W5fAK>trsZ?BZB$B3Q0ei#>kU8Hk}{-0*gWCQ{$85hD?jb>rpPjLFM?j zrZ`RMqhcu@oocVwNf)@>?l#8Cw&q)HZey)RRb`tlHJU)8?+}_XjLMVlo(dnw&)zTK88oeJ*d|j|6YCgR@Behm2&;@?~Q3T4&M0}oKIVx z!Ae3Q<5Jx`5M9|rrV@GBEu*+7kzouC!FxeFQ%RUNOa;>etbg&aGo!9DQv$AZf3PCd zoo`yM9Xf{y+XBkHa{m05cH7{&^Dp<%n&{{Bf6)up%o4f^UO!0AQshBDB5Ja-X%f_O zKWJHI-W_Zv|UlgXMxmC~w0@Be$0Qys^8LIN-}Q z$WQBd`v~9pmppV`8r)Z!i!{93Xcr^lb+R7&+jFutcDmJQ{BEt5x9@t8Bzxr6D1qXd zB08s@qd1CGE9nG6M4p|sUH04QTZ{Y;27n9wVm*<*9*YYSRTepCr7)>d+R?{=p0ALd z_c{GW1-M3}`sn~bnCxhqtTlwNS{f-ia`aR>_|N;uDA?1X?O^lfZxpy+E5%(nHIHo; z#tZo{+N=R+j3u@^R@8NuHV*<5%i5g;PK03v;m@2J?4K8ci%e;Jtr~ga>)<;Pu32mH znDz@<$h8TO0c5L3k!>8qPv67;`KCO!2s8J`WJ2^9H3FV3ELwL1XXco{{Vh0dplkak zvA1a`OHj`dh-6OC!qHj`x~}yVLMmGp?D-|@R6pj($bYwRPWys;cJlJ*#)|`n=NF@4 z1NmxS8J*S*=9>KB(v^uRr+}%dMII8EO-vYZ!o*9@;X2mk6V7l8%Uw*W|6_Q@R?>kJOF8!O7=vs+ zx}bUxP~{$0&VEhf{aw^kgWqJ?#ad*=kL^1&K#!Ply^rf-0C8fYhr&%yw-&!{)m0Ejg!>-q$5)ej~%@HD66g;bh z>Hb8s*R~QpS>r%d>Yv}}G_(v}1vFBzW|E=JiLUo zRTY}G)4yZ+Hz}qtjgZ`YE0ES%PZGEW7WAfsD*P#rSlr|ds_Jl`Uw>MGzj)1|9y5NJIe}MA6w(} zqs%F_FLOh&79!r=KV$}oTSnWvmIU~C_qfFMZ}2|8C@Isr5_>h*KC(=EYf9=f={?Gy z9K4Rn?ZbVBsF1RSP_lrVQ#sEFgOf7OtcoJ-u;HRC@M5>esa^8OUuww7p!GEdf7~U zw1dh%R_HIfT-XaG%k5ui@MNsGTvgv}Km2NaI%_EKw01Z<-?(8huow1ed^)`zs${d{_@?o1(FA$xi5?{NTcI#YUSGN-$b zoc$DKBb>xPcX$5TAqBBh*{_*NAqFzIEV!g}NSXS`q5pIFTbO4;=w04IjTLC5`V%M% z8mRu~__a0vIsT{UpW}av{&W1l1lRrmsop`a{?8@SA9PWndTrv+NV})%k5!}BAoo9| z81T4o|BAJ}mJ+-wz>R@Hl8oj@rU?y8R|qhXs{5`-rnm!1(Beo%uQ8$4c|k2m<>KTVJP{WjiW-)|{MtAJccv`3zv%UN$BLUmj~Q-p5TR9vnte zzgom^9IXKL>RWN}Fq^iZeW?Z?IH*Uvn$;_o1_S2zh)^LI>R=v+2J&Cs-LDb=yGpNu z0Izo6tLGb<`R@-^Ecu_O&~^a0-YFXcSK7oZTpbS`Q@~;$R64cGSa?U*W}9`_TUap; zT{U)^hVs)0_on*AJvrr~Q}~1J!xeJifo0d#Qw;sj8Ds@(hi#e)F~yQ*_9w9`Mf6@m ze^uVYApUh)6u?S+2X8)JdRtmhFQ^ETODK2>L)UUK#{F zoL8jiAf3@k8N@$GVr`68OGcx3a+|w_n6-k~B6} z3P(jv+_kE>V>~~ZKH`U_i4Xz08(oqD{t*M^z`v6y+Dt!V(Nu9 z(Gx_VFj7`F=-Kc3UT(R)qSUN?H9gWNgi&lJ-g`>4Mmwv~Riz9T6$3f@#8he_J3&VqR5eO+i*jI}X^fTb zeC%GPm5t{{_P7~4p6ehmd2U)Z3%z)dT9#Y=OY0Xv&bQ_L@E_xfKpeEAHRHI|)wBV4 zp8>0-ma#Q%d!zxt!^xR3rTZ!jbdY>`;@n`m{ceY`4nA{D;RmiE8_+Df8{LpwMc zAbyKg{_tx^N66J@OX(Z!b`eo@DJ(tS`1w2~H+BZ5??+E8gn)Lt3Se2QYco(vReN?V z!o%6u7vPEI+6nMs&*zfEA=mKY2 zRabTEhPKSi#QMYug=5)|=3ykQvz~xoh;B~BajDql$c_ESa?;J!P9ySOwAlSk|NZoF z%VQbO-Q|H$;daHN)9|meuZ2AvEW})g3h@ZbCo{g~Kiy+FvT7MQD>dtxyK$@K+jl`c>q=jhc9mw`PAzmd)dffLVp`-*!5{BS8+0)pT6Tjfv+ef~!g{=2k>8wU$$n70t6$<;(l1 zUy5a5ngalDb6jkBag#CoA`)e0UIs<{=v=)!!BnEnO1ctPBXll_TwUN*SYdr4*rD>U zQ7|hWD$aKR8kHrh1a!{U&VWhDy-a#*IL@4ew}d~E3HMv~T&jzEA+Vi3(AHh}H(Jbb z9Up;k|Lkf2OhLyaM%YL&U^etQ*gzEb%k>xVPg+kVGqz!#@vJu1Mh#0Ho1qqvztcFV zV@N^h(Bhi}tw-ziBs+;%15l6Ljy)tYKmVuN%R~XL9O~?Y^UVN)_WOr zh1NBteW&lo?hsH6Dr&OwJc|?m7eVPQ)IwVfK=3xcnM0mxWxFE=uYvwgKw$gV^1oLW z{@HKBUx&33i1aQ>b(SNqa$Ya*{Y;V)+stt8507#n5*tg8oSmtCjXl+_&(p>{UGtRT z#L=pBI7{qXugONd-j6l{rb6{>@1#v0Iq7Q?BP140)TMsLU22|ShQim$JO|_xr7rDE;R|Wt+&*)dUSUZA;-nSi-F(q{?pynh}5~EtaqaNaRe1n!niDh zlXSzN1>Cv$BF6$ky7U@)v~Thk0CL9F;39&nUg%Bc{k1zpIX|HHKr#(s@>K#HmRHw)(ZMb>Pa|BdRe&`^XiwP`mEDIFI*12vru>jCxsdt~(}0*xXh z^|^6^u!X4~{a#=5{Yj-~?3QfysfV7)1pJk;em(R>q{KW56CDff3{r8B_}24qAiB!Q zEYAi0dMp)pY8!%&{1KUyySVAss=Folq%(um0fkjC!pKma+Lz{V$C&(V)u&-074On$ zt$>Bkaws9-T{|jq2j(LKM~++bs$XBCX{Wf&3B;+iP`fEkRjF16-@@+CpCV5jGm@}Q zMAylpy0qMWjoNWyO^ps8114`vhLqb+)K1cx7^{|M1r*hOY*>tEB;?|tUdZ@I!x#E@ zOZ$x&3tdu%M6G-|Nm~L_yxio+wa6WT__}UA8>fLng{anKb3@}!g#g^3jv6tEA=O0q z(Nq@Q?ww2aJC;LD)mQ{7{@VasPc%Yk9->O!=j z>olT9NQvlA(`vybO`Boq<6mN1slB;a-4C=>rNvR-$SLxV;tUo5Ef&|K+$j?0;^W9l z)HIe16&;6(F+O>G|B8Z>yln%1Kk$zmOZ1`cx1bm6tZAOpvGL!ENc2X*=Isze-gq9! z4KFwF%`tft>Fbcc>no&P51f!zgT`=E^AO#@NHkuScNhT%e{ zR36F`1J?HB270TNLzUEgCP4IcNDE2pr8LO~#g_|Xo`#NI@(d(gi17p*9S%c3MyjtD zJDSD_Q@=mOF`sRjM0v+(#h;Dpy{ZNIzkCPnUwJBifbqUGOB<&7Yav~pO4j*o#PwZ_ zf|En7FY=owx5&J+^pK5^R0$kx1gmH zl>BcC(R4e(33BhcX+&mEEn2Y4*l%TEjzTBqUQ)4GM@z83vf4_4e(@T7; zgk%J)^U)zeQ&?CKSjggxcM8kP>cRKQY{~k(;^4XkfYInmG#$!9qyv1^%G z?lG>4Y9CGn<-$`OY(JK+!8$ucJvM0Jf{%vDhG$5VvX5f2EOeTrw{o!W45a@C#VP)6 zV$DfURkl9L8%%DfjYkJTi8s)B2z?QBGopL?cBf7ouJpl4ke;UeW$Xx@02Ts|HQtNk z#L8xPs8(lIow>GtMi51KEE7>35P4(xpxZd!zm$&H&|Q1@&zVIt4Mh^j$lW!mI8>p_ z)*N|Lhh~}tnU0$}@W!Qm>DdNLrt@3+C6EJ5!YJlIWdiB9B#FtbVypFxe{W&;(Lnm0 z!^TtkGp*GI%|O+2P_(l(YAg_f@ z$N6c`uF1D~V%VW*wVfsqck~h&JwDuJ^YwVN7U-TI0yrn>9`$Nid-1#AIm3t89q_Hj zxy-$@^R5X9>8I%N7NRL-q>*K!nB~jCg~MsvMp(Ik7S3Kz5%G$<+isSR?@6RGwFwYI zpwmO>e~#9oDl0n$2ZzuUV>>3Yd+KK3FP0Bo!nzqmKeD5-FczJ^!>rM|%ui*En#-&% zLmAr8Wu{H!aC*pk;ww?x*$Yj!M?6=qwdK8$Hvhbt?H(b7>Q_;NTz4&7cKMxlaNKu& zM&*x7-2wW7y9KprO^2)>n`FCMyI6tQ#7DD)C>tmlUod?=V99*Ww@jf;`adX8|B;XT z$6)-|a{Mm>)qgAWxc<8CKwJQ;xI%KMJo9a(U7z3KehTtMT+ z8J*I~V{Jeid(f>tk#3+{XO;5CE&zw1I7Mci|>Oab-3Fvpo<&(E0v$d@U@YRRiMB+0c2smyRN;x4ZFJzxBxpa*) zDNfWaRxpz}EN6+Gb$z&blgIuRmH3%fb1v&6tUyyEGJcPQxKci^-mawkX$@n)C*j-v z)&SMyIvoRg$D+1WJKw}9=Gh@Udpt~gKYpaJVX)`~vMg)2x;;#v_Yb`giBvI=mY8HqsLC)gTD>S2S z;xAOiXNh%E7Q(#Qqg#B??B5IvdvQMu@nIGi>HQrD)TZD3v*3%TY96pE z=uogb&4VmSA}hs56YNGG5Dg168Z2BG>2f*}oWY0i3R8Svj>FcS)v@S-3VQdC`*Zgn z<^w?(yq@Pp5V!Xp<$|GG8D1~X5+lOWtE(%xGt44{UkP=H(mlLf?jW22<({Rt`FYPh zaUNqWc|KTc-_Jb9jY%b|Xmld#BZekIgEDVzLThbYQQy!JRzmItreeK2Y;!k>QkM3# zlX-3gUE63>jhQj6Hgls)N+!>hz!dyPwI{n^#28_QKvtu}H-^l@g(sI|$8Y=j1)~6v zZ<5UA$N9yH{HF2^$MhYkIv+3q2z#l0b9D=}Gy?H!`G_!IN$&DJ<(_yVJ=$7~-w7jM z-aLT^W>^focg}NeN_`rHZD=Air!ZJGBi`bfA zAXKSyTGJ#2ORa2%L0J2ngS$+^K>e@gPGBQ0K;O_jf}eqei8y>#$M8mPNmb3wjGCW2 zF74b}s#rY;41n<$sRKQGt-XjkY>$cQ$9*|f`USV{5S(T_*Q$HJ7SMoGwCS@Ah9Bp}aH=pwGgQFJk*3y>X2^!Uw-qi_x86*GSzya) z2Bn=d7$be*1s{VKAkh~%=f;u1i(cH{(;a#1qNe-_8L5(8-SvOJ*9^HNq2c%QsTEK5 z5+yU6^6tNZxea*8UsH&mS#j42ep-oOQAb z_CTe)Zu3K<>DiHQK(Ib1U=*$R4n}c8#^v?Bg98$h9i*<-^4p7uz|a8;{Jxg^=Y3G2 zaA@ZXl=Qu2p%}oLiI#D@+a9p&6MNoz+C3i!UhC1E*J~~JyfBa=Fr3nzqpgf2_N+LZ zIAJ3x3AEuPx0E(!CwkY745=Oo0xZK@<`N9dfBz=jj{awx(xSQBXug+wNT+0a&^iVn zL(w}uCM&eZb|pC7fudta7cqWEw+a5_Z^@ZUK8- z_VvM{`GcoT8acZ%R%*Y#9#G`t%SF7p!9+M$^+s6qkxK9_O(W0+|}`$!(U& z82yWh8>Q@8kc?41KU3^POjMid1Tb!>U<6?tFQhIJ$YAnvu$+uI~h;+WB>9~6F1shXfCH;y3|T#u84ASjWs4lvWey4G&>Kn9d?Nb z^MW&bD*3Zf3S4k-p*u!*r=a_En)v4rES?xoD)3C;P~`SVQz&+m>F|Bg4{3aQ&^f?WnC`{GH=fN$+L)Za=-sha&L6867>Hs;7tE!ML4 zTtjy$(`E0S>=LRUrM6j_#>TggHhA#9{*ayq(#pGs< z&-Cs(z)XaXHAIGtJ|EwwOr`Bcx6aUpVFSiC4;5Di_j|5~A9ff0n;S&RYg+Il0j2i> zs-%%A9B?h>18Wg8gkwwH8QRrGHNSgDV+5rQPTUE?ZAwa*P7Yb*@9Y$RD@49WsJV7E zxQ;XWQ#V24KA1m}2=oa4%VcH2K?nMzo69w)lg6C8JYiK-M@D7D<`Ffqef-TUQQIpn z`e&a})5_@V=GUg0LL>Y)6J=DDVMC?d31JmH>=J2I!(co`3wmN|2&WSf4t|)ZLNYw$W3Nni3%qd%HiC>m!h5?x*{B`$abH#lSjOg@*JBL;{V#0$a52#mo(E*0p zj`u3z=L=$RzE%(8MnU{Qv8hpHH;v`6bLa?X%(La-k%@PrVSx>Q@FlROrldwP%GK7c zqH4;KiLmSOP}Djlcw>waQmg;rK8mvnY&(}Brln-T2N?xNDB zt~`Dx05k~?GK4nb*e~pdJyp;%ggfP}B$)yy7Hy z?`1yhKt_9KX6n_&*afx;a}x;5%pon?dnRu~9Hs*qeabFvF&+EGss>51te6vmG+Cim zst6`S+iZ)#|Au^`tYqQ>lGxN09}NujM7ppDm8&2}-d|)|W{1#<<25%)N=p6$a%?Ew znQQdW1xgFth8YXx5Ct=*KIF#^~&XCwEsQ)TzXjuD_sz@j%N z6JV*yK*BN5d1B-NML6yQhybWQ-4mWiyWez?Kl#;EcZL!OC$GULk}Yc@4UQf_>E_J{ zlq7PL`9b|P2=YmbiIYTbNhT4fVuRf26qZ_oBh%cePu{uqVtC0xO-V`F1x~uiXN^8* z4Q&w#b*^|h2`O~1BhvC6w;TSb;@{8m(9zmZJ6ZxB4>$bTB7&E6O!x84uPE&@KWo!L z>D&SEOHjrGCsvn#mN#uR&rAyXPj@VW7Gw$iTh#U0CBa^>2Fr`_$+G8s4J=HgAIZRV zg*A=5Vk!!ybbOZ_>`(nq)nw%uqIF+1s_hP@ORTgY>Rw+Htk6~R)XIZ}4lS*@YP)kx8S?uwLK$d^2U{P-6!STbu>3L;*lVYGPWBt`Z%Lucg+i(_c|HEOJ1zErwZ@+ z;5Y&UzBlY3ROMXNYyYK_*O+b**|(H(ZMgaJ&9**%0eoDn&Cs9BqJvLwf;tz9n|oi` zeiR8~uMgeGw$y7g&-oX_Re zlh;7&ljlqOkhQs^zS5GW0Z89(2V%X#vhr-{v2|Um0gTypotZf(=wskHUe?lXfOzk0 z?9xPv;w)7Zzs$qAA0G!Bd~x~Yf_LKuD>RR1 z2ReWBglT*B)we*KdNXDC%9HQ;J!x!?%Y5YlWfuG+l<=yU@XD(2jD3~Qg*sb8X`Cwc zL~p(k!N3fyfaPmm9ajzL;v4du>FO>qU*5o2tOKT=zJKF9esx~mkU6x4QxVZDs0qnI z-C3!rHX4j;zu>5$H-ri@aQ^=gW`H8LI-!R0@=rhh+4UGr$@U-K#|X+>n6E4u|G>4S z;4B9k0)?W6J}80{RGTV(M4xc+BPEIucId1YPYqK8|TJEnO8*xVzaHEqM7Y(@XhV`QTR@d`1f);*e zZ}#uxI_A3fm?KJE&-X>DZfBa%V^wpBb1oE#k5g1Q55{&6oAtR;#+(=Ys3n9sl;odq z*dD5{W!BZ_;&Ym>)_#SnI==^p6GR@ijz?z7T15Qrn!8yNo^Q`$`qXQ42pTT#rCM4p zNJh|Wkfmf9pIv;oPY=B~6X}{&4~3Ek+}w!vPMGA-(spgDF6SH1a~~ShzDB(9640nT z@0JENPXN@Sfz;ASX;AEz=CKul;+jA7KU#b1usF8uZIB=#L4$<^X&}MfgESB` z1PL14o8aETT^o0I2?0WI8V~N!cxYU|Cg=Rlz4OeyGxN>NANBO!RlBNIZ>x9h^{%~c ztKiBbd$1Y<&_l~M7v-D`&bywsH&57x*0Hd7Q`{VFZP9fdBo#C`%J2fqig-J!jKZdg zF?Pa~%e3_dW?gy+!lnGho|B?K0lmVj)6lU_v!EySoOu6=ysj;bnU@XEUbU=UXdjyF z<>pP)W7U8gue8q6^Il6$p~G^!y0u>N`J%Fq^^vj|7ci`Kzf!7x_nq_g@YYVsv|WgY zr}|Z5I{aQ{EdsHlwxfF^Ix;<<$|IdT)qX8WcE(q)ax!#PS{{&}hWSFRrq&yqvH@P% zS=Mk4&CQ(;&wGk5(2AJSKi`VGJkfueK`b_pzZTmHayz(f6&cw&2*#`cx=P8i(E{>1 z=!H|eUgHwfq>=FgN#9w7BXA3i#6f62Kgs)^1y4w>Qc*7tC6fvz;v+K)?5`er=kjBq zsVKj&pGVq4SS6}>L}*;)ZSS)Q_2=P^@e7W3kfW)&Wl`?sm!nUO!_&sr$7Ys|?LF_8 z#hu){HM!Oo=4Y`bmHaYn8A8@JS3`C$JuI$=T)11$)LQ(Mo~yn{>4(gdE+~0Lw+#)Q z`a^gcc4^U7`}$N47mY3^Kkz@E5CR-xQMIrG^2l1H5d;D`#c18E(K&%VoSgQ&E?svn zVsK9YV@q9AOH*LwOfikMn?b}eX{s)1h@w@Uu5Z_%)o-sl=!z5H6Fq`#wUkw0*rVvy zVo|mMg%*rigIb`6fmB3ADQyaopElAHr#cDorxarW&~XqobuLpEWRNk=YjIrA*NqVN z30~;*jr4Se%CtWz-&Hdx7s-r09=7^oBJ$#O$0F3hB!t1v@er+mB@NZq=R}Ab)BYz^ z;Q}nu?yk<}`z-N{#cQU$LIJgY{=MoG4$29vJAlEgm=$d>JSce6osQhv$ST)~JS5NyzF2%S87gKkEvdF1DL2##nus zolptu`ORSyam+;YD(xg&+UT>cdQOB@_{15`_^H=3I9zUMH~|&B*%xt2Vo3rXJD1&3 z*^ELuCUWtNj6rdb8*C^ha?cefefNe9A0K~JCFn<U4R6>>sRA3oL$#yC+nX^HJd& zQ9MblhkyO`uY=ih?5mjzU4Usyzt`t||I^V3yx`C-((~JurW91oyI*WzR}N51%l#-J z#lW?e;wH)3mwlD@)n6Al?+?K3#sUBuy*-iFrkqvOv@$K%_C+0(&{gLOzS~mPuTl)5 zC;OT6j{c6w|Gxv0Pp>=_WUPz{N-w-Nc;IBf!ci6zK2iiOtLuC zv{mj@-D1ni+lzS@o&i)i+O`u4>=RQy8~Sk<5FI7e_xupYcISr9?vB&yG}StQV;wJ zi`cxV7bRPVAj3MFEbmN;YZ)TSJd^7 zqAZzXpW?3{(=FvNYz@i;bKm5qbAj50Ec+sRv$b8!21Cj9O#rgQ;Y|^&ZvYKsF5KRh zrj)a82C!;6#)G#7hfONxR~F^7AGZ?1f|n?;U2=oEhaZa#)JcHDbPSkeKg}=mnB@>N2nE4D~3c0Sl2l*p)$; zvHn3JiX#-4B8^iG7gt@2wx@H`ZMTu}!O6;Nly@E{w>9WYnI&_E#dZ5icKr-%jk~*b z?SWdmORn|xyd{F9hX$Jj9^?4IXuMa(HT8GYjwC|O`UZ`LDTb_ZW{yqz0#JrC`X@<6 z*`a}6pIwT%K_hJo)=xSXDMqn4*=Zvk*8N3!)Mi86#*`y>_Ib@5BIk5jhy?vhQ z)Rqe{L&J{dW?ArRgs1w~p^U;JPb|Ju{|1?2Gn&$@&3nEa+KmscN?H=Ando`1ZPD7m zMFTMok6Q@jQi>S*m*}XnLRk2Pf*2kNl@`&Zmh`{Vj!(_RDm2d7(p{|S4!avc%A7@M zbhuveEW|Cuy-w`lJE3m`-Rcks_1_|5HxO_5WCM0kcDfz_39(En@ake8{`(n%veO8B zIQ4%5gcu^$bR`y|!zf70H|K@>rZ7aIL}j35Z|rTgnLQ&C(pIBXowPY~M(v*~U&m~z zJ#6Osa7=BHk?e(kDe^+CnD!t41qg4H{)=&T+s`LjN)D_1~Q9hd9`OKl=Yk(QTEd4?B_l!_hWL%Cczd8&D3cw?+Dp zQ#`u)F=Xn2BA%Yz`s7-ipx_s;%%f%3oqY0JJr*q}Z{P7NgfQD!lM18-f?D{#eP`QW za}S9Tk8{cp+L1tJ-vE$Dz@X*3)K3sRcjN~d5ghYT2tKL_0_GwpS^p5Qejr+S6ygzg zzX$E%6f(#-@9o|95N?|hJTJ%{V~@HQ8H|qgZ#>Z9C$o(WWhkvR_#j>V-exY+qGobx z%w>+3lg&~lY%OqdHmpuQZR$JIhbJL94$%QTCih^WO7z)6Eh~q#Y^eO%W#)y~*PBZx{H9 zYn=(#`cT*G10{_OY@Y`eT+*j9G15PCs8)5i1!T&TS35Eoux_2c)0w~Vm-N%e2~U^$ zib~pr$N$OD>~@biEghuII5>brA*oGHCG7d8lH-V6@@tGgO)#1l?G`yR1M@Se|8$e~ zU0zN=^4EZ6ISjBmA#sSZuXqo9-*L}XoCM=a>w5tXFBlmxpbUSaoIfn! zU-=04Q@d>_Xqi^hrUD#srg0IhHNZ%YNUMEE6E&&Q<+gIT(T|3!d2)!cHk5%SrJJ=3U4B0096AkFWdT~oh(StK_ z2dFcMuAVWhpZRX0(1XsN7SCei_9Cq%Upk@tyW0D_CO*uykXp0Du%fVVh*w-7Q_Dg& zG&Ah82YJ+93=Nrr8y%z6eyTQTS7&Sbd)j?%KBk8XA`GLHYF=@o3u~Z7ongkdrpCLs zOwmsQJ_nc|pwoe9IDjhEw{H^@^+kOa@D3)kylV%S&qxN?Y0b4#rwA59`&F)Kr!sBu zmy<7VwoCGw=u8smz;yXriJJKbH<}E;eo2vgpv78wc$d)H04omQrC1sT2Zx#M#L{pT zg$lu=N`q|rgkAT3$cf}De~w7{D;vJ zU-y_6#N<8;^mOw+8I_b9Jgy;}WVkVVFg>M$eeTfs*}X*aa!-7Pa~~=(){8K9r+{~p zjr_Y#m>%nh4O&n~$@-)P`R}XImpet@b#!K1R0<6G2StQmn0cxDm>AtEo<5=-fu@K* zJ(O7g=`He-B9Eb2fWY02Dzo&B)cbuRd7n?~J*>hWgWG436Uy?#A|^Qbm-IrNg371) z7OybQPp&N7d#z{6*&xd4$IA%_+S50m3ybEMd{e><3N+4dQs`EE*1%x7__*{>p!J0` zE46A%wr;tKIWcgGT+{EWtSP?hT1lc20%4MjH;hj`($r?yH{m!%{3qHjLjb)OKsD*F zV=1fyvvNPAI4t4o`3om%O|_NE&PEmjFXY|mUP&FKZbFzN41L-+YJqIxUfcqu1jqDL zO4{1F0_JJp!TO>ns?H`&vyYAI@4#X6BQKb5*pt?^1)RJVSwhixISqjTq zHj`0>8@vt+=1lLi-4_sTp)7vp9)r~qWc)d_xOwq|PSi}Zm2`}lGe*ZtL<3yIAPh8l z+2}{Y1YhxlNv^LqM4)BbGaa07_-C_OHa|qbKgxl&Q!y^Qg%4AfY&kOG>|CvbI4+vSK5KB#aHDFFzS(L+Rl+2&^fvW*Yz1~t$A7hcJLg_YmibChp$P{M>OGlpA zI*`a&>Kp7O81XSm9j4-7ZdGys$M4zVmgNGBsT?oZJYC1}C0`)f-&|wW)F{@JBYa#- z_L}pf5?CeF3tF|E=KV*hAbMzR&s2$(JhyxN-RNtu@~GjXqT6#PVrXTh>SxtExIWB- zbB;6aZWmm^$(i=K%6m$^=q|0f=JusyODAGXe7@&GFcqAs^IiW+?zYA{rwW(N?(QN? ztphfYoA;JAp;_ww`K`wTG=hpSGpDrxuiaLD^^XT${wj&>v33c*MIa;H8mZmWX21qI zEy7XY4?p_>FCj?G>@N{m>A^?t0WLkDmA=1#0-=z{vS;n+oOwKIR*xrlgfRk2EM)e_ zJ1t*WWH(r3qh@_XoLw!?Fm8%LxYKP56VhECe-Mb%43>J7C5fFkdHjKd&K+?R!UvA- z&(9wW^lyah{|M#&vkzPc%O|Dgl3va}DiGQq26qP-I{$O9Z)airJwY4DgzYtns(~#d8S%6pW&URCm*?<#PTVUWGcHj`5-(ba|&VpgY|ri2(!T) zmr<--vlU!VZroeuS-rEN$I032>&@#}38WtgBdCi#hvBi>$fe7<#1#QR$2 zl%Q~_frplByJ9~O*R5$JE&7B^WHsq0*x||e;>49&vtY>k*NgDgU&3q|LL=t~lknA= zEthND;hLBy#V*l#bymj32`PRXquzM>G6({|Hz`q3B<&zpiK;ABR!jQBJ!Z)M0pH%w zgiOO9NzI2wwL6o?^x1K#^|&~gunMB%8guywsxDj3(<$P6gPDc_DCX6vG;^|ynd2dg zRuer}q)37)+ANppF-u$Z8QE^Z3L;OrD}mk@3t1?NQ4LY(5=x6s32btj(T}9Ivndo zr<@p-=Qwz+oW7B<@gra)Vj^6IWKkk9i@So-?P8%(lw%vxeXfE2D`(6wl57DX*DmuS z-*H&}FUn5=;`#Q`0gwv!4licqJS`vdxRO0(;2+x?-vC2Ct41X1IGFze@sREhPzA~*eG z?E)H&W41|!<|TD3h`U>#eoG`>Qq$ZlqWQ7wgjZmRQCb18-!YUaLxh999_e@mH310czfp zoNva@Fqi}a{E@R2-=X7ve3lXrgSmhpy|I=)e++Hv$i+4t)Lv29M#=12_Fhc)31$Nx z!pSIvL}J1Mr#E*;dIdWu+KxeHZ$2$GMDqJtHDyft!qrKmcZMZR zrc=h#hcS!Qg{L%@-&KL@Vb^}~+lmL2)8?6xtb0?b##C4Zo}QCCwx>f7 z>zva(KyhMeMJQQw_$lPJ-wc`q1Zp~v=hcd(U%e|WUC z&tz_2n&!tHG1eYNT{M%sJBe8pFr$$lIIw*qCX-3F4*61`y(h~=KnWMo9`bj zI-c@Br|XD^9}G}`-{kU-=ns*<|6hG1{v|r1V*ba$=dX(SceUI6pEu#7{^#`aKVH@U zUBkcksEwgSh`KD1_t)aWABQ4!zlX(>f7%8Cp#HDZ^d1xcl=)8$wcH6;XP(&YcU67&@LquXX zXaWm7K@2v;cz!rW5&iDrc#F97a3Faj?86TS;-c?lr7&AkCxZFtS~>WxtmBFF}{ zq8JSE<(127^PGH(ZDSxtlOl|9bl`P4`-_$CbkXU4iHe)DD{qM~#c)_0bi75=x#_Iwer2tI=qqJtta50AufjQWakBwBB6GTxwRoNRa>%N@ zCdL`}sqf>6e#6s)+J;LZCD8&=lnxN5^~)x##aGu2kLNZNCt+t=KEed-90a`4nTt&v zEyISPWjn@YJQfs7xm_DBe+#_9`-m7=d+BvICaimcuHmgb2?`eZv9PGwqGrlb(KIYO z1-CuB*hX$G@&N4&Bm{k2=X49-WA2@NEx2_(CPCKw88nQ}>A_Y2NME6}}2x5}lw)e^#g%iuVgi<CK^L?w&0u^xr7SHHKBk=|Z<5r!i%f}JUxwe7E? zG0!xW-}HXK&~Ty1yH!*jR27QvJ=|@2gITd9BJ9RDnG>KOUP#fBix}J#Xh$j+^j;G| zV3Of*Y1xTJt{vPb6%L0ruOX2+^2P$W*drZ!SfSOHiE-~%+z1{}?ISNjVGN48T}aOS zCjqV$09lIHNw+qX4q!O2s1Hhr`>_f`&>YH6Rfrpw=4QP`TfD8DsJ&@7&6y2L!>A`} z@Nk)6>q=$?2kFm~;`!qP5`ZN1n#jFu=2wC@WO`OdJ*vsJp@-b^78EJ z-gQo%k#_jCF$sQ?5~}m6A>$>|*uM&mVl~+qduwLw;BjkD5-#!u8OfLBM1^<@hsK;Z zQFst$GFvGOcW{UOnygH1(jGaP-tuvGykoQ{N zw5OGn7d`Q;hx3W80;jo9@=9?itIs>P(* zGsYb%U-a|mbP@G4@|%tfuGcE`N*i|y?kC?V=+Cw|I@W^tRp&T@b841Ar=8k5_I7~u z^vmPeWtEJ#Y2W1T#@W~UxTGSnp|{$sn{RI&lyf9P$9mVG^CrAoq0Z7tDz%Sm78<`O ze8T{Anf785@UvsTUA+MEl4J2|J3U*^&ke!XV%CAyiU2*vYNNggRyMpRan?r9NMDLT z#l}=@^4xI>4{}l@GyJKij*sOh5y)(i#wvp~^z}tb^jBKqAAtfedp~Lo-%71+DF+H9 z{IDJwX5`P(*we!hCwduXJk|!ZnM!tzxRBAVAnJPVAj*onJA18kWK=CV^Ur~#idZ-q zzXsRfRAY2Ma7F^YP}98HlLnyiBM)}_cwmB`geQTpj<%Z#Szm+}6nXJtf8a>6MnbZb z59l$*({LBSG~(1jdJ7ci{rRmdMDD93grjPHl#9Ch+fPxQ=toG6-KZbl%JT+b?)xa`Z^`9j??3??vbhs6LNu~*^xhHx77KeJ{+ehx zRp6S9!1d@<6%#p?!0&gxo&8qcIGz;?ToaB}+(ZTkZ`t@@wG?BhWWJ}7V9y=7Pi0X- z*hWVVnLo{0Nq!`zrrAm-X6XHZ+`QnoHDVr+nsHm2MyDTX&o%Nt~AW66AM!8jj zk*nRg@4}P~vnlz<1$2m&Su1 z4|m$z)x^%aLsT2`eX^F4C@zW2r<{TmhV54Tpl~n|pofUgupq(S(&>wSMQjL{6Tds` z6)VnYpQ=r}Vj`q(cse)Kk4N1jM?j6~$3}Hnw1D)6CQ?lwhW+4?=LOMr;6q-}@K_R6q-ETYo}| z6u2rjN6*^>r`70p&h;wV0|#ic?7Yvt3hmrfb$O}yxZo5Mc06qhPM!zxY|r3V=a=Z( z+7a+V`Rqr@&t%@3CA9Y%&o%E45Q+3SaG>ub`r{+oQh3#)%{sVf#PS|?ayN#(zm6#-;!|Gd1qLp z2wzaq9P)tm8I0fM;71c>f~rBwstjyMs?A|cWsik2aeO1X9mb^(h27aVF4M0f#hzf{ z>1DQ@GNFa^)WmD-Ll)`1^cS|?z_YDi>>?uW+P>^)(i1albO8kR%Ny*9Y9MJQ6(5p- z6OTBUf;>Re!*bxLL^v=B&K4#yfcY&Dc^F|A{ZhPskzQve9`w!`>FtJH$sXqR;2>+7&PNum!_mMf+DsU(Q$JVHjqxghUWJm{jpLm-4TC^RVC_ND6lH7SRGcB{EsgZcRr6#N7OK)c3}p1;jvC|%pB4mAu_HfA0+ z>(2e@j-v{s&VZX{jvuzJT4zuYO?5)aX?CphfgUT`vReJAbyDB=9yPP#Zc3yvBzDDw z0z5~QpLm(&vEd|cxd#~rp zJd?iziKAD^AzVB3%z0Ev=?b1u$pUAa!f}HLoP#Nve11Mk zB*i$JcWEH}k=PJfYx9MJZ#garn7@Ccpa6N}8lFgnN2{#i*n9e+0t3TeMO8EsmfvH! z-?U`gd?9y#aXB;q3)7tEG+U%Y`kvU|obkjiLvFNeE8!Qt6fvi8G#HGG zEE(ve;eWQytNdKM>-#u3KG^ePNWZ<^jDzmTBZ%9{Z)j=d)0XwI@$2|LDCw(lt7ZyP zE4`rEj*Zoh`KN}ijVVztY`|ES(Lrv4TX71BrAXFbol% z4T}k`?-F_woMd0X>%vfAv!*y@cB}V|OFVTB-K70}UQt8~HP(7bzXHneCNk}d%6*aY z$nV4s0JQa>;b`=hJZczK9CeG@)ztHf9!m7CrNeO|cc*WSQAZIeAnVd;g0M{$iSnVi zE~Lh4e^xER-o%f)7tHog!QbU}A@+^J7}R|ZHG#^7nM?z?aC!~_RGOk%9oXj+?RDrc zQNtsA4taj_QCGFQXW%J$5%Js2d}VgkTidynhEP6lF^%>`hLfR^rs+aZB}-)ycQ?e5 zYXg)DKMg2^pZ@8njzA1MTYy7@<_1kCGs(|bq6vLh93E?5VrVQYD+e}zc>>*dXCCls z464iU@e4O4M@zfs#jpWXmiw#o;0oY(O36m{O82#ZRI9DEC>d@PS>DM`<>~yU#Mhy3 zlD$FW_k7!&M2;wP^lPU-t{3Co2q(zvBNhv%#9q9k6zD3es-pLbIHp=taS#`4wXsn& z?{&R0ef<0f;nse~nnO~U{|A;5i`{np7(zajVI5Hajt$$!hb~JTr=~eZ9(Lb4G)e4p z+rw!t&0;XG?RQ%Wzt}OZhKc%LKMpARKO!Nj9zfHg_Eu+HXmMPdF?_4<4Y{Rjd&s8B z-dV4$CT8d4CdC|3c8`MSv8$h$ei7??GNl`nuIJ`fTnDqpiHvFYEASP0`_V=~zOxI4 zq&QIb?Ce`1$jG?K_@)g8TKH;bbLaRtGJf(Gn=nw# z+?0f3;V@u`!fr|Tc5`-pE74HM=*HV7VF35oB0<)56zxP}3$y>X*u@;YJLD?5Ak;;`;oi>5_S zEuCe5K_Sw)9Q|_4KqJ$_lVcKE7JPuh0f$3UCT$e!op~Bu>he@a9=G-I8+M%UA9NxW z(EGR7{4Tik@Q#L&e_I_SIeB+;u-DeM5a}7|ad0<3 zJV|k2g@1oGI2=8n*>|$3m#C{NIB54P^C?mo3pi!udrR?Jl{9u!NXD@^A?lMveN9;H69PhSpxwmZEqOU5a<6$&*l=!?_Dm?sNb&ncw9O5^Q zTNx+Aidhq*Z^a7c^nY=~4-Idw0}}-2aavxFcn+%E#m(7{RQ75I3OP#sN@tB+qFA^U zW4Z>pAFRG#`n6And{BQM*^<$||GEj(cDZ*mb0Kl~%Lk5iJ&p3#a?j9iQ>B??&v4}K z6!#67F+9bcTcq>o_pF7uL1JNB`Kl5zQj{s6&ef2t-K#hTh;dBsUTbk&I02~8>gJY` zsZl1aL&S%)eUxQi^)^=h1f?`rkM$f_`~$2pLY5)r3A&&Q*F#g5GN~AqD91rPPIiC|{0Uq2;DKlb z#P82pN37r>0mpy>K;Ygf+BPO4r!CLmWNO;YD=h3&U6g!)IOE$BCl)Vh6N^wPJPooD zF0x1;IP#P~s?6=}?TwBqQ)-KY5$C>f_Gijw_vh62^z^`BurFUQogVg{VusxC^YG*` zf6~*{P07rpASE3yddmZOSO%iM_*odUwY^=aT3B7klCH?c%se~cXto&E&~h>!QEX#La+o4?yiH*;1=B7J;8#z+va)R_xi4V z&fe$z=<4db)~s56*Q)AXt9l~URpmY)6CnctfDa1t(wYDOOzhi1{vP%X;mcmT1OP-= z6{ID!eU^`w)a++mNCwU)$sS0eu!XM@UJ^{q(>u3==@I6?SiToM&XpO?e9wg=YU66I zaYlDU0cuxh_t6kJqVQqCEgpo+JA(NMy+Z0`B53_#t5h-4U($xWGg6Jw@sDf55=eHslk!S5PYcs>UX2d>U{w8#+m7Tog{}2 zk7h$i@zrzy7%TKFE9VGB=5M{0$NT_@I}DYNpTkm*lru2uAp&}n85jliEtN-p5sL#N z?hPboAcOTU+Ywb3q)~6jbge^2I66^}~vke;H zG*597Y1172;jLqM?&esOTF|5cKr;a+41KC!a)5#)Fn*5g__b%t)6iAoDt(<^mi4cA zz9{cFO}m#P7wgoc@%ZWM)Lgq{Xh1+zH-6lBeCxgY*9V%vVC)Fq;L>0YVf=gT*B5r_ zzt@nlv3{c33H&+Dfy%2w>@-sd&BxDn2ne{jT3cA)hi0vr2`-5koZ&E*Pm}Lr0!u|+ zFZNycZ+k1Mg8j9i`JLDbHz?K)KOP0mBJ%|s1~tkNe`WSzw=@D-Fll-kwS8~l&(h6^ zMI@#GFZp$l)zjMo(+?$f*Xz&w3yL$4Q`lscmX;Q67BK2`im@Qy&_cO+&)8bhO@xCp z-h9i==vsyKL}rJNoztU;eA!ne5@@Xz3IF48Ght*+_h7s2#51($21DOsBPuN1*;vr~ z`?)#kG}m{CO%h6E zJystdUd>L0rvMum-D5LXG>AsJuKY`2Tkau9Npf+EC&>##4@pS^e5g{vBc8YOd115p zhJjgK>lXZ0^n_Ay63e$gg1cM*uH;p^+aVIgE?9?Qqy!L z={mo4_u3rvsmYX;9fwz;Cbp^@HLS?^#;>B=G7hxcmN`gHhH@-`c$nF9sgi~`a>K!B zTok@~r-k3@C}a}zFV{mj1U5JT;nYZIhhERdwgJScs%v#4KGkHz$oXCtSxu9oM87 z*|({Q6fiPcis!ME3c{mzfuZF^!IV`Z#5`p*5|F`;tL3MpM7;VXv8Tx#d3EO8VeO>R znwxUq#;{nYQAGiN4{>x#bR!;I*=<#VN7l?vzuthQH%+2;eooTa=p^JLd6?tG2rrt? zjLC-ZO7H{FqL~L@zP~_&HVLVD$4jL{v2mOJ_w{1%2O+dunafkHET-#|5xt+twM*@zTOLV5qH*Z!oDvD@FZv_0|wZ^+rg_d+rhgq zYE+yar9Ntb#TbMji{OPPBp7Z=GgPnFB0OK07w%I9ut z$f%TnEa1PxXpn{K`j&IVoTD=u#-%TlkFwKzJQ}?XueUy3^B+(17prfL0Lsfp08OY+ zadKmwT{|Fe!vm?`q_Uu~{gCl3y4_IUQWd239^8Rwn*S;222 zN2$UMo_JIsN=i^5Geigpi*rNi198`D5iSCOp@t4)tS8?mSGWKo_&*NuaY_>cjLI6e zNaV0SC#*vCP5EC3@&R``ir?$2&ICtSF*xo5*uqiih%+K`NO!qH;Vms`MM%1$A>Dwa zan{}1v?WI5`Wi@ngv@{oTFOKp3>6VpzK)npl8@`@i9MCW{QL14rkS}vkIwVA*pD3m zSC=NSGDf(#qu+BH&r$3I$n@CuvFV+}b@%W5rT_F=&mj+W6zSTjj4yU6EBP!k7(UWkLD_o^dlI-sc!>&xwfGqT3AKGvpG`TkSF=y6tv+#qdJTj*rGNGfrfS=|#azb^oQVn4dY6y;W^0g*WgC*CG;19v!4%`^tsmN8-U`F4#9uUY%KiM`=W92utf zZ(X<&qJlItjdX>`G}Bs`7(StdFeYJR{6^=s~t{l_Z<_JVL zqvrY%^ZPQn;(l3KG_z@)8UMc)WZNcTwR;6{n`dx1TiWXI<3?0_L@sS776smnOeqTP zUaK*|JPp&*HFF=(%Fcn4BMMqmQMsUvRLUQG`1r(`O}Y0!$@;6=yUPvUY@qXbbRV&~ ziH{~6ew1$fVaL}T8qiDXwz=7fP$PFm0QKvm@SXE4{$RsBM+IrpO_-~Ju#=7ut7ROuQK_*(QljU zV`)-E-+@yiHIWQNQW7#O43Z9ldq)pAHm-!IVN6>!rA^Yk1rnV6(hBGdm+`(?kj6&&-I;8 zlUvj7Q8Il!rcBolR_8ZZTSF_@m&Aute-9>lQ&O_5zZ=M1HMt9689d@CRfW;7sBIs& zeW84<<4;H>E*;k?)C?$d{}aeicg1+t)^5q53>F7_lqP-4BvhdtAi@Xm@2pwR~&pNiQk3aQi>Zw{TT%F z_9%LLCcQ--{~r~fa>C6rE4w~o3&1)-56(M7#n~tj%q;ROaxQQdc~f&Z0fbZpxv44{ zV&ujrbKY;&vQVo&KD~aLpSjKj*G`+qm4oX%qGVz!=G;e;N)9Q)G=nBOCrxPQK6=we zr6ZQeR;LWLqZR*0<3$Tj-!|^PwTqoDLW0exh4}Ssf9`|pDuBzKT><45Ud647&+f0%9sYD; zb1X#@Bk{=g3z52)Oc(&r$}&&t16dmW)G>$g`6+lCY|$D9OL`%Eze40h5dfzp5s=r zOTf^KvyoIhQ9^}94x?aVYkhB5<^!(}Lrn#12dDd$#hAYi)CDV41c*y9nKH|(a=SelM5SfV!3wwIZ0-SVKM&4UC_2eXX~RjfNvlUGns#d@+7VYGCn zrIf)A--!*FC@%t-tAzffJsDnPEJ0Gt(`IDE@FiiAQWn7{=VZK(p%EliVOA_wQJ8Xp zS&W(CQu(G@L;IW44W@qcdcYl+D{^?4zYvz7_nQ5mcy+JS89->G4Ye zlDcV7I`6FE%5CzwvVSsu2^slBT;Q;3dSJ)IZ*=7qBeKRdk57nvJNam*rYc=MkZ-WmZzWz}FHCkx|Rgni6en_*G%A+snnkUVe^KxcfJcN)FuY8JWDs4^_ zTOwy=3+Wr_#DTXG<>6>K--=YUQ;NIUl9}zG#u+-1EjOB=Xh+!Dg^j!Z`CP63tw?>_ zABeU+3=RzeceZX!A|@d(^tX2tyq41If zw$6$z01^ciZl@$1SR?6Lxg~u3m1f{JFa!7s0}lBkzNh@xn2}YoqEf5YgQ_C_BxkKknPIeua}?bPRyna2LbM%d6>ik6Vd8?#IlSoOEav zA*jc3!Jv-C>@)9U@FMBCtPuVcPJxG^`Fo`FF7M0=l*grhUTXuzt7{ECj%&<(YlD|DmDQm1*Ul`LtQ<= zedNW=vE{M`R#MS`pWw!jU!3L_!Fiio8>-h;kIf3@7=6eeok$M}mb;N3cn2lSe$V5@ zM?XqZJRf&K?&rAJ29yDz3W%pmYd!dNJ9izBIJ#p?=EJxXhIBflS*jAsa02});E2OxM&8SysTYvcWfFI=|H2~{hYc3WB`rC zL2kh{@5?62^4X(k3Ffvt_G!DF2@B+)9DhfyS^c^+;I|Cty^~{FL0@$uMB2*4{bf1V z@zKK@8F$xhiNxzNVr{1V)Y~<-#k(w!vjJy{ltd`!mhX-a;nCm2^sG$mYUJZkAD2p5 zl@Qx_JSAUqo z*db}HOT?GWIeFypP(X{MUie#XnKpN0oPbOV9|&7EA@j z!+WoBH(rbT^^%a-5BBOR2ZRM&R3lgu#%}mAMqA0%{xhF-Cz)#fOph(ssu_p2&@%jxs@p&$ZJ18B$cpNku^h%Xt; zjL+AuX9`8I&Ibl10q9E2jMIurfIM8PcXG^i7aEIZ%CUFoUg7yR&}DDU`+Mr`_Z~RZ z{>;TC5m5r4X()|lv0GP2N1tKG6+S^-{3{5qFlm}RL1vckFK{)&PDAB6B$=zaZNB)N zK*l&6!*JW98&HTCK%7s=kw>z`)7ySq_MHV49AtkUJ5(yFTefQVj87+bBv~K1diSH! zWdpw#iD1@;hu#-ADm9?Aya%_8*dV*%L&++~KdxvO=?1()6uDK|J}lUqev064)~^^f zftF#(hZMFudcE36afL{O#@rug%1L=(XwGSGGk<^HI_D6w3}tx^47yUr^2h#;G;J@J ze6;I+zi(?BhVSPP^ih&o8X*Qq@mL<{SpMjfu9;*!FShwipi(h;w+2f)5)a{KC-7Fo zY}co>v`P2jUrc3whcW2(W4;E=8J+88lJy>Seot}f#5%Li$NJ=Fuj6N6C|si|b0w-o zhmQ`GQ5PbLO&E_5=wq-YpGqr<>PD9gZHPH8w$a->EUX%?8x*OSPr#VDbp!>j-6$sM zKK>o+9&Qz%BsVs5Kp)?x`J)QeBh%5B9PLpgjIR$Wetg;b_ym1ni_ab0Ml&G3-uMXa z>O5L|Rl;_C%kI8fuRi%Sffb?g5uyf3n(Q?Ny;Pk0Z>|BLu2 zna=oaW>k^hk5=n1`w%c-@^RIs29rq5=gBRn^>{sp{nq@;+PL@4y7uB?ro$Mg^Gnd& zkDFR`A5YGurA@Gx!T2 zqfs~U3tD+k*@(P9F=SZ7eRIZ~Ogxb}DMm&5qp4lzJ1ziqZOWfI!RC zM|*vpO8p4W8b+`(bz6PVP_p;++Wh2DXI*ee{7Lmx5#Os&L0P_ z@D<%7Phl)S?D*vT$Ux3sK9z+mwpKdj47af&0r&Q$eXkk2hSvH3;}Q(J6cpI{Oa75h zt*bwe5Viv8W%$@0o@hqVe1^zQ|o?%kGBQ+8&z z$i<;w&b3C=%XG@>F@o`DF@XoMjk5&I(zyQX3d~MUnnbUC>g=P`%E3XW=pO7aRoU(QLpFZyB?8H)JT_hq4qQ4vX z^_9*OKOt5qHV&^5lB{?y4@>_cj;(q1BJnHy3u=ywSZDt3?-0Tu1%(Ma8z(J@*TXhQ z=0h~`{jF)m3@Nj}(!si1IlXA-lfC9CgbgjZ*rlz}=YCj1Cmtif@%X^Fv&Hk_wHwHj z3;{XTRga;G0zJKAJGfhQvrVw4-(l1$_E+{;#`O(`Ha)V#RW=J;x$dj(M(&{&6p*A> zaQSUYwzlG%(WLF9QpCk6$nEae2`Z zN6(aw!9WRRktc?+__a#gKW7CUjt!Fh^gl4^PeCp$o`o>tJpyg1HkqnM9Qz@dNlbVb zXsE6Y9JCT$c|BE+`!Z7^Ib<$d$Yjo`577k*xlS!hh z3()cd3Avy8EyJAAB4aH_TiomFLi7cnkaVA00{#blV1k4c5z}{CoJjMj^jBWf>^6+O zGU{~Y{j}nhcp?-Bv{|}IvVU2s^@5f zU6teMxW%Y%wOr)`*Jyi)Yb`^iTDTfN#8E;+B^~4c{*=ZMYh?2-k_Flta`T%{_DY5` z9_uXyi18dwu5R~$lf2ZdiOC37E^Beq0Y7t%axDPIT#+6JPS1ByOIN?P8`#lPN@`*q zw*GcopZ?IA+b8*xo>4$x;Jz}yfVw+Z(&)F#GO<12V8MfTg~Z+=pglu6wk6c=YT>^j zbgSME!?1nUgZj}R4~zOq&NJ)JiZwG!{$KowN=Xs{yjE_bSDd=R7%r}rok30RXTu0fuR+Ah-OR3f^ltb*C6F%-8hN=pF zEHPcu8LhsZxdFY+i>dO*{cR^IJ=FT=C<)-F-vzsObCihFVWviO@h8M`Oj|G%)8pDN zPB~h1X5n9{nkGxQJHSJmaa9*&gqd3(OJK}lvZpv|caS=Md>N2jI*68n^825n62JRC z_~i8bvhqzD&W*tA#c~mu?eW_Za@14pv6QaUrrzj6AIvb#;jbu9h%`+Uwx5ii*q>CV znEnvm&M9w%>_!-7SPn`9(&hBW2`;qx!u6RY#0fL`_o(bX2Zl})bXC7%rRHz5%7Prx zlE7T2hf*2n5BD&ap8{zRUQ)q-n{Pi{XBIE)a08fibG{(P zhSkmChuf8DNbjp{@TcV%ic^GR?s_*oV2=Zz4y$i@bu|9!siUy9FS;%zdp^)IB!USE z&JLi8l+)-@;N0V*V}T~_^WpQgGGtZL_nHKC^u=>>Sl@Mc{-*0tY&_-F;Fc&}x*iDO zKe=M2(y}|Go8!^IPjtn?08@gV;WiA;>kj>lJNcVM>}S4s*1-uO#4x`{Muv)FWnoCn zISa28GhQERNUjIeoK&sFQ}7UBDGvexi5{%ZXxY&K4P5M)NDz!{EiNqi@AEd}Ei-&g zB$Y|FM0x03I68PKt71Of8m8>^2*jO1viyxFvnk9k*V2RKaWTrXrD@-bns#S@bQ3!) z#xg=GsWQMh5cE|Xje7Ld!SjzpSY#4t!CVSUAQ@bUuDj4u1mhXLS8VNuP&AMlz(#yq zJJ)is`rCKli6<6jBM;J3EkV$IN@ISKfN2fm=t**CvWSe$Kh|7Atog*#WC*R&-QkW= zro_{^?_Uarxi!*NIqI}mzG}_LH@vIt=tn16B~VE6TjTmk4)Y%dWb(1AyKb#onX3(1 zX^WK0T)|?4JsbdGTp?v46~#JSvkbjgr#;L4{o-bD9hbzk;1Jv5p552Y zrZ*P&$+x@Z2B%XDP_EBIW;=Ch+-Zh=!#hm+X+`CQQox;f(&}V-z4w?-TNrf6q$3#L zkjKE5}U1i!YPnSQf zUw^M`C^{ZWc94v%txp0?7%HVZD|)T-p}<^^S9lHyE@h5U&C3;Pm$Fu@?NO^YP|-f0 z9JH*P25Pyxn8yyaF9Df;hZ&m}S6cTqbE*6CeH-gdMQY}?`(lyD`6IF=$~m21?fe#jL6SssS*gJ+8Z*!?Mw?Qs{M!FH2 zy|4IL7d?l9FB1G7qX8L)PQ(Dy-p7*Yv1JMJ+Y^2XIV=`P2R;7Mh=L96V1qlZJ9RM& z(#h$ixxmTXpBA*jL3*K<#!o6g-mNY75vmn8{McSV{3NKGnaNV&%eUtOx6%L0BF}`seQkyiN}N)KusbKnCxv_!6tP(6tU z2b%t}-B{&uZcQ=K{P@G{d?(Nyuf)1R2rte)=-_p2zsA8ypwYAE1D2|IWyorR!JhZW zm}D6Z^8Jsc-^m=KX|Axa+bH51e;kmoO_VrJWc!*yH1LRLagRS9t(_|2NcO(7);yGQ8C^din?ABCxy%+(#PHa`EX#hZ$QlE^lX;MX#4~GM_IQeMgELdo zvVrN=6K7W?pS;7)>(hYq7mmRG_mk+mY>THC6N(dkP9??GbR`=TG|qfUcfiV!WKu%; zcBM@4)q6(1qwhw+9NB*7G-9i>lPM6AEmRAACnY)lq&GbYb4dyyFeTw4&McUIAQ6{k z`R{F&fU3S`FHAzzK8c3)NO^R9?NI4si*aLIa82(?rn+&AAZ^CSqUP`F@{rvU zlk@wY45^@;v0lR$Q-MOIeN&0dD`?C~W?)*3KD}w@V4C-mNR?AN#m}5W5V)0@J~xBE zC#Ir0+k^>wdP(o5l0WG(0XL=WX(jLGJAy2?r!6$bmP&hWb&5PVOTg$ip5`xn7vA4% z(~D$`+AiFT1V-0zqZUUFmHG3J^^qfT70eB~qymCG9dRvs7AJKN|LPhd_LqHEKxli7 zZc|r&+%JE6zFhNDWbWy8W|g<((>A_8b3 z5GpVr2J4^bq7YXJH3ahKgAu^_?;FS<1%jb?OKJb_p+y`J@D@z`2mMco@o&I^{15sT zdBg$Hd>U>J!o?1!*a-VD`a8-5c$7K6-?n$V)aR3(r7d3u@J zaqS(x-#Cw)qKM>6lb-t8^mSHyeXt$X*v;K*;z42JFiIRPOsF9aTqMPvWL`cuS5@2J zyHIWhc37fGObMjWHZ)^`$oC7S6wZ>caIl{-D3Id*a;%oOKlZS$wYKv5d(R!yY%igX z{bE~iTR7Ja96k^c2^{wwcfUC1SrojNv*1(3qq8((8&N?1t;t7Dn9W1m*x*0OqvI{| zbvwNGi(Q`rSaPD%?fJynd85OU2St;ajCQpG+G5hQQvX{hqTQ;7w?u;{+%*J!@$LN^ zKeNhq%Br+M$kjUN_KT9gm_t_$iU3V&EIA+2g+)~ay~6O!7>nw4AsJhr^@Ox zsISC#HajYkx3P#V>?JHZH&b;W8ajZlV>rA^UBE=*LVOTgzLlhz{$|f*g$}cf4-Lni znB5%b1HNo|_R>qvcZY_7eNUj0RA6kuVZa%?XZS$MH`&wgagpz|kx1y@fBHRa%IK@0 z@Ke#3Blx+FF}d7JmT^c)-z8PEw`4!=c%_jM4rh!_SXQ~uEs09LWeQ56SM@Vdiy%UR zGlwpjF_MXiDO2!FCHD(e%xZR)?Vu*tOxqWlaz|vs9KCk|nDwEPO?o;tuO5*icDfMB zem4oKrA=l&E@|a9KpvJJpPANYaiuM#o}YP?u&-Y7k~)-{Q{O1=6V6I)aDPI)>C#qI zBfXMhmmZnC5bsl{)v9?#PlfPLbSma>8|!x}Hbd@L*lt=q?T=+Nb_QSj zt@P;{lqQ`muHDxT3-~&v)Sm_rsPtP7(cYFAqRePn9e#S=LC9B}6p-jQeD?#l_T{$c zH^Sr9v4>T7uWR!d!d{!DRsTNU8v7FKx#M1?T`4-*_A+#&kXZ%!237KnZ?iAOu%DCrqg(ELTp>KFy(E`gBjI?Doj;HViD?xE z4|$eIxBB-9qct)x$8TQ7Y223?UutCs9CV@tT-4)%xkKwJ=Lt-7yC)4dE3F+!l--!G zS+05Q`!D5cV7@UCb-qnJ>|CMMnl<(1SUZ)MCy$(1Ij0QW(n;0gyA~%1}PeSB12cPgtna$lIvY z#H~{b41DZdp)a!eEYjW&nb#sX&Pdb~6fF{M5;Li3U9FkTB4_{P5M@5`u?9Vb_E$&1 z+t#d3P+r0vFeP*2Ts)De-@1*#&2#*mxt7QE7c%31`qX(1hsIHD{noZ=5J!PfNS26VPuG-?=z6sTyPM5q;Iq9zle~QyHIgmk^7=i;? zQ7p&a`yKiGnEI#I70u<;B`1QiQTYqizKC|aqjP2Yj$}|+F7%%t;=;v4gg!a)$9XY) z+y+F!4BnLF0v&$A79wj`ybcCmKU}Zq2K!-aT_Gl3R*#CBj!aIB-U01!J!Bg{wL!9O z2!`ebWvhQGn zAKX%6*YCH-@aT9NY4?^pV`7rrk?=goZf}%Um^&ijP`W@2+a<$ zPdT@ATx36E=Q|PJG9RHtM#BX%4{CP>xY(}8&FUgJw(Opi1*xD~&~V7VtTWYX1&cCE zFk1TWC48z9hPDoG6Mmgzk_jjc3Tkt!mQ$up1EW|J)m|Z0gJAwyLB`*~R7usXxhxBo z;$pmuJ62NyN0@%rgx^5oLw^~Kn0;*v7<;ledCYiP9RbfN#y|R4`tC+nwSM{i`|c^a ztisOup3K=`zY|MBESz5ZyRSV8(S~VqaPBylKDd~MkahfJy2pO?LzUx)&ZeKd?MmL>G@DlH zq!v5RkyOX4kIAxJTT1*4F0>lYe|b~?<5vkgUZTNnbT%l^d`8cmMZ*zFN>V?d^MZ-C zdeDD83em|^9`Aig))x>ACt&HNa5m~a9)nH|=$Y=e=+T?3?BUX6{fP1L9XD(#?)}(~ zUGP{lcAB8Es$ENFB0suMm6bvx9ayF8W#oOc%7` zeddW?Ge3@^Y_T=Vo&Pq$U@((B`5OvV;ED){h+4}&dr9R(nS?RQ0kOs9tFLsElSBg8 zy{#IgVvl|wBRY~+u3+AWerKL^+OdMSm)Mp{ahg!T{s99@#Re;BE~cVKj2_4A{^Yrl zl-3Hu%!p&$P_nUw%XSIgX)0(vuAS3^W)#2sf;#vqRY;fL(Ov~#k{On2uSrYW;XJpZ z(Z#weLO?~>ak}`@S3wMFt_eEx*a^=R?faqxrWffx&|;>z6kqD{rRe%EPw7(s@Ra$2 z*z=3{_4YBP`f}w(Symhx)xx!TB3g1r6A@sOV{!Av3M(t12l7-(qaZmYqF-0+ZP};g zvMC^3*mQAjo9JsCtZ~x%h(VJ~oJ?+{nymS@pv|K;A+irVvVEIJgcp`pwq?4}Avz8$ zS}6^2+?Ct;iwC}Sr@kNkx){2SYcbO3Z((7s$I<~a zs1#(`&IKOTP-=a?{9P0|r#p}u7@34DR$_TYkdR>|B^vt^0!~TK`_&M z5GKL>ys!*L(Y&evUHbSjdSVpAR6{$yht;EtMq5NcgZVC8&JifHVSLojE};}R&NtT~ zM5JaIp;W3)oZ*;c7CGW=4cp5FQq+KsS%^&z9_Uh$+w#de96fo0D9;yHjn2Ld?bM8q zfd-(ybK4U_lL9@mi~%ogTb~#RG5fM@iM&|G$MjQ-EI#5KVS%0Zu0wBJSK5|#v-u%b z1M++?j36&P;gbV)HM8MRWEEB$KP~~WYvqm9EDbXsc4$78+8p{a$;nHb%QErYf;R#l zekCy4@mFc}qvOv-{vSK`Gs3@)1zD{g>nz;5%_nu{uODZbCgBD9_6|LI;(DAiGCWy` zS5W4-CMXc0=3yUFk>Nn;6sp{&7a3% z_Y5vF;@j#si{xE0QIHF?g0=k@*QItl(h~;759n>!S0pH%4iGS56&=@rG&pxu%JQ8f z=Vv8St}fdR5V(tLg=gh%d(2L1E|R zdkMF)V6;>5wNYtL*h3sAvj}NT{aj&ChYID+cC@2nE;psx)I^elVBAN%7!x zQ_mJ6GnI~J`g->x;wqy=oFXC3E)iMg{q6X>TcNKrx zbo?%Ps9H|2h`$PFXJ5ouoU7*bFGSM-W57wM|4#vxe7pRgVh&jX1MZ(fn^~LrAF;D| z(tl1hn3SQpAlIElCuw=1jwHhy^{eAFcCEQQo)7+%C5A#ck!mZH@5|@p9Hjpq=6#x+ z)3HCD4SF?+RgSrjPtMHZ>Ov8;%zFTo%yK>hkwjL`|+L*JH-#gfrWmafxfi1u`mq|nRlEL*; z!#LN9o4n#XJE*>z&_6c{4JE>K(_ofdK87!TBz}|jAGRA%n9eo`^z2WqXtY z--p`4^HXiVEHP~>--2|V82%BADxmv8cR3+yIFp5hFc-C!i1x;xIyhJM>*`!o)EugS z9J04KcqX9aY1UWvB{qd_;8=<$HfL>@&(?flkK7Ri(q;DguNCHR+Vg=b1HG^yst+4U z37+jEbzDztFfi~9s+VPR)ADODg6=Glf*X!do|4G4t*=~L%X%|q1kGGGdP(ayTP>hk z|LHu?+eqM|u-QRxo(5jMI`s@dsiY^zm4h+(T0FD;Ib^*XCARwIcxT2AN$hVp#VayYlcA?3#}wM2YOt}+dysnAnM@Pf8^P1?=DBgP z9}2X|pxr^CL7bW?#K2a!o?NR?X>bTqZ>2ODlo>>cLb9#YU68Ls)e*dlfO1F5^}bsM zT$4jVc%R>A*_8gC7n{ZoUNyIuvt;F)16#(Dxebno^wrBcLjO*6b_rB7x zvo*hZ)?8D{!PZ&iN2CX1&0qV_M5SxFUUhDXeBjyF?WY*T^jQuu85Epmp}o5#y>$9} z&>o+OR&*MYq^GNviYfuL8NN~;?sqOQ$NA$goe}woxA3xKL7;i_?~Ik~G|#MZTq!Qk zFJ~YPQnZqQUS-B(*)`nQ)+v}v_!;=)KX(+Lj}a6kvj_FZ^mk9cp`+C?)@1Da zb<3^wJ7n!4=}1^mSo*V`nGp|Bx(r_jy=wbcVSn%2X>F1L*Qbw+`^7)^Yj-oj(ws7c zbdl(~5Z>A1UQ)#SFwu(&-iB>R4$p>Hqd_iPwNtu7o z(67aptpzv79lK?pvkTHOa^>Rw98{S`aH$)v@n>h6t_=Q$$fXb1nfEX)pWPN3dGA+) zpV2z1g7gyy>X5-*-k6(Ut93gB8)m?!s085d5+4n=@ zRR4Vu*mTrLN3Ee;(s|h6ad_8%Z#2sq0Ixw3rpW`ZJxXP$gK|i0@HEmYB&16yE`cML zXTzK-tESxYfzf$Q3wTmLZ#;)vwU$u)9ywnCxyT4U(@H?cqQ0}eqSz}kFf4^lMjfM1 zgJBuDxNM21SgFO~^8yHa;Tz|hB1m3Hl>XPwf|0Fj+a7|b0(A(k@)lD2JT16{{;_Hp z^$l587w=<{R8zY#N+SGk*9-OA><&GPdBu**m#WN;@XJvR`C!1mWk~QmFkUSj+hoQ$ zI3PPyv8eM%`0Vg9{cM0b?X>zEo#=1x=CxY`#Lo82)E2iJDX`f6=}LL`j{Qc>v$enV zGTUu+Or^TsSBW8fh@`GMrpl zvqOA2llc`gKpWOsJlkVXF*QrZN|m^;O`fPQv=6}Pps(4++qsLq86>zLefcNxQuucL zaz$51>$$o3tbJ5EwAbuv^L)XXon22Y(UckBfQxG|Rb>3E^|hZ~SY>N(C196cf`_mv zh|avC`c*m+=&Yw>0HTxCm@FbUF1Kp#Z1^+P><=|UA8o0pw~LwQTdQdiCYnPQBa|_S zZ%q%HI*$Jg2Gr=_IE0aEd$5NDycDFDeY#aQGQp_rAZ=Z$0S6cvgT|aDAN0RIsH`X1 zB|j!n#OBTe8*bM{5?CpzBY8T<7Z(hFit&adGc8zgC}^c94B5IxsbN7R%ajL#N1xud zA!`*G)poJBD_sN=ze?5M0;D&9Ve2c{Hk;#TSHr4q-M9S6#$~aKcD+Yq4rkAJ;=zk; zhsfPM0?y1Yh$SRzbI#Gl;mYelW8)5?a+Y|Rt&Z+1%6NxT3&+cjT%iGU>B-cHosQg~ z7Nh4Ig8>}{%>4);K0iUGmy&MEj76IdM+RMqWcZlF$zR{aAhTCc6r2H!_)o_o!b^%- zuOKTuE^~>&AYTrLiu2D%jpTXsTLw{!dP|mjtipxv_8)16@aOD_Z+VTTOQ-MP7)_&~ z<2h*7VUE{&w|!QeXhrwX{+B~UF3B(Of;tF$=tY7&5?FsSrC6QXtwh!0jBI`G);f42 z_1v}RPn6mpq747J|0SS~MJz_k%;5O@+wZ9uZiYV=k%RkXzXahlEuHaLIV2f5LU^|^ zw@HeqvKsu>S}m6!JF}k|9GIobxVVC(JX4@A&yzKo?7}Ub%cH@@hf|R>xX8yU!b!tL zQ5aE1_T@PH}E9KEp=;Cla?)%M(0XMyPmBj4NBudX4V$0)XR!Ap4$rzR2| zF8{3&^UpYTCajrDX2`-1KCRZ5x|=5*_u#FVrTn4URcOvK5mDRm>I9gmkLOTE(pt?w zW9lrE8TYUY2*5Ztx^a`k@D21Av!qt|Wtv7BJ*2D2Wm!R1Ej>0}v@YB;xbQLA1P8nI zNZ+z?l@$@Fq9pChd;6XD{z-+NW!&DMn|7l6`vhnYf@>_RPkaFvpP8#kV~)vw>r>Lq zmqPJ7n*uO;dI`iW5EbM3^}udSTxLUN?}bRGG3jQD@oL`!J}b(13ZRHU5NaPW|BZy8 zsI&0by&cQXqyww><}MJ&uy|dc&QlarHL54Vhmqh~SNU=) z>qREFq1{8e00y7i?%dQ%weXAgQ9~6Dc88vRWX&S6;D=&O9Y2P>>btw|WeYohk7@`Y z1F8qRb-3S#nMA?2X2kQ*d5Q|Ql5c_U&t!v%nxl_1Ke6Q;p~|KsRR7ZQQ1{2 zW+v_Y&Tv>^hE(aaT$uHel(^5<=d_y2vVIpw=Y<}E)Y%LC&f6CrZ@$$KdJFeT{ z8-%>31_KZpdG~70Z1|yD8UW~Wttop`1n7+b7nX5}F@9|Z07B|bz25c`JJ{`k=r+oNx6d4GC+}lUlfDo;W!$qY<)$?lBsDXy=&Kk(s1g#Pl z0o{j%@Sp0a-bgS2pU8x6yl%E`*>2->H|U0knK0l#U5003bFh0$8pcW;1#jH+~% Iq-p5?0uQJ6QUCw| literal 0 HcmV?d00001 diff --git a/doc/ides/images/vscode_cmake_preset_selection.png b/doc/ides/images/vscode_cmake_preset_selection.png new file mode 100644 index 0000000000000000000000000000000000000000..f73ede5be02143c3737181b5c970d91b1656dae2 GIT binary patch literal 12993 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 literal 0 HcmV?d00001 diff --git a/doc/ides/jetbrains.md b/doc/ides/jetbrains.md new file mode 100644 index 000000000..09a95b92f --- /dev/null +++ b/doc/ides/jetbrains.md @@ -0,0 +1,81 @@ +# [Jetbrains IntellIJ CLion](https://www.jetbrains.com/clion) + +## Linux + +When opening the folder for the first time, select `Open as CMake project` if the IDE ask you between Make and CMake. + +The IDE will open the folder and display the open project wizard: + +![Open Project Wizard](images/jetbrains_open_project_wizard_profiles.png) + +CLion try to determine a base configuration, but Minetest define it's own presets for easier setup. So you need to +delete the `Debug` profile with the `-` sign and close the dialog. + +You should notice a notification telling you 4 presets have been loaded in the bottom right corner. + +![Notification Popup](images/jetbrains_notification_profiles.png) + +Clicking on the `View` link or going to `Settings > Build, Execution, Deployment > CMake` you should get a window +similar to the Open Project Wizard, but with the readonly presets listed. + +![CMake Profiles](images/jetbrains_cmake_profiles.png) + +By default, none of the presets are enabled. You can select them and enable the ones you want. Keep in mind that +triggering the CMake project reload (VCS updates, config changes, etc) will reload all the enabled profiles, so unless +you need the other ones you can enable just `Debug` and `Release`. + +If none of the availlable profiles fit your needs, you can create a `CMakeUserPresets.json` file, edit it by hand and +CLion will load the presets in this window. But the easiest solution is to create an editable copy of one of the availlable +presets with the `Copy` button icon. + +After these steps you should get an IDE like this. + +On the main toolbar at the top right, you have a dropdown for selecting the CMake profile to use for the build. You have another dropdown next to it to select the build target; by default the `minetest` executable will be selected, but you may also have to use `IrrlichtMt` for building just the library . + +![Jetbrains IDE](images/jetbrains_ide.png) + +You can rightclick the topbar to change the project icon and color, for fancier looking IDE. + +## Windows + +Under Windows, the recommended compiler is the [Visual Studio](https://visualstudio.microsoft.com) compiler. + +From the Visual Studio installer, you need to install the `Desktop development with C++` Workload. CMake is already +bundled in CLion. + +By default, CLion have a MinGW compiler bundled, so if you want to use Visual Studio, you need to configure it as the default compiler. + +CLion may ask you in the open project wisard for your compilers, with MinGW and Visual Studio if you have installed it predefined. You can use the arrows to make `Visual Studio` the default. + +If not you can go to `Settings > Build, Execution, Deployment > Toolchains` to change it. + +![Jetbrains Open Project Wizard](images/jetbrains_open_project_wizard_windows_compiler.png) + + + + +Then, the process is roughly similar to Linux, you just need to pick `Visual Studio` as toolchain. + +![Jetbrains Open Project Wizard](images/jetbrains_open_project_wizard_windows_cmake.png) + + + + +[Vcpkg](https://vcpkg.io) is the recommended way of installing Minetest dependencies. + +You need to let CLion know about a `vcpkg` installation to let the bundled CMake use the dependencies seamlessly and get +IDE integration. (Require CLion 2023 or later) + +Go to `View > Tool Windows > Vcpkg` and click the add button. I will open a popup allowing you to add a Vcpkg +installation. By default it will download a new one that you can use to install your dependencies, but if you already +have one installed or you do not plan on using CLion only then install Vcpkg by hand and select your installation +directory. Don't forget to check `Add vcpkg installation to existing CMake profiles`. If you haven't already installed +Minetest dependencies in your vcpkg installation, you can do it right from CLion's Vcpkg tool window. + +![Jetbrains Vcpkg](images/jetbrains_vcpkg.png) + +Reloading the CMake project (should happen automatically, or display a notification for outdated CMake project) will now +load the dependencies. + +[More infos on Vcpkg integration in CLion](https://blog.jetbrains.com/clion/2023/01/support-for-vcpkg-in-clion) + diff --git a/doc/ides/visual_studio.md b/doc/ides/visual_studio.md new file mode 100644 index 000000000..a08a58ea2 --- /dev/null +++ b/doc/ides/visual_studio.md @@ -0,0 +1,7 @@ +# [Visual Studio](https://visualstudio.microsoft.com) + +From the Visual Studio installer, you need to install the `Desktop development with C++` Workload. You need to make sure the `C++ CMake tools for Windows` component is included in the installation details panel. + +You need to install [Vcpkg](https://vcpkg.io) and install Minetest dependencies as stated in the compilation documentation. + +For the packages to be discoverable and used by Visual Studio, you need to run `vcpkg integrate install`. diff --git a/doc/ides/vscode.md b/doc/ides/vscode.md new file mode 100644 index 000000000..6814a7a88 --- /dev/null +++ b/doc/ides/vscode.md @@ -0,0 +1,51 @@ +# [Visual Studio Code](https://code.visualstudio.com) + +VSCode suppport for C/C++ and CMake is provided by +the [Microsoft C/C++ extension pack](https://marketplace.visualstudio.com/items?itemName=ms-vscode.cpptools-extension-pack). +You can install it from the VSCode extensions tab. + +If you use a unofficial VSCode distribution like [VSCodium](https://vscodium.com), you will need to install the +extension pack manually by downloading the VSIX files and going to `Extensions > ... > Install from VSIX`. + +CMake support for VSCode uses CMake presets provided by the project by default. + +When you open the Minetest folder with VSCode, you should get a quick pick asking you for the default preset. + +![VSCode CMake Preset Selection](images/vscode_cmake_preset_selection.png) + +You can use the bottom bar to change the CMake profile, change the build target, build, run and debug (running/debugging doesn't build first). + +![VSCode Toolbar](images/vscode_toolbar.png) + +Like most of the VSCode experience, it may be faster to use commands directly (most of the VSCode UI just trigger commands). + +| Command Name | Usecase | +|----------------------------------|----------------------------------| +| `CMake: Select Configure Preset` | Change the current CMake profile | +| `CMake: Set Build Target` | Change the current build target | +| `CMake: Build` | Build current target | +| `CMake: Clean` | Clean build files | +| `CMake: Run Without Debugging` | Run selected run target | +| `CMake: Debug` | Debug selected run target | + +## Windows + +Under Windows, the recommended compiler is the [Visual Studio](https://visualstudio.microsoft.com) compiler. + +From the Visual Studio installer, you need to install the `Desktop development with C++` Workload. + +[Vcpkg](https://vcpkg.io) is the recommended way of installing Minetest dependencies. + +Follow the official documentation to install it and install Minetest dependencies as explained in [Windows compilation process](../compiling/windows.md). + +You need to let CMake know about the `vcpkg` installation in VSCode. + +Modify your `.vscode/settings.json`: + +```json +{ + "cmake.configureSettings": { + "CMAKE_TOOLCHAIN_FILE": "C:/path/to/vcpkg/scripts/buildsystems/vcpkg.cmake" + } +} +``` From 5ddfbb1961575241fbd7c47cbc00dae173d42b1e Mon Sep 17 00:00:00 2001 From: Muhammad Rifqi Priyo Susanto Date: Sun, 26 May 2024 19:28:24 +0700 Subject: [PATCH 053/146] Translation updater: Add support for function call without parantheses (#14574) Co-authored-by: Lars Mueller --- util/mod_translation_updater.py | 62 +++++++++++++++++++++++++-------- 1 file changed, 48 insertions(+), 14 deletions(-) diff --git a/util/mod_translation_updater.py b/util/mod_translation_updater.py index 8d537841a..d23b96e59 100755 --- a/util/mod_translation_updater.py +++ b/util/mod_translation_updater.py @@ -118,21 +118,47 @@ def main(): else: update_folder(os.path.abspath("./")) -# Group 2 will be the string, groups 1 and 3 will be the delimiters (" or ') +# Compile pattern for matching lua function call +def compile_func_call_pattern(argument_pattern): + return re.compile( + # Look for beginning of file or anything that isn't a function identifier + r'(?:^|[\.=,{\(\s])' + + # Matches S, FS, NS, or NFS function call + r'N?F?S\s*' + + # The pattern to match argument + argument_pattern, + re.DOTALL) + +# Add parentheses around a pattern +def parenthesize_pattern(pattern): + return ( + # Start of argument: open parentheses and space (optional) + r'\(\s*' + + # The pattern to be parenthesized + pattern + + # End of argument or function call: space, comma, or close parentheses + r'[\s,\)]') + +# Quoted string +# Group 2 will be the string, group 1 and group 3 will be the delimiters (" or ') # See https://stackoverflow.com/questions/46967465/regex-match-text-in-either-single-or-double-quote -pattern_lua_quoted = re.compile( - r'(?:^|[\.=,{\(\s])' # Look for beginning of file or anything that isn't a function identifier - r'N?F?S\s*\(\s*' # Matches S, FS, NS or NFS function call - r'(["\'])((?:\\\1|(?:(?!\1)).)*)(\1)' # Quoted string - r'[\s,\)]', # End of call or argument - re.DOTALL) +pattern_lua_quoted_string = r'(["\'])((?:\\\1|(?:(?!\1)).)*)(\1)' + +# Double square bracket string (multiline) +pattern_lua_square_bracket_string = r'\[\[(.*?)\]\]' + +# Handles the " ... " or ' ... ' string delimiters +pattern_lua_quoted = compile_func_call_pattern(parenthesize_pattern(pattern_lua_quoted_string)) + # Handles the [[ ... ]] string delimiters -pattern_lua_bracketed = re.compile( - r'(?:^|[\.=,{\(\s])' # Same as for pattern_lua_quoted - r'N?F?S\s*\(\s*' # Same as for pattern_lua_quoted - r'\[\[(.*?)\]\]' # [[ ... ]] string delimiters - r'[\s,\)]', # Same as for pattern_lua_quoted - re.DOTALL) +pattern_lua_bracketed = compile_func_call_pattern(parenthesize_pattern(pattern_lua_square_bracket_string)) + +# Handles like pattern_lua_quoted, but for single parameter (without parentheses) +# See https://www.lua.org/pil/5.html for informations about single argument call +pattern_lua_quoted_single = compile_func_call_pattern(pattern_lua_quoted_string) + +# Same as pattern_lua_quoted_single, but for [[ ... ]] string delimiters +pattern_lua_bracketed_single = compile_func_call_pattern(pattern_lua_square_bracket_string) # Handles "concatenation" .. " of strings" pattern_concat = re.compile(r'["\'][\s]*\.\.[\s]*["\']', re.DOTALL) @@ -281,9 +307,17 @@ def read_lua_file_strings(lua_file): with open(lua_file, encoding='utf-8') as text_file: text = text_file.read() + strings = [] + + for s in pattern_lua_quoted_single.findall(text): + strings.append(s[1]) + for s in pattern_lua_bracketed_single.findall(text): + strings.append(s) + + # Only concatenate strings after matching + # single parameter call (without parantheses) text = re.sub(pattern_concat, "", text) - strings = [] for s in pattern_lua_quoted.findall(text): strings.append(s[1]) for s in pattern_lua_bracketed.findall(text): From 0804008ec201051634ae71dce569ad9fd38fc5f6 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Sun, 26 May 2024 14:28:30 +0200 Subject: [PATCH 054/146] Mark Redis database backend as deprecated (#14679) --- src/database/database-redis.cpp | 21 +++++++++++++++++++-- 1 file changed, 19 insertions(+), 2 deletions(-) diff --git a/src/database/database-redis.cpp b/src/database/database-redis.cpp index a9e549c33..a9a4a37f1 100644 --- a/src/database/database-redis.cpp +++ b/src/database/database-redis.cpp @@ -32,6 +32,9 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include +#if VERSION_MAJOR > 5 || VERSION_MINOR > 9 +#define DEPRECATION_PERIOD_OVER +#endif Database_Redis::Database_Redis(Settings &conf) { @@ -66,6 +69,14 @@ Database_Redis::Database_Redis(Settings &conf) } freeReplyObject(reply); } + + warningstream << "/!\\ You are using the deprecated redis backend. " +#ifdef DEPRECATION_PERIOD_OVER + << "This backend is only still supported for migrations. /!\\\n" +#else + << "This backend will become read-only in the next release. /!\\\n" +#endif + << "Please migrate to SQLite3 or PostgreSQL instead." << std::endl; } Database_Redis::~Database_Redis() @@ -73,16 +84,22 @@ Database_Redis::~Database_Redis() redisFree(ctx); } -void Database_Redis::beginSave() { +void Database_Redis::beginSave() +{ +#ifdef DEPRECATION_PERIOD_OVER + throw DatabaseException("Redis backend is read-only, see deprecation notice."); +#else redisReply *reply = static_cast(redisCommand(ctx, "MULTI")); if (!reply) { throw DatabaseException(std::string( "Redis command 'MULTI' failed: ") + ctx->errstr); } freeReplyObject(reply); +#endif } -void Database_Redis::endSave() { +void Database_Redis::endSave() +{ redisReply *reply = static_cast(redisCommand(ctx, "EXEC")); if (!reply) { throw DatabaseException(std::string( From b81b760d55ad57f16563841e020c93e7d634317a Mon Sep 17 00:00:00 2001 From: grorp Date: Sun, 26 May 2024 19:49:32 +0200 Subject: [PATCH 055/146] Android: Don't crash if there is no web browser installed (#14683) --- .../src/main/java/net/minetest/minetest/GameActivity.java | 8 +++++++- android/app/src/main/res/values/strings.xml | 1 + 2 files changed, 8 insertions(+), 1 deletion(-) diff --git a/android/app/src/main/java/net/minetest/minetest/GameActivity.java b/android/app/src/main/java/net/minetest/minetest/GameActivity.java index 2646721d1..b0e301c53 100644 --- a/android/app/src/main/java/net/minetest/minetest/GameActivity.java +++ b/android/app/src/main/java/net/minetest/minetest/GameActivity.java @@ -23,6 +23,7 @@ import org.libsdl.app.SDLActivity; import android.content.Intent; +import android.content.ActivityNotFoundException; import android.net.Uri; import android.os.Bundle; import android.text.InputType; @@ -33,6 +34,7 @@ import android.view.inputmethod.InputMethodManager; import android.widget.Button; import android.widget.LinearLayout; +import android.widget.Toast; import android.content.res.Configuration; import androidx.annotation.Keep; @@ -201,7 +203,11 @@ public int getDisplayWidth() { public void openURI(String uri) { Intent browserIntent = new Intent(Intent.ACTION_VIEW, Uri.parse(uri)); - startActivity(browserIntent); + try { + startActivity(browserIntent); + } catch (ActivityNotFoundException e) { + runOnUiThread(() -> Toast.makeText(this, R.string.no_web_browser, Toast.LENGTH_SHORT).show()); + } } public String getUserDataPath() { diff --git a/android/app/src/main/res/values/strings.xml b/android/app/src/main/res/values/strings.xml index 9eb1d3ca1..5885a7b7a 100644 --- a/android/app/src/main/res/values/strings.xml +++ b/android/app/src/main/res/values/strings.xml @@ -7,4 +7,5 @@ Loading Minetest Less than 1 minute… Done + No web browser found From ef1116b54e10b9bb92b3386fd2f30d2c3b1afe9f Mon Sep 17 00:00:00 2001 From: Gregor Parzefall Date: Fri, 24 May 2024 17:34:32 +0200 Subject: [PATCH 056/146] Immediately apply gui_scaling changes to IGUISkin --- src/client/clientlauncher.cpp | 48 +++++++++++++++++++++++++---------- src/client/clientlauncher.h | 4 ++- 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/src/client/clientlauncher.cpp b/src/client/clientlauncher.cpp index da8118a7e..6379fc141 100644 --- a/src/client/clientlauncher.cpp +++ b/src/client/clientlauncher.cpp @@ -36,6 +36,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "network/networkexceptions.h" #include #include +#include #if USE_SOUND #include "sound/sound_openal.h" @@ -68,6 +69,7 @@ static void dump_start_data(const GameStartData &data) ClientLauncher::~ClientLauncher() { delete input; + g_settings->deregisterChangedCallback("gui_scaling", setting_changed_callback, this); delete g_fontengine; g_fontengine = nullptr; @@ -126,7 +128,8 @@ bool ClientLauncher::run(GameStartData &start_data, const Settings &cmd_args) setAttribute(scene::ALLOW_ZWRITE_ON_TRANSPARENT, true); guienv = m_rendering_engine->get_gui_env(); - init_guienv(guienv); + config_guienv(); + g_settings->registerChangedCallback("gui_scaling", setting_changed_callback, this); g_fontengine = new FontEngine(guienv); @@ -326,7 +329,12 @@ void ClientLauncher::init_input() } } -void ClientLauncher::init_guienv(gui::IGUIEnvironment *guienv) +void ClientLauncher::setting_changed_callback(const std::string &name, void *data) +{ + static_cast(data)->config_guienv(); +} + +void ClientLauncher::config_guienv() { gui::IGUISkin *skin = guienv->getSkin(); @@ -345,23 +353,35 @@ void ClientLauncher::init_guienv(gui::IGUIEnvironment *guienv) skin->setSize(gui::EGDS_CHECK_BOX_WIDTH, (s32)(17.0f * density)); skin->setSize(gui::EGDS_SCROLLBAR_SIZE, (s32)(21.0f * density)); skin->setSize(gui::EGDS_WINDOW_BUTTON_WIDTH, (s32)(15.0f * density)); + + static u32 orig_sprite_id = skin->getIcon(gui::EGDI_CHECK_BOX_CHECKED); + static std::unordered_map sprite_ids; + if (density > 1.5f) { - std::string sprite_path = porting::path_share + "/textures/base/pack/"; + // Texture dimensions should be a power of 2 + std::string path = porting::path_share + "/textures/base/pack/"; if (density > 3.5f) - sprite_path.append("checkbox_64.png"); + path.append("checkbox_64.png"); else if (density > 2.0f) - sprite_path.append("checkbox_32.png"); + path.append("checkbox_32.png"); else - sprite_path.append("checkbox_16.png"); - // Texture dimensions should be a power of 2 - gui::IGUISpriteBank *sprites = skin->getSpriteBank(); - video::IVideoDriver *driver = m_rendering_engine->get_video_driver(); - video::ITexture *sprite_texture = driver->getTexture(sprite_path.c_str()); - if (sprite_texture) { - s32 sprite_id = sprites->addTextureAsSprite(sprite_texture); - if (sprite_id != -1) - skin->setIcon(gui::EGDI_CHECK_BOX_CHECKED, sprite_id); + path.append("checkbox_16.png"); + + auto cached_id = sprite_ids.find(path); + if (cached_id != sprite_ids.end()) { + skin->setIcon(gui::EGDI_CHECK_BOX_CHECKED, cached_id->second); + } else { + gui::IGUISpriteBank *sprites = skin->getSpriteBank(); + video::IVideoDriver *driver = m_rendering_engine->get_video_driver(); + video::ITexture *texture = driver->getTexture(path.c_str()); + s32 id = sprites->addTextureAsSprite(texture); + if (id != -1) { + skin->setIcon(gui::EGDI_CHECK_BOX_CHECKED, id); + sprite_ids.emplace(path, id); + } } + } else { + skin->setIcon(gui::EGDI_CHECK_BOX_CHECKED, orig_sprite_id); } } diff --git a/src/client/clientlauncher.h b/src/client/clientlauncher.h index dc0794c88..7b070451a 100644 --- a/src/client/clientlauncher.h +++ b/src/client/clientlauncher.h @@ -38,7 +38,9 @@ class ClientLauncher void init_args(GameStartData &start_data, const Settings &cmd_args); bool init_engine(); void init_input(); - void init_guienv(gui::IGUIEnvironment *guienv); + + static void setting_changed_callback(const std::string &name, void *data); + void config_guienv(); bool launch_game(std::string &error_message, bool reconnect_requested, GameStartData &start_data, const Settings &cmd_args); From 35f0b02591cd8adf600e3dc937ba73008c0d8710 Mon Sep 17 00:00:00 2001 From: Gregor Parzefall Date: Fri, 24 May 2024 18:19:12 +0200 Subject: [PATCH 057/146] Make the CDB dependency dialog use more space --- builtin/mainmenu/content/dlg_install.lua | 38 +++++++++++++++--------- 1 file changed, 24 insertions(+), 14 deletions(-) diff --git a/builtin/mainmenu/content/dlg_install.lua b/builtin/mainmenu/content/dlg_install.lua index 8c2a43b09..11cb0674b 100644 --- a/builtin/mainmenu/content/dlg_install.lua +++ b/builtin/mainmenu/content/dlg_install.lua @@ -66,35 +66,45 @@ local function get_formspec(data) message_bg = mt_color_orange end + local ENABLE_TOUCH = core.settings:get_bool("enable_touch") + + local w = ENABLE_TOUCH and 14 or 7 + local padded_w = w - 2*0.375 + local dropdown_w = ENABLE_TOUCH and 10.2 or 4.25 + local button_w = (padded_w - 0.25) / 3 + local button_pad = button_w / 2 + local formspec = { "formspec_version[3]", - "size[7,7.85]", + "size[", w, ",9.05]", + ENABLE_TOUCH and "padding[0.01,0.01]" or "position[0.5,0.55]", "style[title;border=false]", - "box[0,0;7,0.5;#3333]", - "button[0,0;7,0.5;title;", fgettext("Install $1", package.title) , "]", + "box[0,0;", w, ",0.8;#3333]", + "button[0,0;", w, ",0.8;title;", fgettext("Install $1", package.title) , "]", - "container[0.375,0.70]", + "container[0.375,1]", - "label[0,0.25;", fgettext("Base Game:"), "]", - "dropdown[2,0;4.25,0.5;selected_game;", table.concat(game_list, ","), ";", selected_game_idx, "]", + "label[0,0.4;", fgettext("Base Game:"), "]", + "dropdown[", padded_w - dropdown_w, ",0;", dropdown_w, ",0.8;selected_game;", + table.concat(game_list, ","), ";", selected_game_idx, "]", - "label[0,0.8;", fgettext("Dependencies:"), "]", + "label[0,1.1;", fgettext("Dependencies:"), "]", "tablecolumns[color;text;color;text]", - "table[0,1.1;6.25,3;packages;", table.concat(formatted_deps, ","), "]", + "table[0,1.4;", padded_w, ",3;packages;", table.concat(formatted_deps, ","), "]", "container_end[]", - "checkbox[0.375,5.1;will_install_deps;", + "checkbox[0.375,5.7;will_install_deps;", fgettext("Install missing dependencies"), ";", will_install_deps and "true" or "false", "]", - "box[0,5.4;7,1.2;", message_bg, "]", - "textarea[0.375,5.5;6.25,1;;;", message, "]", + "box[0,6;", w, ",1.8;", message_bg, "]", + "textarea[0.375,6.1;", padded_w, ",1.6;;;", message, "]", - "container[1.375,6.85]", - "button[0,0;2,0.8;install_all;", fgettext("Install"), "]", - "button[2.25,0;2,0.8;cancel;", fgettext("Cancel"), "]", + "container[", 0.375 + button_pad, ",8.05]", + "button[0,0;", button_w, ",0.8;install_all;", fgettext("Install"), "]", + "button[", 0.25 + button_w, ",0;", button_w, ",0.8;cancel;", fgettext("Cancel"), "]", "container_end[]", } From c6b1e25df68513c87ec478c8ed5f9bf8d3ea9d49 Mon Sep 17 00:00:00 2001 From: Desour Date: Sat, 25 Mar 2023 21:40:17 +0100 Subject: [PATCH 058/146] Move sha256.c to lib/ Precompiled headers don't work if we're not a pure C++ project. --- CMakeLists.txt | 1 + lib/sha256/CMakeLists.txt | 14 ++++++++++++++ lib/sha256/cmake_config.h.in | 5 +++++ {src/util => lib/sha256}/sha256.c | 8 ++------ {src/util => lib/sha256/sha256}/sha256.h | 0 src/CMakeLists.txt | 2 ++ src/script/lua_api/l_util.cpp | 4 ++-- src/util/CMakeLists.txt | 1 - src/util/srp.cpp | 2 +- 9 files changed, 27 insertions(+), 10 deletions(-) create mode 100644 lib/sha256/CMakeLists.txt create mode 100644 lib/sha256/cmake_config.h.in rename {src/util => lib/sha256}/sha256.c (99%) rename {src/util => lib/sha256/sha256}/sha256.h (100%) diff --git a/CMakeLists.txt b/CMakeLists.txt index 4c6c60ba9..2cd2e7336 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -277,6 +277,7 @@ find_package(Lua REQUIRED) if(NOT USE_LUAJIT) add_subdirectory(lib/bitop) endif() +add_subdirectory(lib/sha256) if(BUILD_UNITTESTS OR BUILD_BENCHMARKS) add_subdirectory(lib/catch2) diff --git a/lib/sha256/CMakeLists.txt b/lib/sha256/CMakeLists.txt new file mode 100644 index 000000000..555cb8ba9 --- /dev/null +++ b/lib/sha256/CMakeLists.txt @@ -0,0 +1,14 @@ +project(sha256 C) + +add_library(sha256 STATIC sha256.c) + +target_include_directories(sha256 INTERFACE .) + +INCLUDE(CheckIncludeFiles) +check_include_files(endian.h HAVE_ENDIAN_H) + +configure_file( + "${PROJECT_SOURCE_DIR}/cmake_config.h.in" + "${PROJECT_BINARY_DIR}/cmake_config.h" +) +target_include_directories(sha256 PRIVATE "${PROJECT_BINARY_DIR}") diff --git a/lib/sha256/cmake_config.h.in b/lib/sha256/cmake_config.h.in new file mode 100644 index 000000000..1ce07a107 --- /dev/null +++ b/lib/sha256/cmake_config.h.in @@ -0,0 +1,5 @@ +// Filled in by the build system + +#pragma once + +#cmakedefine HAVE_ENDIAN_H diff --git a/src/util/sha256.c b/lib/sha256/sha256.c similarity index 99% rename from src/util/sha256.c rename to lib/sha256/sha256.c index 2c1c6303f..56bdec098 100644 --- a/src/util/sha256.c +++ b/lib/sha256/sha256.c @@ -56,17 +56,13 @@ #include #include -#include "util/sha256.h" +#include "sha256/sha256.h" #if defined(_MSC_VER) && !defined(__clang__) && !defined(__attribute__) #define __attribute__(a) #endif -/* pull HAVE_ENDIAN_H from Minetest */ -#include "config.h" -#if !HAVE_ENDIAN_H -#undef HAVE_ENDIAN_H -#endif +#include "cmake_config.h" /* HAVE_ENDIAN_H */ /** endian.h **/ /* diff --git a/src/util/sha256.h b/lib/sha256/sha256/sha256.h similarity index 100% rename from src/util/sha256.h rename to lib/sha256/sha256/sha256.h diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 6ea544980..a37b02f87 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -565,6 +565,7 @@ if(BUILD_CLIENT) ${GMP_LIBRARY} ${JSON_LIBRARY} ${LUA_BIT_LIBRARY} + sha256 ${FREETYPE_LIBRARY} ${PLATFORM_LIBS} # on Android, Minetest depends on SDL2 directly @@ -637,6 +638,7 @@ if(BUILD_SERVER) ${JSON_LIBRARY} ${LUA_LIBRARY} ${LUA_BIT_LIBRARY} + sha256 ${GMP_LIBRARY} ${PLATFORM_LIBS} ) diff --git a/src/script/lua_api/l_util.cpp b/src/script/lua_api/l_util.cpp index f98eb0288..c868f949c 100644 --- a/src/script/lua_api/l_util.cpp +++ b/src/script/lua_api/l_util.cpp @@ -42,7 +42,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "version.h" #include "util/hex.h" #include "util/sha1.h" -#include "util/sha256.h" +#include #include "util/png.h" #include @@ -573,7 +573,7 @@ int ModApiUtil::l_sha256(lua_State *L) auto data = readParam(L, 1); bool hex = !lua_isboolean(L, 2) || !readParam(L, 2); - + std::string data_sha256; data_sha256.resize(SHA256_DIGEST_LENGTH); SHA256(reinterpret_cast(data.data()), data.size(), diff --git a/src/util/CMakeLists.txt b/src/util/CMakeLists.txt index 68e9eae5f..01b932c72 100644 --- a/src/util/CMakeLists.txt +++ b/src/util/CMakeLists.txt @@ -13,7 +13,6 @@ set(UTIL_SRCS ${CMAKE_CURRENT_SOURCE_DIR}/quicktune.cpp ${CMAKE_CURRENT_SOURCE_DIR}/serialize.cpp ${CMAKE_CURRENT_SOURCE_DIR}/sha1.cpp - ${CMAKE_CURRENT_SOURCE_DIR}/sha256.c ${CMAKE_CURRENT_SOURCE_DIR}/string.cpp ${CMAKE_CURRENT_SOURCE_DIR}/srp.cpp ${CMAKE_CURRENT_SOURCE_DIR}/timetaker.cpp diff --git a/src/util/srp.cpp b/src/util/srp.cpp index a290c9995..ea64efca6 100644 --- a/src/util/srp.cpp +++ b/src/util/srp.cpp @@ -50,7 +50,7 @@ #include #endif -#include "util/sha256.h" +#include #include "srp.h" //#define CSRP_USE_SHA1 From 9f603fa97c5c370a4885e1f99b80df7905fe8959 Mon Sep 17 00:00:00 2001 From: Desour Date: Sat, 25 Mar 2023 23:19:05 +0100 Subject: [PATCH 059/146] Add precompiled header support Note: the header is not included in the default precompiled_headers.txt, because we don't use it yet, and it might be big --- doc/compiling/README.md | 2 + src/CMakeLists.txt | 27 ++++++++++ src/precompiled_headers.txt | 103 ++++++++++++++++++++++++++++++++++++ 3 files changed, 132 insertions(+) create mode 100644 src/precompiled_headers.txt diff --git a/doc/compiling/README.md b/doc/compiling/README.md index 3652c2d74..a1ab1ebbd 100644 --- a/doc/compiling/README.md +++ b/doc/compiling/README.md @@ -20,6 +20,8 @@ General options and their default values: SemiDebug - Partially optimized debug build RelWithDebInfo - Release build with debug information MinSizeRel - Release build with -Os passed to compiler to make executable as small as possible + PRECOMPILE_HEADERS=FALSE - Precompile some headers (experimental; requires CMake 3.16 or later) + PRECOMPILED_HEADERS_PATH= - Path to a file listing all headers to precompile (default points to src/precompiled_headers.txt) ENABLE_CURL=ON - Build with cURL; Enables use of online mod repo, public serverlist and remote media fetching via http ENABLE_CURSES=ON - Build with (n)curses; Enables a server side terminal (command line option: --terminal) ENABLE_GETTEXT=ON - Build with Gettext; Allows using translations diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index a37b02f87..bcbe6d7f5 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -48,6 +48,25 @@ if(NOT (BUILD_CLIENT OR BUILD_SERVER)) endif() +option(PRECOMPILE_HEADERS "Precompile some headers (experimental; requires CMake 3.16 or later)" FALSE) +set(PRECOMPILED_HEADERS_PATH "" CACHE FILEPATH "Path to a file listing all headers to precompile") + +if(PRECOMPILE_HEADERS) + if(${CMAKE_VERSION} VERSION_LESS 3.16) + message(FATAL_ERROR "PRECOMPILE_HEADERS is on, but precompiled headers require at least CMake 3.16.") + endif() + if(PRECOMPILED_HEADERS_PATH) + set(PRECOMPILED_HEADERS ${PRECOMPILED_HEADERS_PATH}) + else() + set(PRECOMPILED_HEADERS "${CMAKE_SOURCE_DIR}/src/precompiled_headers.txt") + endif() + message(STATUS "Reading headers to precompile from: ${PRECOMPILED_HEADERS}") + # ignore lines that begin with # and empty lines + file(STRINGS ${PRECOMPILED_HEADERS} PRECOMPILED_HEADERS_LIST REGEX "^[^#].*$") + set_property(DIRECTORY APPEND PROPERTY CMAKE_CONFIGURE_DEPENDS ${PRECOMPILED_HEADERS}) +endif() + + option(ENABLE_CURL "Enable cURL support for fetching media" TRUE) set(USE_CURL FALSE) @@ -619,6 +638,10 @@ if(BUILD_CLIENT) if(BUILD_UNITTESTS OR BUILD_BENCHMARKS) target_link_libraries(${PROJECT_NAME} Catch2::Catch2) endif() + + if(PRECOMPILE_HEADERS) + target_precompile_headers(${PROJECT_NAME} PRIVATE ${PRECOMPILED_HEADERS_LIST}) + endif() endif(BUILD_CLIENT) @@ -682,6 +705,10 @@ if(BUILD_SERVER) if(BUILD_UNITTESTS OR BUILD_BENCHMARKS) target_link_libraries(${PROJECT_NAME}server Catch2::Catch2) endif() + + if(PRECOMPILE_HEADERS) + target_precompile_headers(${PROJECT_NAME}server PRIVATE ${PRECOMPILED_HEADERS_LIST}) + endif() endif(BUILD_SERVER) # See issue #4638 diff --git a/src/precompiled_headers.txt b/src/precompiled_headers.txt new file mode 100644 index 000000000..24c92b783 --- /dev/null +++ b/src/precompiled_headers.txt @@ -0,0 +1,103 @@ + +# stdlib +# ------ + +# C stuff: + + + + + + + + + + + + + + + + + + + + + + + + + +# Containers: + + + + + + + + + + + + +# Input/Output: + + + + + + + + + + +# Multi-threading: + + + + + + + +# Other: + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + +# libs +# ---- + +# jsoncpp + From 44adebd21ae6e7309f1174b3e1bd2355b7df340a Mon Sep 17 00:00:00 2001 From: Desour Date: Tue, 9 Apr 2024 15:35:32 +0200 Subject: [PATCH 060/146] Sound loading: Improve error handling --- src/client/sound/ogg_file.cpp | 81 ++++++++++++++++++++++++++++++----- 1 file changed, 70 insertions(+), 11 deletions(-) diff --git a/src/client/sound/ogg_file.cpp b/src/client/sound/ogg_file.cpp index 62cc71327..17bc7be20 100644 --- a/src/client/sound/ogg_file.cpp +++ b/src/client/sound/ogg_file.cpp @@ -24,6 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "ogg_file.h" +#include #include // memcpy namespace sound { @@ -98,18 +99,31 @@ const ov_callbacks OggVorbisBufferSource::s_ov_callbacks = { std::optional RAIIOggFile::getDecodeInfo(const std::string &filename_for_logging) { OggFileDecodeInfo ret; + ret.name_for_logging = filename_for_logging; - vorbis_info *pInfo = ov_info(&m_file, -1); - if (!pInfo) + long num_logical_bitstreams = ov_streams(&m_file); + if (num_logical_bitstreams < 1) { + warningstream << "Audio: Can't decode. Has not even one logical bitstream (but " + << num_logical_bitstreams << "): " + << ret.name_for_logging << std::endl; return std::nullopt; + } - ret.name_for_logging = filename_for_logging; + // We only support sounds where the sample rate and channel count doesn't + // change. + // Read the info for the first logical bitstream: + vorbis_info *info0 = ov_info(&m_file, 0); + if (!info0) { + warningstream << "Audio: Can't decode. ov_info failed for: " + << ret.name_for_logging << std::endl; + return std::nullopt; + } - if (pInfo->channels == 1) { + if (info0->channels == 1) { ret.is_stereo = false; ret.format = AL_FORMAT_MONO16; ret.bytes_per_sample = 2; - } else if (pInfo->channels == 2) { + } else if (info0->channels == 2) { ret.is_stereo = true; ret.format = AL_FORMAT_STEREO16; ret.bytes_per_sample = 4; @@ -119,10 +133,42 @@ std::optional RAIIOggFile::getDecodeInfo(const std::string &f return std::nullopt; } - ret.freq = pInfo->rate; + ret.freq = info0->rate; + + if (!ov_seekable(&m_file)) { + warningstream << "Audio: Can't decode. Sound is not seekable, can't get length: " + << ret.name_for_logging << std::endl; + return std::nullopt; + } - ret.length_samples = static_cast(ov_pcm_total(&m_file, -1)); - ret.length_seconds = static_cast(ov_time_total(&m_file, -1)); + auto pcm_total = ov_pcm_total(&m_file, -1); + assert(pcm_total >= 0); + auto time_total = ov_time_total(&m_file, -1); + assert(time_total >= 0); + ret.length_samples = static_cast(pcm_total); + ret.length_seconds = static_cast(time_total); + + // Check that channel count and sample rate do indeed not change + for (int strm = 1; strm < num_logical_bitstreams; ++strm) { + vorbis_info *info = ov_info(&m_file, strm); + if (!info) { + warningstream << "Audio: Can't decode. ov_info failed for: " + << ret.name_for_logging << std::endl; + return std::nullopt; + } + if (info->channels != info0->channels) { + warningstream << "Audio: Can't decode. Channel count changes from " + << info0->channels << " to " << info->channels << ": " + << ret.name_for_logging << std::endl; + return std::nullopt; + } + if (info->rate != info0->rate) { + warningstream << "Audio: Can't decode. Sample rate changes from " + << info0->rate << " to " << info->rate << ": " + << ret.name_for_logging << std::endl; + return std::nullopt; + } + } return ret; } @@ -134,6 +180,9 @@ RAIIALSoundBuffer RAIIOggFile::loadBuffer(const OggFileDecodeInfo &decode_info, constexpr int word_size = 2; // we use s16 samples constexpr int word_signed = 1; // ^ + assert(pcm_end <= decode_info.length_samples); + assert(pcm_start <= decode_info.length_samples); + // seek if (ov_pcm_tell(&m_file) != pcm_start) { if (ov_pcm_seek(&m_file, pcm_start) != 0) { @@ -141,6 +190,7 @@ RAIIALSoundBuffer RAIIOggFile::loadBuffer(const OggFileDecodeInfo &decode_info, << decode_info.name_for_logging << std::endl; return RAIIALSoundBuffer(); } + assert(ov_pcm_tell(&m_file) == pcm_start); } const size_t size = static_cast(pcm_end - pcm_start) @@ -150,14 +200,23 @@ RAIIALSoundBuffer RAIIOggFile::loadBuffer(const OggFileDecodeInfo &decode_info, // read size bytes size_t read_count = 0; - int bitStream; + int bitstream; while (read_count < size) { // Read up to a buffer's worth of decoded sound data long num_bytes = ov_read(&m_file, &snd_buffer[read_count], size - read_count, - endian, word_size, word_signed, &bitStream); + endian, word_size, word_signed, &bitstream); if (num_bytes <= 0) { - warningstream << "Audio: Error decoding " + std::string_view errstr = [&]{ + switch (num_bytes) { + case 0: return "EOF"; + case OV_HOLE: return "OV_HOLE"; + case OV_EBADLINK: return "OV_EBADLINK"; + case OV_EINVAL: return "OV_EINVAL"; + default: return "unknown error"; + } + }(); + warningstream << "Audio: Error decoding (" << errstr << "): " << decode_info.name_for_logging << std::endl; return RAIIALSoundBuffer(); } From 3c0a75a4fba6dafef3b2948641c29f46a836838c Mon Sep 17 00:00:00 2001 From: Desour Date: Tue, 9 Apr 2024 15:54:03 +0200 Subject: [PATCH 061/146] Sound loading: Fix issues where ov_read skips samples --- src/client/sound/ogg_file.cpp | 26 ++++++++++++++++++++++++-- 1 file changed, 24 insertions(+), 2 deletions(-) diff --git a/src/client/sound/ogg_file.cpp b/src/client/sound/ogg_file.cpp index 17bc7be20..11659c706 100644 --- a/src/client/sound/ogg_file.cpp +++ b/src/client/sound/ogg_file.cpp @@ -184,13 +184,15 @@ RAIIALSoundBuffer RAIIOggFile::loadBuffer(const OggFileDecodeInfo &decode_info, assert(pcm_start <= decode_info.length_samples); // seek - if (ov_pcm_tell(&m_file) != pcm_start) { + s64 current_pcm = ov_pcm_tell(&m_file); + if (current_pcm != pcm_start) { if (ov_pcm_seek(&m_file, pcm_start) != 0) { - warningstream << "Audio: Error decoding (could not seek) " + warningstream << "Audio: Error decoding (could not seek): " << decode_info.name_for_logging << std::endl; return RAIIALSoundBuffer(); } assert(ov_pcm_tell(&m_file) == pcm_start); + current_pcm = pcm_start; } const size_t size = static_cast(pcm_end - pcm_start) @@ -199,6 +201,7 @@ RAIIALSoundBuffer RAIIOggFile::loadBuffer(const OggFileDecodeInfo &decode_info, std::unique_ptr snd_buffer(new char[size]); // read size bytes + s64 last_byte_offset = current_pcm * decode_info.bytes_per_sample; size_t read_count = 0; int bitstream; while (read_count < size) { @@ -221,6 +224,25 @@ RAIIALSoundBuffer RAIIOggFile::loadBuffer(const OggFileDecodeInfo &decode_info, return RAIIALSoundBuffer(); } + // This usually doesn't happen, but for some sounds ov_read seems to skip + // some samples, see #14453. + s64 current_byte_offset = ov_pcm_tell(&m_file) * decode_info.bytes_per_sample; + if (current_byte_offset != last_byte_offset + num_bytes) { + infostream << "Audio: ov_read skipped " + << current_byte_offset - (last_byte_offset + num_bytes) + << " bytes, re-seeking: " + << decode_info.name_for_logging << std::endl; + s64 expected_offset = (last_byte_offset + num_bytes) / decode_info.bytes_per_sample; + if (ov_pcm_seek(&m_file, expected_offset) != 0) { + warningstream << "Audio: Error decoding (could not seek): " + << decode_info.name_for_logging << std::endl; + return RAIIALSoundBuffer(); + } + assert(ov_pcm_tell(&m_file) == expected_offset); + current_byte_offset = last_byte_offset + num_bytes; + } + last_byte_offset = current_byte_offset; + read_count += num_bytes; } From 7ced48c1ed8c7c88f25264ba25bb22d5b0c4a23e Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Wed, 29 May 2024 19:46:24 +0200 Subject: [PATCH 062/146] Particles: fix crash caused by absent texture Introduced in commit f8bff34. The related code does already expect that 'texture.ref' may be nullptr. --- src/client/particles.cpp | 1 - 1 file changed, 1 deletion(-) diff --git a/src/client/particles.cpp b/src/client/particles.cpp index b164da788..d94313ac9 100644 --- a/src/client/particles.cpp +++ b/src/client/particles.cpp @@ -1008,7 +1008,6 @@ video::SMaterial ParticleManager::getMaterialForParticle(const ClientParticleTex video::EMFN_MODULATE_1X, video::EAS_TEXTURE | video::EAS_VERTEX_COLOR); material.BlendOperation = blendop; - assert(texture.ref); material.setTexture(0, texture.ref); return material; From 9a58ec67629cd35ff28b1be70d93416736a0215f Mon Sep 17 00:00:00 2001 From: rubenwardy Date: Wed, 29 May 2024 19:10:22 +0100 Subject: [PATCH 063/146] Update no games message to give more context (#14695) --- builtin/mainmenu/tab_local.lua | 14 +++++++++++--- 1 file changed, 11 insertions(+), 3 deletions(-) diff --git a/builtin/mainmenu/tab_local.lua b/builtin/mainmenu/tab_local.lua index 98ba17299..1ed08d825 100644 --- a/builtin/mainmenu/tab_local.lua +++ b/builtin/mainmenu/tab_local.lua @@ -156,10 +156,18 @@ local function get_formspec(tabview, name, tabdata) -- Point the player to ContentDB when no games are found if #pkgmgr.games == 0 then + local W = tabview.width + local H = tabview.height + + local hypertext = "" .. + fgettext_ne("Minetest is a game-creation platform that allows you to play many different games.") .. "\n" .. + fgettext_ne("Minetest doesn't come with a game by default.") .. " " .. + fgettext_ne("You need to install a game before you can create a world.") + + local button_y = H * 2/3 - 0.6 return table.concat({ - "style[label_button;border=false]", - "button[2.75,1.5;10,1;label_button;", fgettext("You have no games installed."), "]", - "button[5.25,3.5;5,1.2;game_open_cdb;", fgettext("Install a game"), "]"}) + "hypertext[0.375,0;", W - 2*0.375, ",", button_y, ";ht;", core.formspec_escape(hypertext), "]", + "button[5.25,", button_y, ";5,1.2;game_open_cdb;", fgettext("Install a game"), "]"}) end local retval = "" From 483f4fdd7a762696de2137a0596b932cfb80d5c5 Mon Sep 17 00:00:00 2001 From: Lars Mueller Date: Fri, 31 May 2024 21:35:15 +0200 Subject: [PATCH 064/146] Fix fog moon tint not working --- src/client/sky.cpp | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/client/sky.cpp b/src/client/sky.cpp index 705161100..92e5df218 100644 --- a/src/client/sky.cpp +++ b/src/client/sky.cpp @@ -490,9 +490,9 @@ void Sky::update(float time_of_day, float time_brightness, ); } else { pointcolor_moon_f = video::SColorf( - (m_sky_params.fog_moon_tint.getRed() / 255) * pointcolor_light, - (m_sky_params.fog_moon_tint.getGreen() / 255) * pointcolor_light, - (m_sky_params.fog_moon_tint.getBlue() / 255) * pointcolor_light, + (m_sky_params.fog_moon_tint.getRed() / 255.0f) * pointcolor_light, + (m_sky_params.fog_moon_tint.getGreen() / 255.0f) * pointcolor_light, + (m_sky_params.fog_moon_tint.getBlue() / 255.0f) * pointcolor_light, 1 ); } From 9955804bfd8509cc146af020cdd174b3f42d158d Mon Sep 17 00:00:00 2001 From: sfence Date: Sat, 1 Jun 2024 16:36:20 +0200 Subject: [PATCH 065/146] Allow game to specify first and last mod in mod loading order (#14177) Co-authored-by: Lars Mueller Co-authored-by: sfan5 --- doc/lua_api.md | 2 + games/devtest/game.conf | 2 + games/devtest/mods/first_mod/init.lua | 1 + games/devtest/mods/first_mod/mod.conf | 2 + games/devtest/mods/last_mod/init.lua | 1 + games/devtest/mods/last_mod/mod.conf | 5 ++ games/devtest/mods/unittests/mod.conf | 3 +- src/content/mod_configuration.cpp | 70 ++++++++++++++++--- src/content/mod_configuration.h | 3 + src/content/subgames.cpp | 96 +++++++++++++------------- src/content/subgames.h | 14 +++- src/unittest/test_servermodmanager.cpp | 6 +- 12 files changed, 141 insertions(+), 64 deletions(-) create mode 100644 games/devtest/mods/first_mod/init.lua create mode 100644 games/devtest/mods/first_mod/mod.conf create mode 100644 games/devtest/mods/last_mod/init.lua create mode 100644 games/devtest/mods/last_mod/mod.conf diff --git a/doc/lua_api.md b/doc/lua_api.md index 7331f0a93..7fa80d6ce 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -63,6 +63,8 @@ The game directory can contain the following files: * `name`: (Deprecated) same as title. * `description`: Short description to be shown in the content tab. See [Translating content meta](#translating-content-meta). + * `first_mod`: Use this to specify the mod that must be loaded before any other mod. + * `last_mod`: Use this to specify the mod that must be loaded after all other mods * `allowed_mapgens = ` e.g. `allowed_mapgens = v5,v6,flat` Mapgens not in this list are removed from the list of mapgens for the diff --git a/games/devtest/game.conf b/games/devtest/game.conf index 0f5656c99..8184881a2 100644 --- a/games/devtest/game.conf +++ b/games/devtest/game.conf @@ -1,2 +1,4 @@ title = Development Test description = Testing environment to help with testing the engine features of Minetest. It can also be helpful in mod development. +first_mod = first_mod +last_mod = last_mod diff --git a/games/devtest/mods/first_mod/init.lua b/games/devtest/mods/first_mod/init.lua new file mode 100644 index 000000000..3909a39cc --- /dev/null +++ b/games/devtest/mods/first_mod/init.lua @@ -0,0 +1 @@ +-- Nothing to do here, loading order is tested in C++ unittests. diff --git a/games/devtest/mods/first_mod/mod.conf b/games/devtest/mods/first_mod/mod.conf new file mode 100644 index 000000000..d35fa8d28 --- /dev/null +++ b/games/devtest/mods/first_mod/mod.conf @@ -0,0 +1,2 @@ +name = first_mod +description = Mod which should be loaded before every other mod. diff --git a/games/devtest/mods/last_mod/init.lua b/games/devtest/mods/last_mod/init.lua new file mode 100644 index 000000000..3909a39cc --- /dev/null +++ b/games/devtest/mods/last_mod/init.lua @@ -0,0 +1 @@ +-- Nothing to do here, loading order is tested in C++ unittests. diff --git a/games/devtest/mods/last_mod/mod.conf b/games/devtest/mods/last_mod/mod.conf new file mode 100644 index 000000000..734bf4c0c --- /dev/null +++ b/games/devtest/mods/last_mod/mod.conf @@ -0,0 +1,5 @@ +name = last_mod +description = Mod which should be loaded as last mod. +# Test dependencies +optional_depends = unittests +depends = first_mod diff --git a/games/devtest/mods/unittests/mod.conf b/games/devtest/mods/unittests/mod.conf index fa94e01a6..ccff73701 100644 --- a/games/devtest/mods/unittests/mod.conf +++ b/games/devtest/mods/unittests/mod.conf @@ -1,3 +1,4 @@ name = unittests description = Adds automated unit tests for the engine -depends = basenodes +# Also test that it is possible to depend on first_mod +depends = first_mod, basenodes diff --git a/src/content/mod_configuration.cpp b/src/content/mod_configuration.cpp index 47ad3e0dc..d814c1b68 100644 --- a/src/content/mod_configuration.cpp +++ b/src/content/mod_configuration.cpp @@ -24,7 +24,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "gettext.h" #include "exceptions.h" #include "util/numeric.h" - +#include std::string ModConfiguration::getUnsatisfiedModsError() const { @@ -116,6 +116,9 @@ void ModConfiguration::addGameMods(const SubgameSpec &gamespec) std::string game_virtual_path; game_virtual_path.append("games/").append(gamespec.id).append("/mods"); addModsInPath(gamespec.gamemods_path, game_virtual_path); + + m_first_mod = gamespec.first_mod; + m_last_mod = gamespec.last_mod; } void ModConfiguration::addModsFromConfig( @@ -221,13 +224,20 @@ void ModConfiguration::checkConflictsAndDeps() void ModConfiguration::resolveDependencies() { - // Step 1: Compile a list of the mod names we're working with + // Compile a list of the mod names we're working with std::set modnames; - for (const ModSpec &mod : m_unsatisfied_mods) { - modnames.insert(mod.name); + std::optional first_mod_spec, last_mod_spec; + for (ModSpec &mod : m_unsatisfied_mods) { + if (mod.name == m_first_mod) { + first_mod_spec = mod; + } else if (mod.name == m_last_mod) { + last_mod_spec = mod; + } else { + modnames.insert(mod.name); + } } - // Step 1.5 (optional): shuffle unsatisfied mods so non declared depends get found by their devs + // Optionally shuffle unsatisfied mods so non declared depends get found by their devs if (g_settings->getBool("random_mod_load_order")) { MyRandGenerator rg; std::shuffle(m_unsatisfied_mods.begin(), @@ -236,25 +246,55 @@ void ModConfiguration::resolveDependencies() ); } - // Step 2: get dependencies (including optional dependencies) + // Check for presence of first and last mod + if (!m_first_mod.empty() && !first_mod_spec.has_value()) + throw ModError("The mod specified as first by the game was not found."); + if (!m_last_mod.empty() && !last_mod_spec.has_value()) + throw ModError("The mod specified as last by the game was not found."); + + // Check and add first mod + if (first_mod_spec.has_value()) { + // Dependencies are not allowed for first mod + if (!first_mod_spec->depends.empty() || !first_mod_spec->optdepends.empty()) + throw ModError("Mod specified by first_mod cannot have dependencies"); + + m_sorted_mods.push_back(*first_mod_spec); + } + + // Get dependencies (including optional dependencies) // of each mod, split mods into satisfied and unsatisfied std::vector satisfied; std::list unsatisfied; for (ModSpec mod : m_unsatisfied_mods) { + if (mod.name == m_first_mod || mod.name == m_last_mod) + continue; // skip, handled separately + mod.unsatisfied_depends = mod.depends; // check which optional dependencies actually exist for (const std::string &optdep : mod.optdepends) { if (modnames.count(optdep) != 0) mod.unsatisfied_depends.insert(optdep); } + mod.unsatisfied_depends.erase(m_first_mod); // first is already satisfied + + if (last_mod_spec.has_value() && mod.unsatisfied_depends.count(last_mod_spec->name) != 0) { + throw ModError("Impossible to depend on the mod specified by last_mod"); + } // if a mod has no depends it is initially satisfied - if (mod.unsatisfied_depends.empty()) + if (mod.unsatisfied_depends.empty()) { satisfied.push_back(mod); - else + } else { unsatisfied.push_back(mod); + } } - // Step 3: mods without unmet dependencies can be appended to + // All dependencies of the last mod are initially unsatisfied + if (last_mod_spec.has_value()) { + last_mod_spec->unsatisfied_depends = last_mod_spec->depends; + last_mod_spec->unsatisfied_depends.erase(m_first_mod); + } + + // Mods without unmet dependencies can be appended to // the sorted list. while (!satisfied.empty()) { ModSpec mod = satisfied.back(); @@ -270,8 +310,18 @@ void ModConfiguration::resolveDependencies() ++it; } } + if (last_mod_spec.has_value()) + last_mod_spec->unsatisfied_depends.erase(mod.name); } - // Step 4: write back list of unsatisfied mods + // Write back list of unsatisfied mods m_unsatisfied_mods.assign(unsatisfied.begin(), unsatisfied.end()); + + // Check and add last mod + if (last_mod_spec.has_value()) { + if (last_mod_spec->unsatisfied_depends.empty()) + m_sorted_mods.push_back(*last_mod_spec); + else + m_unsatisfied_mods.push_back(*last_mod_spec); + } } diff --git a/src/content/mod_configuration.h b/src/content/mod_configuration.h index e945a785a..200694652 100644 --- a/src/content/mod_configuration.h +++ b/src/content/mod_configuration.h @@ -87,6 +87,9 @@ class ModConfiguration void checkConflictsAndDeps(); private: + std::string m_first_mod; // "" <=> no mod + std::string m_last_mod; // "" <=> no mod + std::vector m_sorted_mods; /** diff --git a/src/content/subgames.cpp b/src/content/subgames.cpp index 611b09308..9d5d528a8 100644 --- a/src/content/subgames.cpp +++ b/src/content/subgames.cpp @@ -94,6 +94,50 @@ std::string getSubgamePathEnv() return ""; } +static SubgameSpec getSubgameSpec(const std::string &game_id, + const std::string &game_path, + const std::unordered_map &mods_paths, + const std::string &menuicon_path) +{ + const auto gamemods_path = game_path + DIR_DELIM + "mods"; + // Get meta + const std::string conf_path = game_path + DIR_DELIM + "game.conf"; + Settings conf; + conf.readConfigFile(conf_path.c_str()); + + std::string game_title; + if (conf.exists("title")) + game_title = conf.get("title"); + else if (conf.exists("name")) + game_title = conf.get("name"); + else + game_title = game_id; + + std::string game_author; + if (conf.exists("author")) + game_author = conf.get("author"); + + int game_release = 0; + if (conf.exists("release")) + game_release = conf.getS32("release"); + + std::string first_mod; + if (conf.exists("first_mod")) + first_mod = conf.get("first_mod"); + + std::string last_mod; + if (conf.exists("last_mod")) + last_mod = conf.get("last_mod"); + + SubgameSpec spec(game_id, game_path, gamemods_path, mods_paths, game_title, + menuicon_path, game_author, game_release, first_mod, last_mod); + + if (conf.exists("name") && !conf.exists("title")) + spec.deprecation_msgs.push_back("\"name\" setting in game.conf is deprecated, please use \"title\" instead"); + + return spec; +} + SubgameSpec findSubgame(const std::string &id) { if (id.empty()) @@ -137,8 +181,6 @@ SubgameSpec findSubgame(const std::string &id) if (game_path.empty()) return SubgameSpec(); - std::string gamemod_path = game_path + DIR_DELIM + "mods"; - // Find mod directories std::unordered_map mods_paths; mods_paths["mods"] = user + DIR_DELIM + "mods"; @@ -149,40 +191,13 @@ SubgameSpec findSubgame(const std::string &id) mods_paths[fs::AbsolutePath(mod_path)] = mod_path; } - // Get meta - std::string conf_path = game_path + DIR_DELIM + "game.conf"; - Settings conf; - conf.readConfigFile(conf_path.c_str()); - - std::string game_title; - if (conf.exists("title")) - game_title = conf.get("title"); - else if (conf.exists("name")) - game_title = conf.get("name"); - else - game_title = id; - - std::string game_author; - if (conf.exists("author")) - game_author = conf.get("author"); - - int game_release = 0; - if (conf.exists("release")) - game_release = conf.getS32("release"); - std::string menuicon_path; #ifndef SERVER menuicon_path = getImagePath( game_path + DIR_DELIM + "menu" + DIR_DELIM + "icon.png"); #endif - SubgameSpec spec(id, game_path, gamemod_path, mods_paths, game_title, - menuicon_path, game_author, game_release); - - if (conf.exists("name") && !conf.exists("title")) - spec.deprecation_msgs.push_back("\"name\" setting in game.conf is deprecated, please use \"title\" instead"); - - return spec; + return getSubgameSpec(id, game_path, mods_paths, menuicon_path); } SubgameSpec findWorldSubgame(const std::string &world_path) @@ -190,25 +205,8 @@ SubgameSpec findWorldSubgame(const std::string &world_path) std::string world_gameid = getWorldGameId(world_path, true); // See if world contains an embedded game; if so, use it. std::string world_gamepath = world_path + DIR_DELIM + "game"; - if (fs::PathExists(world_gamepath)) { - SubgameSpec gamespec; - gamespec.id = world_gameid; - gamespec.path = world_gamepath; - gamespec.gamemods_path = world_gamepath + DIR_DELIM + "mods"; - - Settings conf; - std::string conf_path = world_gamepath + DIR_DELIM + "game.conf"; - conf.readConfigFile(conf_path.c_str()); - - if (conf.exists("title")) - gamespec.title = conf.get("title"); - else if (conf.exists("name")) - gamespec.title = conf.get("name"); - else - gamespec.title = world_gameid; - - return gamespec; - } + if (fs::PathExists(world_gamepath)) + return getSubgameSpec(world_gameid, world_gamepath, {}, ""); return findSubgame(world_gameid); } diff --git a/src/content/subgames.h b/src/content/subgames.h index d5d168243..17a219669 100644 --- a/src/content/subgames.h +++ b/src/content/subgames.h @@ -32,6 +32,8 @@ struct SubgameSpec std::string title; std::string author; int release; + std::string first_mod; // "" <=> no mod + std::string last_mod; // "" <=> no mod std::string path; std::string gamemods_path; @@ -49,10 +51,16 @@ struct SubgameSpec const std::unordered_map &addon_mods_paths = {}, const std::string &title = "", const std::string &menuicon_path = "", - const std::string &author = "", int release = 0) : + const std::string &author = "", int release = 0, + const std::string &first_mod = "", + const std::string &last_mod = "") : id(id), - title(title), author(author), release(release), path(path), - gamemods_path(gamemods_path), addon_mods_paths(addon_mods_paths), + title(title), author(author), release(release), + first_mod(first_mod), + last_mod(last_mod), + path(path), + gamemods_path(gamemods_path), + addon_mods_paths(addon_mods_paths), menuicon_path(menuicon_path) { } diff --git a/src/unittest/test_servermodmanager.cpp b/src/unittest/test_servermodmanager.cpp index f023e3a3b..90c29e125 100644 --- a/src/unittest/test_servermodmanager.cpp +++ b/src/unittest/test_servermodmanager.cpp @@ -121,7 +121,8 @@ void TestServerModManager::testGetMods() { ServerModManager sm(m_worlddir); const auto &mods = sm.getMods(); - UASSERTEQ(bool, mods.empty(), false); + // `ls ./games/devtest/mods | wc -l` + 1 (test mod) + UASSERTEQ(std::size_t, mods.size(), 31 + 1); // Ensure we found basenodes mod (part of devtest) // and test_mod (for testing MINETEST_MOD_PATH). @@ -139,6 +140,9 @@ void TestServerModManager::testGetMods() UASSERTEQ(bool, default_found, true); UASSERTEQ(bool, test_mod_found, true); + + UASSERT(mods.front().name == "first_mod"); + UASSERT(mods.back().name == "last_mod"); } void TestServerModManager::testGetModspec() From 21f33ef23358ec17a74e3ec015fc1fda3c7618e4 Mon Sep 17 00:00:00 2001 From: grorp Date: Sat, 1 Jun 2024 17:03:06 +0200 Subject: [PATCH 066/146] Mainmenu: Unify gamedata.errormessage and messagebox formspecs (#14716) --- builtin/fstk/dialog.lua | 9 +++----- builtin/fstk/ui.lua | 27 ++++++++++++++-------- builtin/mainmenu/content/dlg_contentdb.lua | 2 +- 3 files changed, 22 insertions(+), 16 deletions(-) diff --git a/builtin/fstk/dialog.lua b/builtin/fstk/dialog.lua index c50d23855..991ec20ab 100644 --- a/builtin/fstk/dialog.lua +++ b/builtin/fstk/dialog.lua @@ -78,15 +78,12 @@ function dialog_create(name,get_formspec,buttonhandler,eventhandler) return self end +-- "message" must already be formspec-escaped, e.g. via fgettext or +-- core.formspec_escape. function messagebox(name, message) return dialog_create(name, function() - return ([[ - formspec_version[3] - size[8,3] - textarea[0.375,0.375;7.25,1.2;;;%s] - button[3,1.825;2,0.8;ok;%s] - ]]):format(message, fgettext("OK")) + return ui.get_message_formspec("", message, "ok") end, function(this, fields) if fields.ok then diff --git a/builtin/fstk/ui.lua b/builtin/fstk/ui.lua index a63cb2cd0..a3a8fed77 100644 --- a/builtin/fstk/ui.lua +++ b/builtin/fstk/ui.lua @@ -50,6 +50,20 @@ function ui.find_by_name(name) return ui.childlist[name] end +-------------------------------------------------------------------------------- +-- "title" and "message" must already be formspec-escaped, e.g. via fgettext or +-- core.formspec_escape. +function ui.get_message_formspec(title, message, btn_id) + return table.concat({ + "size[14,8]", + "real_coordinates[true]", + "set_focus[", btn_id, ";true]", + "box[0.5,1.2;13,5;#000]", + ("textarea[0.5,1.2;13,5;;%s;%s]"):format(title, message), + "button[5,6.6;4,1;", btn_id, ";" .. fgettext("OK") .. "]", + }) +end + -------------------------------------------------------------------------------- -------------------------------------------------------------------------------- -- Internal functions not to be called from user @@ -76,6 +90,9 @@ function ui.update() } ui.overridden = true elseif gamedata ~= nil and gamedata.errormessage ~= nil then + -- Note to API users: + -- "gamedata.errormessage" must not be formspec-escaped yet. + -- For translations, fgettext_ne should be used. local error_message = core.formspec_escape(gamedata.errormessage) local error_title @@ -84,15 +101,7 @@ function ui.update() else error_title = fgettext("An error occurred:") end - formspec = { - "size[14,8]", - "real_coordinates[true]", - "set_focus[btn_error_confirm;true]", - "box[0.5,1.2;13,5;#000]", - ("textarea[0.5,1.2;13,5;;%s;%s]"):format( - error_title, error_message), - "button[5,6.6;4,1;btn_error_confirm;" .. fgettext("OK") .. "]" - } + formspec = {ui.get_message_formspec(error_title, error_message, "btn_error_confirm")} ui.overridden = true else local active_toplevel_ui_elements = 0 diff --git a/builtin/mainmenu/content/dlg_contentdb.lua b/builtin/mainmenu/content/dlg_contentdb.lua index 4dafafc45..a975e1326 100644 --- a/builtin/mainmenu/content/dlg_contentdb.lua +++ b/builtin/mainmenu/content/dlg_contentdb.lua @@ -117,7 +117,7 @@ local function resolve_auto_install_spec() end if not resolved then - gamedata.errormessage = fgettext("The package $1 was not found.", auto_install_spec) + gamedata.errormessage = fgettext_ne("The package $1 was not found.", auto_install_spec) ui.update() auto_install_spec = nil From 1f09d29db8b9380b506781cb3e9b8486c87b533d Mon Sep 17 00:00:00 2001 From: grorp Date: Sun, 2 Jun 2024 21:05:16 +0200 Subject: [PATCH 067/146] Allow toggling fullscreen without restart and add keybind (#14714) --- builtin/mainmenu/settings/dlg_settings.lua | 5 +++ builtin/settingtypes.txt | 3 ++ doc/menu_lua_api.md | 3 +- irr/include/IrrlichtDevice.h | 5 +++ irr/src/CIrrDeviceSDL.cpp | 45 +++++++++++++++++----- irr/src/CIrrDeviceSDL.h | 6 +++ src/client/game.cpp | 3 +- src/client/inputhandler.cpp | 14 +++++++ src/client/inputhandler.h | 3 ++ src/client/renderingengine.cpp | 29 +++++++++++++- src/client/renderingengine.h | 1 + src/defaultsettings.cpp | 1 + src/gui/guiEngine.cpp | 13 ++++++- src/gui/guiEngine.h | 2 + 14 files changed, 119 insertions(+), 14 deletions(-) diff --git a/builtin/mainmenu/settings/dlg_settings.lua b/builtin/mainmenu/settings/dlg_settings.lua index d6bbf2570..fe1e1924f 100644 --- a/builtin/mainmenu/settings/dlg_settings.lua +++ b/builtin/mainmenu/settings/dlg_settings.lua @@ -723,6 +723,11 @@ local function eventhandler(event) mm_game_theme.set_engine(true) return true end + if event == "FullscreenChange" then + -- Refresh the formspec to keep the fullscreen checkbox up to date. + ui.update() + return true + end return false end diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index a215a6dd8..e455f0f6f 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -2417,6 +2417,9 @@ keymap_minimap (Minimap key) key KEY_KEY_V # Key for taking screenshots. keymap_screenshot (Screenshot) key KEY_F12 +# Key for toggling fullscreen mode. +keymap_fullscreen (Fullscreen key) key KEY_F11 + # Key for dropping the currently selected item. keymap_drop (Drop item key) key KEY_KEY_Q diff --git a/doc/menu_lua_api.md b/doc/menu_lua_api.md index 439ef3bdf..5017d2e5b 100644 --- a/doc/menu_lua_api.md +++ b/doc/menu_lua_api.md @@ -14,7 +14,8 @@ Callbacks * `core.button_handler(fields)`: called when a button is pressed. * `fields` = `{name1 = value1, name2 = value2, ...}` * `core.event_handler(event)` - * `event`: `"MenuQuit"`, `"KeyEnter"`, `"ExitButton"` or `"EditBoxEnter"` + * `event`: `"MenuQuit"`, `"KeyEnter"`, `"ExitButton"`, `"EditBoxEnter"` or + `"FullscreenChange"` Gamedata diff --git a/irr/include/IrrlichtDevice.h b/irr/include/IrrlichtDevice.h index 38ba8c63d..11619010c 100644 --- a/irr/include/IrrlichtDevice.h +++ b/irr/include/IrrlichtDevice.h @@ -179,6 +179,11 @@ class IrrlichtDevice : public virtual IReferenceCounted /** \return True if window is fullscreen. */ virtual bool isFullscreen() const = 0; + //! Enables or disables fullscreen mode. + /** Only works on SDL. + \return True on success. */ + virtual bool setFullscreen(bool fullscreen) { return false; } + //! Checks if the window could possibly be visible. /** If this returns false, you should not do any rendering. */ virtual bool isWindowVisible() const { return true; }; diff --git a/irr/src/CIrrDeviceSDL.cpp b/irr/src/CIrrDeviceSDL.cpp index b50fd4b4a..6f2772aa6 100644 --- a/irr/src/CIrrDeviceSDL.cpp +++ b/irr/src/CIrrDeviceSDL.cpp @@ -463,13 +463,7 @@ bool CIrrDeviceSDL::createWindowWithContext() { u32 SDL_Flags = 0; - if (CreationParams.Fullscreen) { -#ifdef _IRR_EMSCRIPTEN_PLATFORM_ - SDL_Flags |= SDL_WINDOW_FULLSCREEN; -#else - SDL_Flags |= SDL_WINDOW_FULLSCREEN_DESKTOP; -#endif - } + SDL_Flags |= getFullscreenFlag(CreationParams.Fullscreen); if (Resizable) SDL_Flags |= SDL_WINDOW_RESIZABLE; if (CreationParams.WindowMaximized) @@ -889,6 +883,14 @@ bool CIrrDeviceSDL::run() IsInBackground = false; break; + case SDL_RENDER_TARGETS_RESET: + os::Printer::log("Received SDL_RENDER_TARGETS_RESET. Rendering is probably broken.", ELL_ERROR); + break; + + case SDL_RENDER_DEVICE_RESET: + os::Printer::log("Received SDL_RENDER_DEVICE_RESET. Rendering is probably broken.", ELL_ERROR); + break; + default: break; } // end switch @@ -1157,14 +1159,37 @@ bool CIrrDeviceSDL::isWindowMaximized() const bool CIrrDeviceSDL::isFullscreen() const { + if (!Window) + return false; + u32 flags = SDL_GetWindowFlags(Window); + return (flags & SDL_WINDOW_FULLSCREEN) != 0 || + (flags & SDL_WINDOW_FULLSCREEN_DESKTOP) != 0; +} + +u32 CIrrDeviceSDL::getFullscreenFlag(bool fullscreen) +{ + if (!fullscreen) + return 0; #ifdef _IRR_EMSCRIPTEN_PLATFORM_ - return SDL_GetWindowFlags(0) == SDL_WINDOW_FULLSCREEN; + return SDL_WINDOW_FULLSCREEN; #else - - return CIrrDeviceStub::isFullscreen(); + return SDL_WINDOW_FULLSCREEN_DESKTOP; #endif } +bool CIrrDeviceSDL::setFullscreen(bool fullscreen) +{ + if (!Window) + return false; + // The SDL wiki says that this may trigger SDL_RENDER_TARGETS_RESET, but + // looking at the SDL source, this only happens with D3D, so it's not + // relevant to us. + bool success = SDL_SetWindowFullscreen(Window, getFullscreenFlag(fullscreen)) == 0; + if (!success) + os::Printer::log("SDL_SetWindowFullscreen failed", SDL_GetError(), ELL_ERROR); + return success; +} + bool CIrrDeviceSDL::isWindowVisible() const { return !IsInBackground; diff --git a/irr/src/CIrrDeviceSDL.h b/irr/src/CIrrDeviceSDL.h index a83e8cf5e..c536a8149 100644 --- a/irr/src/CIrrDeviceSDL.h +++ b/irr/src/CIrrDeviceSDL.h @@ -86,6 +86,10 @@ class CIrrDeviceSDL : public CIrrDeviceStub /** \return True if window is fullscreen. */ bool isFullscreen() const override; + //! Enables or disables fullscreen mode. + /** \return True on success. */ + bool setFullscreen(bool fullscreen) override; + //! Checks if the window could possibly be visible. bool isWindowVisible() const override; @@ -299,6 +303,8 @@ class CIrrDeviceSDL : public CIrrDeviceStub bool Resizable; + static u32 getFullscreenFlag(bool fullscreen); + core::rect lastElemPos; struct SKeyMap diff --git a/src/client/game.cpp b/src/client/game.cpp index d69871714..5da2078a8 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -302,7 +302,8 @@ void Game::run() g_settings->getU16("screen_w"), g_settings->getU16("screen_h") ); - const bool initial_window_maximized = g_settings->getBool("window_maximized"); + const bool initial_window_maximized = !g_settings->getBool("fullscreen") && + g_settings->getBool("window_maximized"); while (m_rendering_engine->run() && !(*kill || g_gamecallback->shutdown_requested diff --git a/src/client/inputhandler.cpp b/src/client/inputhandler.cpp index efe29a32f..614d6a79e 100644 --- a/src/client/inputhandler.cpp +++ b/src/client/inputhandler.cpp @@ -18,6 +18,7 @@ with this program; if not, write to the Free Software Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA. */ +#include "settings.h" #include "util/numeric.h" #include "inputhandler.h" #include "gui/mainmenumanager.h" @@ -124,6 +125,19 @@ bool MyEventReceiver::OnEvent(const SEvent &event) return true; } + // This is separate from other keyboard handling so that it also works in menus. + if (event.EventType == EET_KEY_INPUT_EVENT) { + const KeyPress keyCode(event.KeyInput); + if (keyCode == getKeySetting("keymap_fullscreen")) { + if (event.KeyInput.PressedDown && !fullscreen_is_down) { + bool fullscreen = RenderingEngine::get_raw_device()->isFullscreen(); + g_settings->setBool("fullscreen", !fullscreen); + } + fullscreen_is_down = event.KeyInput.PressedDown; + return true; + } + } + // Let the menu handle events, if one is active. if (isMenuActive()) { if (g_touchscreengui) diff --git a/src/client/inputhandler.h b/src/client/inputhandler.h index 0a2bbe2fa..20bcf5cbd 100644 --- a/src/client/inputhandler.h +++ b/src/client/inputhandler.h @@ -220,6 +220,9 @@ class MyEventReceiver : public IEventReceiver // often changing keys, and keysListenedFor is expected // to change seldomly but contain lots of keys. KeyList keysListenedFor; + + // Intentionally not reset by clearInput/releaseAllKeys. + bool fullscreen_is_down = false; }; class InputHandler diff --git a/src/client/renderingengine.cpp b/src/client/renderingengine.cpp index 66a9c0c62..6fe746de6 100644 --- a/src/client/renderingengine.cpp +++ b/src/client/renderingengine.cpp @@ -207,7 +207,12 @@ RenderingEngine::RenderingEngine(IEventReceiver *receiver) #else u16 screen_w = std::max(g_settings->getU16("screen_w"), 1); u16 screen_h = std::max(g_settings->getU16("screen_h"), 1); - bool window_maximized = g_settings->getBool("window_maximized"); + // If I… + // 1. … set fullscreen = true and window_maximized = true on startup + // 2. … set fullscreen = false later + // on Linux with SDL, everything breaks. + // => Don't do it. + bool window_maximized = !fullscreen && g_settings->getBool("window_maximized"); #endif // bpp, fsaa, vsync @@ -249,18 +254,40 @@ RenderingEngine::RenderingEngine(IEventReceiver *receiver) gui::EGST_WINDOWS_METALLIC, driver); m_device->getGUIEnvironment()->setSkin(skin); skin->drop(); + + g_settings->registerChangedCallback("fullscreen", settingChangedCallback, this); + g_settings->registerChangedCallback("window_maximized", settingChangedCallback, this); } RenderingEngine::~RenderingEngine() { sanity_check(s_singleton == this); + g_settings->deregisterChangedCallback("fullscreen", settingChangedCallback, this); + g_settings->deregisterChangedCallback("window_maximized", settingChangedCallback, this); + core.reset(); m_device->closeDevice(); m_device->drop(); s_singleton = nullptr; } +void RenderingEngine::settingChangedCallback(const std::string &name, void *data) +{ + IrrlichtDevice *device = static_cast(data)->m_device; + if (name == "fullscreen") { + device->setFullscreen(g_settings->getBool("fullscreen")); + + } else if (name == "window_maximized") { + if (!device->isFullscreen()) { + if (g_settings->getBool("window_maximized")) + device->maximizeWindow(); + else + device->restoreWindow(); + } + } +} + v2u32 RenderingEngine::_getWindowSize() const { if (core) diff --git a/src/client/renderingengine.h b/src/client/renderingengine.h index 01b65f9dc..6ee3ff988 100644 --- a/src/client/renderingengine.h +++ b/src/client/renderingengine.h @@ -168,6 +168,7 @@ class RenderingEngine const bool initial_window_maximized); private: + static void settingChangedCallback(const std::string &name, void *data); v2u32 _getWindowSize() const; std::unique_ptr core; diff --git a/src/defaultsettings.cpp b/src/defaultsettings.cpp index 4e004325f..6dd98d5d6 100644 --- a/src/defaultsettings.cpp +++ b/src/defaultsettings.cpp @@ -285,6 +285,7 @@ void set_default_settings() settings->setDefault("keymap_toggle_profiler", "KEY_F6"); settings->setDefault("keymap_camera_mode", "KEY_KEY_C"); settings->setDefault("keymap_screenshot", "KEY_F12"); + settings->setDefault("keymap_fullscreen", "KEY_F11"); settings->setDefault("keymap_increase_viewing_range_min", "+"); settings->setDefault("keymap_decrease_viewing_range_min", "-"); settings->setDefault("keymap_slot1", "KEY_KEY_1"); diff --git a/src/gui/guiEngine.cpp b/src/gui/guiEngine.cpp index 66f9f9864..fdc13fa14 100644 --- a/src/gui/guiEngine.cpp +++ b/src/gui/guiEngine.cpp @@ -193,6 +193,8 @@ GUIEngine::GUIEngine(JoystickController *joystick, m_script = std::make_unique(this); + g_settings->registerChangedCallback("fullscreen", fullscreenChangedCallback, this); + try { m_script->setMainMenuData(&m_data->script_data); m_data->script_data.errormessage.clear(); @@ -319,7 +321,8 @@ void GUIEngine::run() g_settings->getU16("screen_w"), g_settings->getU16("screen_h") ); - const bool initial_window_maximized = g_settings->getBool("window_maximized"); + const bool initial_window_maximized = !g_settings->getBool("fullscreen") && + g_settings->getBool("window_maximized"); FpsControl fps_control; f32 dtime = 0.0f; @@ -377,6 +380,8 @@ void GUIEngine::run() /******************************************************************************/ GUIEngine::~GUIEngine() { + g_settings->deregisterChangedCallback("fullscreen", fullscreenChangedCallback, this); + // deinitialize script first. gc destructors might depend on other stuff infostream << "GUIEngine: Deinitializing scripting" << std::endl; m_script.reset(); @@ -666,3 +671,9 @@ void GUIEngine::updateTopLeftTextSize() m_irr_toplefttext = gui::StaticText::add(m_rendering_engine->get_gui_env(), m_toplefttext, rect, false, true, 0, -1); } + +/******************************************************************************/ +void GUIEngine::fullscreenChangedCallback(const std::string &name, void *data) +{ + static_cast(data)->getScriptIface()->handleMainMenuEvent("FullscreenChange"); +} diff --git a/src/gui/guiEngine.h b/src/gui/guiEngine.h index b2b396537..fa4e1ebd3 100644 --- a/src/gui/guiEngine.h +++ b/src/gui/guiEngine.h @@ -296,4 +296,6 @@ class GUIEngine { bool m_clouds_enabled = true; /** data used to draw clouds */ clouddata m_cloud; + + static void fullscreenChangedCallback(const std::string &name, void *data); }; From cfd2d5f781cd22cff3bedb05240ed8fbf5389cca Mon Sep 17 00:00:00 2001 From: grorp Date: Sun, 2 Jun 2024 21:05:35 +0200 Subject: [PATCH 068/146] Document keymap_toggle_block_bounds, remove unimplemented BLOCK_BOUNDS_MAX (#14722) --- builtin/settingtypes.txt | 3 +++ src/client/game.cpp | 3 --- src/client/hud.cpp | 2 +- src/client/hud.h | 1 - 4 files changed, 4 insertions(+), 5 deletions(-) diff --git a/builtin/settingtypes.txt b/builtin/settingtypes.txt index e455f0f6f..5c8bbb7f6 100644 --- a/builtin/settingtypes.txt +++ b/builtin/settingtypes.txt @@ -2543,6 +2543,9 @@ keymap_toggle_debug (Debug info toggle key) key KEY_F5 # Key for toggling the display of the profiler. Used for development. keymap_toggle_profiler (Profiler toggle key) key KEY_F6 +# Key for toggling the display of mapblock boundaries. +keymap_toggle_block_bounds (Block bounds toggle key) key + # Key for switching between first- and third-person camera. keymap_camera_mode (Toggle camera mode key) key KEY_KEY_C diff --git a/src/client/game.cpp b/src/client/game.cpp index 5da2078a8..f5671f728 100644 --- a/src/client/game.cpp +++ b/src/client/game.cpp @@ -1663,9 +1663,6 @@ void Game::toggleBlockBounds() case Hud::BLOCK_BOUNDS_NEAR: m_game_ui->showTranslatedStatusText("Block bounds shown for nearby blocks"); break; - case Hud::BLOCK_BOUNDS_MAX: - m_game_ui->showTranslatedStatusText("Block bounds shown for all blocks"); - break; default: break; } diff --git a/src/client/hud.cpp b/src/client/hud.cpp index 6bb9c234b..637a2f500 100644 --- a/src/client/hud.cpp +++ b/src/client/hud.cpp @@ -913,7 +913,7 @@ enum Hud::BlockBoundsMode Hud::toggleBlockBounds() { m_block_bounds_mode = static_cast(m_block_bounds_mode + 1); - if (m_block_bounds_mode >= BLOCK_BOUNDS_MAX) { + if (m_block_bounds_mode > BLOCK_BOUNDS_NEAR) { m_block_bounds_mode = BLOCK_BOUNDS_OFF; } return m_block_bounds_mode; diff --git a/src/client/hud.h b/src/client/hud.h index 746a1b33e..b2b5dd09c 100644 --- a/src/client/hud.h +++ b/src/client/hud.h @@ -40,7 +40,6 @@ class Hud BLOCK_BOUNDS_OFF, BLOCK_BOUNDS_CURRENT, BLOCK_BOUNDS_NEAR, - BLOCK_BOUNDS_MAX } m_block_bounds_mode = BLOCK_BOUNDS_OFF; video::SColor crosshair_argb; From f630546cb449fb7402e5f96adb8d6a6db9ac73ff Mon Sep 17 00:00:00 2001 From: Lars Mueller Date: Mon, 3 Jun 2024 01:14:15 +0200 Subject: [PATCH 069/146] Fix CI not running on Irrlicht-only PRs --- .github/workflows/android.yml | 2 ++ .github/workflows/cpp_lint.yml | 2 ++ .github/workflows/docker_image.yml | 2 ++ .github/workflows/linux.yml | 2 ++ .github/workflows/macos.yml | 3 +++ .github/workflows/windows.yml | 2 ++ 6 files changed, 13 insertions(+) diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index 20da8e551..1ae4ac663 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -20,6 +20,8 @@ on: - 'lib/**.cpp' - 'src/**.[ch]' - 'src/**.cpp' + - 'irr/**.[ch]' + - 'irr/**.cpp' - '**/CMakeLists.txt' - 'cmake/Modules/**' - 'android/**' diff --git a/.github/workflows/cpp_lint.yml b/.github/workflows/cpp_lint.yml index 79b8ffc4e..0ec8ebf1d 100644 --- a/.github/workflows/cpp_lint.yml +++ b/.github/workflows/cpp_lint.yml @@ -20,6 +20,8 @@ on: - 'lib/**.cpp' - 'src/**.[ch]' - 'src/**.cpp' + - 'irr/**.[ch]' + - 'irr/**.cpp' - '**/CMakeLists.txt' - 'cmake/Modules/**' - 'util/ci/**' diff --git a/.github/workflows/docker_image.yml b/.github/workflows/docker_image.yml index 421d2edf4..1a2805e22 100644 --- a/.github/workflows/docker_image.yml +++ b/.github/workflows/docker_image.yml @@ -17,6 +17,8 @@ on: - 'lib/**.cpp' - 'src/**.[ch]' - 'src/**.cpp' + - 'irr/**.[ch]' + - 'irr/**.cpp' - '**/CMakeLists.txt' - 'cmake/Modules/**' - 'util/ci/**' diff --git a/.github/workflows/linux.yml b/.github/workflows/linux.yml index 08fc34ad6..2ecc42052 100644 --- a/.github/workflows/linux.yml +++ b/.github/workflows/linux.yml @@ -22,6 +22,8 @@ on: - 'lib/**.cpp' - 'src/**.[ch]' - 'src/**.cpp' + - 'irr/**.[ch]' + - 'irr/**.cpp' - '**/CMakeLists.txt' - 'cmake/Modules/**' - 'util/ci/**' diff --git a/.github/workflows/macos.yml b/.github/workflows/macos.yml index a7c670ed7..ad9982f66 100644 --- a/.github/workflows/macos.yml +++ b/.github/workflows/macos.yml @@ -19,6 +19,9 @@ on: - 'lib/**.cpp' - 'src/**.[ch]' - 'src/**.cpp' + - 'irr/**.[ch]' + - 'irr/**.cpp' + - 'irr/**.mm' # Objective-C(++) - '**/CMakeLists.txt' - 'cmake/Modules/**' - '.github/workflows/macos.yml' diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 709b98437..7cadec17e 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -21,6 +21,8 @@ on: - 'lib/**.cpp' - 'src/**.[ch]' - 'src/**.cpp' + - 'irr/**.[ch]' + - 'irr/**.cpp' - '**/CMakeLists.txt' - 'cmake/Modules/**' - 'util/buildbot/**' From b2f1942cc9adfbd7b7825ddc482df36503a4efce Mon Sep 17 00:00:00 2001 From: Alex <24834740+GreenXenith@users.noreply.github.com> Date: Mon, 3 Jun 2024 03:40:34 -0700 Subject: [PATCH 070/146] Fix and clarify skybox texture order documentation (#14680) --- doc/lua_api.md | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/doc/lua_api.md b/doc/lua_api.md index 7fa80d6ce..4764ecf28 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -8225,7 +8225,11 @@ child will follow movement and rotation of that bone. * `"plain"`: Uses 0 textures, `base_color` used as both fog and sky. (default: `"regular"`) * `textures`: A table containing up to six textures in the following - order: Y+ (top), Y- (bottom), X- (west), X+ (east), Z+ (north), Z- (south). + order: Y+ (top), Y- (bottom), X+ (east), X- (west), Z- (south), Z+ (north). + The top and bottom textures are oriented in-line with the east (X+) face (the top edge of the + bottom texture and the bottom edge of the top texture touch the east face). + Some top and bottom textures expect to be aligned with the north face and will need to be rotated + by -90 and 90 degrees, respectively, to fit the eastward orientation. * `clouds`: Boolean for whether clouds appear. (default: `true`) * `sky_color`: A table used in `"regular"` type only, containing the following values (alpha is ignored): From cc72d134645dd3e510b20ec950f00dd181ab4927 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Lars=20M=C3=BCller?= <34514239+appgurueu@users.noreply.github.com> Date: Mon, 3 Jun 2024 20:38:09 +0200 Subject: [PATCH 071/146] Fix scrolling in scroll containers (#14702) --- irr/include/IEventReceiver.h | 1 - irr/src/CIrrDeviceOSX.mm | 2 ++ irr/src/CIrrDeviceSDL.cpp | 1 + 3 files changed, 3 insertions(+), 1 deletion(-) diff --git a/irr/include/IEventReceiver.h b/irr/include/IEventReceiver.h index e0660a6f1..cf7dee3ab 100644 --- a/irr/include/IEventReceiver.h +++ b/irr/include/IEventReceiver.h @@ -331,7 +331,6 @@ struct SEvent //! A bitmap of button states. You can use isButtonPressed() to determine //! if a button is pressed or not. - //! Currently only valid if the event was EMIE_MOUSE_MOVED u32 ButtonStates; //! Is the left button pressed down? diff --git a/irr/src/CIrrDeviceOSX.mm b/irr/src/CIrrDeviceOSX.mm index 8e4843441..67c0ce05c 100644 --- a/irr/src/CIrrDeviceOSX.mm +++ b/irr/src/CIrrDeviceOSX.mm @@ -852,6 +852,7 @@ - (BOOL)isQuit ievent.MouseInput.Wheel *= 10.0f; else ievent.MouseInput.Wheel *= 5.0f; + ievent.MouseInput.ButtonStates = MouseButtonStates; postMouseEvent(event, ievent); break; @@ -1048,6 +1049,7 @@ - (BOOL)isQuit ievent.MouseInput.Event = irr::EMIE_MOUSE_MOVED; ievent.MouseInput.X = x; ievent.MouseInput.Y = y; + ievent.MouseInput.ButtonStates = MouseButtonStates; postEventFromUser(ievent); } } diff --git a/irr/src/CIrrDeviceSDL.cpp b/irr/src/CIrrDeviceSDL.cpp index 6f2772aa6..c01e6900e 100644 --- a/irr/src/CIrrDeviceSDL.cpp +++ b/irr/src/CIrrDeviceSDL.cpp @@ -667,6 +667,7 @@ bool CIrrDeviceSDL::run() #else irrevent.MouseInput.Wheel = SDL_event.wheel.y; #endif + irrevent.MouseInput.ButtonStates = MouseButtonStates; irrevent.MouseInput.Shift = (keymod & KMOD_SHIFT) != 0; irrevent.MouseInput.Control = (keymod & KMOD_CTRL) != 0; irrevent.MouseInput.X = MouseX; From 3bbbf738f383d5c080b89db13d617ed61140213a Mon Sep 17 00:00:00 2001 From: SmallJoker Date: Wed, 5 Jun 2024 20:11:47 +0200 Subject: [PATCH 072/146] Chat: Remove tailing punctuation from clickable links --- src/chat.cpp | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/chat.cpp b/src/chat.cpp index 5331a87bd..ec84d125f 100644 --- a/src/chat.cpp +++ b/src/chat.cpp @@ -376,6 +376,13 @@ u32 ChatBuffer::formatChatLine(const ChatLine &line, u32 cols, tempchar = linestring[in_pos+frag_length]; } + // Remove tailing punctuation characters + static const std::wstring tailing_chars = L",."; + tempchar = linestring[in_pos+frag_length - 1]; + if (tailing_chars.find(tempchar) != std::wstring::npos) { + frag_length--; + } + space_pos = frag_length - 1; // This frag may need to be force-split. That's ok, urls aren't "words" if (frag_length >= remaining_in_output) { From d5e2243500bc9c85241a23b258a30d7ef1eb2b4f Mon Sep 17 00:00:00 2001 From: sfan5 Date: Fri, 7 Jun 2024 16:57:30 +0200 Subject: [PATCH 073/146] Call malloc_trim() regularly to improve deallocation behavior (#14707) --- irr/include/IMeshBuffer.h | 26 +++++++++++++++++++++++ src/CMakeLists.txt | 6 ++++++ src/client/mapblock_mesh.cpp | 5 +++++ src/cmake_config.h.in | 1 + src/mapblock.cpp | 1 + src/mapgen/mg_schematic.cpp | 4 +++- src/porting.cpp | 40 +++++++++++++++++++++++++++++++++++- src/porting.h | 12 +++++++++++ src/voxel.cpp | 8 ++++++-- 9 files changed, 99 insertions(+), 4 deletions(-) diff --git a/irr/include/IMeshBuffer.h b/irr/include/IMeshBuffer.h index 3e2e3d74e..c69a12d1d 100644 --- a/irr/include/IMeshBuffer.h +++ b/irr/include/IMeshBuffer.h @@ -176,6 +176,32 @@ class IMeshBuffer : public virtual IReferenceCounted } return 0; } + + //! Calculate size of vertices and indices in memory + virtual size_t getSize() const + { + size_t ret = 0; + switch (getVertexType()) { + case video::EVT_STANDARD: + ret += sizeof(video::S3DVertex) * getVertexCount(); + break; + case video::EVT_2TCOORDS: + ret += sizeof(video::S3DVertex2TCoords) * getVertexCount(); + break; + case video::EVT_TANGENTS: + ret += sizeof(video::S3DVertexTangents) * getVertexCount(); + break; + } + switch (getIndexType()) { + case video::EIT_16BIT: + ret += sizeof(u16) * getIndexCount(); + break; + case video::EIT_32BIT: + ret += sizeof(u32) * getIndexCount(); + break; + } + return ret; + } }; } // end namespace scene diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index bcbe6d7f5..2ea416a4d 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -351,6 +351,12 @@ endif() include(CheckSymbolExists) check_symbol_exists(strlcpy "string.h" HAVE_STRLCPY) +if(UNIX) + check_symbol_exists(malloc_trim "malloc.h" HAVE_MALLOC_TRIM) +else() + set(HAVE_MALLOC_TRIM FALSE) +endif() + check_include_files(endian.h HAVE_ENDIAN_H) configure_file( diff --git a/src/client/mapblock_mesh.cpp b/src/client/mapblock_mesh.cpp index 7aca7dfb0..a64df39c4 100644 --- a/src/client/mapblock_mesh.cpp +++ b/src/client/mapblock_mesh.cpp @@ -996,11 +996,16 @@ MapBlockMesh::MapBlockMesh(Client *client, MeshMakeData *data, v3s16 camera_offs MapBlockMesh::~MapBlockMesh() { + size_t sz = 0; for (scene::IMesh *m : m_mesh) { + for (u32 i = 0; i < m->getMeshBufferCount(); i++) + sz += m->getMeshBuffer(i)->getSize(); m->drop(); } for (MinimapMapblock *block : m_minimap_mapblocks) delete block; + + porting::TrackFreedMemory(sz); } bool MapBlockMesh::animate(bool faraway, float time, int crack, diff --git a/src/cmake_config.h.in b/src/cmake_config.h.in index 33ce41943..2185600af 100644 --- a/src/cmake_config.h.in +++ b/src/cmake_config.h.in @@ -31,6 +31,7 @@ #cmakedefine01 USE_REDIS #cmakedefine01 HAVE_ENDIAN_H #cmakedefine01 HAVE_STRLCPY +#cmakedefine01 HAVE_MALLOC_TRIM #cmakedefine01 CURSES_HAVE_CURSES_H #cmakedefine01 CURSES_HAVE_NCURSES_H #cmakedefine01 CURSES_HAVE_NCURSES_NCURSES_H diff --git a/src/mapblock.cpp b/src/mapblock.cpp index b3e951b1f..9a27a9f3d 100644 --- a/src/mapblock.cpp +++ b/src/mapblock.cpp @@ -85,6 +85,7 @@ MapBlock::~MapBlock() #endif delete[] data; + porting::TrackFreedMemory(sizeof(MapNode) * nodecount); } bool MapBlock::onObjectsActivation() diff --git a/src/mapgen/mg_schematic.cpp b/src/mapgen/mg_schematic.cpp index 5ce0b8844..bc1b35ab4 100644 --- a/src/mapgen/mg_schematic.cpp +++ b/src/mapgen/mg_schematic.cpp @@ -19,7 +19,6 @@ with this program; if not, write to the Free Software Foundation, Inc., */ #include -#include #include "mg_schematic.h" #include "server.h" #include "mapgen.h" @@ -32,6 +31,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "serialization.h" #include "filesys.h" #include "voxelalgorithms.h" +#include "porting.h" /////////////////////////////////////////////////////////////////////////////// @@ -80,6 +80,8 @@ Schematic::~Schematic() { delete []schemdata; delete []slice_probs; + u32 nodecount = size.X * size.Y * size.Z; + porting::TrackFreedMemory(nodecount * sizeof(MapNode)); } ObjDef *Schematic::clone() const diff --git a/src/porting.cpp b/src/porting.cpp index 2b2405d38..0763e92e1 100644 --- a/src/porting.cpp +++ b/src/porting.cpp @@ -63,7 +63,11 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #endif -#include "config.h" +#if HAVE_MALLOC_TRIM + // glibc-only pretty much + #include +#endif + #include "debug.h" #include "filesys.h" #include "log.h" @@ -72,6 +76,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include +#include #if !defined(SERVER) && defined(_WIN32) // On Windows export some driver-specific variables to encourage Minetest to be @@ -903,4 +908,37 @@ double perf_freq = get_perf_freq(); #endif +#if HAVE_MALLOC_TRIM + +/* + * On Linux/glibc we found that after deallocating bigger chunks of data (esp. MapBlocks) + * the memory would not be given back to the OS and would stay at peak usage. + * This appears to be a combination of unfortunate allocation order/fragmentation + * and the fact that glibc does not call madvise(MADV_DONTNEED) on its own. + * Some other allocators were also affected, jemalloc and musl libc were not. + * read more: + * + * As a workaround we track freed memory coarsely and call malloc_trim() once a + * certain amount is reached. + */ + +static std::atomic memory_freed; + +constexpr size_t MEMORY_TRIM_THRESHOLD = 128 * 1024 * 1024; + +void TrackFreedMemory(size_t amount) +{ + constexpr auto MO = std::memory_order_relaxed; + memory_freed.fetch_add(amount, MO); + if (memory_freed.load(MO) >= MEMORY_TRIM_THRESHOLD) { + // Synchronize call + if (memory_freed.exchange(0, MO) < MEMORY_TRIM_THRESHOLD) + return; + // Leave some headroom for future allocations + malloc_trim(1 * 1024 * 1024); + } +} + +#endif + } //namespace porting diff --git a/src/porting.h b/src/porting.h index a98e66bcf..27fabe7cd 100644 --- a/src/porting.h +++ b/src/porting.h @@ -290,6 +290,17 @@ void osSpecificInit(); // This attaches to the parents process console, or creates a new one if it doesnt exist. void attachOrCreateConsole(); +/** + * Call this after freeing bigger blocks of memory. Used on some platforms to + * properly give memory back to the OS. + * @param amount Number of bytes freed +*/ +#if HAVE_MALLOC_TRIM +void TrackFreedMemory(size_t amount); +#else +inline void TrackFreedMemory(size_t amount) { (void)amount; } +#endif + #ifdef _WIN32 // Quotes an argument for use in a CreateProcess() commandline (not cmd.exe!!) std::string QuoteArgv(const std::string &arg); @@ -298,6 +309,7 @@ std::string QuoteArgv(const std::string &arg); std::string ConvertError(DWORD error_code); #endif +// snprintf wrapper int mt_snprintf(char *buf, const size_t buf_size, const char *fmt, ...); /** diff --git a/src/voxel.cpp b/src/voxel.cpp index bb270f53b..0f87cf282 100644 --- a/src/voxel.cpp +++ b/src/voxel.cpp @@ -23,6 +23,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "nodedef.h" #include "util/directiontables.h" #include "util/timetaker.h" +#include "porting.h" #include // memcpy, memset /* @@ -40,12 +41,15 @@ VoxelManipulator::~VoxelManipulator() void VoxelManipulator::clear() { - // Reset area to volume=0 - m_area = VoxelArea(); + // Reset area to empty volume + VoxelArea old; + std::swap(m_area, old); delete[] m_data; m_data = nullptr; delete[] m_flags; m_flags = nullptr; + + porting::TrackFreedMemory((sizeof(*m_data) + sizeof(*m_flags)) * old.getVolume()); } void VoxelManipulator::print(std::ostream &o, const NodeDefManager *ndef, From 5815da3f5bc6fc52d0930fa39d65b4b8e53d86dd Mon Sep 17 00:00:00 2001 From: cx384 Date: Sat, 8 Jun 2024 12:37:22 +0200 Subject: [PATCH 074/146] Improve register_on_leaveplayer documentation --- doc/lua_api.md | 1 + 1 file changed, 1 insertion(+) diff --git a/doc/lua_api.md b/doc/lua_api.md index 4764ecf28..7eac5e3ac 100644 --- a/doc/lua_api.md +++ b/doc/lua_api.md @@ -5796,6 +5796,7 @@ Call these functions only at load time! * `last_login`: The timestamp of the previous login, or nil if player is new * `minetest.register_on_leaveplayer(function(ObjectRef, timed_out))` * Called when a player leaves the game + * Does not get executed for connected players on shutdown. * `timed_out`: True for timeout, false for other reasons. * `minetest.register_on_authplayer(function(name, ip, is_success))` * Called when a client attempts to log into an account. From 86da35c1573686049c528d04efc34eb87eb7a316 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 14 May 2024 19:26:22 +0200 Subject: [PATCH 075/146] Stop misusing volatile keyword --- src/log.cpp | 4 ++-- src/log.h | 10 +++++----- src/network/connection.cpp | 4 ++-- src/unittest/test_threading.cpp | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) diff --git a/src/log.cpp b/src/log.cpp index 5ee66c070..6dbc43372 100644 --- a/src/log.cpp +++ b/src/log.cpp @@ -253,7 +253,7 @@ const std::string Logger::getThreadName() void Logger::log(LogLevel lev, const std::string &text) { - if (m_silenced_levels[lev]) + if (isLevelSilenced(lev)) return; const std::string thread_name = getThreadName(); @@ -267,7 +267,7 @@ void Logger::log(LogLevel lev, const std::string &text) void Logger::logRaw(LogLevel lev, const std::string &text) { - if (m_silenced_levels[lev]) + if (isLevelSilenced(lev)) return; logToOutputsRaw(lev, text); diff --git a/src/log.h b/src/log.h index 4255e55bc..f92d17737 100644 --- a/src/log.h +++ b/src/log.h @@ -79,6 +79,10 @@ class Logger { return m_has_outputs[level].load(std::memory_order_relaxed); } + bool isLevelSilenced(LogLevel level) { + return m_silenced_levels[level].load(std::memory_order_relaxed); + } + static LogColor color_mode; private: @@ -91,11 +95,7 @@ class Logger { std::vector m_outputs[LL_MAX]; std::atomic m_has_outputs[LL_MAX]; - - // Should implement atomic loads and stores (even though it's only - // written to when one thread has access currently). - // Works on all known architectures (x86, ARM, MIPS). - volatile bool m_silenced_levels[LL_MAX]; + std::atomic m_silenced_levels[LL_MAX]; std::map m_thread_names; mutable std::mutex m_mutex; }; diff --git a/src/network/connection.cpp b/src/network/connection.cpp index 84a6c53c5..00b4fe4b0 100644 --- a/src/network/connection.cpp +++ b/src/network/connection.cpp @@ -1078,7 +1078,7 @@ bool UDPPeer::processReliableSendCommand( bool have_sequence_number = false; bool have_initial_sequence_number = false; std::queue toadd; - volatile u16 initial_sequence_number = 0; + u16 initial_sequence_number = 0; for (SharedBuffer &original : originals) { u16 seqnum = chan.getOutgoingSequenceNumber(have_sequence_number); @@ -1118,7 +1118,7 @@ bool UDPPeer::processReliableSendCommand( return true; } - volatile u16 packets_available = toadd.size(); + u16 packets_available = toadd.size(); /* we didn't get a single sequence number no need to fill queue */ if (!have_initial_sequence_number) { LOG(derr_con << m_connection->getDesc() << "Ran out of sequence numbers!" << std::endl); diff --git a/src/unittest/test_threading.cpp b/src/unittest/test_threading.cpp index 9d7b7c808..a91e1b532 100644 --- a/src/unittest/test_threading.cpp +++ b/src/unittest/test_threading.cpp @@ -161,7 +161,7 @@ void TestThreading::testAtomicSemaphoreThread() -static volatile bool g_tls_broken; +static std::atomic g_tls_broken; class TLSTestThread : public Thread { public: @@ -226,7 +226,7 @@ class TLSTestThread : public Thread { */ void TestThreading::testTLS() { - static const int num_threads = 10; + constexpr int num_threads = 10; for (int j = 0; j < num_threads; j++) { g_tls_broken = false; From 66b20155966870563d10445d055db0db5add4040 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Tue, 14 May 2024 19:56:31 +0200 Subject: [PATCH 076/146] A few clean ups in log.cpp --- src/log.cpp | 68 +++++++++++++++++++++-------------- src/log.h | 18 ++-------- src/terminal_chat_console.cpp | 2 +- 3 files changed, 45 insertions(+), 43 deletions(-) diff --git a/src/log.cpp b/src/log.cpp index 6dbc43372..98939c9bf 100644 --- a/src/log.cpp +++ b/src/log.cpp @@ -34,6 +34,10 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #endif +#if !defined(_WIN32) +#include // isatty +#endif + #include #include #include @@ -104,21 +108,19 @@ thread_local LogStream dout_con(trace_target); static unsigned int g_level_to_android[] = { ANDROID_LOG_INFO, // LL_NONE - //ANDROID_LOG_FATAL, ANDROID_LOG_ERROR, // LL_ERROR ANDROID_LOG_WARN, // LL_WARNING - ANDROID_LOG_WARN, // LL_ACTION - //ANDROID_LOG_INFO, + ANDROID_LOG_INFO, // LL_ACTION ANDROID_LOG_DEBUG, // LL_INFO ANDROID_LOG_VERBOSE, // LL_VERBOSE ANDROID_LOG_VERBOSE, // LL_TRACE }; -void AndroidLogOutput::logRaw(LogLevel lev, const std::string &line) { +void AndroidLogOutput::logRaw(LogLevel lev, const std::string &line) +{ static_assert(ARRLEN(g_level_to_android) == LL_MAX, "mismatch between android and internal loglevels"); - __android_log_print(g_level_to_android[lev], - PROJECT_NAME_C, "%s", line.c_str()); + __android_log_write(g_level_to_android[lev], PROJECT_NAME_C, line.c_str()); } #endif @@ -156,9 +158,7 @@ void Logger::addOutput(ILogOutput *out) void Logger::addOutput(ILogOutput *out, LogLevel lev) { - MutexAutoLock lock(m_mutex); - m_outputs[lev].push_back(out); - m_has_outputs[lev] = true; + addOutputMasked(out, LOGLEVEL_TO_MASKLEVEL(lev)); } void Logger::addOutputMasked(ILogOutput *out, LogLevelMask mask) @@ -187,9 +187,7 @@ LogLevelMask Logger::removeOutput(ILogOutput *out) MutexAutoLock lock(m_mutex); LogLevelMask ret_mask = 0; for (size_t i = 0; i < LL_MAX; i++) { - std::vector::iterator it; - - it = std::find(m_outputs[i].begin(), m_outputs[i].end(), out); + auto it = std::find(m_outputs[i].begin(), m_outputs[i].end(), out); if (it != m_outputs[i].end()) { ret_mask |= LOGLEVEL_TO_MASKLEVEL(i); m_outputs[i].erase(it); @@ -218,9 +216,9 @@ void Logger::deregisterThread() m_thread_names.erase(id); } -const std::string Logger::getLevelLabel(LogLevel lev) +const char *Logger::getLevelLabel(LogLevel lev) { - static const std::string names[] = { + static const char *names[] = { "", "ERROR", "WARNING", @@ -229,26 +227,29 @@ const std::string Logger::getLevelLabel(LogLevel lev) "VERBOSE", "TRACE", }; - assert(lev < LL_MAX && lev >= 0); static_assert(ARRLEN(names) == LL_MAX, "mismatch between loglevel names and enum"); + assert(lev < LL_MAX && lev >= 0); return names[lev]; } LogColor Logger::color_mode = LOG_COLOR_AUTO; -const std::string Logger::getThreadName() +const std::string &Logger::getThreadName() { - std::map::const_iterator it; - std::thread::id id = std::this_thread::get_id(); - it = m_thread_names.find(id); + + auto it = m_thread_names.find(id); if (it != m_thread_names.end()) return it->second; - std::ostringstream os; - os << "#0x" << std::hex << id; - return os.str(); + thread_local std::string fallback_name; + if (fallback_name.empty()) { + std::ostringstream os; + os << "#0x" << std::hex << id; + fallback_name = os.str(); + } + return fallback_name; } void Logger::log(LogLevel lev, const std::string &text) @@ -256,13 +257,15 @@ void Logger::log(LogLevel lev, const std::string &text) if (isLevelSilenced(lev)) return; - const std::string thread_name = getThreadName(); - const std::string label = getLevelLabel(lev); + const std::string &thread_name = getThreadName(); + const char *label = getLevelLabel(lev); const std::string timestamp = getTimestamp(); - std::ostringstream os(std::ios_base::binary); - os << timestamp << ": " << label << "[" << thread_name << "]: " << text; - logToOutputs(lev, os.str(), timestamp, thread_name, text); + std::string line = timestamp; + line.append(": ").append(label).append("[").append(thread_name) + .append("]: ").append(text); + + logToOutputs(lev, line, timestamp, thread_name, text); } void Logger::logRaw(LogLevel lev, const std::string &text) @@ -320,6 +323,17 @@ void FileLogOutput::setFile(const std::string &filename, s64 file_size_max) "-------------\n" << std::endl; } +StreamLogOutput::StreamLogOutput(std::ostream &stream) : + m_stream(stream) +{ +#if !defined(_WIN32) + if (&stream == &std::cout) + is_tty = isatty(STDOUT_FILENO); + else if (&stream == &std::cerr) + is_tty = isatty(STDERR_FILENO); +#endif +} + void StreamLogOutput::logRaw(LogLevel lev, const std::string &line) { bool colored_message = (Logger::color_mode == LOG_COLOR_ALWAYS) || diff --git a/src/log.h b/src/log.h index f92d17737..9ac4e5767 100644 --- a/src/log.h +++ b/src/log.h @@ -26,9 +26,6 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include -#if !defined(_WIN32) // POSIX - #include -#endif #include "threading/mutex_auto_lock.h" #include "util/basic_macros.h" #include "util/stream.h" @@ -73,7 +70,7 @@ class Logger { void logRaw(LogLevel lev, const std::string &text); static LogLevel stringToLevel(const std::string &name); - static const std::string getLevelLabel(LogLevel lev); + static const char *getLevelLabel(LogLevel lev); bool hasOutput(LogLevel level) { return m_has_outputs[level].load(std::memory_order_relaxed); @@ -91,7 +88,7 @@ class Logger { const std::string &time, const std::string &thread_name, const std::string &payload_text); - const std::string getThreadName(); + const std::string &getThreadName(); std::vector m_outputs[LL_MAX]; std::atomic m_has_outputs[LL_MAX]; @@ -120,16 +117,7 @@ class ICombinedLogOutput : public ILogOutput { class StreamLogOutput : public ICombinedLogOutput { public: - StreamLogOutput(std::ostream &stream) : - m_stream(stream) - { -#if !defined(_WIN32) - if (&stream == &std::cout) - is_tty = isatty(STDOUT_FILENO); - else if (&stream == &std::cerr) - is_tty = isatty(STDERR_FILENO); -#endif - } + StreamLogOutput(std::ostream &stream); void logRaw(LogLevel lev, const std::string &line); diff --git a/src/terminal_chat_console.cpp b/src/terminal_chat_console.cpp index b1512d8f3..752d7c7b8 100644 --- a/src/terminal_chat_console.cpp +++ b/src/terminal_chat_console.cpp @@ -426,7 +426,7 @@ void TerminalChatConsole::step(int ch) printw("[ESC] Toggle ESC mode |" " [CTRL+C] Shut down |" " (L) in-, (l) decrease loglevel %s", - Logger::getLevelLabel((LogLevel) m_log_level).c_str()); + Logger::getLevelLabel((LogLevel) m_log_level)); } refresh(); From cbdc44150dd0875b94ff9550e58c1bad724d8b13 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 5 Jun 2024 22:43:06 +0200 Subject: [PATCH 077/146] Identify when compiled with openresty LuaJIT Debian testing ships it --- src/main.cpp | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main.cpp b/src/main.cpp index 7f078d9b2..7474ae84c 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -427,7 +427,11 @@ static void print_version(std::ostream &os) os << PROJECT_NAME_C " " << g_version_hash << " (" << porting::getPlatformName() << ")" << std::endl; #if USE_LUAJIT - os << "Using " << LUAJIT_VERSION << std::endl; + os << "Using " << LUAJIT_VERSION +#ifdef OPENRESTY_LUAJIT + << " (OpenResty)" +#endif + << std::endl; #else os << "Using " << LUA_RELEASE << std::endl; #endif From dd29d3d61f87c7d875be7d7148e4013b2922d8e3 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 5 Jun 2024 22:58:33 +0200 Subject: [PATCH 078/146] Make safeWriteToFile safe for thread-concurrent use --- src/filesys.cpp | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/filesys.cpp b/src/filesys.cpp index 91e4ccb69..11d3b9009 100644 --- a/src/filesys.cpp +++ b/src/filesys.cpp @@ -25,6 +25,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include #include #include +#include #include "log.h" #include "config.h" #include "porting.h" @@ -854,10 +855,12 @@ const char *GetFilenameFromPath(const char *path) return filename ? filename + 1 : path; } +// Note: this is not safe if two MT processes try this at the same time (FIXME?) bool safeWriteToFile(const std::string &path, std::string_view content) { - // Note: this is not safe if two MT processes try this at the same time (FIXME?) - const std::string tmp_file = path + ".~mt"; + // Prevent two threads from writing to the same temporary file + static std::atomic g_file_counter; + const std::string tmp_file = path + ".~mt" + itos(g_file_counter.fetch_add(1)); // Write data to a temporary file std::string write_error; From aaacc6e8960ffb3d57369f0040ee87360792de10 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 5 Jun 2024 23:04:09 +0200 Subject: [PATCH 079/146] Rename sha256 header fixes #14710 --- lib/sha256/{sha256/sha256.h => my_sha256.h} | 0 lib/sha256/sha256.c | 2 +- src/script/lua_api/l_util.cpp | 2 +- src/util/srp.cpp | 2 +- 4 files changed, 3 insertions(+), 3 deletions(-) rename lib/sha256/{sha256/sha256.h => my_sha256.h} (100%) diff --git a/lib/sha256/sha256/sha256.h b/lib/sha256/my_sha256.h similarity index 100% rename from lib/sha256/sha256/sha256.h rename to lib/sha256/my_sha256.h diff --git a/lib/sha256/sha256.c b/lib/sha256/sha256.c index 56bdec098..cd78e24f6 100644 --- a/lib/sha256/sha256.c +++ b/lib/sha256/sha256.c @@ -56,7 +56,7 @@ #include #include -#include "sha256/sha256.h" +#include "my_sha256.h" #if defined(_MSC_VER) && !defined(__clang__) && !defined(__attribute__) #define __attribute__(a) diff --git a/src/script/lua_api/l_util.cpp b/src/script/lua_api/l_util.cpp index c868f949c..883b9480c 100644 --- a/src/script/lua_api/l_util.cpp +++ b/src/script/lua_api/l_util.cpp @@ -42,7 +42,7 @@ with this program; if not, write to the Free Software Foundation, Inc., #include "version.h" #include "util/hex.h" #include "util/sha1.h" -#include +#include "my_sha256.h" #include "util/png.h" #include diff --git a/src/util/srp.cpp b/src/util/srp.cpp index ea64efca6..56b2aa763 100644 --- a/src/util/srp.cpp +++ b/src/util/srp.cpp @@ -50,7 +50,7 @@ #include #endif -#include +#include "my_sha256.h" #include "srp.h" //#define CSRP_USE_SHA1 From 98c0c8a8d47ed3e70ae6e41c36bcceca71b5a803 Mon Sep 17 00:00:00 2001 From: sfan5 Date: Wed, 5 Jun 2024 23:07:40 +0200 Subject: [PATCH 080/146] Fix file write warning in devtest closes #14721 --- games/devtest/mods/testnodes/textures.lua | 8 -------- 1 file changed, 8 deletions(-) diff --git a/games/devtest/mods/testnodes/textures.lua b/games/devtest/mods/testnodes/textures.lua index 0caaaed9d..86bc3e343 100644 --- a/games/devtest/mods/testnodes/textures.lua +++ b/games/devtest/mods/testnodes/textures.lua @@ -181,14 +181,6 @@ fractal = nil frac_emb = nil checker = nil -do - -- we used to write the textures to our mod folder. in order to avoid - -- duplication errors delete them if they still exist. - local path = core.get_modpath(core.get_current_modname()) .. "/textures/" - os.remove(path .. "testnodes_generated_mb.png") - os.remove(path .. "testnodes_generated_ck.png") -end - local textures_path = core.get_worldpath() .. "/" core.safe_file_write( textures_path .. "testnodes1.png", From b38e42df413bdb898421b197802d865067b5031e Mon Sep 17 00:00:00 2001 From: sfan5 Date: Fri, 7 Jun 2024 16:52:31 +0200 Subject: [PATCH 081/146] Set some useful SDL hints fixes #14596 --- irr/src/CIrrDeviceSDL.cpp | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/irr/src/CIrrDeviceSDL.cpp b/irr/src/CIrrDeviceSDL.cpp index c01e6900e..81ff37511 100644 --- a/irr/src/CIrrDeviceSDL.cpp +++ b/irr/src/CIrrDeviceSDL.cpp @@ -272,6 +272,19 @@ CIrrDeviceSDL::CIrrDeviceSDL(const SIrrlichtCreationParameters ¶m) : SDL_SetHint(SDL_HINT_ENABLE_SCREEN_KEYBOARD, "0"); #endif + // Minetest has its own signal handler + SDL_SetHint(SDL_HINT_NO_SIGNAL_HANDLERS, "1"); + + // Disabling the compositor is not a good idea in windowed mode. + // See https://github.com/minetest/minetest/issues/14596 + SDL_SetHint(SDL_HINT_VIDEO_X11_NET_WM_BYPASS_COMPOSITOR, "0"); + +#if defined(_IRR_COMPILE_WITH_JOYSTICK_EVENTS_) + // These are not interesting for our use + SDL_SetHint(SDL_HINT_ACCELEROMETER_AS_JOYSTICK, "0"); + SDL_SetHint(SDL_HINT_TV_REMOTE_AS_JOYSTICK, "0"); +#endif + // Minetest has its own code to synthesize mouse events from touch events, // so we prevent SDL from doing it. SDL_SetHint(SDL_HINT_TOUCH_MOUSE_EVENTS, "0"); From a8b243cb25fc4521a254a04d2d1157e7529b028c Mon Sep 17 00:00:00 2001 From: sfan5 Date: Fri, 7 Jun 2024 17:00:36 +0200 Subject: [PATCH 082/146] Update vcpkg used in CI --- .github/workflows/windows.yml | 10 ++-------- 1 file changed, 2 insertions(+), 8 deletions(-) diff --git a/.github/workflows/windows.yml b/.github/workflows/windows.yml index 7cadec17e..874788c41 100644 --- a/.github/workflows/windows.yml +++ b/.github/workflows/windows.yml @@ -69,8 +69,8 @@ jobs: name: VS 2019 ${{ matrix.config.arch }}-${{ matrix.type }} runs-on: windows-2019 env: - VCPKG_VERSION: 8eb57355a4ffb410a2e94c07b4dca2dffbee8e50 - # 2023.10.19 + VCPKG_VERSION: 01f602195983451bc83e72f4214af2cbc495aa94 + # 2024.05.24 vcpkg_packages: zlib zstd curl[winssl] openal-soft libvorbis libogg libjpeg-turbo sqlite3 freetype luajit gmp jsoncpp sdl2 strategy: fail-fast: false @@ -94,12 +94,6 @@ jobs: steps: - uses: actions/checkout@v4 - # Workaround for regression, see https://github.com/minetest/minetest/pull/14536 - - name: Pin CMake to 3.28 - uses: lukka/get-cmake@latest - with: - cmakeVersion: "~3.28.0" - - name: Restore from cache and run vcpkg uses: lukka/run-vcpkg@v7 with: From e10f82c56e7b7c838f71c3525f15ab6d0b679a74 Mon Sep 17 00:00:00 2001 From: minetest Date: Fri, 7 Jun 2024 18:44:58 +0200 Subject: [PATCH 083/146] Replace Catch2 with v3 amalgamation --- lib/catch2/catch.hpp | 17976 ----------------------------- lib/catch2/catch_amalgamated.cpp | 11638 +++++++++++++++++++ lib/catch2/catch_amalgamated.hpp | 14057 ++++++++++++++++++++++ 3 files changed, 25695 insertions(+), 17976 deletions(-) delete mode 100644 lib/catch2/catch.hpp create mode 100644 lib/catch2/catch_amalgamated.cpp create mode 100644 lib/catch2/catch_amalgamated.hpp diff --git a/lib/catch2/catch.hpp b/lib/catch2/catch.hpp deleted file mode 100644 index 9b309bddc..000000000 --- a/lib/catch2/catch.hpp +++ /dev/null @@ -1,17976 +0,0 @@ -/* - * Catch v2.13.10 - * Generated: 2022-10-16 11:01:23.452308 - * ---------------------------------------------------------- - * This file has been merged from multiple headers. Please don't edit it directly - * Copyright (c) 2022 Two Blue Cubes Ltd. All rights reserved. - * - * Distributed under the Boost Software License, Version 1.0. (See accompanying - * file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) - */ -#ifndef TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED -#define TWOBLUECUBES_SINGLE_INCLUDE_CATCH_HPP_INCLUDED -// start catch.hpp - - -#define CATCH_VERSION_MAJOR 2 -#define CATCH_VERSION_MINOR 13 -#define CATCH_VERSION_PATCH 10 - -#ifdef __clang__ -# pragma clang system_header -#elif defined __GNUC__ -# pragma GCC system_header -#endif - -// start catch_suppress_warnings.h - -#ifdef __clang__ -# ifdef __ICC // icpc defines the __clang__ macro -# pragma warning(push) -# pragma warning(disable: 161 1682) -# else // __ICC -# pragma clang diagnostic push -# pragma clang diagnostic ignored "-Wpadded" -# pragma clang diagnostic ignored "-Wswitch-enum" -# pragma clang diagnostic ignored "-Wcovered-switch-default" -# endif -#elif defined __GNUC__ - // Because REQUIREs trigger GCC's -Wparentheses, and because still - // supported version of g++ have only buggy support for _Pragmas, - // Wparentheses have to be suppressed globally. -# pragma GCC diagnostic ignored "-Wparentheses" // See #674 for details - -# pragma GCC diagnostic push -# pragma GCC diagnostic ignored "-Wunused-variable" -# pragma GCC diagnostic ignored "-Wpadded" -#endif -// end catch_suppress_warnings.h -#if defined(CATCH_CONFIG_MAIN) || defined(CATCH_CONFIG_RUNNER) -# define CATCH_IMPL -# define CATCH_CONFIG_ALL_PARTS -#endif - -// In the impl file, we want to have access to all parts of the headers -// Can also be used to sanely support PCHs -#if defined(CATCH_CONFIG_ALL_PARTS) -# define CATCH_CONFIG_EXTERNAL_INTERFACES -# if defined(CATCH_CONFIG_DISABLE_MATCHERS) -# undef CATCH_CONFIG_DISABLE_MATCHERS -# endif -# if !defined(CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER) -# define CATCH_CONFIG_ENABLE_CHRONO_STRINGMAKER -# endif -#endif - -#if !defined(CATCH_CONFIG_IMPL_ONLY) -// start catch_platform.h - -// See e.g.: -// https://opensource.apple.com/source/CarbonHeaders/CarbonHeaders-18.1/TargetConditionals.h.auto.html -#ifdef __APPLE__ -# include -# if (defined(TARGET_OS_OSX) && TARGET_OS_OSX == 1) || \ - (defined(TARGET_OS_MAC) && TARGET_OS_MAC == 1) -# define CATCH_PLATFORM_MAC -# elif (defined(TARGET_OS_IPHONE) && TARGET_OS_IPHONE == 1) -# define CATCH_PLATFORM_IPHONE -# endif - -#elif defined(linux) || defined(__linux) || defined(__linux__) -# define CATCH_PLATFORM_LINUX - -#elif defined(WIN32) || defined(__WIN32__) || defined(_WIN32) || defined(_MSC_VER) || defined(__MINGW32__) -# define CATCH_PLATFORM_WINDOWS -#endif - -// end catch_platform.h - -#ifdef CATCH_IMPL -# ifndef CLARA_CONFIG_MAIN -# define CLARA_CONFIG_MAIN_NOT_DEFINED -# define CLARA_CONFIG_MAIN -# endif -#endif - -// start catch_user_interfaces.h - -namespace Catch { - unsigned int rngSeed(); -} - -// end catch_user_interfaces.h -// start catch_tag_alias_autoregistrar.h - -// start catch_common.h - -// start catch_compiler_capabilities.h - -// Detect a number of compiler features - by compiler -// The following features are defined: -// -// CATCH_CONFIG_COUNTER : is the __COUNTER__ macro supported? -// CATCH_CONFIG_WINDOWS_SEH : is Windows SEH supported? -// CATCH_CONFIG_POSIX_SIGNALS : are POSIX signals supported? -// CATCH_CONFIG_DISABLE_EXCEPTIONS : Are exceptions enabled? -// **************** -// Note to maintainers: if new toggles are added please document them -// in configuration.md, too -// **************** - -// In general each macro has a _NO_ form -// (e.g. CATCH_CONFIG_NO_POSIX_SIGNALS) which disables the feature. -// Many features, at point of detection, define an _INTERNAL_ macro, so they -// can be combined, en-mass, with the _NO_ forms later. - -#ifdef __cplusplus - -# if (__cplusplus >= 201402L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201402L) -# define CATCH_CPP14_OR_GREATER -# endif - -# if (__cplusplus >= 201703L) || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L) -# define CATCH_CPP17_OR_GREATER -# endif - -#endif - -// Only GCC compiler should be used in this block, so other compilers trying to -// mask themselves as GCC should be ignored. -#if defined(__GNUC__) && !defined(__clang__) && !defined(__ICC) && !defined(__CUDACC__) && !defined(__LCC__) -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic push" ) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "GCC diagnostic pop" ) - -# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) - -#endif - -#if defined(__clang__) - -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic push" ) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION _Pragma( "clang diagnostic pop" ) - -// As of this writing, IBM XL's implementation of __builtin_constant_p has a bug -// which results in calls to destructors being emitted for each temporary, -// without a matching initialization. In practice, this can result in something -// like `std::string::~string` being called on an uninitialized value. -// -// For example, this code will likely segfault under IBM XL: -// ``` -// REQUIRE(std::string("12") + "34" == "1234") -// ``` -// -// Therefore, `CATCH_INTERNAL_IGNORE_BUT_WARN` is not implemented. -# if !defined(__ibmxl__) && !defined(__CUDACC__) -# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) (void)__builtin_constant_p(__VA_ARGS__) /* NOLINT(cppcoreguidelines-pro-type-vararg, hicpp-vararg) */ -# endif - -# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wexit-time-destructors\"" ) \ - _Pragma( "clang diagnostic ignored \"-Wglobal-constructors\"") - -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wparentheses\"" ) - -# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wunused-variable\"" ) - -# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wgnu-zero-variadic-macro-arguments\"" ) - -# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS \ - _Pragma( "clang diagnostic ignored \"-Wunused-template\"" ) - -#endif // __clang__ - -//////////////////////////////////////////////////////////////////////////////// -// Assume that non-Windows platforms support posix signals by default -#if !defined(CATCH_PLATFORM_WINDOWS) - #define CATCH_INTERNAL_CONFIG_POSIX_SIGNALS -#endif - -//////////////////////////////////////////////////////////////////////////////// -// We know some environments not to support full POSIX signals -#if defined(__CYGWIN__) || defined(__QNX__) || defined(__EMSCRIPTEN__) || defined(__DJGPP__) - #define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS -#endif - -#ifdef __OS400__ -# define CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS -# define CATCH_CONFIG_COLOUR_NONE -#endif - -//////////////////////////////////////////////////////////////////////////////// -// Android somehow still does not support std::to_string -#if defined(__ANDROID__) -# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING -# define CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE -#endif - -//////////////////////////////////////////////////////////////////////////////// -// Not all Windows environments support SEH properly -#if defined(__MINGW32__) -# define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH -#endif - -//////////////////////////////////////////////////////////////////////////////// -// PS4 -#if defined(__ORBIS__) -# define CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE -#endif - -//////////////////////////////////////////////////////////////////////////////// -// Cygwin -#ifdef __CYGWIN__ - -// Required for some versions of Cygwin to declare gettimeofday -// see: http://stackoverflow.com/questions/36901803/gettimeofday-not-declared-in-this-scope-cygwin -# define _BSD_SOURCE -// some versions of cygwin (most) do not support std::to_string. Use the libstd check. -// https://gcc.gnu.org/onlinedocs/gcc-4.8.2/libstdc++/api/a01053_source.html line 2812-2813 -# if !((__cplusplus >= 201103L) && defined(_GLIBCXX_USE_C99) \ - && !defined(_GLIBCXX_HAVE_BROKEN_VSWPRINTF)) - -# define CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING - -# endif -#endif // __CYGWIN__ - -//////////////////////////////////////////////////////////////////////////////// -// Visual C++ -#if defined(_MSC_VER) - -// Universal Windows platform does not support SEH -// Or console colours (or console at all...) -# if defined(WINAPI_FAMILY) && (WINAPI_FAMILY == WINAPI_FAMILY_APP) -# define CATCH_CONFIG_COLOUR_NONE -# else -# define CATCH_INTERNAL_CONFIG_WINDOWS_SEH -# endif - -# if !defined(__clang__) // Handle Clang masquerading for msvc - -// MSVC traditional preprocessor needs some workaround for __VA_ARGS__ -// _MSVC_TRADITIONAL == 0 means new conformant preprocessor -// _MSVC_TRADITIONAL == 1 means old traditional non-conformant preprocessor -# if !defined(_MSVC_TRADITIONAL) || (defined(_MSVC_TRADITIONAL) && _MSVC_TRADITIONAL) -# define CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -# endif // MSVC_TRADITIONAL - -// Only do this if we're not using clang on Windows, which uses `diagnostic push` & `diagnostic pop` -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION __pragma( warning(push) ) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION __pragma( warning(pop) ) -# endif // __clang__ - -#endif // _MSC_VER - -#if defined(_REENTRANT) || defined(_MSC_VER) -// Enable async processing, as -pthread is specified or no additional linking is required -# define CATCH_INTERNAL_CONFIG_USE_ASYNC -#endif // _MSC_VER - -//////////////////////////////////////////////////////////////////////////////// -// Check if we are compiled with -fno-exceptions or equivalent -#if defined(__EXCEPTIONS) || defined(__cpp_exceptions) || defined(_CPPUNWIND) -# define CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED -#endif - -//////////////////////////////////////////////////////////////////////////////// -// DJGPP -#ifdef __DJGPP__ -# define CATCH_INTERNAL_CONFIG_NO_WCHAR -#endif // __DJGPP__ - -//////////////////////////////////////////////////////////////////////////////// -// Embarcadero C++Build -#if defined(__BORLANDC__) - #define CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN -#endif - -//////////////////////////////////////////////////////////////////////////////// - -// Use of __COUNTER__ is suppressed during code analysis in -// CLion/AppCode 2017.2.x and former, because __COUNTER__ is not properly -// handled by it. -// Otherwise all supported compilers support COUNTER macro, -// but user still might want to turn it off -#if ( !defined(__JETBRAINS_IDE__) || __JETBRAINS_IDE__ >= 20170300L ) - #define CATCH_INTERNAL_CONFIG_COUNTER -#endif - -//////////////////////////////////////////////////////////////////////////////// - -// RTX is a special version of Windows that is real time. -// This means that it is detected as Windows, but does not provide -// the same set of capabilities as real Windows does. -#if defined(UNDER_RTSS) || defined(RTX64_BUILD) - #define CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH - #define CATCH_INTERNAL_CONFIG_NO_ASYNC - #define CATCH_CONFIG_COLOUR_NONE -#endif - -#if !defined(_GLIBCXX_USE_C99_MATH_TR1) -#define CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER -#endif - -// Various stdlib support checks that require __has_include -#if defined(__has_include) - // Check if string_view is available and usable - #if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # define CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW - #endif - - // Check if optional is available and usable - # if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # define CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL - # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) - - // Check if byte is available and usable - # if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # include - # if defined(__cpp_lib_byte) && (__cpp_lib_byte > 0) - # define CATCH_INTERNAL_CONFIG_CPP17_BYTE - # endif - # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) - - // Check if variant is available and usable - # if __has_include() && defined(CATCH_CPP17_OR_GREATER) - # if defined(__clang__) && (__clang_major__ < 8) - // work around clang bug with libstdc++ https://bugs.llvm.org/show_bug.cgi?id=31852 - // fix should be in clang 8, workaround in libstdc++ 8.2 - # include - # if defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) - # define CATCH_CONFIG_NO_CPP17_VARIANT - # else - # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT - # endif // defined(__GLIBCXX__) && defined(_GLIBCXX_RELEASE) && (_GLIBCXX_RELEASE < 9) - # else - # define CATCH_INTERNAL_CONFIG_CPP17_VARIANT - # endif // defined(__clang__) && (__clang_major__ < 8) - # endif // __has_include() && defined(CATCH_CPP17_OR_GREATER) -#endif // defined(__has_include) - -#if defined(CATCH_INTERNAL_CONFIG_COUNTER) && !defined(CATCH_CONFIG_NO_COUNTER) && !defined(CATCH_CONFIG_COUNTER) -# define CATCH_CONFIG_COUNTER -#endif -#if defined(CATCH_INTERNAL_CONFIG_WINDOWS_SEH) && !defined(CATCH_CONFIG_NO_WINDOWS_SEH) && !defined(CATCH_CONFIG_WINDOWS_SEH) && !defined(CATCH_INTERNAL_CONFIG_NO_WINDOWS_SEH) -# define CATCH_CONFIG_WINDOWS_SEH -#endif -// This is set by default, because we assume that unix compilers are posix-signal-compatible by default. -#if defined(CATCH_INTERNAL_CONFIG_POSIX_SIGNALS) && !defined(CATCH_INTERNAL_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_NO_POSIX_SIGNALS) && !defined(CATCH_CONFIG_POSIX_SIGNALS) -# define CATCH_CONFIG_POSIX_SIGNALS -#endif -// This is set by default, because we assume that compilers with no wchar_t support are just rare exceptions. -#if !defined(CATCH_INTERNAL_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_NO_WCHAR) && !defined(CATCH_CONFIG_WCHAR) -# define CATCH_CONFIG_WCHAR -#endif - -#if !defined(CATCH_INTERNAL_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_NO_CPP11_TO_STRING) && !defined(CATCH_CONFIG_CPP11_TO_STRING) -# define CATCH_CONFIG_CPP11_TO_STRING -#endif - -#if defined(CATCH_INTERNAL_CONFIG_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_NO_CPP17_OPTIONAL) && !defined(CATCH_CONFIG_CPP17_OPTIONAL) -# define CATCH_CONFIG_CPP17_OPTIONAL -#endif - -#if defined(CATCH_INTERNAL_CONFIG_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_NO_CPP17_STRING_VIEW) && !defined(CATCH_CONFIG_CPP17_STRING_VIEW) -# define CATCH_CONFIG_CPP17_STRING_VIEW -#endif - -#if defined(CATCH_INTERNAL_CONFIG_CPP17_VARIANT) && !defined(CATCH_CONFIG_NO_CPP17_VARIANT) && !defined(CATCH_CONFIG_CPP17_VARIANT) -# define CATCH_CONFIG_CPP17_VARIANT -#endif - -#if defined(CATCH_INTERNAL_CONFIG_CPP17_BYTE) && !defined(CATCH_CONFIG_NO_CPP17_BYTE) && !defined(CATCH_CONFIG_CPP17_BYTE) -# define CATCH_CONFIG_CPP17_BYTE -#endif - -#if defined(CATCH_CONFIG_EXPERIMENTAL_REDIRECT) -# define CATCH_INTERNAL_CONFIG_NEW_CAPTURE -#endif - -#if defined(CATCH_INTERNAL_CONFIG_NEW_CAPTURE) && !defined(CATCH_INTERNAL_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NO_NEW_CAPTURE) && !defined(CATCH_CONFIG_NEW_CAPTURE) -# define CATCH_CONFIG_NEW_CAPTURE -#endif - -#if !defined(CATCH_INTERNAL_CONFIG_EXCEPTIONS_ENABLED) && !defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) -# define CATCH_CONFIG_DISABLE_EXCEPTIONS -#endif - -#if defined(CATCH_INTERNAL_CONFIG_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_NO_POLYFILL_ISNAN) && !defined(CATCH_CONFIG_POLYFILL_ISNAN) -# define CATCH_CONFIG_POLYFILL_ISNAN -#endif - -#if defined(CATCH_INTERNAL_CONFIG_USE_ASYNC) && !defined(CATCH_INTERNAL_CONFIG_NO_ASYNC) && !defined(CATCH_CONFIG_NO_USE_ASYNC) && !defined(CATCH_CONFIG_USE_ASYNC) -# define CATCH_CONFIG_USE_ASYNC -#endif - -#if defined(CATCH_INTERNAL_CONFIG_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_NO_ANDROID_LOGWRITE) && !defined(CATCH_CONFIG_ANDROID_LOGWRITE) -# define CATCH_CONFIG_ANDROID_LOGWRITE -#endif - -#if defined(CATCH_INTERNAL_CONFIG_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_NO_GLOBAL_NEXTAFTER) && !defined(CATCH_CONFIG_GLOBAL_NEXTAFTER) -# define CATCH_CONFIG_GLOBAL_NEXTAFTER -#endif - -// Even if we do not think the compiler has that warning, we still have -// to provide a macro that can be used by the code. -#if !defined(CATCH_INTERNAL_START_WARNINGS_SUPPRESSION) -# define CATCH_INTERNAL_START_WARNINGS_SUPPRESSION -#endif -#if !defined(CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION) -# define CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION -#endif -#if !defined(CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_PARENTHESES_WARNINGS -#endif -#if !defined(CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS -#endif -#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_UNUSED_WARNINGS -#endif -#if !defined(CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_ZERO_VARIADIC_WARNINGS -#endif - -// The goal of this macro is to avoid evaluation of the arguments, but -// still have the compiler warn on problems inside... -#if !defined(CATCH_INTERNAL_IGNORE_BUT_WARN) -# define CATCH_INTERNAL_IGNORE_BUT_WARN(...) -#endif - -#if defined(__APPLE__) && defined(__apple_build_version__) && (__clang_major__ < 10) -# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS -#elif defined(__clang__) && (__clang_major__ < 5) -# undef CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS -#endif - -#if !defined(CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS) -# define CATCH_INTERNAL_SUPPRESS_UNUSED_TEMPLATE_WARNINGS -#endif - -#if defined(CATCH_CONFIG_DISABLE_EXCEPTIONS) -#define CATCH_TRY if ((true)) -#define CATCH_CATCH_ALL if ((false)) -#define CATCH_CATCH_ANON(type) if ((false)) -#else -#define CATCH_TRY try -#define CATCH_CATCH_ALL catch (...) -#define CATCH_CATCH_ANON(type) catch (type) -#endif - -#if defined(CATCH_INTERNAL_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_NO_TRADITIONAL_MSVC_PREPROCESSOR) && !defined(CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR) -#define CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -#endif - -// end catch_compiler_capabilities.h -#define INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) name##line -#define INTERNAL_CATCH_UNIQUE_NAME_LINE( name, line ) INTERNAL_CATCH_UNIQUE_NAME_LINE2( name, line ) -#ifdef CATCH_CONFIG_COUNTER -# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __COUNTER__ ) -#else -# define INTERNAL_CATCH_UNIQUE_NAME( name ) INTERNAL_CATCH_UNIQUE_NAME_LINE( name, __LINE__ ) -#endif - -#include -#include -#include - -// We need a dummy global operator<< so we can bring it into Catch namespace later -struct Catch_global_namespace_dummy {}; -std::ostream& operator<<(std::ostream&, Catch_global_namespace_dummy); - -namespace Catch { - - struct CaseSensitive { enum Choice { - Yes, - No - }; }; - - class NonCopyable { - NonCopyable( NonCopyable const& ) = delete; - NonCopyable( NonCopyable && ) = delete; - NonCopyable& operator = ( NonCopyable const& ) = delete; - NonCopyable& operator = ( NonCopyable && ) = delete; - - protected: - NonCopyable(); - virtual ~NonCopyable(); - }; - - struct SourceLineInfo { - - SourceLineInfo() = delete; - SourceLineInfo( char const* _file, std::size_t _line ) noexcept - : file( _file ), - line( _line ) - {} - - SourceLineInfo( SourceLineInfo const& other ) = default; - SourceLineInfo& operator = ( SourceLineInfo const& ) = default; - SourceLineInfo( SourceLineInfo&& ) noexcept = default; - SourceLineInfo& operator = ( SourceLineInfo&& ) noexcept = default; - - bool empty() const noexcept { return file[0] == '\0'; } - bool operator == ( SourceLineInfo const& other ) const noexcept; - bool operator < ( SourceLineInfo const& other ) const noexcept; - - char const* file; - std::size_t line; - }; - - std::ostream& operator << ( std::ostream& os, SourceLineInfo const& info ); - - // Bring in operator<< from global namespace into Catch namespace - // This is necessary because the overload of operator<< above makes - // lookup stop at namespace Catch - using ::operator<<; - - // Use this in variadic streaming macros to allow - // >> +StreamEndStop - // as well as - // >> stuff +StreamEndStop - struct StreamEndStop { - std::string operator+() const; - }; - template - T const& operator + ( T const& value, StreamEndStop ) { - return value; - } -} - -#define CATCH_INTERNAL_LINEINFO \ - ::Catch::SourceLineInfo( __FILE__, static_cast( __LINE__ ) ) - -// end catch_common.h -namespace Catch { - - struct RegistrarForTagAliases { - RegistrarForTagAliases( char const* alias, char const* tag, SourceLineInfo const& lineInfo ); - }; - -} // end namespace Catch - -#define CATCH_REGISTER_TAG_ALIAS( alias, spec ) \ - CATCH_INTERNAL_START_WARNINGS_SUPPRESSION \ - CATCH_INTERNAL_SUPPRESS_GLOBALS_WARNINGS \ - namespace{ Catch::RegistrarForTagAliases INTERNAL_CATCH_UNIQUE_NAME( AutoRegisterTagAlias )( alias, spec, CATCH_INTERNAL_LINEINFO ); } \ - CATCH_INTERNAL_STOP_WARNINGS_SUPPRESSION - -// end catch_tag_alias_autoregistrar.h -// start catch_test_registry.h - -// start catch_interfaces_testcase.h - -#include - -namespace Catch { - - class TestSpec; - - struct ITestInvoker { - virtual void invoke () const = 0; - virtual ~ITestInvoker(); - }; - - class TestCase; - struct IConfig; - - struct ITestCaseRegistry { - virtual ~ITestCaseRegistry(); - virtual std::vector const& getAllTests() const = 0; - virtual std::vector const& getAllTestsSorted( IConfig const& config ) const = 0; - }; - - bool isThrowSafe( TestCase const& testCase, IConfig const& config ); - bool matchTest( TestCase const& testCase, TestSpec const& testSpec, IConfig const& config ); - std::vector filterTests( std::vector const& testCases, TestSpec const& testSpec, IConfig const& config ); - std::vector const& getAllTestCasesSorted( IConfig const& config ); - -} - -// end catch_interfaces_testcase.h -// start catch_stringref.h - -#include -#include -#include -#include - -namespace Catch { - - /// A non-owning string class (similar to the forthcoming std::string_view) - /// Note that, because a StringRef may be a substring of another string, - /// it may not be null terminated. - class StringRef { - public: - using size_type = std::size_t; - using const_iterator = const char*; - - private: - static constexpr char const* const s_empty = ""; - - char const* m_start = s_empty; - size_type m_size = 0; - - public: // construction - constexpr StringRef() noexcept = default; - - StringRef( char const* rawChars ) noexcept; - - constexpr StringRef( char const* rawChars, size_type size ) noexcept - : m_start( rawChars ), - m_size( size ) - {} - - StringRef( std::string const& stdString ) noexcept - : m_start( stdString.c_str() ), - m_size( stdString.size() ) - {} - - explicit operator std::string() const { - return std::string(m_start, m_size); - } - - public: // operators - auto operator == ( StringRef const& other ) const noexcept -> bool; - auto operator != (StringRef const& other) const noexcept -> bool { - return !(*this == other); - } - - auto operator[] ( size_type index ) const noexcept -> char { - assert(index < m_size); - return m_start[index]; - } - - public: // named queries - constexpr auto empty() const noexcept -> bool { - return m_size == 0; - } - constexpr auto size() const noexcept -> size_type { - return m_size; - } - - // Returns the current start pointer. If the StringRef is not - // null-terminated, throws std::domain_exception - auto c_str() const -> char const*; - - public: // substrings and searches - // Returns a substring of [start, start + length). - // If start + length > size(), then the substring is [start, size()). - // If start > size(), then the substring is empty. - auto substr( size_type start, size_type length ) const noexcept -> StringRef; - - // Returns the current start pointer. May not be null-terminated. - auto data() const noexcept -> char const*; - - constexpr auto isNullTerminated() const noexcept -> bool { - return m_start[m_size] == '\0'; - } - - public: // iterators - constexpr const_iterator begin() const { return m_start; } - constexpr const_iterator end() const { return m_start + m_size; } - }; - - auto operator += ( std::string& lhs, StringRef const& sr ) -> std::string&; - auto operator << ( std::ostream& os, StringRef const& sr ) -> std::ostream&; - - constexpr auto operator "" _sr( char const* rawChars, std::size_t size ) noexcept -> StringRef { - return StringRef( rawChars, size ); - } -} // namespace Catch - -constexpr auto operator "" _catch_sr( char const* rawChars, std::size_t size ) noexcept -> Catch::StringRef { - return Catch::StringRef( rawChars, size ); -} - -// end catch_stringref.h -// start catch_preprocessor.hpp - - -#define CATCH_RECURSION_LEVEL0(...) __VA_ARGS__ -#define CATCH_RECURSION_LEVEL1(...) CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(CATCH_RECURSION_LEVEL0(__VA_ARGS__))) -#define CATCH_RECURSION_LEVEL2(...) CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(CATCH_RECURSION_LEVEL1(__VA_ARGS__))) -#define CATCH_RECURSION_LEVEL3(...) CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(CATCH_RECURSION_LEVEL2(__VA_ARGS__))) -#define CATCH_RECURSION_LEVEL4(...) CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(CATCH_RECURSION_LEVEL3(__VA_ARGS__))) -#define CATCH_RECURSION_LEVEL5(...) CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(CATCH_RECURSION_LEVEL4(__VA_ARGS__))) - -#ifdef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -#define INTERNAL_CATCH_EXPAND_VARGS(...) __VA_ARGS__ -// MSVC needs more evaluations -#define CATCH_RECURSION_LEVEL6(...) CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(CATCH_RECURSION_LEVEL5(__VA_ARGS__))) -#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL6(CATCH_RECURSION_LEVEL6(__VA_ARGS__)) -#else -#define CATCH_RECURSE(...) CATCH_RECURSION_LEVEL5(__VA_ARGS__) -#endif - -#define CATCH_REC_END(...) -#define CATCH_REC_OUT - -#define CATCH_EMPTY() -#define CATCH_DEFER(id) id CATCH_EMPTY() - -#define CATCH_REC_GET_END2() 0, CATCH_REC_END -#define CATCH_REC_GET_END1(...) CATCH_REC_GET_END2 -#define CATCH_REC_GET_END(...) CATCH_REC_GET_END1 -#define CATCH_REC_NEXT0(test, next, ...) next CATCH_REC_OUT -#define CATCH_REC_NEXT1(test, next) CATCH_DEFER ( CATCH_REC_NEXT0 ) ( test, next, 0) -#define CATCH_REC_NEXT(test, next) CATCH_REC_NEXT1(CATCH_REC_GET_END test, next) - -#define CATCH_REC_LIST0(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST1(f, x, peek, ...) , f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0) ) ( f, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST2(f, x, peek, ...) f(x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1) ) ( f, peek, __VA_ARGS__ ) - -#define CATCH_REC_LIST0_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST1_UD(f, userdata, x, peek, ...) , f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST0_UD) ) ( f, userdata, peek, __VA_ARGS__ ) -#define CATCH_REC_LIST2_UD(f, userdata, x, peek, ...) f(userdata, x) CATCH_DEFER ( CATCH_REC_NEXT(peek, CATCH_REC_LIST1_UD) ) ( f, userdata, peek, __VA_ARGS__ ) - -// Applies the function macro `f` to each of the remaining parameters, inserts commas between the results, -// and passes userdata as the first parameter to each invocation, -// e.g. CATCH_REC_LIST_UD(f, x, a, b, c) evaluates to f(x, a), f(x, b), f(x, c) -#define CATCH_REC_LIST_UD(f, userdata, ...) CATCH_RECURSE(CATCH_REC_LIST2_UD(f, userdata, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) - -#define CATCH_REC_LIST(f, ...) CATCH_RECURSE(CATCH_REC_LIST2(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0)) - -#define INTERNAL_CATCH_EXPAND1(param) INTERNAL_CATCH_EXPAND2(param) -#define INTERNAL_CATCH_EXPAND2(...) INTERNAL_CATCH_NO## __VA_ARGS__ -#define INTERNAL_CATCH_DEF(...) INTERNAL_CATCH_DEF __VA_ARGS__ -#define INTERNAL_CATCH_NOINTERNAL_CATCH_DEF -#define INTERNAL_CATCH_STRINGIZE(...) INTERNAL_CATCH_STRINGIZE2(__VA_ARGS__) -#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -#define INTERNAL_CATCH_STRINGIZE2(...) #__VA_ARGS__ -#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) -#else -// MSVC is adding extra space and needs another indirection to expand INTERNAL_CATCH_NOINTERNAL_CATCH_DEF -#define INTERNAL_CATCH_STRINGIZE2(...) INTERNAL_CATCH_STRINGIZE3(__VA_ARGS__) -#define INTERNAL_CATCH_STRINGIZE3(...) #__VA_ARGS__ -#define INTERNAL_CATCH_STRINGIZE_WITHOUT_PARENS(param) (INTERNAL_CATCH_STRINGIZE(INTERNAL_CATCH_REMOVE_PARENS(param)) + 1) -#endif - -#define INTERNAL_CATCH_MAKE_NAMESPACE2(...) ns_##__VA_ARGS__ -#define INTERNAL_CATCH_MAKE_NAMESPACE(name) INTERNAL_CATCH_MAKE_NAMESPACE2(name) - -#define INTERNAL_CATCH_REMOVE_PARENS(...) INTERNAL_CATCH_EXPAND1(INTERNAL_CATCH_DEF __VA_ARGS__) - -#ifndef CATCH_CONFIG_TRADITIONAL_MSVC_PREPROCESSOR -#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) decltype(get_wrapper()) -#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__)) -#else -#define INTERNAL_CATCH_MAKE_TYPE_LIST2(...) INTERNAL_CATCH_EXPAND_VARGS(decltype(get_wrapper())) -#define INTERNAL_CATCH_MAKE_TYPE_LIST(...) INTERNAL_CATCH_EXPAND_VARGS(INTERNAL_CATCH_MAKE_TYPE_LIST2(INTERNAL_CATCH_REMOVE_PARENS(__VA_ARGS__))) -#endif - -#define INTERNAL_CATCH_MAKE_TYPE_LISTS_FROM_TYPES(...)\ - CATCH_REC_LIST(INTERNAL_CATCH_MAKE_TYPE_LIST,__VA_ARGS__) - -#define INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_0) INTERNAL_CATCH_REMOVE_PARENS(_0) -#define INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_0, _1) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_1_ARG(_1) -#define INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_0, _1, _2) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_2_ARG(_1, _2) -#define INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_0, _1, _2, _3) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_3_ARG(_1, _2, _3) -#define INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_0, _1, _2, _3, _4) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_4_ARG(_1, _2, _3, _4) -#define INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_0, _1, _2, _3, _4, _5) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_5_ARG(_1, _2, _3, _4, _5) -#define INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_0, _1, _2, _3, _4, _5, _6) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_6_ARG(_1, _2, _3, _4, _5, _6) -#define INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_0, _1, _2, _3, _4, _5, _6, _7) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_7_ARG(_1, _2, _3, _4, _5, _6, _7) -#define INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_8_ARG(_1, _2, _3, _4, _5, _6, _7, _8) -#define INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_9_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9) -#define INTERNAL_CATCH_REMOVE_PARENS_11_ARG(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10) INTERNAL_CATCH_REMOVE_PARENS(_0), INTERNAL_CATCH_REMOVE_PARENS_10_ARG(_1, _2, _3, _4, _5, _6, _7, _8, _9, _10) - -#define INTERNAL_CATCH_VA_NARGS_IMPL(_0, _1, _2, _3, _4, _5, _6, _7, _8, _9, _10, N, ...) N - -#define INTERNAL_CATCH_TYPE_GEN\ - template struct TypeList {};\ - template\ - constexpr auto get_wrapper() noexcept -> TypeList { return {}; }\ - template class...> struct TemplateTypeList{};\ - template class...Cs>\ - constexpr auto get_wrapper() noexcept -> TemplateTypeList { return {}; }\ - template\ - struct append;\ - template\ - struct rewrap;\ - template class, typename...>\ - struct create;\ - template class, typename>\ - struct convert;\ - \ - template \ - struct append { using type = T; };\ - template< template class L1, typename...E1, template class L2, typename...E2, typename...Rest>\ - struct append, L2, Rest...> { using type = typename append, Rest...>::type; };\ - template< template class L1, typename...E1, typename...Rest>\ - struct append, TypeList, Rest...> { using type = L1; };\ - \ - template< template class Container, template class List, typename...elems>\ - struct rewrap, List> { using type = TypeList>; };\ - template< template class Container, template class List, class...Elems, typename...Elements>\ - struct rewrap, List, Elements...> { using type = typename append>, typename rewrap, Elements...>::type>::type; };\ - \ - template