diff --git a/AutomatedTesting/Gem/PythonTests/AWS/Windows/cdk/cdk.py b/AutomatedTesting/Gem/PythonTests/AWS/Windows/cdk/cdk.py index 5dbde29b9d..9254c3d4eb 100644 --- a/AutomatedTesting/Gem/PythonTests/AWS/Windows/cdk/cdk.py +++ b/AutomatedTesting/Gem/PythonTests/AWS/Windows/cdk/cdk.py @@ -97,16 +97,19 @@ class Cdk: env=self._cdk_env, shell=True) - def deploy(self, context_variable: str = '') -> List[str]: + def deploy(self, context_variable: str = '', additonal_params: List[str] = None) -> List[str]: """ Deploys all the CDK stacks. :param context_variable: Context variable for enabling optional features. + :param additonal_params: Additonal parameters like --all can be passed in this way. :return List of deployed stack arns. """ if not self._cdk_path: return [] deploy_cdk_application_cmd = ['cdk', 'deploy', '--require-approval', 'never'] + if additonal_params: + deploy_cdk_application_cmd.extend(additonal_params) if context_variable: deploy_cdk_application_cmd.extend(['-c', f'{context_variable}']) diff --git a/Code/CryEngine/CryCommon/Maestro/Types/AnimParamType.h b/Code/CryEngine/CryCommon/Maestro/Types/AnimParamType.h index f7caf2db7f..4614b1638d 100644 --- a/Code/CryEngine/CryCommon/Maestro/Types/AnimParamType.h +++ b/Code/CryEngine/CryCommon/Maestro/Types/AnimParamType.h @@ -137,5 +137,6 @@ enum class AnimParamType Invalid = static_cast(0xFFFFFFFF) }; +static const int OLD_APARAM_USER = 100; #endif // CRYINCLUDE_CRYCOMMON_MAESTRO_TYPES_ANIMPARAMTYPE_H diff --git a/Code/CryEngine/CrySystem/DebugCallStack.cpp b/Code/CryEngine/CrySystem/DebugCallStack.cpp index 2a219ce674..eea4725639 100644 --- a/Code/CryEngine/CrySystem/DebugCallStack.cpp +++ b/Code/CryEngine/CrySystem/DebugCallStack.cpp @@ -561,7 +561,7 @@ void DebugCallStack::LogExceptionInfo(EXCEPTION_POINTERS* pex) if (pex) { - MINIDUMP_TYPE mdumpValue; + MINIDUMP_TYPE mdumpValue = MiniDumpNormal; bool bDump = true; switch (g_cvars.sys_dump_type) { diff --git a/Code/CryEngine/CrySystem/System.cpp b/Code/CryEngine/CrySystem/System.cpp index 6fcbc32b72..bf34700c39 100644 --- a/Code/CryEngine/CrySystem/System.cpp +++ b/Code/CryEngine/CrySystem/System.cpp @@ -66,7 +66,7 @@ LRESULT WINAPI WndProc(HWND hWnd, UINT uMsg, WPARAM wParam, LPARAM lParam) } if (pSystem && !pSystem->IsQuitting()) { - LRESULT result; + LRESULT result = 0; bool bAny = false; for (std::vector::const_iterator it = pSystem->m_windowMessageHandlers.begin(); it != pSystem->m_windowMessageHandlers.end(); ++it) { diff --git a/Code/CryEngine/CrySystem/SystemInit.cpp b/Code/CryEngine/CrySystem/SystemInit.cpp index 52744519bb..4a4296bbb5 100644 --- a/Code/CryEngine/CrySystem/SystemInit.cpp +++ b/Code/CryEngine/CrySystem/SystemInit.cpp @@ -634,7 +634,7 @@ ICVar* CSystem::attachVariable (const char* szVarName, int* pContainer, const ch IConsole* pConsole = GetIConsole(); ICVar* pOldVar = pConsole->GetCVar (szVarName); - int nDefault; + int nDefault = 0; if (pOldVar) { nDefault = pOldVar->GetIVal(); diff --git a/Code/Framework/AzCore/Tests/AZStd/String.cpp b/Code/Framework/AzCore/Tests/AZStd/String.cpp index 5b8c176f02..a48725309a 100644 --- a/Code/Framework/AzCore/Tests/AZStd/String.cpp +++ b/Code/Framework/AzCore/Tests/AZStd/String.cpp @@ -1914,7 +1914,7 @@ namespace UnitTest TEST_F(String, StringView_CompareIsConstexpr) { using TypeParam = char; - auto MakeCompileTimeString1 = []() constexpr -> const TypeParam* + auto ThisTestMakeCompileTimeString1 = []() constexpr -> const TypeParam* { return "HelloWorld"; }; @@ -1922,7 +1922,7 @@ namespace UnitTest { return "HelloPearl"; }; - constexpr const TypeParam* compileTimeString1 = MakeCompileTimeString1(); + constexpr const TypeParam* compileTimeString1 = ThisTestMakeCompileTimeString1(); constexpr const TypeParam* compileTimeString2 = MakeCompileTimeString2(); constexpr basic_string_view lhsView(compileTimeString1); constexpr basic_string_view rhsView(compileTimeString2); @@ -1937,11 +1937,11 @@ namespace UnitTest TEST_F(String, StringView_CompareOperatorsAreConstexpr) { using TypeParam = char; - auto MakeCompileTimeString1 = []() constexpr -> const TypeParam* + auto TestMakeCompileTimeString1 = []() constexpr -> const TypeParam* { return "HelloWorld"; }; - constexpr const TypeParam* compileTimeString1 = MakeCompileTimeString1(); + constexpr const TypeParam* compileTimeString1 = TestMakeCompileTimeString1(); constexpr basic_string_view compareView(compileTimeString1); static_assert(compareView == "HelloWorld", "string_view operator== comparison has failed"); static_assert(compareView != "MadWorld", "string_view operator!= comparison has failed"); @@ -1955,7 +1955,7 @@ namespace UnitTest { auto swap_test_func = []() constexpr -> basic_string_view { - constexpr auto MakeCompileTimeString1 = []() constexpr -> const TypeParam* + constexpr auto ThisTestMakeCompileTimeString1 = []() constexpr -> const TypeParam* { if constexpr (AZStd::is_same_v) { @@ -1977,7 +1977,7 @@ namespace UnitTest return L"InuWorld"; } }; - constexpr const TypeParam* compileTimeString1 = MakeCompileTimeString1(); + constexpr const TypeParam* compileTimeString1 = ThisTestMakeCompileTimeString1(); constexpr const TypeParam* compileTimeString2 = MakeCompileTimeString2(); basic_string_view lhsView(compileTimeString1); basic_string_view rhsView(compileTimeString2); @@ -2001,7 +2001,7 @@ namespace UnitTest TYPED_TEST(BasicStringViewConstexprFixture, HashString_FunctionIsConstexpr) { - auto MakeCompileTimeString1 = []() constexpr -> const TypeParam* + auto ThisTestMakeCompileTimeString1 = []() constexpr -> const TypeParam* { if constexpr (AZStd::is_same_v) { @@ -2012,7 +2012,7 @@ namespace UnitTest return L"HelloWorld"; } }; - constexpr const TypeParam* compileTimeString1 = MakeCompileTimeString1(); + constexpr const TypeParam* compileTimeString1 = ThisTestMakeCompileTimeString1(); constexpr basic_string_view hashView(compileTimeString1); constexpr size_t compileHash = AZStd::hash>{}(hashView); static_assert(compileHash != 0, "Hash of \"HelloWorld\" should not be 0"); diff --git a/Code/Framework/AzCore/Tests/Jobs.cpp b/Code/Framework/AzCore/Tests/Jobs.cpp index 553123496e..664b163417 100644 --- a/Code/Framework/AzCore/Tests/Jobs.cpp +++ b/Code/Framework/AzCore/Tests/Jobs.cpp @@ -395,7 +395,8 @@ namespace UnitTest } else { - int result1, result2; + int result1 = 0; + int result2 = 0; Job* job1 = aznew FibonacciJob2(m_n - 1, &result1, m_context); Job* job2 = aznew FibonacciJob2(m_n - 2, &result2, m_context); StartAsChild(job1); diff --git a/Code/Framework/AzCore/Tests/Math/Matrix4x4Tests.cpp b/Code/Framework/AzCore/Tests/Math/Matrix4x4Tests.cpp index a9baa3ddea..8c4fac86c2 100644 --- a/Code/Framework/AzCore/Tests/Math/Matrix4x4Tests.cpp +++ b/Code/Framework/AzCore/Tests/Math/Matrix4x4Tests.cpp @@ -59,7 +59,7 @@ namespace UnitTest TEST(MATH_Matrix4x4, TestCreateFrom) { - float testFloats[] = + float thisTestFloats[] = { 1.0f, 2.0f, 3.0f, 4.0f, 5.0f, 6.0f, 7.0f, 8.0f, @@ -67,20 +67,20 @@ namespace UnitTest 13.0f, 14.0f, 15.0f, 16.0f }; float testFloatMtx[16]; - Matrix4x4 m1 = Matrix4x4::CreateFromRowMajorFloat16(testFloats); + Matrix4x4 m1 = Matrix4x4::CreateFromRowMajorFloat16(thisTestFloats); AZ_TEST_ASSERT(m1.GetRow(0) == Vector4(1.0f, 2.0f, 3.0f, 4.0f)); AZ_TEST_ASSERT(m1.GetRow(1) == Vector4(5.0f, 6.0f, 7.0f, 8.0f)); AZ_TEST_ASSERT(m1.GetRow(2) == Vector4(9.0f, 10.0f, 11.0f, 12.0f)); AZ_TEST_ASSERT(m1.GetRow(3) == Vector4(13.0f, 14.0f, 15.0f, 16.0f)); m1.StoreToRowMajorFloat16(testFloatMtx); - AZ_TEST_ASSERT(memcmp(testFloatMtx, testFloats, sizeof(testFloatMtx)) == 0); - m1 = Matrix4x4::CreateFromColumnMajorFloat16(testFloats); + AZ_TEST_ASSERT(memcmp(testFloatMtx, thisTestFloats, sizeof(testFloatMtx)) == 0); + m1 = Matrix4x4::CreateFromColumnMajorFloat16(thisTestFloats); AZ_TEST_ASSERT(m1.GetRow(0) == Vector4(1.0f, 5.0f, 9.0f, 13.0f)); AZ_TEST_ASSERT(m1.GetRow(1) == Vector4(2.0f, 6.0f, 10.0f, 14.0f)); AZ_TEST_ASSERT(m1.GetRow(2) == Vector4(3.0f, 7.0f, 11.0f, 15.0f)); AZ_TEST_ASSERT(m1.GetRow(3) == Vector4(4.0f, 8.0f, 12.0f, 16.0f)); m1.StoreToColumnMajorFloat16(testFloatMtx); - AZ_TEST_ASSERT(memcmp(testFloatMtx, testFloats, sizeof(testFloatMtx)) == 0); + AZ_TEST_ASSERT(memcmp(testFloatMtx, thisTestFloats, sizeof(testFloatMtx)) == 0); } TEST(MATH_Matrix4x4, TestCreateFromMatrix3x4) diff --git a/Code/Framework/AzCore/Tests/Math/ObbTests.cpp b/Code/Framework/AzCore/Tests/Math/ObbTests.cpp index de267b4265..5eb9057761 100644 --- a/Code/Framework/AzCore/Tests/Math/ObbTests.cpp +++ b/Code/Framework/AzCore/Tests/Math/ObbTests.cpp @@ -119,10 +119,10 @@ namespace UnitTest TEST(MATH_Obb, Contains) { - const Vector3 position(1.0f, 2.0f, 3.0f); - const Quaternion rotation = Quaternion::CreateRotationZ(DegToRad(30.0f)); - const Vector3 halfLengths(2.0f, 1.0f, 2.5f); - const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(position, rotation, halfLengths); + const Vector3 testPosition(1.0f, 2.0f, 3.0f); + const Quaternion testRotation = Quaternion::CreateRotationZ(DegToRad(30.0f)); + const Vector3 testHalfLengths(2.0f, 1.0f, 2.5f); + const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(testPosition, testRotation, testHalfLengths); // test some pairs of points which should be just either side of the Obb boundary EXPECT_TRUE(obb.Contains(Vector3(1.35f, 3.35f, 3.5f))); EXPECT_FALSE(obb.Contains(Vector3(1.35f, 3.4f, 3.5f))); @@ -134,10 +134,10 @@ namespace UnitTest TEST(MATH_Obb, GetDistance) { - const Vector3 position(5.0f, 3.0f, 2.0f); - const Quaternion rotation = Quaternion::CreateRotationX(DegToRad(60.0f)); - const Vector3 halfLengths(0.5f, 2.0f, 1.5f); - const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(position, rotation, halfLengths); + const Vector3 testPosition(5.0f, 3.0f, 2.0f); + const Quaternion testRotation = Quaternion::CreateRotationX(DegToRad(60.0f)); + const Vector3 testHalfLengths(0.5f, 2.0f, 1.5f); + const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(testPosition, testRotation, testHalfLengths); EXPECT_NEAR(obb.GetDistance(Vector3(5.3f, 3.2f, 1.8f)), 0.0f, 1e-3f); EXPECT_NEAR(obb.GetDistance(Vector3(5.1f, 1.1f, 3.7f)), 0.9955f, 1e-3f); EXPECT_NEAR(obb.GetDistance(Vector3(4.7f, 4.5f, 4.2f)), 0.6553f, 1e-3f); @@ -146,10 +146,10 @@ namespace UnitTest TEST(MATH_Obb, GetDistanceSq) { - const Vector3 position(1.0f, 4.0f, 3.0f); - const Quaternion rotation = Quaternion::CreateRotationY(DegToRad(45.0f)); - const Vector3 halfLengths(1.5f, 3.0f, 1.0f); - const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(position, rotation, halfLengths); + const Vector3 testPosition(1.0f, 4.0f, 3.0f); + const Quaternion testRotation = Quaternion::CreateRotationY(DegToRad(45.0f)); + const Vector3 testHalfLengths(1.5f, 3.0f, 1.0f); + const Obb obb = Obb::CreateFromPositionRotationAndHalfLengths(testPosition, testRotation, testHalfLengths); EXPECT_NEAR(obb.GetDistanceSq(Vector3(1.1f, 4.3f, 2.7f)), 0.0f, 1e-3f); EXPECT_NEAR(obb.GetDistanceSq(Vector3(-0.7f, 3.5f, 2.0f)), 0.8266f, 1e-3f); EXPECT_NEAR(obb.GetDistanceSq(Vector3(2.4f, 0.5f, 1.5f)), 0.5532f, 1e-3f); diff --git a/Code/Framework/AzFramework/Platform/Windows/AzFramework/Windowing/NativeWindow_Windows.cpp b/Code/Framework/AzFramework/Platform/Windows/AzFramework/Windowing/NativeWindow_Windows.cpp index fd49f37dc8..b96ec81b5f 100644 --- a/Code/Framework/AzFramework/Platform/Windows/AzFramework/Windowing/NativeWindow_Windows.cpp +++ b/Code/Framework/AzFramework/Platform/Windows/AzFramework/Windowing/NativeWindow_Windows.cpp @@ -353,6 +353,7 @@ namespace AzFramework // Get the dimensions of the display device on which the window is currently displayed. MONITORINFO monitorInfo; + memset(&monitorInfo, 0, sizeof(MONITORINFO)); // C4701 potentially uninitialized local variable 'monitorInfo' used monitorInfo.cbSize = sizeof(MONITORINFO); const BOOL success = monitor ? GetMonitorInfo(monitor, &monitorInfo) : FALSE; if (!success) diff --git a/Code/Framework/AzNetworking/AzNetworking/Utilities/TimedThread.cpp b/Code/Framework/AzNetworking/AzNetworking/Utilities/TimedThread.cpp index d078149996..7e1f41f745 100644 --- a/Code/Framework/AzNetworking/AzNetworking/Utilities/TimedThread.cpp +++ b/Code/Framework/AzNetworking/AzNetworking/Utilities/TimedThread.cpp @@ -46,7 +46,7 @@ namespace AzNetworking } else if (m_updateRate < updateTimeMs) { - AZLOG_INFO("TimedThread bled %d ms", aznumeric_cast(updateTimeMs - m_updateRate)); + AZLOG(NET_TimedThread, "TimedThread bled %d ms", aznumeric_cast(updateTimeMs - m_updateRate)); } } OnStop(); diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorSelectionUtil.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorSelectionUtil.cpp index 88b92e8c41..d0143c5517 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorSelectionUtil.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorSelectionUtil.cpp @@ -124,4 +124,15 @@ namespace AzToolsFramework return cameraState; } + + float GetScreenDisplayScaling(const int viewportId) + { + float scaling = 1.0f; + ViewportInteraction::ViewportInteractionRequestBus::EventResult( + scaling, viewportId, + &ViewportInteraction::ViewportInteractionRequestBus::Events::DeviceScalingFactor); + + return scaling; + } + } // namespace AzToolsFramework diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorSelectionUtil.h b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorSelectionUtil.h index 9936fb9afd..e904277078 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorSelectionUtil.h +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorSelectionUtil.h @@ -60,6 +60,9 @@ namespace AzToolsFramework /// Wrapper for EBus call to return the CameraState for a given viewport. AzFramework::CameraState GetCameraState(int viewportId); + /// Wrapper for EBus call to return the DPI scaling for a given viewport. + float GetScreenDisplayScaling(const int viewportId); + /// A utility to return the center of several points. /// Take several positions and store the min and max of each in /// turn - when all points have been added return the center/midpoint. diff --git a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp index 91ac495e0e..5acfa5df59 100644 --- a/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp +++ b/Code/Framework/AzToolsFramework/AzToolsFramework/ViewportSelection/EditorTransformComponentSelection.cpp @@ -3573,9 +3573,10 @@ namespace AzToolsFramework debugDisplay.SetLineWidth(1.0f); const float labelOffset = cl_viewportGizmoAxisLabelOffset; - const auto labelXScreenPosition = (gizmoStart + (gizmoAxisX * labelOffset)) * editorCameraState.m_viewportSize; - const auto labelYScreenPosition = (gizmoStart + (gizmoAxisY * labelOffset)) * editorCameraState.m_viewportSize; - const auto labelZScreenPosition = (gizmoStart + (gizmoAxisZ * labelOffset)) * editorCameraState.m_viewportSize; + const float screenScale = GetScreenDisplayScaling(viewportId); + const auto labelXScreenPosition = (gizmoStart + (gizmoAxisX * labelOffset)) * editorCameraState.m_viewportSize * screenScale; + const auto labelYScreenPosition = (gizmoStart + (gizmoAxisY * labelOffset)) * editorCameraState.m_viewportSize * screenScale; + const auto labelZScreenPosition = (gizmoStart + (gizmoAxisZ * labelOffset)) * editorCameraState.m_viewportSize * screenScale; // draw the label of of each axis for the gizmo const float labelSize = cl_viewportGizmoAxisLabelSize; diff --git a/Code/LauncherUnified/Launcher.cpp b/Code/LauncherUnified/Launcher.cpp index 922397c325..c73cdd1981 100644 --- a/Code/LauncherUnified/Launcher.cpp +++ b/Code/LauncherUnified/Launcher.cpp @@ -9,6 +9,7 @@ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * */ + #include #include @@ -22,6 +23,8 @@ #include #include #include +#include +#include #include @@ -45,6 +48,19 @@ extern "C" void CreateStaticModules(AZStd::vector& modulesOut); namespace { + void OnViewportResize(const AZ::Vector2& value); + + AZ_CVAR(AZ::Vector2, r_viewportSize, AZ::Vector2::CreateZero(), OnViewportResize, AZ::ConsoleFunctorFlags::DontReplicate, + "The default size for the launcher viewport, 0 0 means full screen"); + + void OnViewportResize(const AZ::Vector2& value) + { + AzFramework::NativeWindowHandle windowHandle = nullptr; + AzFramework::WindowSystemRequestBus::BroadcastResult(windowHandle, &AzFramework::WindowSystemRequestBus::Events::GetDefaultWindowHandle); + AzFramework::WindowSize newSize = AzFramework::WindowSize(aznumeric_cast(value.GetX()), aznumeric_cast(value.GetY())); + AzFramework::WindowRequestBus::Broadcast(&AzFramework::WindowRequestBus::Events::ResizeClientArea, newSize); + } + void ExecuteConsoleCommandFile(AzFramework::Application& application) { const AZStd::string_view customConCmdKey = "console-command-file"; diff --git a/Code/Sandbox/Editor/EditorViewportWidget.cpp b/Code/Sandbox/Editor/EditorViewportWidget.cpp index 24d1590808..667179e3cd 100644 --- a/Code/Sandbox/Editor/EditorViewportWidget.cpp +++ b/Code/Sandbox/Editor/EditorViewportWidget.cpp @@ -1233,7 +1233,7 @@ void EditorViewportWidget::SetViewportId(int id) auto controller = AZStd::make_shared(); controller->SetCameraListBuilderCallback( - [](AzFramework::Cameras& cameras) + [id](AzFramework::Cameras& cameras) { auto firstPersonRotateCamera = AZStd::make_shared(AzFramework::CameraFreeLookButton); auto firstPersonPanCamera = @@ -1243,17 +1243,17 @@ void EditorViewportWidget::SetViewportId(int id) auto orbitCamera = AZStd::make_shared(); orbitCamera->SetLookAtFn( - [](const AZ::Vector3& position, const AZ::Vector3& direction) -> AZStd::optional + [id](const AZ::Vector3& position, const AZ::Vector3& direction) -> AZStd::optional { - AZStd::optional manipulatorTransform; - AzToolsFramework::EditorTransformComponentSelectionRequestBus::EventResult( - manipulatorTransform, AzToolsFramework::GetEntityContextId(), - &AzToolsFramework::EditorTransformComponentSelectionRequestBus::Events::GetManipulatorTransform); + AZStd::optional lookAtAfterInterpolation; + AtomToolsFramework::ModularViewportCameraControllerRequestBus::EventResult( + lookAtAfterInterpolation, id, + &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::LookAtAfterInterpolation); - // initially attempt to use manipulator transform if one exists (there is a selection) - if (manipulatorTransform) + // initially attempt to use the last set look at point after an interpolation has finished + if (lookAtAfterInterpolation.has_value()) { - return manipulatorTransform->GetTranslation(); + return *lookAtAfterInterpolation; } const float RayDistance = 1000.0f; diff --git a/Code/Sandbox/Editor/LogFile.cpp b/Code/Sandbox/Editor/LogFile.cpp index 0841a6b288..0bbfd09a21 100644 --- a/Code/Sandbox/Editor/LogFile.cpp +++ b/Code/Sandbox/Editor/LogFile.cpp @@ -553,7 +553,7 @@ void CLogFile::OnWriteToConsole(const char* sText, bool bNewLine) // remember selection and the top row int len = m_hWndEditBox->document()->toPlainText().length(); - int top; + int top = 0; int from = m_hWndEditBox->textCursor().selectionStart(); int to = from + m_hWndEditBox->textCursor().selectionEnd(); bool keepPos = false; diff --git a/Code/Sandbox/Editor/QtViewPaneManager.cpp b/Code/Sandbox/Editor/QtViewPaneManager.cpp index b242f3d914..3949f0516e 100644 --- a/Code/Sandbox/Editor/QtViewPaneManager.cpp +++ b/Code/Sandbox/Editor/QtViewPaneManager.cpp @@ -121,7 +121,7 @@ protected: }; #endif -Q_GLOBAL_STATIC(QtViewPaneManager, s_instance) +Q_GLOBAL_STATIC(QtViewPaneManager, s_viewPaneManagerInstance) QWidget* QtViewPane::CreateWidget() @@ -611,12 +611,12 @@ void QtViewPaneManager::UnregisterPane(const QString& name) QtViewPaneManager* QtViewPaneManager::instance() { - return s_instance(); + return s_viewPaneManagerInstance(); } bool QtViewPaneManager::exists() { - return s_instance.exists(); + return s_viewPaneManagerInstance.exists(); } void QtViewPaneManager::SetMainWindow(AzQtComponents::DockMainWindow* mainWindow, QSettings* settings, const QByteArray& lastMainWindowState) diff --git a/Code/Sandbox/Editor/TrackView/SequenceBatchRenderDialog.cpp b/Code/Sandbox/Editor/TrackView/SequenceBatchRenderDialog.cpp index 69322c481c..0abe3ced75 100644 --- a/Code/Sandbox/Editor/TrackView/SequenceBatchRenderDialog.cpp +++ b/Code/Sandbox/Editor/TrackView/SequenceBatchRenderDialog.cpp @@ -59,7 +59,7 @@ namespace { int fps; const char* fpsDesc; - } fps[] = { + } fpsOptions[] = { {24, "Film(24)"}, {25, "PAL(25)"}, {30, "NTSC(30)"}, {48, "Show(48)"}, {50, "PAL Field(50)"}, {60, "NTSC Field(60)"} }; @@ -213,9 +213,9 @@ void CSequenceBatchRenderDialog::OnInitDialog() m_ui->m_resolutionCombo->setCurrentIndex(0); // Fill the FPS combo box. - for (int i = 0; i < AZStd::size(fps); ++i) + for (int i = 0; i < AZStd::size(fpsOptions); ++i) { - m_ui->m_fpsCombo->addItem(fps[i].fpsDesc); + m_ui->m_fpsCombo->addItem(fpsOptions[i].fpsDesc); } m_ui->m_fpsCombo->setCurrentIndex(0); @@ -306,9 +306,9 @@ void CSequenceBatchRenderDialog::OnRenderItemSelChange() m_ui->m_destinationEdit->setText(item.folder); // fps bool bFound = false; - for (int i = 0; i < arraysize(fps); ++i) + for (int i = 0; i < arraysize(fpsOptions); ++i) { - if (item.fps == fps[i].fps) + if (item.fps == fpsOptions[i].fps) { m_ui->m_fpsCombo->setCurrentIndex(i); bFound = true; @@ -621,7 +621,7 @@ void CSequenceBatchRenderDialog::OnFPSEditChange() void CSequenceBatchRenderDialog::OnFPSChange(int itemIndex) { - m_customFPS = fps[itemIndex].fps; + m_customFPS = fpsOptions[itemIndex].fps; CheckForEnableUpdateButton(); } @@ -1543,13 +1543,13 @@ bool CSequenceBatchRenderDialog::SetUpNewRenderItem(SRenderItem& item) item.frameRange = Range(m_ui->m_startFrame->value() / m_fpsForTimeToFrameConversion, m_ui->m_endFrame->value() / m_fpsForTimeToFrameConversion); // fps - if (m_ui->m_fpsCombo->currentIndex() == -1 || m_ui->m_fpsCombo->currentText() != fps[m_ui->m_fpsCombo->currentIndex()].fpsDesc) + if (m_ui->m_fpsCombo->currentIndex() == -1 || m_ui->m_fpsCombo->currentText() != fpsOptions[m_ui->m_fpsCombo->currentIndex()].fpsDesc) { item.fps = m_customFPS; } else { - item.fps = fps[m_ui->m_fpsCombo->currentIndex()].fps; + item.fps = fpsOptions[m_ui->m_fpsCombo->currentIndex()].fps; } // prefix item.prefix = m_ui->BATCH_RENDER_FILE_PREFIX->text(); diff --git a/Code/Sandbox/Editor/Util/AffineParts.cpp b/Code/Sandbox/Editor/Util/AffineParts.cpp index e294f93089..139519c078 100644 --- a/Code/Sandbox/Editor/Util/AffineParts.cpp +++ b/Code/Sandbox/Editor/Util/AffineParts.cpp @@ -157,7 +157,7 @@ static Quatern Qt_FromMatrix(HMatrix mat) * |w| is greater than 1/2, which is as small as a largest component can be. * Otherwise, the largest diagonal entry corresponds to the largest of |x|, * |y|, or |z|, one of which must be larger than |w|, and at least 1/2. */ - Quatern qu; + Quatern qu = { 0.0f, 0.0f, 0.0f, 1.0f }; double tr, s; tr = mat[X][X] + mat[Y][Y] + mat[Z][Z]; @@ -531,7 +531,7 @@ Quatern snuggle(Quatern q, HVect* k) #define swap(a, i, j) {a[3] = a[i]; a[i] = a[j]; a[j] = a[3]; } #define cycle(a, p) if (p) {a[3] = a[0]; a[0] = a[1]; a[1] = a[2]; a[2] = a[3]; } \ else {a[3] = a[2]; a[2] = a[1]; a[1] = a[0]; a[0] = a[3]; } - Quatern p; + Quatern p = { 0.0f, 0.0f, 0.0f, 1.0f }; float ka[4]; int i, turn = -1; ka[X] = k->x; diff --git a/Code/Sandbox/Editor/Util/FileUtil.cpp b/Code/Sandbox/Editor/Util/FileUtil.cpp index 8dd379f096..ece516659e 100644 --- a/Code/Sandbox/Editor/Util/FileUtil.cpp +++ b/Code/Sandbox/Editor/Util/FileUtil.cpp @@ -2239,7 +2239,8 @@ uint32 CFileUtil::GetAttributes(const char* filename, bool bUseSourceControl /*= bool CFileUtil::CompareFiles(const QString& strFilePath1, const QString& strFilePath2) { // Get the size of both files. If either fails we say they are different (most likely one doesn't exist) - uint64 size1, size2; + uint64 size1 = 0; + uint64 size2 = 0; if (!GetDiskFileSize(strFilePath1.toUtf8().data(), size1) || !GetDiskFileSize(strFilePath2.toUtf8().data(), size2)) { return false; diff --git a/Code/Sandbox/Editor/Util/ImageBT.cpp b/Code/Sandbox/Editor/Util/ImageBT.cpp index 30c4911cb8..79ce4bba35 100644 --- a/Code/Sandbox/Editor/Util/ImageBT.cpp +++ b/Code/Sandbox/Editor/Util/ImageBT.cpp @@ -116,6 +116,7 @@ bool CImageBT::Load(const QString& fileName, CFloatImage& image) // Get the BT header data BtHeader header; + memset(&header, 0, sizeof(BtHeader)); // C4701 potentially uninitialized local variable 'header' used bool validData = true; validData = validData && (fread(&header, sizeof(BtHeader), 1, file) != 0); diff --git a/Code/Sandbox/Editor/Util/StringHelpers.cpp b/Code/Sandbox/Editor/Util/StringHelpers.cpp index 5e44c3b0bd..12865dfe73 100644 --- a/Code/Sandbox/Editor/Util/StringHelpers.cpp +++ b/Code/Sandbox/Editor/Util/StringHelpers.cpp @@ -419,7 +419,7 @@ static inline bool MatchesWildcardsIgnoreCaseExt_Tpl(const TS& str, const TS& wi const typename TS::value_type* savedStrBegin = 0; const typename TS::value_type* savedStrEnd = 0; const typename TS::value_type* savedWild = 0; - size_t savedWildCount; + size_t savedWildCount = 0; const typename TS::value_type* pStr = str.c_str(); const typename TS::value_type* pWild = wildcards.c_str(); diff --git a/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/ComponentEntityEditorPlugin.cpp b/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/ComponentEntityEditorPlugin.cpp index 54ac71db16..ed21b5b6e9 100644 --- a/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/ComponentEntityEditorPlugin.cpp +++ b/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/ComponentEntityEditorPlugin.cpp @@ -179,11 +179,11 @@ ComponentEntityEditorPlugin::ComponentEntityEditorPlugin([[maybe_unused]] IEdito LyViewPane::EntityOutliner, LyViewPane::CategoryTools, outlinerOptions); - } - AzToolsFramework::ViewPaneOptions options; - options.preferedDockingArea = Qt::NoDockWidgetArea; - RegisterViewPane(LyViewPane::SliceRelationships, LyViewPane::CategoryTools, options); + AzToolsFramework::ViewPaneOptions options; + options.preferedDockingArea = Qt::NoDockWidgetArea; + RegisterViewPane(LyViewPane::SliceRelationships, LyViewPane::CategoryTools, options); + } RegisterModuleResourceSelectors(GetIEditor()->GetResourceSelectorHost()); diff --git a/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp b/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp index 5ff2debe3d..8161d07547 100644 --- a/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp +++ b/Code/Sandbox/Plugins/ComponentEntityEditorPlugin/SandboxIntegration.cpp @@ -1732,13 +1732,14 @@ void SandboxIntegrationManager::GoToEntitiesInViewports(const AzToolsFramework:: // compute new camera transform const float fov = AzFramework::RetrieveFov(viewportContext->GetCameraProjectionMatrix()); const float fovScale = (1.0f / AZStd::tan(fov * 0.5f)); - const float distanceToTarget = selectionSize * fovScale * centerScale; + const float distanceToLookAt = selectionSize * fovScale * centerScale; const AZ::Transform nextCameraTransform = - AZ::Transform::CreateLookAt(aabb.GetCenter() - (forward * distanceToTarget), aabb.GetCenter()); + AZ::Transform::CreateLookAt(aabb.GetCenter() - (forward * distanceToLookAt), aabb.GetCenter()); AtomToolsFramework::ModularViewportCameraControllerRequestBus::Event( viewportContext->GetId(), - &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform, nextCameraTransform); + &AtomToolsFramework::ModularViewportCameraControllerRequestBus::Events::InterpolateToTransform, nextCameraTransform, + distanceToLookAt); } } } diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/manager/configuration_manager.py b/Gems/AWSCore/Code/Tools/ResourceMappingTool/manager/configuration_manager.py index b679921196..fc188582c7 100755 --- a/Gems/AWSCore/Code/Tools/ResourceMappingTool/manager/configuration_manager.py +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/manager/configuration_manager.py @@ -48,7 +48,8 @@ class ConfigurationManager(object): def configuration(self, new_configuration: ConfigurationManager) -> None: self._configuration = new_configuration - def setup(self, config_path: str) -> None: + def setup(self, config_path: str) -> bool: + result: bool = True logger.info("Setting up default configuration ...") try: normalized_config_path: str = file_utils.normalize_file_path(config_path); @@ -63,5 +64,7 @@ class ConfigurationManager(object): self._configuration.account_id = aws_utils.get_default_account_id() self._configuration.region = aws_utils.get_default_region() except (RuntimeError, FileNotFoundError) as e: - logger.exception(e) + logger.error(e) + result = False logger.debug(self._configuration) + return result diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/resource_mapping_tool.py b/Gems/AWSCore/Code/Tools/ResourceMappingTool/resource_mapping_tool.py index e99bf5d441..6a56491b24 100755 --- a/Gems/AWSCore/Code/Tools/ResourceMappingTool/resource_mapping_tool.py +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/resource_mapping_tool.py @@ -74,11 +74,18 @@ if __name__ == "__main__": logger.warning("Failed to load style sheet for resource mapping tool") logger.info("Initializing boto3 default session ...") - aws_utils.setup_default_session(arguments.profile) + try: + aws_utils.setup_default_session(arguments.profile) + except RuntimeError as error: + logger.error(error) + environment_utils.cleanup_qt_environment() + exit(-1) logger.info("Initializing configuration manager ...") configuration_manager: ConfigurationManager = ConfigurationManager() - configuration_manager.setup(arguments.config_path) + if not configuration_manager.setup(arguments.config_path): + environment_utils.cleanup_qt_environment() + exit(-1) logger.info("Initializing thread manager ...") thread_manager: ThreadManager = ThreadManager() diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/style/editormainwindow_resources.py b/Gems/AWSCore/Code/Tools/ResourceMappingTool/style/editormainwindow_resources.py index fb819cac34..67181fa36d 100644 --- a/Gems/AWSCore/Code/Tools/ResourceMappingTool/style/editormainwindow_resources.py +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/style/editormainwindow_resources.py @@ -3649,968 +3649,1082 @@ PNG\x0d\x0a\x1a\x0a\x00\x00\x00\x0dIHDR\x00\ Z\x80d\xdf%\x00\x7f\x12T\x1b\x97qJ\x10\x92\xa7\ \x22:BG\x84z\xfa\x9d{\x88\xac\x1d\xf5-\x8f\xc3\ r\xe1\x95\x00\x00\x00\x00IEND\xaeB`\x82\ -\x00\x00;\xfb\ +\x00\x00C\x13\ \x00\ -\x01\xa2\x08x\x9c\xed]\x09\x5cL\xdf\x17\x7f-\xb4 \ -\xc9\xde\xa2\xc5RD%\x7f\x89HB\x96(;E\x14\ -\xd1\x0fQ\xb4\x92(B\xd6V*d/E*[Q\ -hAE\x08\xadZ\x94\x92\xd4\xb4\xafS\xcd\xf4\xfe\xf7\ -\xbey\xed\x13M\xcd\xd4\xc4\xdc\xcf\xe7+\xaff\xde;\ -\xf7\x9e\xfb\xce=\xf7l\x17A\xd8\x90\xfe\x08ll\x88\ -\x18rI\x18Av\x80\xff\xdb\xd8P\xae\xa5\xb8\xd9\x10\ -D\x04ATT\xf0kU\x04\x91\x1a\xcf\x86\xc8\xc9Q\ -\xae}\xc6#\xc8\x0a\x03\xf0?1\xfc\x9a\x1fA\x88g\ -\xd9\x10~~\xca\xf5\x7f\x9c\x08r\xdd\x93\x0d9\xb5B\ -c\xd1 ^A^p\xebAK\x16/X\x05\xff\x0a\ -\xc1\x0d\x1f\xbdi\xbf\x0fx\xa6\xb0\xde\x92\x05\xf3\xd6X\ -\xa6\x16e\x1c\xb8\xe2\x9a\xe8YR\xa69E\xe7E\xc0\ -\x0a[\xb5\xc9*Zk9\xfb\xb1\x9f\x97\xf2\xd9\xf6\xfa\ -\xbf\xb3[\xcfN\x10/\x1eU5)w\x94\xe4\x0c\x0f\ -\xb9%\x13.]~\x9b%\xad]p\xc1naN\x22\ -\xff\xca[Q*\xe3.}\x9e\xa72o\xdeW\xd7\xbd\ -*\xb6\xec;\x9f\x5c\xcf\xb7\xf6\xf5\xcfmX\x99\xb8A\ -\xc5(qm\xdeL\xcd\xc4\x0a\xab\xb0\xcd\xa4\xb1\xab\xcc\ -\xae\xde)8D0\x8c\x9b\xa90\xf9u\xce\xa0\x1f\x1c\ -96\xabm\x87\x84\x1bg\xe9\xeb\xaaD\x8a\x8e\x5c\xe1\ -\x17\xea\xa7zJM\x8c[X\x83\xf8H4e@!\ -\x9br\x7fs\xa2u\xf1\xba\x8c~\xd3O/\xb7`[\ -Sf\xc9\xf1,|\x9c\xee,\xb9\xd1\xee\xc8Y\xce\xc2\ -\x90\xd8\x09\xcb\xa6\xb0\x17\xf6\x0fB\x84l\x9f\x84O\x08\ -\x8b\xcb\xf0\x0f\xd5\x9d\xa4\xbf\x1c\xb1|\xce&\xf4`\xe9\ -\xe1\x89r\x03\xdcy\x7f\x14\xc8\xb8\xc5\xf0\x16\x85\xf7\xd7\ -\x1d^:)\xf0M\x89\xc9\x22\xb3\x91\x93D\xe4g!\ -\xb3\xbf\xd9\x88(\x8e|:\xc2}\xd0\x9a\x02\x19\x97@\ -;\xc1\xac\x85\xba3\x03?\x82\x0f\x98\x8e\xf4\x09\x93\x97\ -\x07\x1f\x08\xff/k\x9d.\xdf\xaa\xfc\xe3\x9b\xa7\x87O\ -\x0b\x17\x08\x8b{\xb1\xdaB\xcf\xf9\xc2T\xb6\xac\xb2\xfe\ -W\xc2\x13\xc3E\xc2\x07f\xe4\x0e\xb99\xe0\x9e\xed\xf1\ -\xf0kde\x8b\xc5\x16zu\x91\x95\xf0\xaf\xa7m\x9e\ -\xd9\x8c\x1c\xe5\x1d:[\xb52B9\x9ckru}\ -\xfa\xf2\x90\x01\xf1C\xe3\xfb\xab<\xcf\xce\xecG\x92^\ -q\x03\xfem\xfe\x94@\xc4\xf2\xbd\xdf<\xdd\x10\xa4\xd4\ -\x92Cf\xeb\x07Rm\xb8\x0a\xb8\xa5\xc4 >\xb6\xc1\ -\xc8\x00p\xcb\x15\x16z\x02\x1e\x93 -\xb9\xe1F\xb6\ -+Cb%\xec\xd8Cm\xac\xc8\xca5\xbc\x19\xfd\xf2\ -\xf5O\xc3\xef\x8d\xb5\x89>\xf5!s\x80S\xd1:6\ -\x8b:\xeb\xe29!\x03nN\x87\xdd\xe3p_\xe4w\ -`\xcd\xf5\xfa\x1d\xdcz\xd7Gl\xcdH_DR\xb6\ -\xd8\xb0^\xfcq?\x9bh!\xc5\x0b)\xa4\xfc\xff\xe9\ -l\xe7\x8f\xe4L\xe20!)\xd7D\xcf\xfa\xb6)6\ -\xc4\xd2a\x86\xf1\xd8\xc0\xd1\xbe$\xd5e\x1c\x0a\xd1\x04\ -\xbb\xef\x03\xf7\xb0Y>\xb7U\xd5\x1dR\xaa#2R\ -\xda\xee\xed\x14\x8e\xbd\x15\xf5\xe9\x1f\xd4^\xdc\xd0\x83\xfd\ -T\x13\xd88w\xce7\x9f\x9aa\x12\x1c{8v\xd4\ -[\x17g\xa8=\xf7\x10\xd7A\xc0m\xa6\x96\x8eYU\ -{\xfc\xd7\x07\xcb\xac\x17\x96\x9e+5\x0e\x00\xae\xd8^\ -\xb6\xbd\xa0\xe21d\xe4\xb8\x05#\xa48\xd6E\x11\xce\ -\xafZ\xca\x11\x9e3\x88\xc4\x158,f\xa5\xb5\xb8\xaa\ -\xbe\x87\xb0\xaeo\xb4\x10\xd1\xeb\xdd\x8c\x95\xf2\xfd\xf93\ -\xaf\xe4\xe8m5!\xa9\xda'\xccfO\xbf\xb5u\xa8\ -\xf3c.\x95C\xfb\xe2J\xec\xe7\x04\xc8\xda-v\x18\ -\x1f^\x94\xbf\xf9\x7f\xaeI\x88=Z.\xab:)\x8c\ -\x7f\xd2dq\x85\xe5\xeeA\x17UB\xa2\x911j\xe0\ -\x17w\xd3\x97\xfa\x9d\xf7Z\x9e\xca.\xa6\xbcA\xdc\xfa\ -\xb0x\xc2%\xb7\x8f\xb6\x11\x8fM\xd9\xb2\xe3\x87\xab?\ -\xc9\x10\x10\xf7\xcb\x1b\x11\x16G^pW\xd6y?\xf2\ -z\xe4d\xbf\x05\xdeuvr\xab\xb8\x8c=2\xf3\x0e\ -\xcf\xb90r\xc9D$;\xd6*s\xfe\x88Z\xee\x1b\ -\xf3E\xe4\xc6_\xd8\xfe\x8a<\x86\xc3~nJ\x85\x04\ -\xe8\xda'\xaf\x81\x96S7\x8f\xac\x1f\x7fa^\xf8\xc2\ -\x98\xd4\xc3\xe2\xa6B\x9a~2\xd5\xeaa\xecb\x0d\x85\ -\x0fg\x7f\x91\xb6\x9by(8\xac\x9eg\xdc\x1c\xd0\x95\ -\xb7\x06\xd3V\xdc\xae\xbf{\x9c\x7f\xd1t\xb6\xa1.\x19\ -ZR\xbe#T\x0ey\x94L[\x9c;2\xd2\xf0\xb4\ -\x8dA\xb6\xd9\xe38\xe7\x22\xf6\xf7\xf3\x83\xca\x04\x22\xfd\ -\xbc\x9d~l}\xael\xc1\x96\xbd\xd1\xc2h\x91y\xc9\ -\xf0\xc8\xf7\xda6\xd3\xeaBr-\xe5\xa7\xd8D;\xbd\ -\x93\xb5\xfb:\x9c\xff\xde=\xeb/w\x10\xfbk)c\ -O\x9as;\x8d\xde\xf1\x86`\x18p\xb0\x88\xfdu\x5c\ -A\xday\xe1\xe4\xf1\x0b&|]\xe8'\xfd)G\xcb\ -\xcez'[\xba\xed\x9a\xc3\xe2\xf2\x1f\xa4\xb6\x9a\x05/\ -\x9a\x13\x84d/2-\x99\xa7\x91?\x5cb\xa8\x08\xbb\ -Z\xa5\xe9\xff\x0e\x18M\xb1\x09u|'\xbb\xc0[p\ -\xb1\x1f\x9f\xc5\xbd\xb4\x11*\xa4\xe4#\xa2+n\xd4O\ -\xb2\xe3'\x9bs\x96\x9a&\xf3\xfe\xf2\xed'7\x12\xcc\ -\x0b\xfd\x90\xfd[\xcd\x9e\xa9\xeb\x04\xc1\x196\x948\xaa\ -\xf4\x97\xcc\x0b~\x89\xed\x87\xb3\xd6\x84\xc5\x89\xccj\x90\ -\x0d\xd9\xcd\xb6\xe9\x1cx\xda\xc3\xe3'7x*\xcf\xb3\ -`\xfbD\xa8\xb4\xe5\x97\xb8e\xaauG\xf3\x17;\x98\ -\x03SU.\x14\x7f\xe1\x15\xff\xb2\xd7/3\xcf_\x83\ -4X\xe7\x09\xb2\xf7\xb3\xceRq;\x01\xc9\x84\xa2m\ -\xda\xc8\x18\xa3p>ad\x92\x99\xdb\x1an'\xf3\xe9\ -\xe1+\xc0m\xf3I\x83\xa3\x83\x91\x8f\x0b\x01K\xbf\xde\ -u\xde 2\xb7\x80\x88<\xd5\xb0\xe09\xce\xbfTd\ -u\xb4\x839[6\x97\xd5\xe1\xd3\x8f\x11\xbb\x98\x1d\xd3\ -\xc3\x97xr\x9d\x9d\x10\x84L\x10\xbb\xb0\xc5\xdep\xfd\ -\x12?\x7f\x8e\xace\xc43\xeafn~\xd2^\xfd\x14\ -\x9e\xbb\x8d\x0b\x1b\xf2\xd9h\xdd\xe3O\xdf\x9c\x82V\xbb\ -\x1e\x8f\x99\xe0&\x90\xf6\xb5\xde\x1d\x91\xcb=?w\xe1\ -\x01\x85\xbd\x1eN\xd7*\xc3\xac~%\x8eHU2u\ -\xdb\xcc\xbd\xfaR?\x92`6\x9c\xd0\x8e\x03\x96%\xea\ -L\x12\xb7\x13\xf7\x8e\x22\x90>O\xb5Y\xa2\xe2_\xbc\ -\x8aG|\xa0\x93\xba\xdfG\xd1A6\xf9!\xea\xe3\xec\ -\xe4\x12L\x89\xf2\xc8\x04\x82\xcc9\xfe\xc8\x15.\xdfc\ -\x11w\xc7\xc8\x01<\xe2\xf2q\x9cHh\xb4\x90\x14\xa2\ -V\x80\xfde\xec\x9co*\xb6\x22\xa3\xd4\xe7:\xf0\x8c\ -T\xd5\x9f\xac$\x97t\x8aT\xb71\xcaq\xbf\x92\xe4\ -\x05%\x9f\xc9\x8f\xa6=}\xff1k\x95\xd2\xb1\xa5\x85\ -?\x14\xf7\x0c\xcb\xfbj\x9crCQ6h\xd6la\ -\xdb\x9f\xd5R\xdf\xb9\x85w\x9e_\x91&\xec\xe5?3\ -\xd2\xa0j\x08\xe9\xe4+a\x9fs\x81a\x0a\xcb\xa4\xfc\ -\xea\xef.\xe0U\x9be\xfb=D]\xfc\x8b\x91\xa1\x97\ -\xcb\xa3E\x8a\xa3g\xbc~\x9b\x22\xce\x17\xb5n\xd6\xc4\ -\xb7%\x15Z\x81\xd6\x02\xbc\xaf\xa2\xb7\xd8\xf9\xce\x1a\x95\ -}\xeb\x98\xb8B\x11\xcf\x837\xa2\x12\xa3\x9c\x87\xbb\x8f\ -\xde\xfat\xe8\xa8\x1b\xa1\xc3$\xcc8\xce\x14\x1d\xb2>\ -z\xc6D-W\xae*A\xb8\xff0\xdf\x92\xc4\x99\x22\ -r\xde_\x22\xdc\x97re^\x19\xb6Q\xed\xcb\x90\x13\ -{\xea\xe7\xfb\xf9\xf2\x95\xff\xf6{\ -\x83\xd1\xf0|\x99\xfe3\xf9\x14\xad\x9e\xba<:\xa9T\ -\xb6yU\xb8\xd3O\xa9\x80=\xbb\x0dr\x7f\x8a\x11\xcc\ -\xfb\xf3,\x5cud\xb0\x93\xc2|\xcfp$\xc3\xeb\xd7\ -\x12u+\xd7\xbc\xcc=\xeau\x99f\x95\xb7\x8cBH\ -\xfe\xab\xbc\xcf\xbd\xffl~\xdf\xc4M\xa145H\xb7\ -@V_\x22\xf7\xb8\x82\xbf\xe7\xc5\xe8'\xf1\xe5>\xbb\ -O\x95\x97G)\x5c|\x11\xaffvn\xe0\x18\xf7p\ -$\x9e\x0b\xde\xbcf\xca\xee\xa5\xe4\xe3v\xe6\xdb\xfb)\ -\xec\x9d=\xf7\x9aP\x5c\x9c\xc1\xa3\xb9\xaf]\xeem\xd4\ -*\x9b\x17\xfa\x98\xb7r\xb4n\xf4\x0f6\xb1\x87\xf1\x0b\ -\x1c\x86f\xd6\x9c\xd1\xaax\xc4\xbb7T\xee\x1a\xfb\xfa\ -\x83\x5cR\x0b\xaf\x0d\x0c\xe4\x90\xc9\xf0\xe2O\x9a~\xfb\ -\xfdG\xcd\x00)\xfd\x8c\xb8\xd8\xda\x88\xe3\xb5?~.\ -\xe6\x1f\x15\xb3\x9d\xeb\xb4\x99\x9b\xaa\xfdiU%\xd4|\ -\xa1\xf8n\xf4\xc5\x95\xddwr\xf5\x5c\x84\xe5T\x1f\xf8\ -!\xc3\xbc\xcb/\x08(\xa1f:C\xa4\x0fo\x88\xfa\ -1\xa4\xae\xbf\xfe}Uv\x15\xf7/\xa3\xe7{>\x97\ -\x0bpE\x9f\x07\xfd\xfaU\xb0\xc0\xe9\xb0\x00\x12\xf9\xd8\ -P\x81\xf8q\xe6\x01R\x8ed.\xa9\x22_~\x8d\xd7\ -QN\x1b\xb9\x17\x11^\xe6\x17?N\xf9\xbe>y\x98\ -\x93\xe41\xbb\x9d\xe3\xd9\xc4\xe4\xa3\xae\x98Tl8\xb0\ -e\xf4\xb6I\x22!g\x9c\xa7\x8fC\x16\xa8\x8d\x9e\x19\ -\x13\x11\x8b\xae\x8a\xca5x;\xe2b\xbe\x18\xf8\x90\x86\ -ml\xd4\xb6\xe4\x91\x15\xf1\xd2\xbc\x0f\xd4\x1d\xc4\xc6\x0b\ - \x12c\xbf\x1a\x1a\xa7\x1d\xaf\x9d\xfc\xdcwAT\xbf\ -U\xf0C\xa9~\xd3t&\x87>\xb2;\xadZ;\xa9\ -\xe6d\xfat\xa7\xb2\x09\xe7b\xbekZ\x5c\x90\xf3Y\ -\xf3\xf2\xe3[g\xa5\x09\x0f#S\x96J\xc6e\xe8\xbc\ -\xd3\xe5\xb1\xbd\x1d\x93!\xa0\xb5\x8d\xa8\xc2\x15;\x9e\xd3\ -Y\xc2b\xa9\xfe\x85\xef\x1c\x22\xb3\xad\x17\xf9\x9e\xe5\xa9\ -tJ\x1f\xefd\xa66\xd8\xbc\xf4\xa4\xa8k\xcc\x18\x0e\ -\xeb\xace\xe4\xf1\xabf\xecw<&lP\xa7\xec\xb7\ -x\xdc\xa4\x8d\x1aU\xd7\x83fF\x16\xc7\xbd*\x1cd\ -W[\xb9\xd8Ne\xbe\xdd\xab\xe5CR\xe7\x7f\x12<\ -[\x94\xb5\xf3\xd1\x9b n\xd9\xb7\xc1+W\xea\x1b,\ -\xf6\xf0\xbc1\x9cOm\x90y\xd6\xe8,92\xa9\x9e\ -\xd3\xc1%z\xaf\xd4\xcf\xa4\xf1\x83\x1eN\xdc9x\xd9\ -\x10D\x93|\xdd\xee\xc6\xf0\xfc\x8d\xdf\xbe~\x12\xdcN\ -\xd2\xe4#\xbc\x9c\x1c\xef\xe3\xcf\xf3h\xaf\x81\xe3 Q\ -\x8b\xc0D\xd1l]\x9d]V\xc6\xb9\xa1\x86gE\x0b\ -\xf4\xbfpK\xe6\x9d7/K\x5c}\xc5;=&\xe7\ -f\xbc\xce\xfb\xb0\x87\x8e[^&\xf8\x85Jyo?\ -\x8d\xf0\xe7\x07\xef\xd4\xe4\x92V\xe0\xd2z\xe25'\x97\ -\xf7\xc8\xf3$]W\x8b\xb9\xf9Y\x19\x9a\xb36\x103\ -\xef\xff\xf8\x99\xf2Tw\x13\x9f\xdb\xf2\xf7\xb9\xd9\xd36\ -\xfd\xb8\xec\x93o\x1d{\xf6\xd0]\xbe\x13\x07\xf6\xce/\ -\x17\x8a\xd5\xb6\xe5T\x19\xe1kt\xaa\xbc\xb0N\xa1\xa1\ -RC\xc8\x8a=\xca\xcc6U\xe9\xa6_\xaa\xa0~b\ -\x92\xd8\xf1g\x0a\xfeW\x5cc\x07\xe5\xe5^\xfey\xc3\ -\xb3>lmL\x10\x87\xb8\xf9!\xc1/:\xc3\xe2\x9d\ -\x90\x18\x81\xddw\x95\xdc\xeb\xce\xa4\xde\xb2\xb6\xd4\xb9\x94\ -v\x8bTV\xa4\xd0@4\x17\x9a\x16\x15R\xba\xd99\ -n\xdc\x07\xa5\xfa\x1f\xb9)D\xf2\xf27\x87\xc8\x11\xb3\ -.\xa5\xea\xab\x889\xbdg\xd7?\x15G\x18\x9b*\x8d\ -\x96\xad[\xdf\xcf\xa3\xe8^\x9e\x95C\xf2\xa3\x9c\xeb\xdf\ -\xb6d\xeaq\x88\xa4\x1fy\xd2pl\xf5{5v\x15\ -\xa9`s\x03.7\x19\x9f\x9c;q3>\x7f\xba+\ -{{\x87\xe6\xdb\xdc\x87\xa7\xcaG\xed\x93\xe0A\xf4\xec\ -\xff\x1bvU+9\xaa\x1f\x9ag\x9f\x14\xefp\xd8l\ -\xb9\x93x\xde\x84\x1d\xbc\xcf\x86F\xae8)8\xf6\xbd\ -Nj\xb8\xc3\xd5=_\xbel\x9f(\xfb\xb8\xac\xe2\x1e\ -\xafYf\xffxG\x84_{\xf2\x96\xcc\xa8\x19\xa2w\ -\xa7\x99\x06\xdd\x8c\x9f!\x99+o\xfd\xd8p&\x9f\x00\ -\xa2z\xab\xe0\x92\xdczR\xd0\xf9`\x0b\xb3Q\x0d\xdf\ -j\xb6\xac\xb6[\xfd\x92\x0c~\x1fp\x22I*\x13\xd5\ -\x99#k\xb4kF\xa5\xb5\xe6>%\xf7is\xb6\xd4\ -\xe4\xee\x96d\x13{<\xe0xD\xbd\xe9X\xcd\x80i\ -\xe6O\xbe\xcdq\x1b\xb6\xa1\xe1\xd3\x10-n\xd7,\xef\ -\x85\x83\x14\xff\xa7v\xf3\xeb\xcf\xb2s\x0ahm\xca\xd4\ -\xd2\xcc\xb93\xe2\x1ck\xa4\x01!\xc7\x06T\x1c(5\ -\x9a\x12\x22\xab#\xc4c\xc39eD\x80\xcd\x0e9\xb4\ -v\xc2\xe9\x8a\xad\xb1\xa7\xce\x19\xbf:PKj8\xb7\ -NpJ\x88\xea\x8a\x10$\xd2\xf4\xf0S\xbf\xcf\x81\xb6\ -\x11\xce\x87\x84\x82\xdf?.\xde`\x03\xde%\xcf\x15\xa3\ -\xf6\x1ey\xc8\x86\xeam\xf3\xe66\x9a-\x9b\xf9p\xe3\ -\x19u\x84{\xf5]\xa3S\xf2\x16Kn\x89\xe7`b\ -\xe4\x92\xe1\x87\xf1\x91\xf0m\xbc\xbbK5\x1d}8\xf5\ -\xf2\x81\xf4\xd9\xaf.e `\xd4v\x1d9\x14\xfa\xc1\ -\xd1\x80\x0bLs9\xad\x8am\xb1\xb3\x16#\xdc\xf1\x97\ -\xd1\xa3S5\x87l\x9e\x96x0\x03Y\xbd\x8cC&\ -\xf3\x93\x86\xd6\xbc\xba\xfez\xa9g\xb7hM\x1aV\xf7\ -\x5c9\xf2\xc8\xfd\xff~\xda\xeeNP\x8eC\xcf=\xda\ -_\xfb\xe6\xcc\x87\x8b\xda\x0ez\x15\x96\x1cI\xcf\xb4\x9d\ -\x1e\x08\x9b\xc379\xe7\x84\x0f\xf9CH\xc5\xe0s~\ -\xe9\x86\x85\x0f\xf5=o\x0f\x10\x93WUd\x0f;\xbc\ -\x90|\xfa\x85\x96A\x88\xe9\x96\xbb7\xc0k\x1e\xb5A\ -g\xf8\x8f\x0c\xcd\x84\xea\x12\x99Y\x19\x0b\xcf\xcf\xac\x96\ -\x9fn\xc7i\x13#\xe5\xf0\x22\xe9ne\xd5\x00\x93=\ -\xcb\x84vz~\x05\xa4:\xad\x04\x93\xe3\x82\x91\xdc\xc1\ -\x86Q\x04S\x0e\xf8\xcd\x03\xbb\xce\xa5F\x9f\xb8\xe3\xf0\ -@c@\xf8\xb0x\x9b\x1d\xe5\xaf\xd3\x05xM\x12\xfd\ -\x068\xa8}\x0f\x92\x14|\x5c|\xf6\xf2\xa0\xcd\x07\xc2\ -<\x8f\x84=\x5c\xf15\xef\x7f{\xed]\x9c\x0f]\x1e\ -\xaf,\x09Fz\xfe\xbb\xa2M\xa6!!?3\xa3\xe6\ -\x88\x5c$\xae\xf2MJ\x1f\x93[\xb2\xf1\xd8\xfb\xab7\ -w|?\xff\xa1\xbf\xc1\xa0X=\xb3\x0b/3\xce\x9c\ -\x93\xd0\xb1\xe1\x1c:g\xd7\xe5\xc3\x01\xcf\x8d\xde\xe6f\xa2\x16;\ -_\xa6\xefX\xfb\x8e7\xb1\x9f\xcdps#g\xa3\x1d\ -\x9b\xceD\x0e\xda\xf1n\xb8\x8c\xfc\xc3\x10[\xded\xa3\ -\xffI\x93V\xca\x0d\xe0\xb1yz\xf0\xb8I\xd5\xfc\xd8\ -\xed\xa7\xe7]\x10\xcf}h#=\x5c\x00\xd9w-Q\ -z 2`\xf6\xba\xbbg\x0clB\x8c\xef\xef\xb98\ -\xc2f{\x82T\xba\xff\xeeJ\xdd\xdd\x85\xb2w^\xf9\ -D\x14\xb0\xab\xe4:\x1c\xd5\x02\x14\xf8\x92\x02\xd5\x07\x18\ -\xae\xd1\xce\xfa\xbc\xeaV\xae\xa2\xee\x0f1\xb3\xef\x07\xc3\ -\xd95\xf7\xbd\x0b\x96\xd7\xc8\xdd\xe6\x85\x18h\xeb\xbbi\ -\x8d\xf5<\xec\xbc:\x7f\xde\xd5\xc3<\x08\xe1?\xc33\ -\x93\xf6\xf4[6Z\xcf3\xc7\xfb\xac|\xed0\xb3\x9a\ -\xff\x09\x83\xd71\x9f}\xc3;\x99\xe3J5\xa4x)\ -\x1e\x1b\xd3\xcb\xca\xda\x19s2,\x14\xfdL\xf7\xb9\x83\ -\xa7\x8c\xf3\xf1_\x19\x1c\x96\xbeSr\xe6\x85\xbdJ;\ -\xa6?\x9b\xb9\xcb\xa1\xcc\x7f\xd5!\xf6\x07Z13\xe4\ -#N^\xbb[\xffi\x8f\xc2S\xfd\xe7f\xe7=\xd7\ -\x15=\xff\xcb\xc9\xb1\x13\xee\xce|\xff\xe5\xb4\xcc\ -\x5c\xf5\xa2\x17\xe9O\xd7gxp\xec\x90`s\xd4\xcd\ -\xbc!g\xdb\xdf\x8ccn\xbc\xda\xcf\x18S]\xc1a\ -?S\xdd\xcd\xbcC\x92\xd6H\xb1_\xaeY22\xdc\ -\xf9\xdbZ\xcb\xd4\xb3\xaf\xf82\xebv]-V\x7fo\ -\xb9^\xe3\xdd\xdaT\x12Z\xaa\x12#\xefpR\x1d\x11\ -6 \xef}\xb2<\xac\x84\xbcz\x8c\xc6\x81\xff\x89\x8d\ -\x9f\xe1y-\xa0\xb2*\xf3n\xd0\xc7\x99\xe7\x05\x90\xe8\ -\xda\x12\x87\x84\xc3\xc4\x0b\xdb\xcde\xdc\x1e\xd8\x1c\xbe<\ -\xf3\xa3\x97i\xadl\xf8F%\xbf\xcf(\x17\x8f\xcd\xd0\ -C\xdaBz'\xb3'^\x0f\x95\x91Yq\x90OO\ -!4\xcf\x99\xdd+ \x13,g.\x0b\xfb#\xf3\xd8\ -\xaf\xab\xa3\xc4\x15{\xa4u\x13t\xb9\xd6_\x99r\xdf\ -F_\xc7\xf8\xd2\x99G'\xee\xe9\xac\x22;\x14\xcd\x8b\ -\xfc\x1a\x86\x94\x82\xa1\x92y\x1e\xb4\xd6Z\xb4t\x91\xdb\ -p\xc7\xdd\xb6\x15y\xf1o\xf4N\x1e\x09\xbd\xb9\xa2\xb6\ -\xde/\xc5\xcf\xd8`\xd4Pg\xe4Wd\x12Y\xdc+\ -\xf6(\xdb\x89u\x8aO\x8b\xd8\x9f\x1c\xd3\xfa\xfa\xa3.\ -\xbf\xf6\xec\xba\xf4\x1a#\xc33\xe5\xeb.\x98aO\xdb\ -(\xfb<\xd9\x9b4\xa7\xf8\xa5\xe2\xb5\xd5\xe9\xf2\xe3?\ -]~'\xafTc\xad\xb3'S4\xcb\xeb\x5c\x91\xe9\ -\x86mlY+\x05\x90\x0c\xcb\x9d\xdbB\xcb_\xea\xee\ -=\x7f.\xf3\xd0\x1d\xee\xd0J\xce\x83\x95\xd7\x92G\xac\ -\xbcm\x5c\x93\xa9\x11\xe7\xbf^\xb4n\xc7X\xb6O\xe7\ -\xc0\xeaD\x8a\xe3\xab\xfa\x10\x11p\xd5\xf9\x95\xc8\xc8%\ -\xeb\xaf+%k\xc6m\xfb\xa9X\x15\xb7\xc20M4\ -\xfb\x15G\xd2\x06\xb2\xdbb\xf6\xeb\x5c\xea\xeb\x83\x05\xcb\ -\x95k6k\xc7\xe8\x7f\x95\xcd\xe2\xbc3\xd8L\x1e\xbd\ -\x5c\xc0\xd5\x804\x04F\xb9\xc7\xa5\x1a\x8eC\x0e\x06\xee\ -5\xe0\x12\x9dxH5\xd0%\x9e-,\xd4\xd7}\x9e\ -W\xf9>\xd3I\xb2\x8f\x1f\xfc\xfa\xb9u\xfe\x8e\xa5\xca\ -\x01\x0e\xa5\xe5\x13v\x9fk\xb0p6\xda$;\xe2\xc5\ -\xce\xf7\xb2\xc7B_\x7f\x1cj\xa2.\xe58\x8d\xf7\xed\ -\xbe\xf0\xec\x8dKGi\xeb\xbdG\xf2W\xef\xd6\xe6\xd1\ -2,\x0dp\xe1T\x91m\xa8\x9b\xa7\xc4\xb5\xeb\x5c\xad\ -\xf9\x95\xa9\x09o#/\x9f\x9eB\xf6\xb3\xc9\xe8?\xf8\ -\xc3\xc4\x1d\xa7#\x8b^\x0a$*,\xb2\xdcu\xbf\xaa\ -?:k\x09R)1\xecD\xe6\xcb\xd4\xad)\xf5\x1f\ -\x84\xf3\xb2\xcb\x84\x96NV\x9aVz\x99p\x9ax\x22\ -\xdb\x80}\x9a\ -\xb4\xc5\xcaq\x84\xca\x17\xa0\x13\xa5W,\xbb\x15\xb4|\ -,\xd40K\xe6N\xca\xb9\xaa\xe5\xf1\xe4\x9a\xcd+\xbe\ -\x19q1y\xa2se\x92\xd7\xd5\x8a'\x9dsy\x87\ -\x8e_\x94\xffX\x94\xacj!.\xbf|\xb9\x0eg\xe8\ -\xa0=\xe4\xfd'3\xa2f\x8e5yzp\xe9\x98\x93\ -\x97%\xd6\x08).\xfc*:-\xfa\xd9\x86\xfd\xbby\ -\x1eI\xe6e\xef/\x18p\x7f\xbbQp\x99\xd5\xb1u\ -\xe6;x\xf3\xd9+]^\xf1\xc9\xbe\x97\xce1|\xb3\ -qM\xd5\x1b\xff\xb8\xc8\xd9u\xda\x03\xa4\x15\xee\x1d\xb1\ -\x9b9z\xe4\xd5\x9f[\xbf%\xf3\xba\x1a\xe5$\x8e\xbc\ -\xc6\x19\xb9)\xe8\xfa\xc7\xf2\xf8\xc3\xd1`\xad\x89x\xcb\ -!\xecG\xd2\x1b\xed\xd5\xef\xe7\x7f\xa7\x93\x13\x16Z\x1e\ -\xdc#\x1d\xf7\x22\xcd\xbf\xd2\xc6\xe5*\xd8b\xb9\xcf\x19\ -\xef&\xe4\x9dgR f\xe2\xa7\xbf\x8bG\xedY\xc1\ -\x8d\x83w\x87]%\xbb\xfb\x8eCV\xfd\xcf\xe4\xca\x19\ -I\xa3i\xc9_2Vp~\xbb\xcb[\x1e\xfca\xde\ -\xe6\xb4o\x1a\xd6gR\x1d\x143\xb7o\x8bJ\x11\x96\ -\xb9\xec\x1e;>G\xc7[\xe8\xb2{\x0d*X\x1b[\ -\x9ft(5`\x86\x00\xb2\xd38za-\x89(\x14\ -5;\xf8\xaa\x9f1\xa7B\x09A\xb7\xe8#\x9fBq\ -~\xd6\xb1\x17\xe2\x82\xab7\xd5_-y8\xf5\xb0\x8f\ -\xcb\xdb;\x8a\x1e\xca\xa9o\x5cW_\xd1\xae\xaf\x9e\xa3\ -E\xda!=\x98\xf4&e\xed\x15\x8f\x05\xecb\xcb\xcf\ -n\x8e\xbezZs\xf7\xd8B\xd2d;\xd4\xd8N\xd3\ -\xc1\xae\xf6\xa6\xb8\xaa\x8e\xba+Pe\x17s\xaa:I\ -\xc9\xdc9\xb7\xe9\xce\xb9\x87\xaaB\xc5[\x95\xc0j>\ -\xf0\xbe\xb1\xdc\xc1\xc0c@_\xf4\xdeUf\x9a\x07\xd6\ -\xf2\xf4\x06\xfdc\x93\x89\xf9\xb6\x0d\x9f=&\x97\xb9\xe4\ -]\x22.=\x95\xe1Xto\x95\xd9\xaa\xe3\x9e.\x93\ -\xdeo\xf9\xb8\xe6P\xa0\xec\xeb\xe7w\x84\xbc\xcakT\ -\x93\xd8n/\x91\x1a\x18<\xf7k\x0c\xd7\x88\x99k\x93\ -\xdeCE\xe9\xb9\x8fm\x10\x9f/\xf9\x85\x13\xb2\xf8\x16\ -x\xea\xcc\x01\x84\xaf{#>\x8c\xff\xb0\xb5\xe4\x86\x9c\ -\xc9\x08\x85\x15i\x867kW\x0c\x05\xc3\xfc\xc8;H\ -*\xd3\xfa\xf9fmv\xc1\xc29@\xe6.\x22\ -f\xb7\x0b\xbc\x94q\xf8Z\xa8\x97I\xf1\x7fg\x8a\xbf\ -p\x8b\x0f|\xd2\xefe:\xbf\xfe-\xca\xff\x87\xbc\x1c\ -h\xf3$D}\xac\x9d\x5c\x826Q\x1eQ`{\xf8\ -\xf6\x12\x7f\xd2'\x0d\xbf\x8f\xd1\xec\xe1%&\xc1\x1cB\ -\xa6nK\xb9\xf5\xec\x07\x95\xcd:\xf2\xde\xfb\xfa=\xca\ -\xd5-\xb39S\x91\xcd\x14#\xd5\xf9\xa4\xaa\xd7H\x0a\ -{\xc2/\x19\x0f~U_A\x01o\xb2C\x7f\x9b\xcc\ -\xbc\xf3\xec\xb2\xf92\x9e\xe07n\xd9f\x22\x8ae\xba\ -\x8e\x06fn\x1a\xdc7\x12\xa6\x9a\x91\x88\xc8\x86\x93\xa6\ -\x1cv1\xeb\xa6\xbf. \xae\x8c\xe6\x84\x9fF\xe2~\ -\xc9x\xf1\xab\x9e2\xc86#\xf9\xf8W;i\xdf\x0d\ -uU\xe5\x1e\xaf\xe1\x93&\x09\xff\xee\xdf\x7f\x1dA\xc6\ -\x97_\xe2\xfe\xbahB\xf5\xd4\x90\xb4%\xfbo`\xa6\ -\xbd\xbb\xb1\xb32t\x10\xd2\xe8R\x82\xcc\x03~\x89)\ -\xa4\xd7\x05\x95\x8c\xb38\xd1z\xba\xaa\xfd\ -\xedt\xa1\x80\xd09\x05\x15z\xfb\xc0\xbc\x5c\xab{\xb1\ -\xf8\x9e\x80j\xe5\xae\x0b\x99y/\x1f\xcb\xa2\xdb\xe4\x0d\ -\x96f\xf2<\xfe\xf5R^|\x81\x83\xb6}b\xa2r\ -\xbe\x09B\xb4.\x9e\x13~(c\xbd\xa4\xdd\xaf\xcf$\ -K\x9d\x22\x17}m\x04q@\x08f[n\xf0\xdfT\ -'\x9cJ\xfc\xac\xac^\x17\x0em\xe5\x93\x02GK}\ -\xb1~\xa2j\x7f\xeb\x92\xcd\xa9\xc3\xd7\xeas\xf4\x1bV\ ->\x9c\x82d\x1b\x19\x07\x17\xdf\x1b\xae*\x9cS\xac/\ -=\xb7\xf2V\xbfOos\x06\x91\x86\x10\xfd\x8b\xef\x8d\ -P\x0d|_br$`.*\x97?B\xe5h\x9a\ -\xcd\x9e\x90\x80M\xdcg\xd2\xd40\xd3\xf1F\x04\x19c\ -\xa4;\xaet\xcc\x0a\xefz\x19n\xbdyf\xe1\xc3\xc2\ -\xe2^\x84\x86\xc9:.\x1d`C\x14:\x22\xf4\xeb\x93\ -\xb5\xa4]\xa0;_V\x9a\xef\x9a\xc3\x8b\xa7\x22\x08I\ -\xb9\x86\x10^\x14Rr\x82\x7f\x84\x89\xd5\xf3b\xafq\ -\xb9\x08\x1cDM$,\xc1z\x91]\xa0}}\x8e\x99\ -\xcc\xf6\x89\xe7\xd8\x84\xe3r\x06\x11m\x0eh\xef=\xc5\ -\x9fTj\x9f\x99W\xedMx\xf3\xe5\x0b\xfbb\xe3\xc1\ -\x81\xaf?\xa5\x89\xdb\x99M\x16_\x17\xfde\xc2}6\ -\xfe\xb9\xa6\xa5\xe6\xe38\xed\x16\x07\x8f\x7f]G\xfe\xec\ -\xbae\xe2\xbc\x90\x8dpH\xe4\x1e\x1cx{;i\xdc\ -\x00\xf1\x90*n\xe3\x8d\xcf\xf7z\xad\xd5\x8eF\xce\xe7\ -\x95\xf5o\xe0 z\xbc\xab\xe1\x97\x88%\x7f)1A\ -7%\x9e\x91\x1e\x84@\x0f\xc0\xb4R%\xb1EUV\ -;r^F\xe8\xf0\x90QF\ -\x09V\xbcN\xb6Al\x8fV\xeb|O^\xbfS\x94\ -\xb3P\x83t\xf4#\xdb\xc5\xd1UWU\xf5\xdd\xb7\x87\ -\xcfT[{X.\xb0\x9fcL\xce\xa0t\xee\xd9\xa7\ -\x0d'Ip\xbeV\xa8x\x9a\x06\x1e\x1c\xf1kF\x92\ -\x00bljp\xc3\xc0fg\xc8;1;\x1e?\xae\ -\x9f\x95\x06\xd3\x1cL\xe5\xfc\x06\x18?'\xf6\xd3\x944\ -\xffiS\xa80\xd9\xcf\xfbx\x8a\xc5\x91\x10\xffw\x83\ -\x057\x22\xa7\x80\xac\xe4\xce\xda\xf7#\x9dMo\xd0=\ -\x1b%\x97\xaf/\xd6\x0f_\xcd\xa9\x22\x09\xe8\x9fY\xba\ -Q\xd7\xb5\xf8!\xf7\x8dp\x99\xad\x92\xd5~\x9ft\x1c\ -5RC\x10\x9dS\x99\xee\xd3\xec\xe7\x94\x8a\xdd\x118\ -c\xb8\xcf>\xc5\xa2n\xe04@\x93\xd6\x001\xa7h\ -!)\xf6b\xf6\x86\xe1>\x9ft\xc6\x88\xd7\xdd\x18\xb1\ -\x01\x99\x9d\x946\xf1E\x88\xbb\x022\xeb\xcd1]\x99\ -\xac~'\xf2e\x1c\xf8\xad\x03\x07\x97\xb2\x9d\xf9\xa0]\ -\xa6\xe4\xa1\x8c\xcc:\xf0\x9cM\xd2x\xba1\x97\xdc|\ -\x8b\x03v\xbf\x22wm]\xb4\x1f\xfdZbr\xd2t\ -\xa4\xb7\xac\xfc\x04D\xe0\xc0s\xdb\xcf\xe1\xf7\xc3\x85\xaf\ -\xaf\x02\x7f\xf7J\xe4\xdf\x18\xf7\xf0u\x89II\x82\x8e\ -\xbb\xfev}\xc4\xb4\xdc\x92\xc3$\ -\xa3_A\xc0Y>\x9b\x05c\x8c\xc2\xfb/Cd\xc6\ -\x07]\xd5\x9d\xe4x/t\x93\x84\xf4c\x0e!\xfb\x0f\ -\x99a\xbc\x86Kj>\xce\x0e\xd1_\x8e\xc0\xb9\xf1\x83\ -\xdd\xc5\xfe\xbe\xc2\x95e\x93\x85\xd5\xb6>\xf4\xd5\xd4\x9b\ -5DwF`\xbf\x14n\xbf~\x0a\xb5\xd6\xc5\xe6\x19\ -\xfdV\x8b\xef\xe6\xb8.=\xc6HLt\x14\xd7\x15\xde\ -\xb3\xc8\x09\x0eI\x0e\x0e\x81\x86\xa76[3\xb6\x0f_\ -\xfa?\xf6yY\xc6\x91\xd7\xc8\xe1\x9bu\x07\x97N\x0d\ -|\x05\x84T\x81\x8c\xcf\xca\x86\xb9b:\xff\x138\xa9\ -\xb8}3\xf4v.Y\xa8\xb1\xc0_U\xf7\xa8\x14\xf4\ -vR\xdc\x9f\x98\xc7\x12\xc1}\xa1=\xd1\x08Fc\xa8\ -a\x08\x80\x0c\xc0f\x00{\x00?\x80w\x00\xd9\x00\xe5\ -\x00$\x00\xf4\x1f\x01\x09\xefs6>\x06~\xf8\x98l\ -\xc6\xc7h\x08\xb51d\xe6\xd6\x01\xcf\x87\x01,\x008\ -\x06\x10\x0a\x90\x03P\xc9\x04\xe3\xcf\xac\xa8\xc4\xc7(\x14\ -\x1f\xb3\x05\xf8\x182\xed\x5c\xa0B\x1b'>\x87\x0f\x02\ -D\x01\x941\xc1\xb8\xf6U\x94\xe1cx\x10\x1fSN\ -f\x9a\x07T\xf8>\x03\xc0\x15\xe0;\x13\x8c\xdd\xdf\x86\ -\xef\xf8\xd8\xceh;\x0fz\x99\xef\x10\x13\x01\x1c\x00r\ -\x99`\x9c\xfev\xe4\xe2c=\xb1-\x1fz\x81\xf7|\ -\x00;\x01\x92\x99`\x5c\xfe5$\xe3c\xcf\xd7Ss\ -\xa0\x0d\xef\xa7\x00\xf8\x00\x10\x99`,\xfeU\x10q\x1e\ -La\xf4\x1chq\x7f\x0e\x80\x95\x00\xf1L\xd0\x7f\x16\ -(\x88\xc7y\xc2A\xef9@h\xfd\xces\x03\x18\x01\ -\x140A\x9fYh\x8d\x02\x9c7\xdc\x04:\xca\x82\x16\ -\xf7\x1a\x08p\x14\xa0\x8a\x09\xfa\xca\x02uT\xe1<\x1a\ -H\x0f\xfe\xb7\xe1\xfd)\x02k\xad\xef\x0b \xe2\xbc\xea\ -\xd6\x1c \xb4\x96\xf9GY\xbc\xefS\x80\xbc:Fh\ -\xb1\x16t\x91\xf7P\x9f\x80k\x0aK\xe6\xf7=T\xe1\ -\xbc\xa3I'$\xb4\xd6\xf7\xa0N\xc9\xd2\xf5\xfa.\x0a\ -p\x1evZ\x1fl\xf1Y\xb8\xa7d\xed\xf1\xfa>\xe2\ -\x09-\xec\x03\x9d\xe4=\xb4)\xf90\x01\xed,\xd0\x07\ ->\x84\x16v\xc2N\xc8}hWd\xe9{\x7f\x0f\x88\ -8O;\x5c\x07Z\xfc\x0d\xfa\x15X\xf6\xfc\xbf\x0f\xc9\ -\x84\x16>\xa3\x0ex\x0f\xfd\x8a\x0eL@+\x0b\x8c\x81\ -\x03\xa1\x85\xef\x98\x0a\xff\xa1o\x99\xe5\xc3\xfd{\x91\x8b\ -\xf3\x98\x1a\xff\xe1\xbcpe\x02\x1aY`,\x5cq^\ -\xb7}\xf7a|\x11+n\xe7\xef\xc7w\x9c\xd7mu\ -~+&\xa0\x8d\x85\x9e\x81U\x1b\xde\xc3\x18\xd3(&\ -\xa0\x8b\x85\x9eA\x14\xa1u\x5c\xf1B\x02+N\xf7_\ -B\x19\xce\xf3F\xfe\x1fc\x02\x9aX\xe8Y\x1c#4\ -\xe7\xe5\x842\x01=,\xf4,B\x09\xcd9Y9L\ -@\x0f\x0b=\x8b\x1cBs>\x1e+'\xeb\xdfC%\ -\xa19\x17\xb3\xb7i\xe9!\x88\xa0\x84\xbd\x10\xc2(a\ -\x0f\x84\x10\x0e\xc1\x16\x10j\x06\xfc\x1c\xfc<\xfc^\xaf\ -\xd3\xce\x10@\xde\xdfg\x02:\x18\x07\xc8\xbfF~\xee\ -\x97@\x0b\x0f\xc8\xa0\xc5Gg\xa3\xa5\x8e\xab\xd0\xb2+\ -\xdb\xd1\x0a/\x13\xb4\xe2\xeeA\xb4\xc2\xef0Zq\xcf\ -\x0a\xad\xf06A\xcb\xae\x1a\xa0\xa5\xcek\xd1b;e\ -\xb4\xe8\xa0,Zh2\xae\xc5\x9c\xf8\xab\xe6\x02\xe4\xfd\ -;&\xa0\x831<\x07\xff/\x04\xfc+uZ\x85V\ -\xfa\xdb\xa251\xbeh]\xe6\x07\x94\x5c\xf2\x13m\xa8\ -*E\x1bj\xabQ\x94T\x87\xa2\x0dd\x80\x06\xcaO\ -p\x0d\x7f\xdfP]\x8a\x92K\xf3\xd0\xfa\xef\x9fPb\ -\xec}\xb4\xf2\xc1q\xb4\xd4u=Zd5\x15%\x18\ -\x8b\xfe-s\xa11\xff\xbe\xb7\xe9\xa0+\xdf\x0b\xcd\xa5\ -\x00\xcfW\xa3U\xcf\x5c\xd0z\xc0\xef\x86\xaa\x12\x140\ -\x17\xednk\xa8.C\xeb\xb3\xbf\xa0\xd5/=\xd0\xd2\ -\x0bZh\xa1\x854e\x1d\xe9\xbb\xf3\xa0\xb1\xf6Bo\ -\xd3A\x07\xbe\x0bc\xb2\xbd\xfc\x86!Z\x1b\x1f\x82\x92\ -+\x8b\xbb\xcd\xef?\xcd\x85\xda\xe4\x08\xb4\x1c\xac\x1fE\ -V\xf2-t\x05&\x18\x8f\xce\xa3\xef\xd7\xdd\x00\xe3^\ -h>\x11-\xbf\xbe\x0b\xad\xfb\xfa\x1am\xa8\xaba(\ -\xdf\xdb\xcd\x83\xfaZ\xb4.\xe3\x1d\x98\x07\xfb\xd1B\xcb\ -\xc9\x94y\xd0\xdbc\xd2y\xf4]\xdec\xef\x9a(Z\ -r^\x13%~|\x04\xd6\xec\xaa\x1e\xe5{\xbbyP\ -G\x04r'\x14-uY\x07\xf4\x03\xb1\xbe(\x0b\xfa\ -\x0e\xe0;\x0f\xd6\xde\xca\x80\xa3(\xb9\xf8G\xaf\xf2\xbd\ -m#\x97\xe5\xa3UO\xce`kQ\x1f\x93\x05}\x03\ -`L\xe1\xbe\x8c\xf8>\x00E\xeb\xebz\x9b\xdd\xd4\x1b\ -\x99\x84\x12?\x07\xa3\xc5'\x17\xb0\xe6\x00]y/\x02\ -\xf6\xed+\x81N\xff\xbe\xb79\xdc\xa9V\x9f\x13\x0f\xf6\ -\x8c\x1bXk\x01] \x82\x96\xb9oFI\xf9\xe9\xbd\ -\xcdV\x9a\x1a\xa9\xf0;fg\xea\xfd\xf1\xeb\xc3\x00\xef\ -O\xd9%=\x94T\x94\xdd\xdb\xec\xecR\x83v\xa4\xb2\ -\xab\xff\xf5\xfe8\xf6E\x80\xf5\xb3\xf4\xa26\xf6\x1e\xd1\ -\xbd\x81\xbd[CM9\xdaPY\x8c\x92+\x8b\xb0\x9f\ -\xd85\xf8=\xbd\x1b\xb98\x17-\xf3\xd0e\xe9\x034\ -\xf2\xbe\xe4\xccR\xb4>7\x99.Vz\ -qS\xef\xc9R8\x07\x5c\xd7\xa3$\x02\xed6\x8a\x9a\ -W7)\xbaio\x8f}o\x03\xfauN\xcc\x03c\ -\x98E\xd3\xf8A\x1d\x91\xa2K\xf5\xb2.\x0d\x9e\x0fc\ -O`\xdc\x18-\x0d\xc6\xa2\x95\x9c^\xcc\xd2\x03@\xff\ -a\x8c\x1dM\xeb>\xf8,\x94\x17\x84\xfdc\xd1\xde\x8f\ -\xd1\x15\xc1\xde\xe3\xaaPWJ\x1c!\x0d\xad*\xf8|\ -\xef\x8f\x7f\xaf\xf2^\x04-<0\x05\xb3\x8d\xd0\xd2\xea\ -\xb3>\x82\xbd\x82\x22\xf3\xbc;\x98\xcdJ\x01\xad\xcb\x88\ -\xa5\xad\x1f`\xafStH\xbe\xf7eXo\x01\xec\xd3\ -`\xdc\x0c-\xb2\x13\xc6yA\xfb-\xd3\xf0\xbe\xa9/\ -\xc2X,\x1a-6\xe3\x06b%\xb6\x8f`\xd8~\x95\ -\xd9\x01x\x08\xed\xf1\xb4\xb4\xba\x94H\xb4\xd0r\x0a\xf3\ -\xbd3P\x96YLBk\x13_\xd0\xd4\x9f\xea\xe7n\ -\xbdO{o\x8d\x97\xf9D0^/;?X\xa4:\ -\xb4\xc2\xdb\x94y\xdf\x17@W\xf9\xcd=hC}\xe7\ -\xed\x83u\xe91x\xfc(\x93\xcdg\x86\xf3_\x18\xcb\ -\xc7\xa1\xc5\xa6\x0e\xed-L\xedC\x01<,\xb2\x9e\x8e\ -\xd6\xffH\xe8t\x9f`\xdc`\xb1\xfd?\x18/\x06\xde\ -\x15\x18\xd7\x03\xd7\xc0\xce\xb6\x9a7^\xcco73\x16\ -E\xab\xc3\xaft\xbaO\x98>sm'\xf3\xca4\x86\ -\xf1_\x10\xad\xf4;\xdc\xf9}\x1f\x90\xfd0\xd6\x9e\xe9\ -\xc7\x09\xae\x01\xd0\x87EC>B\xd5\xa3S\xcc\xdf/\ -\xbaC\x84&\x9b\x1f\xb9\xa2\x08\x8b\x09az9\x89\xd9\ -\xb3Ti\xf2\x0f\xc1\x18\x03\xc2>\xb1\xde\xa7\xbd\x07y\ -\x0fc,`~eg\x1b\x8c\xc7):4\x8d\xf9\xf5\ -$\xa8\xd7\x1e\x94\xa5\xc9\x8fY\xfb\xe5)Zh&\xc9\ -\xfc}\xa3\xe7\x18\xd1\xa8\xfbc\xfb>\xb0\xbfb\xfe1\ -\x12\xc1xY\xfb9\xb8\xf3}\xc3\xf6\x00L\xb8\xa7e\ -$\xff\xc1\x9e\x07\xf6\xbb\xb3\x8d\xf8\xf1!Zh:\x1e\ -\xed}{o'\xb0O\x02\x8b;\xebl\x83\xf9\x02E\ -Vr\xff\x16\xff\xa1\x8c\xcc\xfa\xd8\xe91\xaay{\x97\ -I\xec\xfd\x9d\x00\xd8\x03\xd4D^\xeb<\xff\xb1\xb5\xed\ -\x1f\xb2\x03cv\x7f\x19\x9a\xf2y\xfej\xfe\xffL\xf9\ -\xf7\xf8\x0f\xd6;\x96\xfc\xc7\xf9\xff/\xca\x7f\xa8\xff%\ -t\xdeVN\xd1\xff\xa4\xfb\xc0\x18uE\xff{\x8b\xf9\ -A\x99\xbfot\x1c#\x9a\xf7\x7f\xa9}l\xff\x17\xd7\ -\xe9\xbe\xd5~y\x06\xe6\x8c\x14\xf3\xf7\x8d\xce\xe3T\xfd\ -\xc2\xad\xd3c\x04\xf3\xb4`\xfcu\x9f\xb0\xff\x1c\x9fG\ -\x9b\xfd\xe7\xcdm\xe6\xb7k\xd3\x1b\x98\xfd\xd7\x9a\x06\xfb\ -o=s\xfb\xfe\x9a\xfa\xd5\x05\xfb\xef\xe3\xd3\xcc\xdf/\ -\x06\x8cS\x99\x9b\x0eM\xf1\x12\xb0\x8e\x1b\xd3\xc7LB\ -\xffO\xd8\xe5N\xf7\x09\xe6\x92\x94_\xdf\xfd\xef\xf1\x1f\ -\xfaJ\x8f(b6@X[\xad\xfe\xfb\xe7\xdf\x03|\ -\x06\xe6o\xc2\xb5\x95i\xd7I\xcc\xff\xfb?\xa0\xcf\xd3\ -\xe0\xff-/@KN\xa91\xff\xba\xc6\x08\x80w\x05\ -\xee\x03a\xad\xcd\xce\x00\xab\xad\xc3\xcc\xeb$\x94\xfd0\ -\x16\x98\x86\xfc0XC\xac\xcf\xea\xfe{E:\x07z\ -\xdc\xa33\xf7\xea\xe5\xb1\xc0\xe2\xbf\x12\x9ew\x9a\xf7\xb0\ -aq\xccF\xa2\xbdO?\xad\xd8?\x16\xeb\xef\x1fa\ -.E\xc9\xcb\xeamz\x19\x8d=x\xee*\x91\x86\xf8\ -O\xa0\xfb\x94]\xda\xda\xf7\xd6~\xa8\xbb]5@k\ -\x93\xc3\x7f\x8f\x94\x08\x94\xf8\xf6.^\x13\x8d\x89\xdf\xdd\ -\xee\xa2\xb1fA\xc6[\x9a\xde}\xa8\xd3\x14Y\xf7\x01\ -\x9bF[\x18\x0aRr6:\xd1`|\x1f\x1c\x9b\xbf\ -W\xbf\x11\xc1\xec\xbdU!.\xb4\xe7\x7f\xc8\xd4v\x8c\x9e\xe2\xbf\xef\x01\ -\xacfnW[\x8f\xc7\x84\xc0\xfa/fR\xd8y1\ -\xb0\xc6TW\x1a\xe9g\x0aZ|\x5c\xa5\xf7\xd7\xae\x7f\ -\x81\xff\xf4:\xc3\x09\xa3]\x14\xe8kK\x80\xcc\xb9\xdf\ -\xe5\xb3\x06\xe0\xf92\xf0\xdc\xa1>\xb7\xdf\xeb\x8b\xfc7\ -\x16\xc3\xce\xec\xc1b\x86\xbaT\xffM\xa4\xb9\xfe\xdb\x89\ -\xf9\xd8^\xad\xbbu\x00a\xfeR\xa1\xe9_\x10\xe7M\ -\xcb\xfe\xbf\xf8G/\xf0\x9f\x12_\x02\xd7Y\xe83\x82\ -6\xf9b[%\xbc\xf6\x9ep\x8b\xf3\xde:Q\xff\xf1\ -\xd5M\xba\xd4\xa0\x85\xb1nE\xb6\xb3\xfa\xb6\xdco\x04\ -V\xf7f\x03\x16\xe3\xf8{\x5cG\xab\x82\xceQ\x8f\xdb\ -\xea\x01\xfeC\xde\xc3\x06\xf3q\xe1\x1e\x8d\xf8)\x08\xab\ -1V\x0e\xcfzs\xdf\x8c\xd5\x94\x87{\x10\xa8\x8b\xc3\ -z\xb0\xad\xea\xbf\xfe\xfcJ\x93-\xf7w\x0d\xd6.\xc7\ -\xf4\xfd\xbf\x81\xf7-\x01\xed\xfa\x9d\x01\xb5\xef\xf6\x04\xff\ -\xe1Y\x11T\x19R\x87\xe5\x9d\xc2\xf3\xc1\xe0yQ\xf0\ -'\xc3\xea?\x97\xe4br\xa4\xcf\xcb|z\xa37\xf9\ -\xdfC\x0d\xd6\x7f/g\xd5\x7f\xff'\xf9\x8f\x9d\xff\xe0\ -\xc9:\xff\xe1_\xe4?\xf4\xeb\xb1\xce\x7f\xf9\x07\xf9O\ -\xae\xc7t\xcc\xe2\x93\xf3\xff>]\x8f\xc5\xff\xdf6\xe8\ -\xcf\x83{\x07\xd6\xf9o\xff\x16\xff)\xe7?\x86`\xe7\ -\x86\xf7\xb1\xf3\x1f\xe9|\x06(\x8d1{}\x9c\xffM\ -\xe7\xbf\xde\xde\x87\x16ZJ\xf7\xb5w\x1e\xf2\x9e\xae\xe7\ -?C;+\xcc\xe9o\x07X\xb3\xc1\x98\x8a\x8f\x96.\ -\xfe\xbf\xfb\xbf\xe1\xffx\x94\xf8!\x90N\xdcnn\x94\ -\xf3\x9f\xc3\xb1ZD\xd8\x99\xf0}\xf7\xfcg:\x9e\xff\ -.\x82\xf1\x12\xda8\xebR\xdf4#-\x0a%\xc6=\ -\xc6b\xe2\xdb\x8d\x11\xf4\xff\x9fU\xc7\xec\x83U\xc1\x0e\ -\xb4\xe3\xa9#\xa5\xe6?U\xff?\xe5\x1c\x19Zrp\ -\xff\xc4\xf3\xfa\xec\xcfX\xdd~\xa8\xd7\xff%\xe7\xbf\xbf\ -\xa3'\xff;\xcay\x81\xe7r\xc0:\x7f\xd4\xe3\x7f\x84\ -1_B\x97\xf1\xbb\xf8\x09cQ\xb4\xd8~!Z\xe1\ -g\x8d\xcd\x03(\xab\xa1/\x02\xb3\xf5\xc1\x9c#R\x1d\ -e\xed\x81\xf9g0n\x0f\xda\x04k\xab\xb1Z\xb4\xd0\ -n\x03\xeb\xf7\xc0\xfcT\xe8\xe7\x801>\xd8\xbb\x0e\xe3\ -\xb41_R\x9f\xe5{# \xef\xef\xd3\x95\xff\x1d\xd4\ -\xf2\x82zq\x87\xfcg4\x9a\xcep\x92\xc0r,\x8a\ -\x8f*av\xf8\xb2\xcb\xdb\xc0\xba\xbd\x1f\xd3?\xe0Y\ -.\x15w\xad\xb0\xdc\xc22O\x03\xa0\xc7\xadA\x8b\x8f\ -)S\xf2M\x80\x0ci\x8e5\xeb\xf3\xd2\xea\ -\xeb\xd8\x0dJ[^\x0fF\xd1\xac\x96\xd7\x5c\xadn\x8f\ -=\xa0-9m\xae\x1bZ_\x1f!\xb5\xbe\x9eKl\ -}-Z\xda\xfazp\xdb\xeb\xac\xd6\xd7\x5c\xe1\xad\xaf\ -9\xda^\xdb\xb4\xbef\xfb\xd35\xc2j\xac\xc6j\xac\ -\xc6j\x0ci6\xad/\xff(\x8f\xc3[_\xb7\x93\xef\ -Y\xad\xaf\xb9\xfe\xb4~\xb4]o\xda\xaeGm\xd7\xab\ -?\xado\xed\xd6\xc3V\x04q\xb5_O\xdb\xae\xb7m\ -\xd7\xe3\xb6\xebu\xbb\xf5\xbc\xf5z/\x05~\xa8 \x94\ -qeC\xc4(\xbfWAhj\x1d\xe8zj\xb8N\ -\x13\x86\xeb9\xd5\x00\x0d\xbd\xa0k5\xe0\xcf\xce\xc6i\ -\xb1\xc7ik\xa7c\xd1\xda\xa8\xf4[\x08\xc0\x10 \x02\ -\xa0\x94\x09\xf4\xcc\x8eP\x8a\xd3h\x88\xd3L\xf38\xb4\ -\xf9\x0e/\x80.@\x5c/\xf1\xb8;s#\x0e\xa7\x9d\ -\xb7\xb3c\xd0\xa6\xef\xe2\x007\x01j\x99\xa0?]E\ --\xde\x07\xf1?\x8dA\x9b\xbe\xcb\x13\xfe\xae\xbc\xe1(\ -\xbcOT\xc7\x80J\xdf?1\x01\xcd\xf4\xc6\xa7\x8e\xc6\ -\x80\xd0z\xce\xffM|\xa76\x0f\x9a\xde\x856}\x87\ -r\xe2&\x13\xd0\xc8h\xdc$\xb4\x91\x898\xa0\xac\xec\ -\xcb\xb2\xae\xb3\xa8\xc5\xfb\xdav}\x8fc\x02\xdaz\x0a\ -q\x84\xd6\xfa\x01\xd4\x17\xfa\xd2\xfa\xde]4\xe0}n\ -\xd4i#\x98\x80\xa6\x9eF\x04\xa1Y\x9fgf\x9d\x96\ -Q(%4\xefez\x9b\x96\xdeB\xe3>\xae\xe7\x9e\ -\xb9\xb7E}Fx\x0dkG\xc1\xb3R\xe19R-\ -\xcfn\xeb\x99\xbaB\xb0\xeft\xac\x93\xdcQ\x9f[\x9c\ -5xT\x09\xabOZ\xf9\xc8\x1e\xady}\x0b%~\ -x\x88\x12?=\xc1\xce\xba\x85\xf5\x87+|-\xd1R\ -\x87\x95X=SJ\x9dJ\x86\xd6\xd5k\xdc\xbf3\xee\ -\x19\xf0\xbc=\xf3\x89X\x9f\xb1s\xd7\x8a\xb2)\xb5y\ -\x7f\xd3\xe0Y]\xf5YqX\xed\xe8\xe2\x93\x0b\x9a\xe7\ -\x0d\xfd\xe9c\x9c\xed\x02\xf2\x1c\xcc\xed\xd2\x8b\x9b\xd0\xda\ -\xa4\xb0.\x9f)J.\xceE\xabB\x5c\xd0\xa2#3\ -\x18q\x9e4\x83\xfa.\x8c\x9d\x93\x01\xcf\xf1\x86\xf5\x96\ -\xe9\xd1`\xbdfx\x86\x1f\xe5\x19L\x5cs\x0c\xf4\xbd\ -\xc8F\x11;'\x86\xde\x0d\xd6\xda\x84u\x8c)\xf5\xf6\ -\x98p\x0c`\xdf\xc1<\x85\xf3\xbd3\x0d\xd6\xe3&W\ -\x14\x82~\x15\xa0\x0dU`\x9e\x90\xea\xff\xfc\x1d \x1b\ -*|,z\xbf\xaf\xed\xfa.\x82\xd5Cl<\x93\xb2\ -C\xfa\xabJ\xd0\xda/\xcf\xd0\xca\xfb\x87\xd1R\xd7\xf5\ -h\x89\xbd\x1aZ|B\x15-9\xbb\x0c-\xf3\xdc\x81\ -\xad\x01\xf5?\x12\x01\xb3\xc9\x1d\xdf\xa3\xb2\x18\x93\xa7\xcc\ -Vs\xb5\xf2\xe1IJ\xaduj\x0d\xc8|b\xdc#\ -\xb4\xd4iu\xeb\xba\xdbMg\xb4\xe2\xf5\x9a\xc18\xc2\ -z\xa2\xb0\xae;\xa9\xe0[\x87c\x00\xcffg\x9as\ -\xb6\x01\xed\xf0\x9cQrY~\x87<\xaf\xf4\xb7E\x0b\ -\xcd$;'\xc31}A\x18\xabo\xff\xbbw\xa9&\ -\xca\x0b%\xec\x97\xe8\xfd\xfe\x03\x1a\xe0Y-T\xfb^\ -]\x86\x9d\xa5\xd1\xd4/\x1a\xc7\x15\x9e\xd3[\x9b\x10\xda\ -\xe1\xbda\xed~\x06\xac\x8b\xb4\xf1\x1e\xbc\xbb\xe4\x8a\xa2\ -\xf6\x04\x92I\xd8\x99Q\xd8\xb9\x02]\x95\xd7\xe0\xfe\xc5\ -v\xca\x14\x99@m\x0e\xc4\xf8R\xf4\xe7\xde\xea?x\ -\xff\xaa\x82\xcfS\xa5\x0d\xd6\x0b.\xb4\x9c\xd2}]\x1e\ -\x8cA\xf9\xf5]Tu(\xa8\x1f\x15\xdb\xcd\xed%9\ - \x82\xe9\xb6\xf0\x5c\x94vs\xb3\xbe\x96r6(=\ -\xe6&\x5c[\xcc\xa4\xa8\xcb\x02 o\xcbo\x1b\xf7\xce\ -;\x00\xc6\x1c\xae]\xd4\xe4^}N\x02Zd%G\ -\xbf}\x1c\xe8\x1f\x5c\xf7\xb1\xf3<\xda\xbe\x03\x91\xd7\xd1\ -^\xd1\x87\x00Me\xee:T\xcf\x15\xaeys\x9b\xce\ -\xcf\x12FKN\xa9a\xfaR\xbb\xf7,%\x02;\x9f\ -\xa9\xe7\xfb/\x08\xf4Q\x13\xaa<\x81\xeb\x1d]\xe7$\ -\xd4\x0b\x0e\xca\xa1\xf5\xb9I\xed\xe7\xda\x8f\x04\xb4\xf0\xa0\ -l\xcf\xd7\x22\x06\xfd\xaf\xbc\x7f\x84\xfa;y\xcb\x98\xa2\ -\xcf\xd0\xedy\x14\x19@M\xd6\x90\xf23\xa8\x9f\xc1\xc4\ -\xea?\xc3\xfb\xdf\xe1\xfc\x0f\xe8\xd9\xf9_\xd4+\xf3\x1f\ -\xc8?7\x1d\xaa\xf6\x1cx&\x13E\xef\xa1\xd7\xb3\x84\ -\xb1\xbd\x12u\xf9\x17\xd9;\xf2\x0f\xae\x7f@7\xab\xcb\ -\x88\x05s0\x1d%\xfdJ\xa3\xa0 \x03;\x17\x0d;\ -G\x8c\x9e\xeb\xdf\x1ds\xea\xeb\xdf\xab\x1b\x0c\xe4q\xa3\ -M\xb6\x05Z\xeaZ\xc6b\xd8:\x0f\xf7lE\x87p\ -\xc0\xff\xc3\xf3\xb4\xe8\xa5\x97\xfeQ\xff\xd9\xcf\x18\xfd\xc7\ -X\x14\x9b\xdf\xd8\xf9Q\xf0,)\x88\xbb\x07\xd1\x923\ -K[\xf3\x95\xd1u\xf1\xa1\xfe{m'u\xfd\xb7$\ -\x97q\xfb`\xc0?h\xabh\xdb\xaa\x9e\x9ci\x1eo\ -l\xaf*\xf8\x1bt\x93/p\xffsL\x19\x93q\xd4\ -\x1a\x11\x9e\x11\xca\xa8\xfdO\x87\xfd?\xdb\xe4\xa3\x80v\ -\xce2\xf7\xcdh\xd9%=*\xd8\x8a\x96:\xae\xec:\ -}p\xff\x0b\xd65x\xfe,\xb5\x86\xed\x7f]\xd73\ -N\xf7\xff\x13\xff\x01J\x9d\xd6`t`g\xedA\xfb\ -]K\x00Y\x05\xd7k\xec\x8c\x90&\xfd\xbc\x13\xe7n\ -\xe1\xfe\x92b\xfb\x05\xe0\x9d\x7fI\xb5\xef\xb0\xd5D\xdd\ -a\xac\xfd\x03\xf6\x9f\x8a\xed\x01\xfa%\x9a\xfa\xef\xbc\x96\ -r\xde`\x07\xad\xee[l\xab\xfeC\x1bP\xa1\xe9\x84\ -\xf6\xe7\x91\xb5\xb4\x7f\x01\x19Zq\xef\x10\xa6\xd7t\xd4\ -Hy\xa9\xe0\xbd\x9f\xc7\xd8}/\xe8\x7f\xd53'\xfc\ -\xdc\xcf(\x0a\xc0\xff)\xba]'\xfb\x9f\xd1\xa2\xffP\ -_\xf0\xdc\x0e\xe6s(\xb6?(\xbd\xa0\x05\xf64\x8b\ -\xb0\xf3\xd4K\xce5\xda?\xddp\xfbg\xc7\xe7\x06c\ -\xf6Ox6h\x0f\xec\xf9\xb1sop\x9ea\x806\ -\xbc\xc69Gs\xff\x05\xb1\xf3\x84\x9b\xfa\x01\xe49\xb9\ -\xb2\x08\xb3\xebc~\x92N\xdb\xbf-\x19\xde\xef\xd6\x10\ -i\x83f\xf9D{\xff\x8d\xa9\xea0\x9diL\xe7\xff\ -\xe8\xc1\xfeC\x7f(\x83\xfc_\x9d\xf3\x01\xb6\xfa\xcc\x1ea\xccw\x00i\ -\x86\xf1\x81\xe4\x92<\xea>\x7fR\x1dJ.\xfa\x81\xe5\ -\xcb\xc2\xf83,_\xaa{\xfc\xe8>\xed\xe0\xf9%\xa7\ -\x97\xa0\xb5\x9f\x82\xd0\x86:b\xa7\xfd\xb7\x0d\xc4J\xcc\ -\xb7\xd7\xbb\xb9Z\x22\xd8\x98\x93\x08Y\x9d\xa6\xbbm\x83\ -~/,\xd6\xaa\xa7\xfb\x00\x9e\x07\xe3b`\xec\x03\xd5\ -\xf1\xad\xa9\xc0b\x81\xb0\x18\x9d\x8c\xb7X\x1cjG\xf1\ -\x080\xf7\x9a\x92/\xd5C}\x801\xbbg\xd5QR\ -av;Z`\xceHu\xd8%,\x1f\xb0\xe8\x90<\ -%\xc6\xc6L\x0a\x8bw+u\xdd\x80\xe5\xc6\xc3\xbc\xb9\ -v|\xf8\x91\x88\xc5\xdc2\x9c\x0f{)\xb9=\xd4b\ -\xef\xea\xd2\xdfb>jJ\x1f\x85Z\xf8\xc4D\x9a\xfd\ -\xbbX\xee\xf7F\xaaq\x8b5\xd1>\x98\xfcb\xec\xd8\ -\xc3\x98\xd1]\xedb3\xea\xd2\xa2\xb1\x1a\x04\x9d\xf2_\ -\xc3\xdc\xb6Sj\xedr\xcf`|\x02\xe4\x11#s\x8e\ -\x0aM\xc6\xb7\x1b{(/a\x9e%M\xcf\xc5rt\ -6\xb7\xcbC\xc7\xe2\xd3\xb18\x22F\xcc\x1da\xacn\ -\x01\xcckn\xd9\xaaC]\xbb\xe6\xd7\x07\xeb\x18\xcc\xf3\ -k\xf5.\x17|\xa3\xd4\x04`\xc4{\x00\xe3tn\xec\ -n\x95\x83\x0c\xf3\xb3i\x1e\xfb\x16\xf7+s\xdf\xd2j\ -\xdd\x80100\xfe\x8f!s\x08\xdc\x13\xe6]\xb6\x92\ -\x1b9\x09X\xecI\x97\xc6\x1f\xcb\xe9\x9f\xd9N\x8eU\ -\xf8\x98\xd39\xc7\xa5\xf9y\xd5//\xb5z\x16\x8c%\ -\xc2\xf4\x80.\xdd\x0f\xc82\xcb\xc9h}\xf6\xe7V\xf7\ -\xac|x\x821\xe3\xdf!\xfd]\xcc\xd1\xe8i\xfa\xa9\ -\xcd\x9f\xdc\xa4\xae\xe7\xd8t8\x7f,\xe84\x7f\xda\xc7\ -e\xc2x5,'\x05\xc8\x09\x08\x18?\x08s5\xbb\ -$/:|\x7f\xb5\xbb=\xfeX|mcl-\x8c\ -\x93\xc3e2\xfc=\xd4\x05Z\xa2\xcb\xf3\x07\x93\x9f>\ -\xad\xe5'!\x13\xc8O\xc5\xee\xc9O@#\xd4a\x1a\ -\xe3\xa3\xe1zU|lNs\xdc\x9a\xe1\xe8\xd6\xe8\xca\ -\xbe\x0a\xcf\xb1j\xbf~\xf9t{\xfd\x82\xe3\xdd2\x9f\ -\x0e\xea\x92P\xc7\x87t\x16YO\xc7t\xc5\xd6X\x0f\ -t/\x15\x9ah\xa7\xae?T\xd0E\xf6c\xf4\x83q\ -o\xbaou\x19F\x7f\xc1\xeeQX\xae\x14\x9c\xa3-\ -\x01\xf7U\xadr\xa5\x1a\xe3\xee\xa9\xeao\xa2\xd8\xfc\x86\ -\xebF\xdb\x86\xe5\xdc\xd3A\x7f\xc3t\x9c/O1\xba\ -\xa1\xae\x0b\xf7'0\x9e\x14\xa3\xdf\xf7@\xbb\xe7b\xcf\ -\x8e\xf2\xc6i\x17\xc1tP\xb8'\x81\xf9\x02Pw.\ -4o\xa3?W\x16\xb7\xfb>\x94ct\xcb\x97\x07c\ -\x04\xf7\xd90\x86\x1f\xf2\x19\xc6\xaab\xef\xb0\xa1\xe0\x9f\ -\xe9\x872\x16\xec\xd7!_`\xce\x1c\xdc\xbb\xd4e\xbc\ -\xeb\xc4\xfee=}\xf7/-\xeb\x915\xdewOg\ -\xe9w\xa4\xfa\x19j\x8d\xa1\xfb\xc7\x969Xx\x9c5\ -\xccS\xa3J?\x94\x1b4\xd0\x8f\xed\xdf\xdf\xde\xa3\xc7\ -\xfe\x9d\xba\x0d\x02\xe6\xbb\x80\xb5\x11\xe6\xb7\xc1=:\x16\ -\xf7\x0b\xd7\x1b\xb0~\xc1\xf9@\x99\x1782\xdf\xa3\x95\ -\x0fN4\xd1\x0fk\xa8Q\xad7\x83\xd9Or\xe8m\ -?\xa1n\xbf\x82\xba\xf2\xcd=\x18\x1d\x98\x8e\x0c\xf6\xbb\ -\x94Zo\x12\xf8\x9e\xb65\x9a\xe5\x86\x08\xf6\xaeR\xec\ -W\xb6=a\xbf\x0a\xeb\x90~\x18\x93\x0e\xf8L.\xfd\ -\x85\x96\x9cY\xd2B.\xb7\xcd\xfbhCG\xd3\xbck\ -c;d\x8c\xfd\x90\xba\xfd\x16\xce\x1f+9,\xcf\x0e\ -\xc6\xe5C\x19\xc8d\xf1\xa9\x8d\xf6\xdb\x8e\xed\xe7\xadb\ -\xc2\x99\x8av\x94\xd0l?\xef\xeb\xfe\x8b\xbe\xee?\xfa\ -\x1b\xfcw}\xd6\x7f\xda\xd7\xfd\xd7\x7fC\xfc@_\x8f\ -\xdf\xe8\xcb\xf13X\xfc\xd0`\x04\x09\x87?9\xf0x\ -#\xb6\xe6\x9f\xac\xd6\xbdf\x03\xffi1\x9e\xe1\xf0'\ -G\xf3\xb8\xc38-1\x84r\x84OS\x9c\x16?\xf5\ -{\xb5\x99[C\x01\xb6\x01<\x05( t\xef4\xces\xda\xbf\xdf\x14\x0b\xd1\xa2n\x09\xed\xb6\xde\ -\xc6w\xac\xf3\xdf\x81\xf5\xb1\xc1\xfe\x14\xdaN`\xbd\x10\ -X[\x01\xd6\x0d\x84\xf5\x10\xb0\xfaA\xb4\xd1\xd1\xf9\xf7\ -\x1b\xdes\xbf\x04\xb6\xd7\x845>0_\x15\x16\x8b\xd0\ -\x80\xd5^\x806)X\xbb\x02\xda\x11\xa0\xed\xbd\x93\xfb\ -\xb9N?\xbb\xd0R\x1a\xcb\x8dn v\x9c;O1\ -@\x90\xb1z\x11t\xb3Ya\xb5\x84$1\xfbX+\ -\x93A\xc17\x94\x18{\x1f\xad~~\x91\x92\xeb\xfa\xfd\ -S\xab\xbaV\xd0\xfePd;\xab\xfb4\x80\xefc\xb6\ -\x192%\xee\x03\xda\xba\xaaC/`6\x10,\x07\x18\ -\xab\x87K\xc9\x93\x866LX\xdf\xa7\xc9v\xd3]\xbb\ -#\xf4C\x1cV@I?\xbf\xe2\x9d\xae\xc7\xf2\xb01\ -;r\xdb~\xe1{\xd22\x8f-M5\xb7\xa0\x8d\xa0\ -[v\xdbF[\x09\xeeW\xacM\x0e\xa7\xd4\x06\xf8\xdd\ -\xfc\x06\x7f\x83\xf9\xd6Mc\x80\xd5z\xec:\xef\xab\xc3\ -=\x9b\xee\xd5)\xff\x0b\xb4a\x9fY\xda\x14\x8b@\xa9\ -g0\x09\xed\xd2>}\x9f\x18\x16\xf7C\xe1{5V\ -;\xe2\x8fc\x89\xd97\xe41\x1b)e\x9efb5\ -q\xbb\xe4\x87\x02|\x86g\x0d`\xcf\xaf'\xe2\xb6T\ -\x06?\xbfe\x8d\x1b\xc8\xcb\xe0\xf3\x98m\x0e\xde\xa7\xcc\ -\xd3\xe0\xcf\xcf\xa76\xfe\x16\x9d\x1c\xff}\xe2\xd8w\xa1\ -\xdd\x15\xda\xd5\xe1\xfbE\x891\xa1\xd4(\xc3\xecq\x7f\ -z\x9f!\xcd\xcf\x9c\x9b\xe7_\xa7k\xadQ\xea\x81C\ -\xb9\x01\xe5i\xed\xe7`L\xdeB\x9f=\xa4\x09\xc3Y\ -uJ\x9d4j9\xd5\x8d\xef\x9f\xfb\xe6\xa6\xd8\x1f\xca\ -\xfb\xd7I\x9f\x1bV\x8f\x5c\x1a\xad\xcf\xfeBy\xd7\x92\ -^b\xe3\x81\xc9\x5c s\x1a\xaa\xcb1?\x0cVK\ -\x06;\xa3A\x8a\x22\x07\x9a\xe4\xcf\x14\x8a\xfc)n-\ -\x7fh\xf2A\x03YU~\xd5\x00\xab\xe5\x0d\xe5\x08\xcc\ -\xa3\x87\xe3\xd7\xb2\xc1z\x1aX\x0d\xe1\x10W\x94\xf8\xf1\ -!\x90\xbfn\x1d\xca\xdf\xe2\xae\xc8_\xd0\xb7\x82\xdd#\ -)\xfe7\xd0\xaf\xb6r\x1f\xd6a\x87\xf7\x84\xcf\xa6\xda\ -h_\x7fZ\xaf\x81\x80\xe70n\xa2\xe4\xf4\x22\xec\x1a\ -\xca[\xe8\x83\xa9\xcf\x89\xc7b\x83\xca\xaf\xef\xc6\xee\x0b\ -\xfd\x0aX]\x94\x86n\xaf\xbf\xcd\xfa\x07\xceG\xf8\xac\ -\xda\xc4\x97\x18?0?+\xb8_\x13p~\x16\xdb*\ -\xd1K\xffh\xa5\x7f\xc1\xda\x19\xe5^&\x98}\x1f\xca\ -\xbf&\x1f[\xdb\x1ay\xf4\xd3\xbf\xda\xeb\x9f-\xebm\ -0\x0e\x8d\xfago\xeb\xdf\xbd\xbd\xff`\x86\xfdW\xaf\ -\xed?{{\xffm\xd3\x8b\xd6\x18\xf8lh\xa7\x80f\ -\x091\xa4\x85\x9d\x82\xb3\xfdgq\x9a\xc7\x00\xb8\xe1}\ -\xfa\x9d\xed\xae\x01\xff\x8c\x1b\xfe\x9d\xc6\xef\x06ua\x0c\ -\x83Z<\x97\xca\xdf)g/4\xe9,\xd4\xef\xd1H\ -s\xeb\xdf\xc3so\x80|\x85\xb1\x7f%\x0e\x9a\xcd:\ -}{9\xd6\xbe\xbf0\xbe\xfa\xec2\xac\xde l\x18\x9b\ -\x02\xfd\x96\xd8\xd9\x22\x8dgl\x18R|\xe1\xf0\xb9\x94\ -x\xd56\xf47\xeeA#\xaea\xeb\x1e<\xaf\x07>\ -\xa7\xd4U\x0b\xd3\xb9\xe0zU\x1d\xe1\xf9\xbb\xf1kh\ -\xf4\xe1\xc28\x22\xe2\xbb\xfb\xd8\x99$\xf0\xfb\xb5\x89/\ -(\xfe\xd4\xdf\xf3\xaf\x00\x8b\xedv\xd3\xc1\xc6\x16\xae\xb5\ -X\x1c4\xb4\x05\x00]\x03\xc6v\xfca\xfeP\xe6/\ -\xd4\xd9\xb0\xfd[\x8b\xf5to\xa7\xe6ow\xdf\x9fn\ -\xbd\xbf\xddm\xff\x07:J\xaf{\ +\x01\x9f\xd1x\x9c\xed\x1d\x09\x5c\x8c\xdb\xf7\x9bR!\xd4\ +\xc3#)e\x8dl\xf9{\xc8Z\xf6}_\x1f\x22<\ +\xbb\x92\x84,\xd5\x90%d\x97\x9d,E$B\xa4\x92\ +\x22\x22k\x12\xd9[H4\xed{\xd3\xcc|\xff{\xee\ +\xf7MM\xd3LM3\x93&\xba\xbf\xdfy\xbdI\xf3\ +}\xf7\xdes\xee\xd9\xef9\x04\xc1 T\x09\x18\x0cB\ +\x9f\x98\xa7M\x10\x0b\xd0\xff3\x99\xd4\xe7\xb65\x19D\ +\x18\xfa\x9d\xa9)\xfdy\x00A\xa4\xb7`\x10FF\xd4\ +g\x8f\xd6\x04\xd1d>\xfa?}\xfa\xb3&A\xdc\xdb\ +\xc1 45\xa9\xcf\x8bj\x10\xc4\xe2\xe3\x0cb\xfb\xb8\ +1C\xeb\xd6\xd6\xae\x8d\x1e]w\xf8\xb0A\x13\xe0_\ +\x01j\xc2\xabgZ{\xa0wj\x1b\x0e\x1f\xd4\x7f\x92\ +\xed\x87\xe4\xcf\xa3\x975\x88\xfa\xa2\x9a\xf6\xa8\xd5\x18+\ +\xa2\xc1\x86\xe8e\xdeM\xe3w\x5c>1\xe4\xb9\xd9?\ +\xb5/\xd5\xb09p\xd0\xccP\xe5\xcc\xe5'\xdb\xa6]\ +\xf3h9\xcd\xac\xd6\xf8I?\xaf\x98\xaeh|\xd9P\ +\xc9\xac\xf9 \xb7\x05C\x0f\x8c\xaeU\xbf\xc5\x88\xc6\xcf\ +'\xef\x98\xda\xff\xf9\xf1\xd0eg{t\xeb\xb4\xeb\x80\ +\xf1\xf6\xe8\x87\xcf\xb4\xe6-{\xa4\xd75\xd3\xa4k&\ +\xa7\xbb\xe9\xf4\xb4\xf4w\x09G3\xbb%\xe4Y\xfb\xe9\ +8=>~\x8c\x11\x9c\x909\xfcp\xc8\x94\x93\xd7n\ +\xae&\xec\x08\xe7\xd9\x8c\x98\x06\xdc\x97\xdf\xec\xdb\x11\xe4\ +\x1b\x87\xa0\xd3\xe4\xda9\xad\xbf\xec\xdb\xdf\xe9\xc8I\xf4\ +\xf7\x1b\xfcf\xbex\xc3\x08o2\x83\xe58%\xcc\xe4\ +\x95C\xb2r\xfb^.\x8765\xaa\xa9\x1e\xae4\x17\ +\xfd\xdd5}\xb3\xb6\x9e\xb7\xffG\xf4\x9a\xb7\xd4\x8c\xd8\ +\xca8\xa1j\xa5z9)\xf2HC\xe6\x05b\x01c\ +\x94\xd7\x83\xde\x87\xdc\xd5\xffk\x14U\x83i\x7f\x98\xac\ +=\xfda\xea\xfeN\x1a~\x1f\xfe\xc7lG\x98\x18\x07\ +\xae\x8a\xf9A\x84\xa7f5>=\x99\xd3\xe9g\xc6^\ +\x83\x8e\xcay\x8c`e\x83[ut\x9a\x10hns\ +\x88\xd9\x03\x97~ip1\xae\x1d3uJ\x9c\xee\xc4\ +\x08\x95\x8b\x87\xe6)\x99\xea\xdd\x0c\x22\xbc\xe2\xd6\x0c?\ +\xbc\xcdc\xd3|\x22\xba\xff\xd2\xbf\xdd\xdc\xd4'n\xcf\ +`\x9c\x9e\xca\xd1\xf2\xd9\x11\xdd\xc9\xac\xd9Ym\xb7\xd3\ +*\xfdg\xbe\xd1\x1f\xd7\x0b&\x16\xcd\xfc\xdb\xf5Dg\ +\x15\xaf\xa5i\xbbl\xfa\xefh\xd7\x84X\xa7\xe7\xa0\xb3\ +,\xf1@@\x07e\xad\x89\x8b\x9dM/\x12\xe1C\xb3\ +S\x1d\x1d\xd7\x7f\xb3\x1c\xcb\x989i\xce[\xfd\xc3O\ +c\x996\xcd\x99\x1bV\x92k\xbc3\x0e\x14\x9cd\xa4\ +\x05\xf0N66\xf8\xa0\x14\xde8\xed\xd3\xa3\x80\xec\xbc\ +A\xb3\xae\x13^\xdd\xbc\xf5\xd3\xe3\xcevd\xbex7\ +\xbdN\xf0K\xf3x\x83\xd4D\xe5\xb4\x1b\xb3\x1a\x0fz\ +\xf7E)\xfcBN\xca\xc0\x19\xb7F\x0e:\xd6n\x91\ +\xd7\xc6\x83f\x9d\x17\xadW7]\x9eA\xd6\x99b\x7f\ +vnH\xf8te\xbb\xd5w=,\x18\xa7\xc7\xa6^\ +\x0b\x1b;i\x96\x9e\x8f\xa6\xd6-\x96\x8aE-\xf4\xb0\ +\x0f*\x84\xc9\xa6\x9d\xeb\x9cr^\xd7\x8b\x19\x1a\xf8b\ +\xda\x8b\x0c\xe2\x87\xb3\xd7\xe7mo\x13\x0c\xce&~\x8e\ +b\xedv\xefP\x97\xe0\xd6\x9d\xe9\xcd\x1e\xad\xe9\x17\xa9\ +\xe290\xf8:\x81^\xfe\xbf-\xee\xcd\xcd\x87\x13\xde\ +\x19\x0d\x8e>\xb9\xaei\xd4\x97\x98\x96\xbf\xbd\xc3\x9c\xe1\ +5\x8fh\x13\x979\x83\x0e\xfd\xafo\x8dQ\xea\x84\xdd\ +\x87\xb8M\xcf\x1bo~\xbc\xb4\xd3\x91\x9e\x83\x17=\x99\ +`n\x17{\x7f\xcf\xf5\xa8-\x8f'\xde]f\xb3\x9c\ +11G\xb9\xed\xb4/\x1f;\xd9\x0dv\xea\xd3J%\ +&s\xc8\xa9V\xcb\x8c\x89,\xb5\xf5\xf6*kR\x9b\ +\xef\xf7&:\xd6\x98\x90\xcd\x99\xdf\xb5\x99\xaa\xbf\xc6\x14\ +\xfb\x16\xed\xec\x16Y\xb9X|f\x9c6\x09\xe9\xd3\xc1\ +N\xc5\xa9\x8f\xe5\xed}\xdc\x1a\x89\x89J\xde\xd1c\xdd\ +\x0e6L\xd4\xd2X\xe4\xa6n\xa5\x87^\x7fi\xd1\xd0\ +\x93f\x1b\x8f)\x9bF?\xf3\x1e\xcb\xd6m\xf0j=\ +;\xf1\xfd\x22\x9f\xf3\x9d\x98wW\xe5\xa6F\xa4\x1et\ +z\xc2^xs\xc6\xee\xa8\xd7*1+\xb9\x83\x96x\ +^\xdc=E\x97`/\x09\xea\x1a\x91\xba\xdb\xc9\xb0\xd7\ +\xc4\xa7\x87\xe6\xde\xf4!B\xdd\xa3\x9b\xfc5\xd9=\xd1\ +\x7f\xfad\xe2\x87\xa3\xbaYD\xea\x0e\xa7K\x11\x93\x93\ +\x991w\x1d\x99l\xeb\xe8\xe8{\x8bMZ\x18\x0c\xef\ +\xdfTs\x94K\x81\xde%\xdd\x1a\xce\x1b]\x5c\x1f/\ +\xfep\xe6\xcd\xe7\x1d\x86\xb7\x08\xdb\x5c\x03k\x9f\x86\x86\ +>J\x97\x163f\x12\xedM\xf6M\xbc9\xcfu\xf4\ +\xbac\xb9\xf9jw\x0fu\xb0k\x15\xb9y%\xd7k\ +\x15\xc95y\x9bk\xd43\xa8]~#'7\xbb\xba\ +\xfa\xb3RL\x0e\xd5%\xec>\x9e\xe9\x14\xb6\x7f\xe8\x00\ +5\xcd\xa6K<\x0bR_v\x9c\xdf\x84\xf8\xb6\xca\x0c\ +\xd1\xa9z\xfb\xf4\xddj\xee\xbcy\xee\x9d\x82\xa7F\xb4\ +6h1\xf1\xa0\xd7\xd3\xd6\xca\xa6\xb33\x1d\x86\xf8G\ +\x0e\xfaoG\xd7u'y\xa6~\x8d\x82U~\xd6o\ +\xb0\xefb\xa7\x83\xec9~\x84y\xa6\xebc\xb3\x11o\ +\x7fj(\x99F'_\x18\xe9\xef3\xe8\xbfK\x9d\x02\ +-\x83.LRg\xaeqh\xb1\x86}\xda\xf2\xf01\ +U\x9dC\x9dZ\x87\xed\xd8z\xfc\xaf:y\x9a\xb5\xba\ +\xc5\x9e\x0b\x0fX?(\xebvN\xe6\x84\xfaa\xban\ +=V\xe6\x9b\x04\x8dJ\xd1?\x196g\x95\x9d\x86'\ ++ b\xf9\xf6\x7fB\x88\xd0\xda\x0ei\xb3\x88\x1f-\ +\xb7\x98E\xb8\xb7Z_\xc7\xa6\xe0\xaf\xba=\xbe\x9e\x9b\ +\xa7\xa6Y\xb7GB{\xfb@rJ\xeb!\x9a\xcf\x02\ +\x9f\xc4\xed\xc9\xee\xdbj\x7f\xefa\xad\xd6\xb8u\xde\x18\ +e\xb3*\xf4\xad\x7f\xed5u\x0c-\x19uF\x1e\xde\ +{\xf9\xafZ\xdd\xe6\x9c\xfe\xb8\xe7\x80\x01\xc7i\xebq\ +\xcd\xba\xa9\xbe5#\x93\xa2L\x98\xc3~\x04\xab\xf5\xef\ +\xe2{\xf0\xe1'\xcf\x0e\xb3\xda\xac1\xf2=8\xb8\xd7\ +\x01\xcf\xcc\xbf=F\x84\xb4\x9d\xd6d\x1a\xa7\xa3qG\ +\xb3^\xff\x8b2_\xaenYOoH\xab\xff\xd8\xc3\ +\xd3\xcek\xfd\xc8\x09j\x5c{\xe5\xc8\x7fG\x91\xb7v\ +\xf9\xb9w\x98Q\xf3\xad\xf1\xae\xda\x0c\xab!\xfe6\xb3\ +\xfe\xfb'h\xfd\xee\xa5k\xbe\xdb\xb4\xd9\xd6\xbe\xc1\xe2\ +\x89\xbc\xe4SV7\xae\xbb\x04\x1d\xbco:\xcd,:\ +\xe4Y\x5c\xa3f\xe4\xf5\x87FF/\x8f\xde7\xf9\xa6\ +l\xea\xdab\xd1\xe1N'?\x0e\x1e0\x1aM*\xbc\ +\x81ehp-\x870\xfb\x1e\xc7\xef\x1d\x1b\xf6\x83\xe8\ +t\xb2^L\xc4O\x02mL\x1f\xde|\xf5\x03\x99\xc7\ +\xef\xf4\xdb\xb58\xd5\xd9+\xbb\xc96\xa7\x86\xb7\x08\xb6\ +\xdb\xc8\xef\xdd\xef15^Yu\x1d\xb3\xba\xe0\xaf\xbe\ +W>\xc7{\xfa5m\xda2M\xa7\xaf\x83\x81\xb6e\ +\xe8s\xab\xaeh\xca\xf7\xbfq\xe0\x95\xdb\xfd\xdc/\xdb\ +\x5c;qI\xe3\xc3\xbd\xb3\xa9*\xc1\x06\x11\xee\xd6\xbd\ +\xe6M\x0aL\xb0]\x19\xa2l\xf1\xcaH\xf9\xf4\x89}\ +\xbc)\xceY\xbes-\xdb\x1d_\xfe\xf7\xb8\xd1\x19\xab\ +\xcc~6\x0aNd7\x8b{\xdd\x94\xa2\x9e\x0e\xedg\ +\xac\x08\xb2\x0bZ\xec=={\xf3\xf1.7n[\xfc\ +\x9b\xff9X\xab\xb3V\xd3%!yY\xaa\x8e\xa7\xd7\ +\xd9\x85.o\xa8\xc14s&'9\x10G\xdcoG\ +[\xae\x9e\xdfy~\xcc\xe6\xec\xab\xb1\x8d\xf6\x853>\ +5\xe9\xfbL\xdfc\x96k\xfa\xf1\x05\xfaA\xb9\xb3\x03\ +l.N\x1a\xf7i\xb9\xde\x8f\x9c\xa8@\xe3\xf3\x7f\xad\ +\xde\xec\xfcQ\xab\xbfU\xca\xeb)\xcec\x97[9\x9f\ +\xbe\xb3:\xf0\xc5\x05\xe7F\xa6\xae6\xdb\xdd\x1a\xe6|\ +T\xf3\xe7\x8e\x18}tp\xc8\xf3\x13\xbcK!\xa6\xbe\ +z{\xfc\xf7\xcc\xb5\xb6W\xff\xfa\xba\xa9\xce\xc8v^\ +\x1a\xc1\x13[\x12\x1f\xdcG\xb6\x8cuxD\x0e\xf5\xaf\ +\xf9w\x17B\xbbkD\xf4\x8e{_\x1a\xa1\xa7\x99\xcf\ +\xed\xde\xd2L\xe7\x8d\x8fr\xea)\xd5\xa4\x03\x9dvm\ +\x9e\xac\xaeV\xeby\xf8\xca;\xc3\xfdk\xeen\x5c'\ +\xcfje\x9ef3\xb3\xbeK\x0a2\x16>\xccf\xdc\ +A+\xea\xbc\x5c\xbe\ +\xd9jaV\xcd!l\x8d\x16\xf5\xaf\x9f\xef\x9c\xb4Y\ +\xc3\xd5\x8bXZC\xb9_\x1f\xffu\x91\x99fJ3\ +\xee\x9aeg\xcdoyF9\xbf\xb5+\xb3\xbb\xf9\xd4\ +w\xf3\x8d<\xb7\xec\x1d\x96\xd2\xd6,l\xddn\xcf\x19\ +\xdd{\xdcF\x13\xeb\xfb\xe8\xf8\x8a\xa5\x89^\x06\x015\ +\x0e9\x04\xb48\xd3f\xe2\xf7\xb7\xedU\xee?\x8b\x9b\ +\xf2YoV\x0b?O\x1b\xc2J?|]K\xae\x17\ +\x22L+^J\xd3\x97[\x98\xf1f\x84\xe7\xdd\xe0!\ +\xfa\x08\x95\xce\x0fI\x8e\xf2\xd8+\xbeQ\xddN\xe7f\ +\xdc\xfc\xd0\x95\x810\xbc\xcf\xc5iV\x03t0\xc3\xb2\ +\xe7\x8d\x88\xaes\xd9|-1\xe95\xd9\xc3\xf1\xda\xbe\ +!^\xc6\xf5L\xb7\x0e\xfb\xc1Lx\xab>\xad\x19\xb1\ +z\xcc\x8d7\x13\x8fN\x9e\xadN\x10\x06\xef\xebz\xef\ +\xeab\xfc\xc3E\xd3\xfb\x1fB\xfb\xfd\xfc[Sv\xf5\ +\xe9\xe5\xb9\xae\xbd\xfe\x93\x8d\xa63\xd6\xb6\xab\x81\x84B\ +o5\xefYw.\x05\xd5\xf7\xfcR;\xed~\xe3\xf1\ +\xddf\xd5b\x8cT\x0e\xbe\xc23\x09\x7fg\xc6\x89_\ +z\xd3\xb5\xd1\xeb[\x91\xdfW2Vg\x0e\xa8y\xfb\ +\xac\xca\xde\x17\xdf\xb4\xfdwz~Q\x22|:\xde\x7f\ +lo\xa9\xc1x\xaa\x14\xfc\x82\xe7p\xce.o\xb3\xbd\ +\xb3\xcb\x93u3\xd9*6J-k|2\xd6]\xfa\ +/a\xecN.\x1f\x17\xf8wp\xc7\xfb\xff\xf9u\xab\ +\xd1!\xc0\x0b\xfd\xc2\x95\xec1k\xbe\xcb\xacU\x17\x06\ +4\xf0\x990ud\xfc\xd2\x85\x87\x16\xddn\xb2f\xc8\ +\x9e#\xab\x17\xed\xac\xdd\xa1`JO\xf4%\xdb\x8b\xb6\ +\xea\xc1\xef<\xc3\x13\x0f=\xb9\xb4\xc2*\x81u\x92S\ ++\xe8f\x8d\xcb\xe3\xc7|\xfd/\xd6z\x9b\xab\xf1\x1b\ +\xc4/B\xef.\x9a8\xf7\xc6\xf4\xae\xd3\xea\x13\xab-\ +\xdd\x16\xbf\xd5\xb1oq1g\x14a\xfe\xbc\xd1\x9c\xd0\ +\xae\x7f\xbf\x0e\xb28jm\x9c\x10\x10lK\xae\xecR\ +\xcf@\x97}{\xf0\xb2s\x93o<\xd8\xfa!mn\ +;O\x0e{\xdf\x12\xf8\xde\x95\x99]\xcf\x91\x1a\x8bf\ +\xec\xbdz%\xb5\xdd\xcdv\x8b\xae\x9d\xfa:\xfa\xc8?\ +\xf1\x81\x1f\x1c\xbe\x7fIdwj\x93\xb0&s\xc6j\ +\x8d\x9c[\xe6?\xe6Y\xbc\xecg\xb0\xf5^\xee\xe1M\ +\xc6\xeb{\xc7\xe7\xd5\x1d\xea\xb5\x98a|\x99\x5c\x8e\xb8\ +\xa1f-\xbf5\xdb\xf4]vG\x1e\xfb\xf7S\xe8\xb3\ +\xb8\xee\x9b&\xf6\xab\x93s+.\xe3\xa2\xe3\x08\xe7\x95\ +\x93\xdak\xa5\xc5\xbe\xf81\xe7DP\xc1\x10\xff\xb6u\ +\xaf]V7W\xbb\xc1&U\xdf\xf8\xd6\xf4L\xf0\xe7\ +\x9dq\xbd\x92ek?2,`r\xaf\x03!\xdf8\ +m\xda$\xf4\xe8\x12\x9e\xfc(~\x22\xc3\xdd\xc4q\x80\ +\xf2g\xfbG{\xd4\x8e|\xf9rs\xe5?\xef\x92z\ +\xbfWWIq.\xb8\x98\xcd\x1e\xf3b\xf9\x5c\x8e\xc1\ +\xc2\xa4]&\x88\xb1\xbal\xff\xa7_A\xce\xd8\xd6\xd3\ +\xdb\xbdq\xaa{\xb3\xe7\xa5\xf8\x09\xcf\xb2\x08\xef\x1b\xa4\ +\x09\xf7\xb8\xdf\xc2\x84\x8bw\x1b\x11\xe3\xe6\xf5\x0a_\x82\ +\xb8\xbd\xb1Cn\xdd\x17\xd1v_\x9d^~C3\xfa\ +\xd9\x22\xf29\xa7\x11s`\x01\xc9\xcc7\xab\xdd\xdev\ +\xf1No\xc4k_|\x19\x13\xfb62z\xc4\xd5H\ +\xe7\xad\xc7\xbb\xe4\x0c>n\xd3%q\xee\x12\x02\x11\xff\ +G\x7f\xc2\x93\xb5\xfe\xd0?K'\xbb\xd6\xcb\x8a\xeb\xca\ +hp\xa3\xc9\x89\xc0\x93\x13\xfcgX\xfd\x9c\x14\xbcO\ +c\xd9\xd1/J\xb7s\xd2:\xb6I\xf0\xed`\xb2\xea\ +\xd1\xa5\xce\x8e\x8936\xfb-\x1e?\xba\xa3\xb3e\xff\ +\x96\x8f\x1e\xce\x083\xde\xf8x\xf1(\x8b\xb1\x8dv\xff\ +Pz\xdb\x89g\xb4\x94\x17t$\xd1\x8c0Ug\xb8\ +\xa4n\x9d\xa2M.V~\xbd\xf3\xe3\x88+O\xf5\x92\ +,V|\xeaY\xef\xd0\x95u\xaf\x1a9,\x8b{2\ +H;\x90\xe4\x05mp\xb0\x5c\xb0\x22 \xb6\xdf\xc9\xc9\ +\x8d\xdf\xd7\xd2;\x15\xe5[sd\xab5i\xef}Y\ +#\xb6f\x9f|\xd52b\xb0\xc7v\xaf\x96#\xb6\x7f\ +\xabW?\xec$g\xb8\xd7+\xef\xd5\xadr\xc3\x1a\xf0\ +\x8e}\xb7\xf7J$\x9b~?\x14io\xd4\xd6v\x9c\ +Wo\xd7\xcdHrd\xae\x89\x8ey\xf1\xe3/\xf7\xb7\ +u\x17\x05L\xd6{\xe6\xbbi\xb2A/\xb7\x99:5\ +\xd1~\x9fH\xca\x99\x17\xb39h\xae\x8a\x0f\xbb\xd3I\ +t\xa4\x1b\x10[4\x12\xee\x84\xbf\x1c7i\xed\xa0O\ +c~\x8e\xaa7\xb5M\x04\xbb\xd6\x04\xaf\x88\x87aM\ +\x9b.\x99GD\xce\xbb\xf2\x83\xb1\xd3z\xa3\xf9d2\ +Z\xff\xe6\xe0#]\xea\xf6\xcc5\xdcy8L\xb7\xd5\ +\xfe\x80\xdd\xd3\x82[&\x7f\x08\x09\x22L\x8fjZM\ +h\x8f\x94\xc6\xb6S\xb4j\x866\xfc\x1a\xd6\xe0\xad\xc6\ +\xf5wo|\xff\x193\xd8>\x7f]\x8fh\xed\xb3l\ +\x8fFJc\x16\xab\xb4\xd5%\xb6D\x1c\x9d\xc9c\xd4\ +\xf7l\xf2|Y\xd7\xa1G\x1b\xb0u\xb9\xf1K\xbd\xce\ +\xf6\x9e\xe3\xfdj\xf1\xa4\xda\x8c\x0b>J:\x0d\x88\xd5\ +K\xff\xd3arW\xf7\xf4=\xff\xa5\x85\xb6\xe5\x84\xb9\ +a>\x0d\xa632\xa6\x0e\x1f\xff\xc6\xf2\xafP\xfb\x80\ +\xe9\xce\xef7&\xf4\x18\x10\xc7\xaa\xd1\xe2\x8c\xf2\x93\xab\ +\xea\xe6\xc4\xc1\xf1\x96;\x8dr>\xbeY\xb89\xf8\x00\ ++9b\xf0=D`\xaa\xcc\x06\xad\xf7\x07$\xf9\xf6\ +\x9e\xd6\xc2\xcfq\xc6D\x87\x9d\x9d_\xea&Y\xf4o\ +\x15\xe9\xb6\xccs/c\x5c(\xa2\x7fD\x0c\xc3\x1b;\ +j\x1bu9\x1e\xd6y\xe0a\x9b+A\x93\xdf\x8f\x1e\ +\x18\xf9\x99\xd8\xf3\xb8FT\xb3\xbb\x7f\xeb1u\xf3\xb3\ +Ng\xcdo\xbb\x93az\xe5\x96\xff\xdd\x9d+b\x0d\ +j\xee\xbag\x8a\x04\xc4\xe3\xec6\x0b\xba\xc6}\xed\x91\ +\xd0\xd0B\xc5\x88x\xdc\xc0m\x06\xc3\xb0\xaf\xf7$'\ +\x86\xe9)\xa4\x02<\xfc\xc69\xf4\x8e\xd5\xd4\xa0\xcd\xc5\ +\xad\xe8\x17\xcd?\xd76\xce\xbd\xc8\xa9\x1f?\xb7\x0e\x91\ +Q\xc7\xc2Q\xdbo\x0as\x87\xf2F\xf4\xa1a\xd8\x14\ +\xe6\x8aE\x97Fh\x11\xdf\xda!\xfa=\xdbU\xe9\xd2\ +M\xa5\xc8\xa6.\x9ai?\x12\xc7\xef\x8b\xea\xb6\xda\xa7\ +\xf9\xfa\xcf\xeb\x0f\xfd\xe5\xe9d06\xcfe\xdd\x80\xf0\ +L\x86y\xd2\xc8\xb6\xfd\xaf\x9b$\xce\x0f=\xefS\x7f\ +\xac\xfa\xa5\xd5W\x1c\x13\x02\x88\xfd\x13\xbb\xe6\x9e\x9a\xef\ +\x16}w\xe1\xf0\xcf[&\xf7\x8e\xba\x97\xc9\x08_9\ +\xb3\xcd\xbe]\xa3\x07\x0e\x0b\xfd:\xfb\xdb\xf4\xd9\x86^\ +ji\x1b\x88avZ]w.\xe3\x8d\x1c\xd1jM\ +\x07\x93\xc0K:./V\xc1o\xb3I\xaf/\xef\x8f\ +\xe4\x18tk\xaa\xd9\xf3\xfc\x0bc\xe6\x05eS\xeb1\ +ow?\x1f}\xf1\xfa\xcf\xe8\xff\xa6\xcf\xd9\xb7\xb7\xd7\ +\x9b1'\xf7\xd7\xdc\xcb\xd6ws\x1d\xd5rWd\xc2\ +\xe9\x88\xe9\xc4X5\xe6$\xd2a\xf7s\x87\xd6n]\ +{^[AL^\xa0\xd4G\x97\x18\xba\xa5\xdf\xe6\xc3\ +:\xdc\xa9['\xb57u9\x8a\xfe\xc84\xeb\xf6\xb3\ +m{\x87]\xf2\xeb\xef\xd6\xd8\x8b\xf73\xf9\xe5\xfd\xd7\ +\x0c\xfd7u&\xae\xda6\xad\xf3\xba\xe6\x91J\xe3\xe6\ +\x1b\xeee\x98w\xadG\x5c\x1a\xbc.d\xdc\xe2S\xf5\ +\x88\xc9\xec9\xad\x1e\xb1<\xbb\xf4\xd5%\xea\x1b+\x9b\ +\x12FV\x9b\xcc\x88\x16\xadm\xfdf\x84\x12\xfa\x19F\ +\xaa\xcc\xe7_l7\x84o\x8f\xbcOx\x1f\x8dR;\ +9\xacc\x1d\xa2\x85\xc6<\xe2\xdd(\x97n1\x83\x08\ +\xe5\xe0\xd5}g\xae7\xad=\x87\xd0\x5c\xab\xa5\xcc\xec\ +3\xf6fH\xaf\xa4\x07\xe8OG|\xec\xbfo\xc8\xdc\ +yZ\x17\xedj\xeeX\xdb\x8bh\xd5\xac\xc6\xf8\xc5\xbd\ +\xdb\xd6\xdf\xd0b\xd2\xbeG{\xfbr-\x0e\x1e9\x90\ +k\xbdr\xe2\xe0Y\x0e\xfa\xcf\xd0\x147v$n\x0e\ +S\xc9\xd0KRz|`\xba\xcb\x85\x88\xf3{g\xe5\ +\xee\x1b\xa7\xefy\xbd\x9d\xf3\x05e\xfd\xc0v7:E\ +\xb3\x9e\x8fq\x19\xd6\xf7\x90\xe7\x93!\xad\xd0\xb4/>\ +\xef\xb9~sN\x0b7\x8d\xe17C'\xadm\xf3\x00\ +=\xc3\xe5~\x92\x92G\xffK\xad\xcf?i\xa8\x1f\x93\ +Nh\xf6\xd66\xb4a\xde`\xd4\xde[{\xeed6\ +\xc9|\xa4L\xec\x1d\xf9\x15\xe9\x19?}S\xd6\x0e\xd4\ +{\xbaA\xd5\xd0B=!\x9f\xd8\x9b\xb8\xf3\xe4\xa4\xff\ +\xc6\x1a_\xad\xad\xbaanGb\xf2y\x82\xa1J\xd4\ +\xbf:_\xc9\xdc\xdbw\xcb\x1eUu\xed$\x8b\xbf\xd7\ +[LVE\xbff\xbe\xbb\xa7\xc9L:\x1c\xceza\ +g\xb82\xf9\xf5Q\x1de\xd3\xd6\xfb\xf5B\xe3'\xaa\ +\xb9G\xdf5@\x8a\xe1)W^W\xdb~D\xab\x06\ +=\xb5-\xbf\x9f\xd5g\xec\xbf\xe7\x19A\xe8\x07\x13\x84\ +~@g\x0e:&?}S\xdb!-\xb0%\xd7\x9a\ +\xa1\xff>\xbe\xc3\x16\x06\x11\xb3\xc5\xdcpO\x96\xaf\xf5\ +h\xe6.\xd5\xf53\x1e\xaa\xce~}\xf4\x84\xf5\xb6\xbd\ +\xc8\xea\x7f\xd6\xc1r\xe7]\xde\xbbq\xc7\xb3Z\xa7\xd5\ +\x09[\xea5=^M\x8bX\xbb\xbfy\xf0\x14f_\ +\xa4M\xec\xe8\xb7\xbb/\xf7x\xd2\xae\xf3\x0dU\x99c\ +\xd8!s\xda\x07O\xbf\xc7\x1c\xcb\xd1~\xd9\xc1r\xf8\ +\xec\xad\x1aF\x84M\x84\xde\x8ba\x0e\xed\x17\xd6\xe8`\ +\x93oc\x9a\x95br\xe3\xb6\xd7\xb3\x96\xc1[\xb4\x08\ +\x7f\x9b6\x06\xfd\x1a!]jo\xd6|+\xf7a\x1e\ +\xb5\xba\xc4\x06\x04\xd9\xa8\xdf\xd0Q5e\xfc\xd4X\x8c\ +\x14\xef\x91\x17zv\x5ct\xab\xf7\xb9m\xee;\xf5\xbf\ +\xdc\xef\xb0\x8d1\xcet\x8b\xc6\xe2e\x1e\xbb\x947\xb5\ +\xf1\xb7\xe9\xa5\x7f\x97\xdct\xe9\xb9\x03\x93\x91v\x0a1\ +\xc0\xb0\xb5\xef5k\xaan\x18\xf1\xe9\xda'\xfd\xfaK\ +\xea\xeb\xe5)3\x1f\x05-\x1b\xd1\xf8L\x07U\xd3\xbf\ +\xfe\xeewy\xa3\xa5I\xe8\xdd\xbdd\x5c\xaf\xcf\xb9\x87\ +w;\x99\xac\xf0\xf3\xbfk\xd0\xef\xd6\xa4Z\xca\xa7\x1f\ +\x7f\x9a0e\xbbZ\xf7\xb9\x03-\xe2FO5\x08\xe8\ +\xban\xd0\xf6\x7f]\x86\xabX\xb6u1\x22F\xdc\xce\ +\x1d\xb5K#\xb9`d\xff\xd6w\x1f\x8c\xe9\x1a\xe1\xa0\ +\xd9\x92\xbb\xa4\xcf\x12\xbb\xe5HC\xdc\xbcC\x8bh=\ +\xea\xc5r\xc3.\x8b\xe69\xab\x0d\xeb1\xef\xe4\x7f\xde\ +\xbe\x83\x8fv9\x18\x96\xdf\xb1\xf5\xdd\xa1\xe8\xdbO\x8e\ +\xd8\x9e\x1fb=4\xe4\xb9\xadMv\xdb\x81\xa7k#\ +\xbd;\xc5\xb9\xd9\xbeNC\xbe\xe4em\xb5\xf9+\xe7\ +!\xd1\xf6\xeb\x97eC\x8ef-\xa8\x994\xf6\xe6\xd2\ +\x7fz\x06\xb9G\xa6\x04\xc5\xa3/\x0e[h\xfd\xc4G\ +\x89\xc8\xbe\x1bc8>\xfd\x83ql\x0a\xdaJ\xc3\x09\ +\x17\xff&<\xf2\x07\x1f>\x1f\xd7m\xc5\x93\xcfz\xe6\ +\xc4\xa4\x83/\xbe\x1d\x1ci\xfdU\xfd\xe5{\x86\x85\x0f\ +\x19\x96\xbd\xda\xdd\xd0$\xe6D\x80I0\xf1.\xea\xc1\ +\xb2!\x17\xcf\x9d`\xaei\x1e\x13\xbfJi\xdel\x9f\ +v]?\xccn\xdb/0\xf6\x08\xfc\xb3\xc7\x13\xaf\x8b\ +\xbe\x11\x83\xf3L\x8dbB\xcey\x0e\xed\xeb;7\xdc\ +6:bl\xae\xcb~\xf4D\xd7\xdc\xce\xa1\x84\x07R\ +\xecn\xed\xff\xd8\x93\xf1a\xc7\xbd\xa1S\xf7\xac\x8bc\ +\x9cE[x*\xbaf\xbb\x8f\x19~\xdd\xb2}'\xcc\ +\x1b\xf2\x06\xcd\xe0\xe7\x87\x10\xdf'\xf6\xb7\x91\x98\xe9\xe3\ +\xf7%\xe5\xc3\xd0\xed\x8e\x0c=dCL\xb0\xf4\xbe\x1a\ +\xfeiB\xa3\x03\x99K.\xeb{\xa9(\x9f\x1e\x81\x98\ +\xdd\x88\xd1\xea\x8d\xda_ne\xb9\xa4\xaes\x96\xde\x83\ +\xcc\xa6\xcd\xe3C\x92?\x0c\xcc\xf6\xdf\x8e\x1e\x14i\xce\ +\xb9P\xb7u\xb3\x86\x97\x06-m\xa3\x17rr|\xbe\ +A\xac\xb6\x9d\xde\x9e+\xcdV=\x98\xb0\xe8B]\xce\ +\xdfs\x89\xc8\xc7\xed\x86\x9b %\x1d\xc9\x01\xad\x82\xb3\ +\xa3\xf3kOz\xec\x917\xe6h<\xd2W\x8cx\x07\ +V\x9e\xe7\xee\xe8B,98,%\x06\x09\xfb\x88\x87\ +\x1e#\x8cb\x06.\xf3\xa8\xbb\x93\xccWC\x9b>\xde\ +\x7f)\xd2\x9f\xe3\xaa*\xf7\xcb\xfa\xe1\xd0\xc3\xfar-\xa4T\x0f\ +\x1a0z\xf7\xb2\xb7\xa9\x8f~\xe4\x8e\x5c\xe3\xfa\xec>\ +\xb1(\x1c)acN\xa5\xb5\xbf\xa1\xe2r\xd4\xd9\xb4\ +\xad\x0e\xcb\xa9\xee\xf0\xc3#\xde\xbal\xf7\xf9Z\xc0\xdd\ +\xf2\xa9\xf1+\xa5+}9#\xdf\xbb\xdf\xff\xef\xd4R\ +\x8dh\xff\x94\x95\x0b\x88#\xdf\x9e\x0c\x9a\x9e?x\xc0\ +\xa4 D\xa8\xedUagG\xcd\xadS\xfb\x7f5>\ +-\xb4\xb6\x7f\xfaj\xa7\x9d\xe1\x1c\xe2\xc8c\x0b\xe5A\ +n\x93yK5N\x06\xbe\xbbP_\x95\x196%j\ +\xcd\x0a\xa4\xf7oe\xaa\xdf\x0e\xccP\xaeC\x8cDS\ +\xae\xc3\xd8\x969 ^\x0d\xd91#\xfc\xf5-\x0f \ +\xec$%7|TC\xe7qK\x17\x13\x95;\xeaL\ +r\xc4\xe1\xd0]\xce)\xc4\xc0\xe0\x9e}&\xe43\x02\ +_\xdcP7\xef2ou\xb0E\xe6#K\x9b\xe4\xd8\ +\x19W'\xf1\xfa\xb4$\x1a\xb9\xa9\xc7-\x18R\x13\x1d\ +\xb6\xe1\x8d\xb7\x8c\x08_\xb0<\xe2\xca\x89\x84\xab\xe6\x8c\ +\x18-\xa6\x17\x9a\xfc\xd3.Mw\xd8>D\xa6'R\ +\x05\x1d\x8f>\xb4\xf8y\xf6]G\x15o\x15\xf3\x80\xb0\ +\xfc\xd1H\xa1\x8b\xab\xb3Q\x09\x99M+rr\x5cg\ +\xe9\x9e\xfb\x97 fw:\xec\xf82a\xe0\xc2e\xc9\ +c\x89\xe4\x08\xf7\x8c\x1d\xe6\xc1\xdbR;\xab\xe8\xde\xbc\ +\xe8t\xfc\xc1\x85\x1e3\x03\xfc\xaf\xd6\xbe\xe8b\xfb\xe8\ +\xda\xccN\xb9_\xf4c6\xd9\x5c\xbc\xf3@\xab\xb1\xd5\ +\x93\x9c\xb9\x96\xd7\xfc\xefn\xbb\xe1;q\xd6\x15\x8ei\ +\x7f\xb3\x85D\xcd\xe3G\x9e[\x052G\xfb,M~\ +\xdf\xc6\xe1\x8c\xca\xd7\x1e[\x18\xc17\x89\x01\xce\x83\x07\ +\xaeP\xd35\xfc\xd9*\xd4\xd4\xb5\xc1\xd8\x94k\xc3\xee\ +^\xb9\xa1rzo\xbakw\xa4\xe09X?gn\ +GV\xb2\xf1\xfa\xe3Q>\x8f7\xa5\x1f\xe7\xe8\xeb\x13\ +\xf5j\x18\x22\xf6\xa1\xd2c\x9bF\x80\xf5\x8c\x8e\xbd\x9f\ +u\x9c\xbf\x02-\x7fj\xf2\x97y\xfb#G\xdc\xf9\xaa\ +E4\xd4PR\x0a\x9e\xf83b\xdf\x82\x05+\x9a\xba\ +\xda\x05\xecF6\xf4q\xafnGf<\x89\xe3|\xee\ +\x01\x8ap{\x87\xc5c\xd5\xc9q:\xc4\x8d\x08\xf7s\ +\xb9\x8b?+\xa9\xde\xb3\x9f\x12\x95k\x85\x9e\xd3)\xe0\ +K\xf4\xfaUwn\xab\x87\xab\xc4\xac\xf9Y\x7f\xc2b\ +\xc4\xff\x0f\x8fU\x03=\xf7\xef\x8fK\x8f7\xdf\xf7\xe9\ +^\xe0\xc2\xe3\xb3f\x84~tr\xb8\xf2\xa0\xf7!\x86\ +\x97z\xda,\x820E[\xb4\x860\x8bpw\xcb\xd8\ +\xc4\xd0C\xa2\xa1K\xf7\xc8\xef\xef\xbblB{\xfci\ +\xfd\xa1\xd8\xcfw\xbb?m\x9f\xbe\xdbA\xdb\xe1q\xbc\ +uk_\x15W%\xcd\x03\x9d\xda\xb9\x1c~\xf1\xcd\xa8\ +\xe7\xd5\xcf\xd9>\x13W\xd7\xf3\xb7\x9e\x911\x17)\xd9\ +G\xfe\xfdt\x1f\xbd!):j\xe1\xa5<5\xf7\xe9\ +\x84\x1da\xef\xd2\xa9\xdd\x9b6Zis\xdc\xc8\xd3_\ +\xf7|2<\xf4\xe2[V\xcbg\xb7\xee\xe4\x05\xef~\ +\xcf\xbbY?u\x9a\x11o\xdcuu\xc2\xbbi\x8c\xcd\ +\xbeq\x0d\xd0\xcbu\xd60/\x9e\xf1\x8ex\xd9q~\ +\xcf\xbc\xc5\xbbs\x8f\x91\xa6\xf9\x9d\x08\x82C\xc4\x9c\xb7\ +\xd9~\xb1\x1fb0G\x8e4\xf5\x9d\xfd\xbe\x8b#\xb2\x01\xbe\xcf\ +\x9a\x9f>\xb7\xddS\xfb\xc0n\xf6m\xb3C\xac;2\ +;Yu\x0fvq6\xfdQ\xab\x07\xd2\x8dW[]\ +\xf8\xdc\xb56\x9a\xe5\x07\x86\xdb\xd55\xd3b\x93\x0f\xe8\ +q{\x1cLV\xc2\xeb_H,t\x1b9\xfa\xad\xba\ +\xea\x86\xfc\x90\xfe{T785\x8b\x1ap/om\ +_\xe7\x11\x81\x88\x16\xde\x98\xacI\x8f0^t\xd0\xc5\ +\xec\xfekD1\x09\x0fM.G\xaa\x10>mB\x9a\ +\xfc\xdc\x7f \xdby\x87\xdf\xec\xf8\xcb\x0dk^~7\ +\xa2\xe0\xca'\x87\xb6\x1f\x95\x08c\xada\xdf\x94\xf4\xea\ +\xf0\x9a\x8c\xe8\xab>.\xa9\xb7\xe6\xbe\x03\x93/\x87\x14\ +\xa8\x04+[\x85\x8ckqaD\x7f\xcf\x90\xf9\x93\xeb\ +0]l\x87\xd6^\xa6\xa2\xd9\xf3\xcd\x9au\xba\x9d<\ +\x8f\x0d\x8f5~W[\xdf\xfaXC\x1f\xff\x19\xaf\xee\ +2\x82\xa7\x10\xfbZ&\xaf\xcfU\xcb9\xaa\xd2\xf6A\ +\x13F\x8bGm{g\xcc\xf68\xf6/s\xc0<\xec\ +\x0c\x9c\xb0\xca\xcc\xcb_\x85\xb0Z\xa5\xbfO\xf9\xe0\xcc\ +\xec\xa9#\xbc:\x12\xc4\xed\xb6\xdc\x14'\x8f\xb3\xc7\x12\ +\x17\x8df\x9c6\x19e5\x92\xad[\x7f\xfcp\xdf\x1d\ +zy\xef\xc7\x8e6#\xe08L%\xec\x22R\x0f9\ +e4\x9a\x12\xa7K^\x8a\xef\x1c\xf9Z\xc5hf\x8d\ +(U\xcd\x8b-\xb6\xf4\x0aHX\xa5\xc2\xbc\xeb\xa5\x19\ +\xa04h\xc9\xed\xc9\xb7\xdfL\xed\x98\x98\xa8\xa4\xd5k\ +\xdeR\x13%\xadC\xf6\xb9\x83\x9auGH\xcb\xbb~\ +*\xe8\xdd\xb0\xc6\xc1V\x7f\xf5\x99\xb8w\xd8\x13%\xed\ +\x13G\x97\xb6%\xb2\xdc\xb7\xeah\xe8_\x9f];\xf8\ +\xe1\x92C\x05S\x22\xd2\xea\xed[\x17\xed\xc8RvJ\ +Zu\xed\x8b\xbdC\xf3\x89\xb7\x9c\xcc\xb6\xaf\x19\xb5\xb1\ +\xb9\xe9\x7f\x87w\xae;\xb9\xf4\xc8\xe2\xf5\x83\xae>\xe8\ +\xdd\xcc\xdc\xf5\xb9\x9a\x93\xc7\x1c\x8d,\xff\x05\xb5'\xf6\ +\xd8>{\xfb\x9a\x995\x062\x1f\x1bjd\xf5Z\xa0\ +\xfdz\xecv\xf3\x9a\x9a\xff\xde\xcfz\xa21M\x93 \ +\xd0\xef\xff;4\xc9>\xf8,Ah)\x0djs\xed\ +r\x86r#U\x22XS\x7f\xc9^w\x17=oc\ +\x82\xb9\xa1\xa6\xd5\x8e\xbd\x1e\xdd\x1b{]\xe10\xeb2\ +\x88o\x17j\x06\xd5\x98Z\xa7~\xb2\x81J\xcc\xc8\xae\ +\x17\xd8\x86\xd7\x92//\xb2\x8b\xbd\x9f\x10sCyP\ +|\x07\x22\xef\xd3\xe5\xc1\xfd\xdexg{\xdb\xda\xfd\x8c\ +K \x96\xadw\x1cT\xaf\x8b\x8aWF\xe7w\xf9g\ +\xeb\xf6\xad\xf7\xc9\x09{\xbcG\x13\xc9\xb7\x82\x8c\xf7\xf5\ +\x9a\x10~*a\xcfG\xee}\xbf\x99c\x10Q\x9e\xee\ +z\xeb\xadZL\xbf\xdb\x81Zk_+\x8dZ\xe7q\ +\xa1Y\x1c\xc3\xfc\x87\xb3\x97\x9d\xb2W7\xd3o\x7f\xfb\ +\xa2\xfd\x8d\x1e\xd6\xc8\xf4(A\x80\xdf\xbc\xb1\xcf\xdb\xe0\ +&?\xafk\xfe\xfb\xa3\x9dO\x00\xef\xbaM\xff\x83\xb5\ +\x01\x1d\x16Z\xc1\xc6\xb6u\x9a\xf8=\xfd\x1f\xb3K\xef\ +\xa1y\xb9\xe7\xfe#\xb4\xb2,\xefE\xc5\xe87\xef9\ +\xa3\xf6\x87\xfe'\xa3\x875v;\x12\xc1 \x08\x1c\xc3\ +\xe8bT\xf3\xf1\xda\x09\xe1c\xde\xec\xee\xe1oqx\ +\xf2A\x0d\xc2\xb1Y\x8d\xc6\xa7U\xfa\x1f\xb6Y\xebd\ +8m\xeb\x94t=\x8f\xe9\xe9uw\xd7b\x0e`k\ +\xa1\xa7\x987\xbf\x18\xdf\xce\xe7<\xef\xa0M\xffK\xf1\ +\x8f\x09\xa3\x1f7T\x1b\x13\x1bTn\xd5\x1e\x16\xfa?\ +\xa6\xde\xe9\xcf\xc9\x87\xdd\xd5'\xb6\x1f\xaaf\xda\xdc\xae\ +\xff\xab\xa9\xe1\xf5\xda\xfd\xa4\xc2\x16>\xf3\x03L\x86u\ +\x8c\xe9\xb2s\xa9\xd2\xe9\xbd_\xfe\xa7\xa5j\xa5=\xbd\ +\xc7\x193\xffQ\xdf\x16\xb5[\xf4\xbc\xe0\xdfE\x17\xd2\ +:\x8cl\xfc\xb8\xc3\x95:\xcc\xb9\xebX\xa75\x82\x0d\ +\xd8W\xed\xcc\xdb\x8e[\xdcn\xd1\xd3\x95\x8d\x09N\xe3\ +\x0f\xdb\x03\xd4#\xb6\x5c\xdfa\xa0t\xba\xa1\xdd\x943\ +\x1a\xd35\xb3~\x9a\xf0j\xd4Q\xbd:\xac\xbfg\x0c\ +s\xca\xfb\x89\xe1\x86\xebG\xf8\x9b\xaf~\xa2F\xfc\xb5\ +\xf65\xa9\xdc\xc0\xa2\x87\x91\x96\xb6\xdfr\x08[\x0d\x1f\ +\x08\x1c\x11\ +\xe8\x15\xd5\xf2\xbe\ +\xeaA:\x8d\xbbr\xdb\x05\x02\xf8\x07\xbb\xa2Z\xcf\xaf\ +\xba\x10K\xe3Pb\xfc\x0b\xe0^\x8fUm\xdf\xff\x0e\ +\x10\xc2\x12\xf0\x11I\x88{\xf0+\xeeV\x80\xb9W\x83\ +|`7K\xc0W,\x01\xfe\xc1\xb7\x5c\xed\xd3\xfd}\ + \x85\xc6\xa9X\xfc\x0b\xe0\x1ebK~\x0a0\xe7j\ +\x90/\xf8\xb1\x04\xe2\x86\xa5\xe0\x1f\xe2\x8b\xd5\xb6\xde\xef\ +\x07\xf94nK\xe0_\x00\xf7\x90_\x10\xaa\x00s\xad\ +\x86\x8a\x81P\x96@\x0e\x89\x08\xfc\xcfb\xfdI\xb9\x1b\ +\xcbu\x11\xe8\x90,\xcb\xa64h\x93,\x0bm\xea\xff\ +\xad\xf5\x114'Y+\xf4\x05~O\xff\x9b\xa5\x0e\xf5\ +=\xf8~e\xaf\xa1|\x00\xb85\x13\x83\x7f\xc81\xbb\ +\xa5\x00s\xfc\x05\xf8Fx\x5c\xa1G&\xad\xed@&\ +o\xecI\xa6\xed\x9fHf\x9cYJf^\xb6#\xb3\ +o\xed\x22\xb3\xfd\x0f\x90\xb9\x0f\xce\x91\xb9\xa1\xee\xe8\xe7\ +Y2\xdbo\x1f\xfa\xbd3\x99yi\x1d\x99\xe1\xba\x88\ +L\xdd3\x86Lfv'\x93l\xdbS\xcf\x85\xe7\xc1\ +s\xad\xaa\x04=\xf8\xd0\xb8\x16>\xfb\x90g\xf8\x1b\xe6\ +\xeb\xe9\xd2\xe7U\x17\xe3+u\xe702\xf3\xe2\x1a2\ +\xe7\xbe+\xc9\xfe\x18JrS\xbe\x91\xbc\x9c4\x92\xc7\ +\xce#I\x1e\x8f,up\xb9\xe8\xefrI^v*\ +\xc9I\x8a!\xd9\xefC\xc8\x9c\xa0cd\xc69K2\ +e\xdb\x002i\xb5\x01\xcd\x17\x14\x9a\x16\x92i\x5c\x0b\ +\xe7\xf28*\xc0\xdc\xe4\x07p\xd6\x11\xafNZ\xd5\x86\ +L\xdd=\x86\xcc\xbe\xb9\x13\xe3\x8b\x9b\xc1Bx\xe4\x94\ +\x8e\xe7r\x0e^A>\xc9M\xfdN\xe6G\xdc&\xb3\ +\xbc\xec\xc9\x14\xa7Ad\xd2\xca\x96\x94\xacPL\x19\xe1\ +(\x84{\xb0\x0b\x1e*\xc0\xbc\xe4\x83wt\xfe\x92\xed\ +\xfe\x87\xce\xa5\x05\x99\xff\xea\x16\xc9\xcdL\x92+\xbeK\ +\x1d\x88G\x00O\xc9{r\x99L?f\x8ee\x0c\x9f\ +\xffT\xfa\xde\x14\xc1CV\xf1\x1cR\xb8o\x90\xa4\x00\ +\xf3\x92\x03\xde\xbb\x229\xbd\x96,\x88~\x86\xcee\xde\ +\xaf\xc3\xbb\x88\xc1\xcb\xcb$\xd9\xef\xee\x91\x19g-(\ +]\x01\xf8\x81b\xc8\x85$\x1a\xe7|\xfc\xc3\x9d\x93\xaa\ +{7\x03\x9d\xaf\xa45m\xd1>/#\xd9_\x9e\x91\ +$\x87]\xa9x\x17\x1e\xc3m9\xf6\xb3\xfc\x8e\x83\xf3\xfd\x1d\xf6C\ +\xffB\x1a\xc8c\x15\xd5\xdd`W>\x8eK\x07\xc0=\ +/+\xb9\xb2\xd1T\xa1\xe3\x17\xd3\x00\x9bUT\x8bD\ +q\xf1\x8f\xe4=\xf0\xfc\xdf\xf5\xdc\x0b\x8f\x82\xf8(2\ +u\xd7\xa8_A\x03\x8a\x8f\x7f\xb4\x07\xa0\xeb\xfdJy\ +\x0f\xf6\x04//\x8b\xe4e\xa7\x90\xdc\xac\x14\x1c\xeb\xe1\ +\xe5e\xffR\x9f\x12\xfbs\x18\x99\xb2\xb9_E\xd3\x80\ +b\xe3\x9f\xb6\xf1*T\xcf\xe7\xf1Hnf2\xf6\x19\ +\xe6>\xba@f\xdd\xd8\x86cy\xe0\xb7O\xdd3\x16\ +\x9f\xc3\xd4}\xe31\xff\xc9<\xbf\x92\xcc\xbe\xb9\x83\xcc\ +{z\x19\xd9\xefo\x10]\xa4U\xdc\xbc\xd0\xc8{q\ +\x9dLZ\xd7\xa9\x22\xe3\x06\x8a\x8b\x7f\x88\xd5\xaei\x87\ +\xed\xfb\x8a\x18\xbc\x9ct2?*\x98\xcc\xf2v\xc4\xbe\ +C\xbc\xcf\xd6-\x04\xf2;\x04r<,u\x8arC\ +\xe0\xe7\xca\x968\xb6\x04r\x1ar\x02\x80>y\xf9\xd9\ +\xf2\x9f$\x97Cf\xfb\xee\xa6rP*\xc6O\xa8\xb8\ +\xf8G\x00~=y\xfbv \x0e\x98\xfb\xd8\x83Ls\ +\xf9\x97L\xb25,\xc2o\xb9\xce\x98nal\x19~\ +&\xaf\xefL\xa6\x9fZ\x80\xfd\xfb 7\xe49xH\ +\xfe\x00\xef\xa9 9\xa0\x98\xf8G\xf8\x00\xbe\xcbI\x8e\ +\x93\xdf>\xb2\xf3\xc8\xfc\xf0\x9bd\xda\x81)\xe8\xfc\xb6\ +\x90o\x5c\x9e\x9fg\x80\xf8U\xfa\xc9\xffpN\x89<\ +\xf3\x0b\x0ab_\x92\xc9L\xe3\x8a\xa0\x01\xc5\xc3?\x9f\ +\xef?\xbf&\xb7\xfd\x03\xdd\x11\xf2\xb6\xe0\xb9\x15\x1a\x7f\ +\xa5\xe9 y\xc3\xff\x90\x1e\xe1D\xe5\x99\xc8e\xf0\xc8\ +\x9c\x80\x83\xb4\x1c\xf8\xcd\xf1\x8f\xce~\xc6\x99er\x8a\ +\xe3\xf1H\xf6\xbb\xfbX\xbeS\xb9\x01\xbf(\xd6\x06\xe7\ +t\x85\x1e\x99~\xc4\x8c,\xf8\x1a!\x87u v\x92\ +\xfe\x13\xd9A\xe3\xe5\xed#V,\xfc\x83,Ez\x15\ +\xd8>2\x0f\x1e\x97\xccE\xbac2\xb3\x07}\xe6+\ +\x87\x96S\xb7\x0f\x22\xf3\xdf\x06\xc9\xbe\x1e4\xf2\x9e]\ +!\x93V\xb5&\xe5\xc8\xbf\x14\x0c\xff:8o\x87\xe4\ +\x14\xc8\x8e\xfb\xb0KdR\xe5\xc5\xd5\x04h\xa0)\x99\ +\xb2\xb1'\xd6\x0de\x1d`o\xa6\x1d\xfaW\x9e\xf4\xac\ +8\xf8\xc7z\xb4\x11\x95\xbb#\xdb.\xe1s\xaf\x10\xb8\ +\x17\xa0k\xec\xc7x\x1f\x223\x0d\xe4=\xbbJ&\xd9\ +\xc8\x8d\x07(\x0e\xfe\x11MC\xfe=\xe4V\xcb2@\ +\xdec\x9e\xaf(\xb8\x17\xa0\x81\xd4\x1dCIN|\x94\ +L\xeb\xe3f%\x93\xa9{\xc7\xc9\x8b\x07(\x08\xfe\x91\ +\xce\xbf\xca\x00\xdbg\xb2\x0c\xd0\xf3\xb1\xaeWY\xf2^\ +\x02\x1aH?1\x0f\xdb\xf4\xb2\x8c\x9c\xc0\xc3X\xbf\xfc\ +m\xf0\x0fg\xc3y\x84L\xf6\x12\xd8\xf7`\xe3)X\ +\x8eu\x09:\x07\x1f#\xe4\xfc\x80\x9c\x92v@\x8c0\ +\xd9\xa1\xbb\xfeA\xef?\xbfR\xea\xd8:\xe4\ +\xd0\xa6\x1f\x9eYu\xce\xbe\x00\xdd\x83\xbe\x22m\x9c\x00\ +\xe2\x97i\x07\xa7\xca\xba\xee\xca\xc7\xbf\x95\x1e\x99\x13|\ +B\xaa=\x80\x0165\xbeW\xa5\xe8r_\x04\xfe\xc1\ +'\xc0\xf9\xf1I\xca\x95\xf3\xc8\xac\xab\x1b\xab6\xfe\xe9\ +\xfb\xd8`\xb3K\xb7\x05h\x0f\xaemV<[_R\ +\xb0\xd6\xc7\xb5\x05\xa4\x1d\xb9\x8f\xce\xcb\xaa\x03T2\xfe\ +up\x0d\x05\xb8G/\x15\xfa\x91\x1d\x9d\xb6gl\xd5\ +\xe3\xfd|(\xf4yIwG\x15\xe2\xcc\xf8n\xb1\xf4\ +\xbc\xafr\xf1\x0f\xf1\x91=cp~\x9d4\xa3 \xf6\ +\x15}w\xa6\x8a\xf1~>\x80\x0c\xd8jJr\xd3~\ +H\xb5~NR\xac\xac~\x80J\xc6\x7fSd\xc7.\ +\x90:\xd6\x0b\xb9a\xf8\xfe\x5cU\xd1\xfbK\xe0_\x17\ +\x9f_\xf6\xc7GR\xad\x1f\xf2\x16Sw\x0e\xaf\xc2\xf8\ +\xd7&3/\xae\x95Z\x07\x86\x5c\xcc*\xcb\xfb\xf9\xb0\ +\xb2\x05\x99\xfb\xf8\xa2T\xeb\xe7\xe5f\xcaj\xfbT.\ +\xfe-\xb41\x0e\xa5\x1a\xc8^\x84|\x5c\x9c\x93Y\xd9\ +8\x94\x05\x10\xee\xb2\xfd\xf7K\x87\x7fv.u\x87\xb8\ +*\xe3\xff\xf6\x1e\xe9\xd6\x9e\x9f\x8ds\xed\xaa\xfc\xf9G\ +\xf4\x0by\xaeR\xc5\x03\x10\xdf\xcc\xbch+\xcb\x19\xa8\ +\xba\xf8\xcf\xcd\x90w.D\xa5\xe1\x1fp(\x95\x0c\xc4\ +\xf8_\xfbg\xe2\x1f\xd9~`;T\xc2\x9d\xe9\x0a\xc0\ +\xbf\x94:\xd0\x9f\x8c\xff\xbcL\xaa\x96\xce\x9f~\xfe/\ +\xfd\xa1\xf8G6\xa3\x9cb\xa0\x95\x0bh\x0fp\xec[\ +\x9a\xc1- 3=VWi\xfcg]\xdf*\xe5\xda\ +e\xd6}\x14\x03\x90\xed\x0e\xf9<\xd2\x0c\xea\x0c,\xa8\ +\xba\xfa?\xf0\xbe\x0b\xab\xa4\xce\xf7\x85\x1a\xbdU\xd6\xf7\ +\x8fA\x17\xe7r\xe6=\xf7\x96\x0e\xff\x10\x03t\x99V\ +\x85\xf1\xdf\x14\xdf\xb3\x95\xf6\xce\x1c\xd4{\xabRq\x7f\ +a\xa0\xef\x0e\x16\xc4\xbd\x92j\xfd\x90/\x97\xe24\xb0\ +\xea\xfa\xff\xe8\x9cX\xf0cJ38?>\x90\xc9\xf6\ +\xdd\xaa\xb4\xff\x1f\xfc\xb7\xd2\xd6\xa6\xe5\xfc\xf8\x88k\x9d\ +V\xd9\xf8\x0f\xd0\xbf}W\x8cGi\x06\xf0\x8d\xb4#\ +U0\xf7\x83\x0fh\xdeR\xeb\xfeh\xc0\xbd\x22|\xa7\ +\xb1\xaa\xe2\x1f\xe4\xdfj\x032?\xc2O\xaa\xf5\xc3\xc8\ +\x09p\xa9|\x8f+5\xfes\xee\x80\xde#\ +3\xed+ \xfe\xab\xe8\x99\x96\x14 \xe7\xcdy8\xc9\ +aI\x97\xf3\x08\x03\xe7}\x1c1\xfb=\xee\xff\x08A\ +\xd2\xea\xb6\x94M\x83\xf7J[\xa06\xd3o@\x13r\ +\xaam#'\xde\xafx\xf8\xb7nN\xd5S\x8b\xbaG\ +\xe6\xdc=\x8a\xefHA\x0d>\xf0q`?\x1f\xbfO\ +\xdf\xf2\xaaK\x13YW\x99\xb2\xd54\x83\x9c\xf7\xeb[\ +\xe5\xc5#+\x06\xff\xfc\x1e{\xe5\xd1M\xf9w\x01\x04\ +s!9l\x1c\xe7\x87\xfb\xae\xe0\xeb\x85Zx\x90\xef\ +\x04\xf7\xc5\x92\x90\xee\xcf\xe2\xf7\xd7*F\x13\x95\x8fc\ +\xd1\xeb\xd3\xa1j\xd8\xcaX\x13\x8a\x9b\x1c\x87\xfb\xcc\xc9\ +)\xee!\x1f\xfc\x0b\xf6TD\x9f!\xa7\x15d\x1c\xf4\ +\xd9\x93\xf8\x9e:\xd8\xc3[L\xca\xac\xf1\x0b\xb9\xf2\xdc\ +\x8cD\xb2 \xe6\x05\xce\xff\x85\xfb\x1f\x10C\x80\xef&\ +\xadmO\xf7\xea\xe4\xd3\x84\x82\xd0\x03\xd4\xb0\xdd?\x09\ +\xfbke\x1d\xd0kP\x8e\xf6\xaet\xf8\x17\xec\x99\x8a\ +e\x9a!\xa2\xc9\x81\xb8njN\xd0Q\xaa\xa7bV\ +2\x99\x1f~\x0b\xcbs\x89\xf0 C,\x08\xbe\x03t\ +\x03\xef\x85\xde:\x99\x977\x90i.S\xc9d\xfb\x7f\ +\xe4U'Av\xdc#\x1e&\xeb\x80\x9er\x10/\x91\ +c\xceS\xf9\xf0O\xf3Y\xd0a\xe0\xbcA\x1fT\xa8\ +K\x07\xfd\xcd\xb8\xe9?J\xc85\xcc\xab\x1c{K\xc6\ +\xab\xd0\xb3\xe1,\xcbe \xbb\x0ah\x22\xf7\x89'\xd5\ +\x8f\xb32\xf4\x04\x9a\xe6\xa1\xde\x07\xe7\xe7g\xb9,+\ +\xe7\xee\x11y\xfb\xba$\xc4?\xe5\xa7\x87\xf3\x09\xf9:\ +\xf9o\xee\xa2\xf3\x16_\xe6\xfdu\x9c\xa3\x0b\xb5K\xcb\ +\xb4St\xb1,\x97w\xad\xdf\x9c{\xa7*\xe7\xfcC\ +/:[C2\xcb{\x93\xdcj@r\x12>P=\ +b\xe4\x9b\xef \x19\xfe\xe98\x1d\xf8\xdb\xcb;p\xfd\ +\xe2\xb2\xe6\x0c2\x04\xe2\xe0H\xa6\xcbm\x94\xea\x1f\xaf\ +\xa8\xfa\x9f\xd4:\xa1\x96\x0d\xd8x\xf2\xaa]\x0c\xcf\xc9\ +\xf4\xdcP\x11\xfa\x8c\xe4\xe7_\xcaX\x15\xf0\x8a2c\ +\x94\xfc\x1a\x10H\xaf\x93\xd7\xc0\xb91H\xee\x96\xe0=\ +\xd0\xf3\x9b\x9f3\x22\x97\xfe{\xba\x85\xf2\x18\xees\x03\ +\xbd\xcb\xb3n1\x0c\xe8\x0f%\xe3=O\x19\xf1\xdfL\ +j\xf9\x0cr\xa2\xa8\xd7\xa1\xf8g\x83]'\xed=X\ +Q\x03\xe7F\x80\xfeWl\xcf(9\x96\xed\xbb\x0b\xdb\ +\xd0)\xdb\xfac\xba.\x9fO\x81_\xfb\x9b\xd2\x7f\xe1\ +y\xa9\xce#q\x7fxN\xc2{\x99j\x18\x89\x5c\x07\ +\xaeiVa\xbd@\xca\x85\x7f\xa87\x01\xe7\xaa<\x03\ +\xdfQBzb\xa9:\x00Z\x1b\x9c\x1by\x0e|f\ +\x84u?~\xad\xa17\x81\xf8o\xb8\xc9_1O\x83\ +\x1c\x0c\xb0U!\x06\x09\xb1H\xfc\x1d\x0bm\xda\xffH\ +\x83\x05m\xdb\xa2gB\xdd\x15\xe8'\x0e}\xbe\xe1=\ +\xb2\xd4\xae)m\x80\x0e\x9by\xdeZ\x0e\ +;\x90\xcc]\x87i#\xd3s=\xf6K\xe6\xdc;\x89\ +{\x89s\xbeGQ5\xfcd\x88\xdf\x949\xd0\x9c\xa0\ +N\x1c\x0b\xf8\x93\x22\xe0\x9f\xaf\xa3\x87]*\xf7R\xd8\ +Q\xc1\xe2u\x00\x9a\xae\xe4a\x1f\xf3\x07\xe8K\xd0\xf7\ +\xbd\x04\xcf\x11\x87\x7fq\x83\xcb\xa5r\xb3*\x12\xcfb\ +F\xde3o\xda\xc7\xaf@\xfd\x7f@\x07@<\xaf\xbc\ +2\xaeT\x1d\x00\xd7\x80\x18+sM\xccb\xefCg\ +\x19t\xf0\x12\xef+/\xfe+i@\xadh\xa8\x11\xa8\ +p\xfd\xbf@\x07\xd87\xa1\xdc}\x8f(\x1d`\xb1h\ +\x1d\x00\xc9V\x88\xf3\xc8\xb3\xb7\x16\xe4\x92%o\xe8\x22\ +9\xffW\xa0\x01\xb8\x87z\xe1\x0a\xd9\xff\x0fx5\xda\ +Wiz\x1a`\xdf\x95\x98gJ{\xffE\xdc\x00\x9d\ +.I\x94\xdcTp\xfc\xffb\xdc\x97\x1f\xff\x00\xd6\xcd\ +\xc9\xdcP\xb7\xb2\x17\x83\xe4&\xf4\xea\x84:\xa7\xb9\x0f\ +\xceQ\xf9\x0a\xd6B\xbeK:\x1e\xce\xd7\xc7\xe55\xb2\ +o\xee\x14\xcdk\x14\x15\xffh\xaf\xf2\x9ezUF\xdd\ +\xf2\xf2\xe3\x1f\xe7\xac\x8b\xb8\xaf\x8ct\x02\x90\xe1\x18\xdf\ +\x88>\xe0o ?\x11\xd7g\x02\xbc\x8b\x8aY@\x1e\ +\x9cC7\x92\x93\xf8En[\x09w\xe22N\x89\xb9\ +\x13\xa7\x80\xf8\x87\x5c\x9el\xbf\xfd\xb8\x1eh%\xdce\ +\xe3\xe3_\xf2\xfe\xcf\xa0\xaf!\xbc\x82_\x1b|\x01\x90\ +\xc3\x086\x01\xe8\x85i{\xc7\xd1\xf8n.YL^\ +J\x9fBi\x03lq\xb1\xf1q\x05\xc3?\xd0}\x86\ +\x9b\x15\x95\xc7P9\xb1j~\xff\xe7r\xf4\x7f\xa7x\ +v\xc6\xd9e\xb8\xff!\xaeA[\x98\x87Q\xce\x1c\x0c\ +\xa8}\xe2e'\xd7=e\x7fyJ\x9f%\xd1\xb6\xa6\ +\x22\xe0\x1f\xfc\x9cy\xe17i\x1b\xa5R\xf3\x98\xf8\xfd\ +\xdfM\x10\xb0$\xff\x9e\xaeP\x8e\x85\xf0\xfcK\xcb\xd5\ +\x16\xf4\xc7\xe9\xe08=\xc8>\x90\x1b\x98\xa7\xc8\xe8\x03\ +\x86\xfb \xb8\x8f\xa7\xa8y+\x00\xfe\xc1w\x04>\xa5\ +\xc2\xde\x93\x95\x83w>\xb0h\xdc\xb7D\x10-\x97g\ +\xae\xd0\xc7>R\xf0\xb3\xe2\x5cNA\x80\xdfa\xff\xaa\ +@Lv9\xe5W\x82\xbb\x1c\x10\x03\x82z\x98\xe0K\ +\x07?\x1c\xc4\xb8\xf7 \x0a\xd2\x0eL\xa2{6T\ +\xfa\x1e\xfc\xa9\x90G\xe3\xb8\xf0\xec\x0b\xe1\xbf1\x82P\ +\xe9\x9f\xaf+$C\x85Aa\xed\xa1?\x05Bi\x1c\ +\x17\xc3\xbf\x10\x0d,@\x90\xaf\x00s\xad\x06\xf9B>\ +\x8d\xdb\x12\xb8\x17\xc2?\xd8\x05~\x0a0\xdfj\x90/\ +\xf8\xb1\x04l>QC\x80\x06\x86#HQ\x809W\ +\x83| \x85\xc6\xa9X\xdc\x0b\xe1\x1f\xfc\xc2\xbb\x15`\ +\xde\xd5 \x1f\xd8\xcd*\xf2\xf5\x8b\xc5\xbf\x10\x0d\xe8!\ +\x08Q\x80\xb9W\x83l\x10B\xe3\xb2L\xdc\x8b\xa0\x81\ +\x81\x08\xbe)\xc0\x1a\xaaA:\x88E0\xa0<\xb8\x17\ +\xc2?\x83E\xe9\x8c\xe9\x0a\xb0\x96j(\x1f\x00\xce\xe6\ +\xd38,\x17\xfe\x85h@\x15\x81\x03\xab\xda&\xacJ\ +\x90O\xe3LU\x1a\xdc\x8b\xa0\x81:\x08vT\xd3@\ +\x95\x80|\x1aWud\xc1}5\x0dTI\x90+\xee\ +\xc5\xd0\x80\x03\xabZ\x1fPDH\xa7q#W\xdc\x8b\ +\xa0\x01\x90)\xa0W\xc4*\xc0\x9a\xab\x81\x82X\x1a'\ +2\xc9\xfbr\xd0\x00\xe8\x94`WT\xfb\x07*\x1fB\ +h\x5cH\xa5\xe7\xcb@\x03|\x1f\x11\xf8\x96\xaa}\xc5\ +\xbf\x1eR\xe8\xbd\xd7\x13\xc4\xc9\xaf\x18B4\x00~E\ +\xf0-C|\xa1Z7\xacx\xc8\xa7\xf7z8K\xc0\ +\xa7\xfb\xabp_\x0a\x1d@l\x09|E\x10c\x962\ +\x87\xa4\x1aJ\x81\x13\xd5\xa3zT\x8f\ +\xeaQ=D\x0ef\xf1\x8fe\xf2\xd3\xe0\xe2\x9fK\xf0\ +\xe7\x98\xe2\x9f\xd5\x84?\x0b\xcb\x83\xb2\xe4\x87\xb0\xbc\x11\ +\x96G\xc2\xf2\xaa\x84<+6A\xe5\x92\xf2PX^\ +\x0a\xcbSay+,\x8fK\xc8kay^\x5c\xde\ +\xb7E?L\x09j\xdf\x19\x84>\xf5{\xf4\x8b\x96\x9d\ +(\x90t\x88\xd0[\xfeB\xd0\x17\xc1r\x04G\x11\xdc\ +D\x10TIp\x93\x9e\xc3rzN\x7f\xc9K\xc7\x12\ +z\x0e\xf8\xe0\x0d\x11\xaccQ~y\xb0\xdd9\xac\xca\ +\xb7)\xf9\xc0\xa1\xe7\x14B\xcf\xd1\x90%\x107(\xef\ +>\x08\xad]\x1f\x81\x13\x828\x05X\xa7\xa4\x10G\xcf\ +Y\xbf\xbc{ \xf0\xf7J\x08& x\xa5\x00\xeb\x91\ +\x16^\xd1kP\x92d\x0f\x04\xd6^\x13\x81-\x824\ +\x05X\x83\xac\x90F\xaf\xa5fi{ \xb4\xf6-,\ +E\xe8\x9d)?`\xd3k\x12\xb9\x07\xac\xe24o\xfb\ +\x9b\xad]p\x0flY\x22\xce\x82\xc0\xfa\xe1\xac\xfc\x0e\ +4/\x0e\xd2\xe85\x16\xae\x9fU\x9c\xcfWe^'\ +)\xbcb\x09\xc9\x05\x16%+\x9d*\x7fn\xbf\xec\xfe\ +\xbc\x13\xab\xb8~\x00\xfa\xc2/\x94\xef\xbaE\xb58\xa0\ +\xee\x06\xd4\xd2Z\xd9\x92\xaa1\x08}\x9c\xa0\xd6,\xfc\ +]\xc5\xf5\xfc\x8c\xa3\xd7\xcc_\xff\xfa_\xb2n\xbav\ +\x14\xf4\xd6\x84\x9a\x0a\x19\xee\xd6dN\xe0!\x5c\xdb<\ +?*\x88d\x7fzL\xb2\xdf?\xc0\xfdgr\x1f\x9c\ +\xc5u\x0f\xd3\x0eM\xc7\xf5\x94q\xcf;\x5c\x0bIn\ +{\xb1\x8eU\xa4\xcfWl\xbe\x0f\xae\x97\xa5\x87k\xf8\ +f\xdd\xd8\x86\xd7I\xf5Z)\xbb\xe7\x04\xd4\x97\x80\xbe\ +O\xb9\x0f\xddp\x0d\x02\xa8\xf5&\xa7}\x08a\x15\xd9\ +2\x15\x14\xcf\xd4\xa5\xeb'\x9a\xe2Z\x87\xd03C\x96\ +\x01\xf56\xf2\xdf\x06\xe1\x9aET}a\x99\xee\xdd'\ +\xb3\x8a\xec8\xf9\xdb2\xb8\xdec\x1b\x5coI\x9e\xb5\ +\xc0\xf1>\xe4e\xe3\x9a\xb0P\xb3I\x86\xda\x03\x1cV\ +\x91\x0d+\xdf\xb5\xa39A\x9d\xcb\xdcG\xe7\xe5\xda\x03\ +Cx@\xbd2\x5c\x1bSz\xfe\xc8\xb7\xdf\xe5\x8aw\ +\xa8\x87\x0a\xfc\xecW\x0c\xa8\xef\x96q\xd6B\xda\xf9\xf2\ +}\x17r[{\xf2\xa6^\xb8N\xe4\xaf\x1c\xd0\x97\x19\ +j\x9bIA\x07Ar[?\xd4\xe1\x5c\xdb\x11\xf7\x87\ +\x96xp\x0aHNR,\xc9~w\x9f\xcc}|\x91\ +\xcc\x09>\x81\xe5\x1e\xd4\xe0+\xf8\x16I\xd5\x84\x95t\ +\x0f\xd2~\x90i.\xff\x96\x97\x1f\xc8o\xfdH\x87\xc9\ +\xf6\xdf\x8ff\x22\x81L\xcb\xcd\xc4\xb5\xc2q\xdf{D\ +/T\xefJ\xbd\x22\xfc!\xfd\x07\xeaiA_\x1e\xa8\ +/\x0c\xfd\x02%\x19\x05q\x11\xf8y\xe5\x90\x0b\xf2Y\ +?\xd4\xfc?4C\xa2\x1eB\xec\xcfad\xfa\xd1Y\ +t\x8fF\xedR\xeb\xba\xf2u?\xa8?\x0c\xb4\xc1\xcb\ +\xcb,\xf3\xf9\xb9\x0f\xce\x88\xaf\x15^\x11\xeb\x07\xba\xb7\ +m\x8f\xf8\xdd\xbd\xd2'\xc6\xe5\x90\xb9\xa1\xeeT\xff\xca\ +\xf2\xea/\xb0G\xd6\xfa\xb8>\x7fY:\x04\x9c\x19\xd0\ +\x19%\xac\x9b)\xfb\xfa\xa1\xcf\xe7\x99\xa5\xa5\xcb9\x1e\ +\x17\xf7m\xa6\xfa\xc6K+\xaf)]*\xfd\xc8,\x5c\ +\x1b\xb8\xb4\x01\xfc\x03j\xc2J\xb0\xc72\xae_\x17\xeb\ +8e\xf5\xba\xc9{\xe9C\xd7\x11\x95C\x9d$\xf4\x8c\ +\x0c\xb7\xe5X\x17\x14\xbb\xdd9\x19\xa2\xfb\xe4\xca{\xfd\ +\xe8\xf9\xd03\x13\xd7\xdd\x1538I1d\x8a\xd3 \ +\x89\xeb\xb8JD\x07\xe8|C\xff\xa1\xd2\x06\xeeMU\ +\xb6<\x94y\xfdY\xd7\xb7\x95:\x8fl\x9f\xed\xf2\xaf\ +\x17\x8cd\x5c\xca\xb6\x81\xb8&\x9f\xb8\x015\x19\xa9^\ +\x83\xa5\xee\x81l\xebG6\x08\xf4\xb7\x12\x8f\xfbXJ\ +G\xaf\xa0z\xc9\xb9\xf7O\x8b}7\xf0A\xa83_\ +\x06\xddI\xbf~\xdc?\xfd\x1fl\x9b\x8a\x1b\xd0C\x07\ +\xfb6*`\xed\xb0.\xb0\x87\xc1>\x16\xbd\x01<2\ +\xf3\xbc\x8d\x98^\xe9rX?\x9c\xfd\x1dC\xb1\xee)\ +nd^\xb2-\xeb\xfd\xd2\x03\xe8\xda\xcc\x1e\xa5\xda\x96\ +\xa0\x8f\x95\xa1\x0f\xca\xb4~\xa8\x09.n\xff\xa1\xb64\ +\xfc\xbb\xfc\xf8\x9e0P\xb2\xa74[\x03\xf7M\x11\xec\ +\xcb!\xd7\xf5kc=\x0etx\x91\xeb\xcfJ!S\ +\x9d\x87W\xe0\xfa\x9ba=\x19\xf4hq#\xef\xc5\xf5\ +\xb2\xce_\x85\xad\x9f\x9b\x99\x8c\xce\xc7\x90\x8a\xad\x8d\x88\ +t\xc2\xbc\x17\xe2{,C\xbf\x95B_jE\xac\xff\ +\xf0L\x5c'_$\xfe\xa1o\xc7\xde\xf1\x15\x8b\x7f\x90\ +?o\xee\x8a_\xffS/\xcaoZ!\xeboJ\xf5\ +\xb8\xcd\x11c\xa3\x22}\x1f\xec\xbb\x8a\xe3\x7f\xba\xd8F\ +\x04;Y\xdc\xc8\x09:^q\xf2\x1f\xfc<\x9bz\x97\ +j\x8fH\xa8\x83I\x07\xb0\xff\xce#K\xd5=\xb3\xae\ +0+N\xfe[Q}G\xf2\x9e_\xc32\x08\xf4\x80\ +b\x80~\x07\xf4\x87m\x9e\x8a\x88\xeb\x80\xee\xe9\xed(\ +v\xed\xd0\xeb\x00\xf7\xe7\xac\xb0\xf5S\x00>\x9fd\xfb\ +\xae\xb8\xdfw\x09\x80~\xbd\xc2\xfdA\xe5D\xfb\xa0\xdb\ +\x16D?\x13\xbb~\xd0=\x93\xcb\xf6\x85\xc8\xbc\xfeB\ +?\x85E\x13\x04\xda\xc5\xfb\x06T\x18\xed\xeb\xe0\x1e\x06\ +\xa5\xf5:\xc1\xb2\xafl?\x88\xe4\xeb\x17\xd7\x0b\x01\xf8\ +\xc0\xd6\xfe\xd8\x07\x0b1-\x0a\xe7\xcd\x8b\xc7\xf7\xe4\xb9\ +\x0fp\xee\xb7\x0fA\xe7+Z\xec\xdaa_\xa0\x7f\x87\ +L\xf6\xefr\x81\x9a\xc7\xd0\xcbl\xa31\xd5\x83P\xf8\ +,\xc39\xbc\x016 \x0f\xf3\xa2\x82\xf8\xb7\xd8\xde\xcf\ +\xbe\xe5L\xa6\x9f\xfc\x0f\xdbi\xb8o\xcc\x0a=\xd9\xf7\ +\x01\xbd\x0bb\x81\xf9Q\xc1\xe2\xd7N\x92\xb8\x973\xee\ +\xe1(\xad\xfd\x8b\xe8\x06\xde\x03\xfa+\xac\x0d\xe2\x91`\ +\xc7C?\xd0\x12{\x8a\xf4+\xe8\x0d&\xfa\x10\xb2q\ +\x0f\x9d\x82\x98\x97d.\xb2\x85Rw\x8d\x94\xde\x16D\ +\xef\x05yS\x9a\xbd\x89\x07\x8fKf]\xb6\x93T\xef\ +*\xbe\xfe\xe5T\xads\xe8A\xc5\xf9\xf9\x19\xd7\xbb/\ +~\xa6\x84\xf4)>\x1f\xfa\xfa\xba\xf49a\x9a\xe4 \ +}qv\xd1\xfeI\x14\xd7\xa6i\x10\xf1P\x88\xf3\x14\ +\xc4\xbc(\xf35\x10[\x95\xac\x17\x97(\xfc\xeb\xe2\x18\ +<\xf4\xdf\x12\x89\xce\x9f\x9f\xa8X4\xff\xd9\xd07\x16\ +l\xc0\xcc\xa4\xb2\x97\x9f\xfa\x9d\xea\x93gI\xf5\x81K\ +;0\x19=\xab;\x89\xed\x93\x12\xfd8\xb4\xa9\xbfC\ +sI\xdb7\x1e\xc7~%\xf1-\xc3\xf9\x93@\xe6\x95\ +N\xff\x88>\xb3\xfd\x0f\x88~~\xbe\x90M\x07}\xee\ +O/.\xd97Y\xc4\xc0=\x12\xd6\xb4\xa3\xe2\xa2X\ +v=\xa7\xe2\xda\xa1\xe7\xc9L\xcf\xf5d\xfa1s\xbc\ +'\xe0\xbb\x05^\x0a=\xa7\xa0\xf7\x94$\xeb\xa66\x98\ +C\xf9\x9aJ\xd7w\xcb^?\xf8\x15\x8e\xcfE\xc8\x16\ +\xad\xd7g]\xdfZ\xb4~\xd8+4OI\x06\xf8\xeb\ +\x8a\xceL\xc7\x92\xfd\xe6\x11\xaf\xa0\xfaI\xe4\x89\xec\x03\ +X\xfa\xe0\xe1\xe7S\xfe\xe5r\xf1X\x91\xf8\x87~s\ +\xdc\x94\xaf\x22\xdfD\xf1\x80\x16\xd4\xd9\x04\xfb\x03\xf1F\ +IF\xa6\xe7\xba\xc23\x8f\xf1\x1f\x17Q\xf6\x97$Z\ +:\x0f\xd3Pao\xb2\xf2\xf1U\x91\xfc\x1fb3\x90\ +g\x80\x07\xf4AK\xfe\x8a}\xdc\xd9\xb7\xf7`\x1a\xc5\ +:\x1d\xf8_\x90\x8e\x07\xfd<\xcb\x9cb^6\xf5=\ +9\xaf\x1f|/9\x81\x87e\xf1\xad\xc3\xdaK\xc6\xbf\ +\xd1\x1c3\xce-\xc7\xb178\x0b)\x8e\xbd\xe9x\x15\ +_\xa7\xd1\xa5}\xdf\xa3%\xeas\xcda\xc5\x90\xc9L\ +\xe3B]H\x1e\xeb\x87\x98`\xe6\xf9\x95\xb2\xf6\x99\xe6\ +\xe7\xcf\x8b\xf87=\x81~Zz\x98\xbe`\xde\x18`\ +\xbf\xd1\xdf\x80\xae\x07}\xb4\xa8\x9e\x8d\x8f\xd0\x99\x89\x17\ +\xe9\x0f\x83\xd8\x18\x15\xe3l&\xf3\xfa\x81\xc7C\x1c-\ +\xc5i \x8ds\x99t*\xfe\xdd\x01\xf1\xf9/\xb4\x9d\ +\x9b\xff\xee\x1e\x96\xf3\x05_#\xb1\xcf\x89\xeaw\xa4S\ +\xc8\x0b\x81\xf7\x00\xdf\x009\x9duu\x13\x99\x17\xe6\x89\ +\xf5\x1e\xf0\x8f\xe6\xdcq)\xc2\x11\x7f\xfd\xa5\xd8\xed%\ +\x06\xe2\xed`\xcf@l\x1cb\xc2\x98\xff\xc8\xeeS\xe7\ +\xe7\xbf\x94\x9e\xff\x04\xeb\xdf6\xa0\x98\x8c\xe7$~\xc6\ +g\xbfd\xbfW\xc1>\x8e\x14\xcd@\xec\x87\xa2\xfd\xa2\ +\xbf\x85\xb3\x94\x89t\xb4b=\xfa\xd09\xc2\xfc?7\ +\x13\xef\x19\xce\x0b@2\x13\xd6\x0c\xba<\xee\x01Z\x98\ +\x03'\xd3\xba\xf9\xc0\xcf\x7f*=\xff\x8d\xbf\xfe\x0cV\ +\xd1\xfaA\x0f*\xb1~Q\xdf\xd5\x15\xdf/\x12~\x07\ +v\x05\xf4\xa8\xdbj\x8ay\x09\xf0\xc8\xd4\xbd\xe3\xb1N\ +\x05{\x86\xf5\x05\xf8[\xbeM)\x9fu\xf3\x81\x9f\xff\ +Vz\xfe#\xce_3\xc1k\xc68\xca\xcd\xc0zh\ +\xf2\x06\x89\xec\x8b\xb2Ad\x8f6\x1dy\x9c\xed\xb2`\ +=K\xc2\xfcW\xe0]\x80\x93\xd4]\xa30\xe0x\xe6\ +J\x89s\x0c\x14\x11\x84\xf3_\xcb\xce\x7f\xb6\x14\xc0\x91\ +\xe2\xf6?\x95\x14\x84\xf3\x9f\xff\xd8\xfc\xf7?\xfd\xfeC\ +\xf5\xfd\x97\xea\xfbO\xd5\xf7\xdf\xc4\xde\x05\xfb\xa3\xee?\ +\x8a\xd8\x83?\xee\xfe\xab\x98=\xf8\xa3\xee?\x97\xb2\x0f\ +\xbf\xdd\xfdw\xba\xcc\x00I\xd7?\xe0\xd7\xd9\xe1\xd7\xd3\ +\xe1\xd7\xd1\xe1\xd7\xcb\xe1\xd7\xc5\xe1\xd7k\xa8\xae{S\ +\xc9\x83I\xfd(\xc4G0\xf5\x93_\x1f\x83_\x07\x83\ +_\xff\x82_\xe7\xc2\x84\x8fw\xa8\x13a\x84\xc0\x9c\x10\ +\xa8\x13\xd1\xaa\xec:\x11\x22\xce\x85\x06\x82A\x086!\ +\xb8\x8e\xe0\x19\x82\x08\x16\xc5\x83\xe5\x09\x11\xf4\xb3\xaf\xd3\ +\xef\x1aD\xbf[b\xba\x171os\x04\x81\x08R\x7f\ +\x01\xaf\x12\x86T\xfa\xdd\xe6\xc2\xeb\x90`\xee=\x10\xf8\ +\x22(\xa8\x84y\x0bC\x01=\x97\x1e\xe2\xd6 4\xf7\ +\xc9\x08b\x14`\xde\xc2\x10C\xcf\xad\xd8\x1aD\xcc\x9d\ +\xa5\x00s\x15\x07,\xe15\xb0\x8a\xd3\x8c\x22\xee\xbb(\ +<\x14\xa3%\x16u>|\x15`n\x92\x82/\xab\xf8\ +\x99\x863.\xdf\xb3Z\xccw\xa6#\xe4O\xd3&%\ +\x8b3\x8b\x85\x02z\xce\xfc\xbd\x0f\x94\xcf\xbc\xe9\xb8\xf6\ +\x0a}\x9cW\x011\xdb\xac+\x0ed\xce\x9dC\xd8\xdf\ +\x9bs\xff\x14\x99\xed\xbb\x1b\xdf\x8f\x06\x7f[a|G\ +:\xbfS \xabH6\xc9\xce\xdf\xd1\xbc!\xef\x16\xe6\ +\x9c\x1bv\x11\xc7vx\x05yb\x82\x15<\x92\x97\x93\ +F\xb2\xbf<%\xb3|\x9c\xb0\x1fU\x8a\xbc\xa0TV\ +\x91\x5c\x95m\xcf\xd1{S\xf7\x8c\xc1\xf1\x8b\xd2\xee\x8f\ +\x88\x1b\xe0\x93\x87|\x10|\xaf\xa0|y\xad|\x9d@\ +J\x1a\xa7b\xad\x10'\x87{~\xb2\x0d\x1e\xce\x9f\x01\ +\x7fn9\xe8\x89\xaf\xcfH5w\xf0\x1fC.\xbaX\ +:\x91b@\xdc5m\xffDI\xd7\xc0\xd7\xc5\xca?\ +\x7f\xeb\xe68\x97\xa9\xb4\x5c3\xbc\xab\xec\x5c\x9c\xe3\xc1\ +\xcf}\xc4\xf7\x02\xca\x88\xe3\xc3\xdf\xe2\x9c\xa0\xb2i\x89\ +\xafG\x96s\xefu\xf0\xbdX\x88?\x8b\x9f\xc3'\x1c\ +\xe7\x85\xbc\x0b\xb8\xab\x02yS\x90\xe3\x02\xb9\xdbp\xcf\ +\x0a\x9f\x95R\xee\xa1\xb2?=\xa2cA\xa5\xae\xe1U\ +\xb9\xe7O\xe7!\x88\x8b\xaf\xc3\x9ar\x82\x8e\x15\xdd\xf7\ +,\x96w(\xc0\xff\xd1\xb9I?2\x13\xf1\xa0'b\ +\xd7\x00\xeb/\xe3\x9e@\xf9\xe7\x8f\x00\xf8\xb9\xc8\xb9g\ +\xa7\x91\x99\x1e\xab\x0b\xf3\x08\xca|\x16\xce{\xeb\x8ep\ +\xe1#\xf2y\x90c\x06\xb9\x87\xa5\xc4\x06\xcb7\x7f\xfa\ +\x0e;'9\xae\xe4\xdc\x0b\xf2\xa9<8\xfc\xb7\xe5\xe0\ +\xe3\xb0\x06\xfb\x7fH\xf6\x87\x07\xa2q\x10|B~\xfb\ +\x8f\xde\x95yi\x9d\xc8\xf7@^(\x8e\xc5K\xa3\x13\ +\xc0\x9d\x97}\x13D\xe6{Q\xb9a\xdd\xc5\xe1\xb3|\ +\xf3\xb7n!\xf2^\x07\xc4\x1f\x0b\xf3H\xca;w>\ +\xac\xd0\xc3:F\x89\x81\xf0\x0a\xb9\x1eb\x9e-\xf9\xfc\ +!/\x1f\xf1\x10\xce\x8f\x92\xe7\x16\xee\xb9\x83\xee S\ +\x5c\x13\xdf\xb5\x9e^\x227\x11\x06\xe8L2\xcf\x1fr\ +\xfa\xb6\x0f\x11y\xaf+\xdbo\xbf\xec\xf1w|o\xae\ +\x1b\xce\xaf,A\x9bO/\x8b\xcb_+\xc7\xfc)\x1a\ +\x15\xa5\xdfd^(\xf3>\x9c\x04@\xe7H\x22\xbe/\ +< G\x0a\xe7\xe3\xc8:\xff\xfd\x93\xf0\xfd\xb1\xe2\xc4\ +\xcf\xa5j<\xc8\xe1>\x09\xce\xef\x12q_\x9d\xfd\xee\ +\x1e\x95\xe7$\xe3\xfc!\x87\x81\x97\x97Ur\xff/\xad\ +\x95\xcf\xfe\xafi\x87u\xea\x12\xfb\x1f\x19 \xfb\xfe\x8b\ +\xc8w\xe1\x8f\x9c{'e\xcfy\xc0\xf9D\xbdpN\ +\x9d\xf0\xa0\xee\xeb\x89\xfc^\xb9\xf8\x0f\xe4W\xe6G\xde\ +\xc1\xb6\x09\xbe\x9b\x02\x80\xfe\x1f\xf2\xb4\x81ve\xe3?\ +\xdad\xfa\xa9\x85\x22\xef\xfbg]\xdb\x22;\xff\x01\x80\ +\xdc\xa1\xf5\x9d\xa9;$p\xb7\x04\xc9M\x0c\x90s\x22\ +\xd3}\x12]L\xdf\x90\x9f/\xf8\x1e\xce\x19\x17:c\xf9\xaf\ +\xfdp\xce\x1b\xf0\xe5\xack\x9b\xb1\x1e\x09:2\xce\xb3\ +-_\x9e1}_x\x98H\xb9\x08#\xfb\xe6\x8e\xd2\ +t\xc1b\xf3\x07\x19\x0awKA\x87\x843S\x10\x1f\ +\x85u\xe1bg\x0c\xe9o\xdc\xb4\x84\xe28F\xf6\x17\ +\xd8(\xec\xcfO0\x0d\x17\xe1\xa2\x14|\xd0~\x07\xb8\ +\xeb\x07w\xeaE\x0d\x90e8?M\x92\xf9\xc3>\xec\ +\x1c^\xe2\xee~A\xec+:\x87W\x97\xbe\xdb=\xab\ +T{\x11\xdf\xed\xb6\xd0\xc6x\xc2:\x85\xa0\xbfG \ +O\x15\xceM\xd6\xd5\x8d\xe2k\x05p9d\xe6\xe5\x0d\ +\x92\xdb/X\xbf)\x99\xf7\x0by\x99p\x1f\x8f\xff~\ +\xe0\x05\xe2\x06\xe8q\x90\xef\x96\xb8L\x0b\xd9XVX\ +nfyo\xc2k\x06\xff\x04\xc8o\xb8;\x095\xd3\ +\x00\xb7\xa5\xd9\x91\x14O+3\xef\xbb8\xfd\x8b\xb9\xb3\ +\x02\xb5\xca\xf0\xbe\xc1\xbf?\xbd\x22\xf6\x9d\x9c\x84\xf7\xd4\ +\xfd\x1at\x9e!\x8f\xb1p]H\x87\x84\xfcE|>\ +%\xb9\x13\xf0>\xa4(/\xb8\xf4\xf3\xc3\x8f{\x14\x9e\ +%\xb0A\x0a\xdf\x8b\xecS\xc8\xa3\xc4\xfa\x0d\xdc/X\ +\xdbQ,\xad\xc2\x80\x9cu\x9c\xbf\x86\xd6\x0a\xb8\x97f\ +\xe4\xbf\xbd[\x16\xcd\x0b\x02?fC\xcf_\x07\xf3\x82\ +\xdcG\x1eh\x1d\x0e\xf8lQw\xab\x9a\xe19\xe1\xda\ +\x16\xe9?\xc5\xbe;\xdboo!\x8d\x97w\xfe\xa0\x97\ +@-\x1a\xea~\x94\xc4\xfe\x1f~\xbc\xa9\xe8w\xc0\xff\ +VPwW\xc0\xee\x815\xa4\xee\x1d\x87\xcf\x1b\xf8+\ +3\x5c\x17\x93\xd9\xc8\xae\xc69\xae\x90;)\xe0C\x00\ +\x1e\xce\xbf\xf3\x92\xe9eW\xcal\x05\xe6\xcd\xce\xc3\xf4\ +\x82m\x94\x95\xe5\xce\xff\xe5\xc7\xcaD\xf27\xb8\xc3\x01\ +\xf2\x0f\xe8\x16\xdf\x99\xb5\xd0.\xba\xbb\x079\xaahM\ +\xb0\xb6\xcc\x0b\xab\xf0\x9a\x0a\xf1\x0e\xf2\xe8\xf4b|\x07\ +\x18\xe7}\x0b\x9eSn\x01>\xe7@\x97\xc0\xa3\xa1f\ +a\x92\xad\xa1\xb4\xf6\x03?\xceW\xd2\x7f\x8b\xe6\x01\xbc\ +\x9f?\xc0?YB\xc7,\xbc\xcbW\xb2\xde\x14\xe8\x03\ +8o\xfb\xe0T\x5c_\x09x\x00\x9c#\xb8\xbb\x07\xbe\ +g|\x9f\x91\x9fw,\x9d\xee\xc7\xf7\xdf\x8a\xf6\x9f\xa3\ +\xf9\x00\x0f)\x88\x0dG\xf0\x12\xdf;(\xf7\x1e\x15\xf3\ +\xf7\x0b\x82\x5cj\xa7\xf2\xfd\xe7b\xe3\x17Pk\x09p\ +\x8bA\xb4\xfdPY \x18\xbf(=~T\x91\xf7u\ +\xa5\x07\xe1\xf8\x11?~W\x15zt\x8b\x8a\xdfU\xd9\ +\xf8iU\x8f_\xff\x0e\xf9\x03\xbfC\xfeFU\xcd\x9f\ +\xc1yD\x1a\x04\x11\x03?\xd5\x08\x22\x18~*\xd3\xf9\ +G\xd5Y`2\x0f&\xfc\x87Q\xb4\xaf1\xf0S\xad\ +h\xdf!OK\x9f\xa0z\xfa\x14\xe6ii\x8a\xce\xd3\ +\x12\xc2e\x0d\x16\x95\x1b\xb8\x07\xc1c\x16\xc5\x1f\xbeJ\ +\x091\xf43\xf6\xd0\xcf\xac!L3B\xefn\x8e\xe0\ +\x10\x82\xc4\x0a8w\x89\xf4\xb3\x9b\x8b8K\x00\xffC\ +\x10V\x01\xef\x15\x860\xfa]\xc2\xeb\xfe\x15\xef\x16\x9c\ +\x03\x7f\x1fj\xd0\xfbR\xceg\xe8\x8a\xd6!%\xb73\ +\x0e\xb1\x8ahMr|\xd3\xbayaM|d\x1b\x81\ +\x9f\x18|;\xe9\xc7\xe6P9\x13\xe0\xd3-\xbbfj\ +\x22\xab\x88\xce%{7\xc4\xe4\xd6\x1b\xe1\xbc\x02\x5c\x93\ +\x1ej\x09\x09\xda\xedP\xbf\x22-\x01\xc7\xa2\xc0\xee\xc2\ +\xbam\xe9\xfb\xc1?c\x12\xac[\x07\xfb9\xe0\xbd\x12\ +\xd5\xc1G\xf6\x19\xd8\xcbP\xf3\xae\x949\xf0\xcfw\x99\ +\xefN?6\xbb\x14\x9f\x10W\xec\x9c\xa0\xde\x5c)v\ +?\x9f\xb7\x94\xb2\xe7:\x18\xcf\x90K\x22\xbc>\xf0)\ +\x80\x9f\x11h\x00\xd7@\xf0\xdfO\xf91\x84\xfc?P\ +\x03\x04|\x1c\x22\xf4\xf5\xaf\xa5\xbf_\x17\xfb\x8a\xc0\xaf\ +)8\xc0\x7f\x07\xef\xa4\xfch\xc5\xed'\xb0\xff\xc07\ +Y\xbc\xce\x17\x8f\xcc\xba\xbe\xa5\xfc\xef\x07\xbf\xde\x89y\ +\xc5\xfcz\xb0\x0f\xa9\xb8N\x9a\xb6h\xfa\xa6\xf7\x19h\ +T\xf0n5\xcc9ycOa<\x94\xfe~\xf0k\ +\x85y\x0a\xe0\x99C\xc7d\xca\xb07\xe9\xfc\x1b\xa8g\ +U\xb4\x05<\xea\xfey\xf1\xef\x8a\x7f?\xf8\x0d\xd1^\ +\x82?\x80?\x0a\xbeGIZ\xa7\x86\x8a\x8b\xb9\xfc[\ +\xcc\xf7-\x22^\x22\xfe\xfdP3\xd5i`\xb1\xb8N\ +\xde\x93\xcbd\x19y\x0f\xc5\xe7\x0f~\xcf\x9f\x9f\x04\xce\ +\xc2\x03:\x9e+\xc9\xfb\x9bb\xba\x17\xac\x17\x00\xfeM\ +\xc9\xf9\xab.\xb6\xaf\xe1Ny\xe1\xfe\xc5\xbc\xc0|S\ +\x80nJ\x7f\xff\xf6!\xc5\xea\xa5\xe5>\xf6\x90\xf0\xdd\ +\xf4\xfa\x11\xaf\x14\xc4\x1f\xc4@\x85\xe2I\xa5\xe3\x1f\xed\ +\x1f\xf0\x0f\xa0y\xc8\xad\xc0\xb9\x08\x92\xc6\xa3p\xbd\xbd\ +1\xc5\xf6/\xef\xd9\x15\xe18\x84\x88\xf7\x0b\xd8\xf5\xe8\ +o\x81\x06!\xbf\x07\xe7\xf9@\xcc\x03\x7f_2\xbb_\ +\xd0\xcf\x04CD=\xa4\xa2\xf7\xf3{\xbd ~S\x18\ +[\xa1c\xeb\xe0\x7f\x84\xfc\x0c\x88\xf3\xe2{\xde6\xad\ +K\xc5;U\x93eN1\xdc\x81\x5c\x02?\xa9\xc8\xf3\ +\x0fy~;\x86\xe2\xde2 _\xf2^\x5c\xa3\xe8\xd4\ +B\x9b\xaa\x1b\xc6?\xc2\xe8,A\x1c2\xeb\x86\x13Y\ +8g\xe1\x18\x02:\xf7\xe0\x8b\x17\x96\x15\x98vK\xc6\ +\x80\xa8\xf7C]\x1c\xc4\xbf\x0b\xe7\x9a\xfe\x83\x92\xe3\xe8\ +\x99\xa2r/ 7\x12\xe2\x18\x10\xa7\x009\x0b\xf1\x11\ +\xf0\xbd\xc13\xa0\x86\xa5p\xfe\x16\xf8\xcaD\xf0>\x81\ +\xf5\xf3\xcf:\x95\xc3\x00\xfc\x16\xea\xeb\x80\xfc\xc6\xf2V\ +`\x80\xcf\x1e\xeaxB\x0c\x0c\xd7\x92E<\x11\xf2\x93\ +\xb0\xbfVD\xdc\x01\xf2\xc5\xc0\x17*&g\x8d\xd6\x91\ +\xa9\xda\x80\xb9\xf7]1\xae3\x5c\x17b\xff6\xe4\xba\ +B\x1cVp\x80\x9f\x1bbU\xc9\x1b\x8c\x8a\xf1\x16Q\ +\x03\xea2\xc1\xbe\x94\xc23\xf8\xfa9\xf5\xd9\xba\x05\xf6\ +E\x83\xdf\x16x5\xd0>\xaeOq}+>;\xd0\ +?\x04\xe2\xc4\x98F\x817\x8b\x88\xcd\x81\xdf\x19jU\ +\x80\x5c\x869\x96\x91\xab\xc7\xb7\x0d\x8ax.Z\x1b\xd4\ +\x82\x85\xb8\x1b\xce\x0f\x82\x18(\x9d\xfb\x0by\xa4\xb8\x9e\ +\x14\xf0`\xc8SC4\x9e\xed\xbb\x0b\xe7\xffB\xedJ\ +\x1cs<<\x93\xf2\xc1JVG\x8eo\x97$\x16\xf1\ +\xac\xce\xb8n\x0d\xc8p*\x8fX\xe0\x19\xc2>?\xc1\ +\xfb\xed\x82y\xd5\x92\xf9\x05\xf9\xfagI\xfd[\xfa\xdc\ +\xe5\xf2\x00_\xff\xe6\xdb\x1fO*\xf8}\x82 h\x7f\ +(\x82\xfdUi\xf6ge\xdb\xdf\xccJ\xf4\xc6\xc0\xbb\ +\xc1O\xa1IP\xbe\x8aB?E\x8d\x92~\x0az\xce\ +\x0d\x100\x11\xbcG\x90\x8e C\x0c\xa4\xd3\x7f\xc3\xa4\ +\xbf\xc3\xff\xee9\x04\xdc\x12\xb8\x11\x7fv\xb9\xf4w\xf8\ +\xef\xe5\x96\xf8\x1e\xe4C >\x0du\xe7 \xa6\x07\xf9\ +'B|\x82+0\xe7b\xdf\x858\x19\xd4\xbc\x05\xdd\ +\x06\xc7\x82\xe9\xdeeX\xbe\xad-\xc6{\xf8\xeb-\xe4\ +C\xa0\x8f\xf0\xfbH\x80L\x86\x5c\xd7\xfc\x08\xbf\xc2\xba\ +3\xa0\xeb\x0a\xd4\x92\xe5\xefU\xe1\xf7!\xf7\x0f\x06<\ +\x03\xea\xc9\xe0\xba\xd7\xd6\xcd\xb1\xbc\x80\xb8\x92\x90\xbe\x9e\ +Q\xf8\xfd\xc2zDo\xb0n\x8c\xf3\xe2-\x04\xe2g\ +\x88_S\xb9\x08\xf9T~\x1b\x15\xdf\x11\xf8>U\x1f\ +\x06x<\xe4d\x95\xc8o\xc49\x08]q\xfdH\x88\ +\x9b'Q9*E\xdf\x07\x1b\x0b\xed7\xd4\xc3\xc3}\ +;\x84\xeb\xe5\xf2\xeb\xb1!\x1d\x02rQh\x1eN}\ +\x1fz\x06B\xdcxe\x0b\xec+H?\xb5\x80\xea\xaf\ +\x22X\xbb\x13\xfd?U\x9f\x93\xc4u\x04i}\x1f\x7f\ +\x1f\xf0\x022\x15\xea{\x81M\x08\xf8\x02\x9d\x18\xea\x99\ +\xa6\xee\x1c\x86\xf5\xf5\xec\xdb{q\xcc\x98\x9b\x9e\x88\xf5\ +OZ\xfee\xa0\x9f\xe9`\x93\xc1\x9cqn\x08]\x03\ +\x12\xf2xA\xbe\xc2w\xf89p \xfb\xa0G\x8a\x00\ +\xbdP\xf4\x8c\xf0\x93q\xde\x1a\xe7\x99C>\x04\xe8f\ +\xd0O\x03\xe4 \xc4z\xa1\x87\x16\xc4[\xa9\xbb\x0a\xc5\ +\xe8\x98:\x0b\xcbu\xb9)\xdb\x07Sz\xa0`\xcc\x16\ +\xeau\x01\x9e\xf8=\xe2\x8a\xcb\x1e>\xfdR\xe7g\xb9\ +\x0e\xb7\x1c\xb2I\xf0\xfc\xc8t~e\x1d\xff\x07\x9d\xab\ +\xf3\x85\ \x00\x00_\x84\ I\ I*\x00\x08\x00\x00\x00\x17\x00\xfe\x00\x04\x00\x01\x00\x00\ @@ -26805,336 +26919,336 @@ qt_resource_struct = b"\ \x00\x00\x03\xa8\x00\x02\x00\x00\x00\x01\x00\x00\x00+\ \x00\x00\x00\x00\x00\x00\x00\x00\ \x00\x00\x06\x0c\x00\x01\x00\x00\x00\x01\x00\x00\xde\xc4\ -\x00\x00\x01y\x1f\xa4\xb3Q\ -\x00\x00\x16\x86\x00\x00\x00\x00\x00\x01\x00\x05\x97+\ +\x00\x00\x01y\xd2\xb2\xf5B\ +\x00\x00\x16\x86\x00\x00\x00\x00\x00\x01\x00\x05\x9eC\ \x00\x00\x01x\xc7F\xed\xa9\ -\x00\x00\x16:\x00\x00\x00\x00\x00\x01\x00\x05\x95\x7f\ +\x00\x00\x16:\x00\x00\x00\x00\x00\x01\x00\x05\x9c\x97\ \x00\x00\x01x\xc7F\xf0\x97\ -\x00\x00\x16`\x00\x00\x00\x00\x00\x01\x00\x05\x96S\ +\x00\x00\x16`\x00\x00\x00\x00\x00\x01\x00\x05\x9dk\ \x00\x00\x01x\xc7F\xeb4\ -\x00\x00\x16\x1c\x00\x00\x00\x00\x00\x01\x00\x05\x94\x8c\ +\x00\x00\x16\x1c\x00\x00\x00\x00\x00\x01\x00\x05\x9b\xa4\ \x00\x00\x01x\xc7F\xf0\x87\ \x00\x00\x03\xa8\x00\x02\x00\x00\x00\x1f\x00\x00\x001\ \x00\x00\x00\x00\x00\x00\x00\x00\ -\x00\x00\x13\xb4\x00\x00\x00\x00\x00\x01\x00\x05\x80\xf9\ +\x00\x00\x13\xb4\x00\x00\x00\x00\x00\x01\x00\x05\x88\x11\ \x00\x00\x01x\xc7D\x85\xca\ -\x00\x00\x14\x06\x00\x00\x00\x00\x00\x01\x00\x05\x83\x8d\ +\x00\x00\x14\x06\x00\x00\x00\x00\x00\x01\x00\x05\x8a\xa5\ \x00\x00\x01x\xc7D\x85\xcc\ -\x00\x00\x15v\x00\x00\x00\x00\x00\x01\x00\x05\x90T\ +\x00\x00\x15v\x00\x00\x00\x00\x00\x01\x00\x05\x97l\ \x00\x00\x01x\xc7D\x85\xc3\ -\x00\x00\x12\xf0\x00\x00\x00\x00\x00\x01\x00\x05z\x87\ +\x00\x00\x12\xf0\x00\x00\x00\x00\x00\x01\x00\x05\x81\x9f\ \x00\x00\x01x\xc7D\x83\xc5\ -\x00\x00\x12|\x00\x00\x00\x00\x00\x01\x00\x05w\x97\ +\x00\x00\x12|\x00\x00\x00\x00\x00\x01\x00\x05~\xaf\ \x00\x00\x01x\xc7D\x85\xbb\ -\x00\x00\x152\x00\x00\x00\x00\x00\x01\x00\x05\x8d\xc0\ +\x00\x00\x152\x00\x00\x00\x00\x00\x01\x00\x05\x94\xd8\ \x00\x00\x01x\xc7D\x85e\ -\x00\x00\x12\x9e\x00\x00\x00\x00\x00\x01\x00\x05x\xe1\ +\x00\x00\x12\x9e\x00\x00\x00\x00\x00\x01\x00\x05\x7f\xf9\ \x00\x00\x01x\xc7D\x85\xd0\ -\x00\x00\x14\xf6\x00\x00\x00\x00\x00\x01\x00\x05\x8b,\ +\x00\x00\x14\xf6\x00\x00\x00\x00\x00\x01\x00\x05\x92D\ \x00\x00\x01x\xc7D\x84\xb4\ -\x00\x00\x11\xce\x00\x01\x00\x00\x00\x01\x00\x05s\x95\ +\x00\x00\x11\xce\x00\x01\x00\x00\x00\x01\x00\x05z\xad\ \x00\x00\x01x\xc7D\x83\xca\ -\x00\x00\x12(\x00\x00\x00\x00\x00\x01\x00\x05u\xe9\ +\x00\x00\x12(\x00\x00\x00\x00\x00\x01\x00\x05}\x01\ \x00\x00\x01x\xc7D\x85\xd2\ -\x00\x00\x12\x0c\x00\x01\x00\x00\x00\x01\x00\x05u>\ +\x00\x00\x12\x0c\x00\x01\x00\x00\x00\x01\x00\x05|V\ \x00\x00\x01x\xc7D\x84\x12\ -\x00\x00\x11\xf0\x00\x00\x00\x00\x00\x01\x00\x05s\xf4\ +\x00\x00\x11\xf0\x00\x00\x00\x00\x00\x01\x00\x05{\x0c\ \x00\x00\x01x\xc7D\x84\x12\ -\x00\x00\x12\xc8\x00\x01\x00\x00\x00\x01\x00\x05z+\ +\x00\x00\x12\xc8\x00\x01\x00\x00\x00\x01\x00\x05\x81C\ \x00\x00\x01x\xc7D\x83\xcf\ -\x00\x00\x13\x12\x00\x00\x00\x00\x00\x01\x00\x05{\xd1\ +\x00\x00\x13\x12\x00\x00\x00\x00\x00\x01\x00\x05\x82\xe9\ \x00\x00\x01x\xc7D\x85\xc7\ -\x00\x00\x13\xe0\x00\x00\x00\x00\x00\x01\x00\x05\x82C\ +\x00\x00\x13\xe0\x00\x00\x00\x00\x00\x01\x00\x05\x89[\ \x00\x00\x01x\xc7D\x85\xc8\ -\x00\x00\x13\x90\x00\x00\x00\x00\x00\x01\x00\x05\x7f\xaf\ +\x00\x00\x13\x90\x00\x00\x00\x00\x00\x01\x00\x05\x86\xc7\ \x00\x00\x01x\xc7D\x85d\ -\x00\x00\x13B\x00\x00\x00\x00\x00\x01\x00\x05}\x1b\ +\x00\x00\x13B\x00\x00\x00\x00\x00\x01\x00\x05\x843\ \x00\x00\x01x\xc7D\x85\xce\ -\x00\x00\x12\x5c\x00\x01\x00\x00\x00\x01\x00\x05w3\ +\x00\x00\x12\x5c\x00\x01\x00\x00\x00\x01\x00\x05~K\ \x00\x00\x01x\xc7D\x85-\ -\x00\x00\x15\xc4\x00\x01\x00\x00\x00\x01\x00\x05\x92\xe8\ +\x00\x00\x15\xc4\x00\x01\x00\x00\x00\x01\x00\x05\x9a\x00\ \x00\x00\x01x\xc7D\x83\xcb\ -\x00\x00\x14.\x00\x01\x00\x00\x00\x01\x00\x05\x84\xd7\ +\x00\x00\x14.\x00\x01\x00\x00\x00\x01\x00\x05\x8b\xef\ \x00\x00\x01x\xc7D\x85\xb9\ -\x00\x00\x14Z\x00\x01\x00\x00\x00\x01\x00\x05\x85\xa4\ +\x00\x00\x14Z\x00\x01\x00\x00\x00\x01\x00\x05\x8c\xbc\ \x00\x00\x01x\xc7D\x83\xcc\ -\x00\x00\x13h\x00\x00\x00\x00\x00\x01\x00\x05~e\ +\x00\x00\x13h\x00\x00\x00\x00\x00\x01\x00\x05\x85}\ \x00\x00\x01x\xc7D\x85d\ -\x00\x00\x14x\x00\x00\x00\x00\x00\x01\x00\x05\x86\x04\ +\x00\x00\x14x\x00\x00\x00\x00\x00\x01\x00\x05\x8d\x1c\ \x00\x00\x01x\xc7D\x85\xb6\ -\x00\x00\x15\x94\x00\x00\x00\x00\x00\x01\x00\x05\x91\x9e\ +\x00\x00\x15\x94\x00\x00\x00\x00\x00\x01\x00\x05\x98\xb6\ \x00\x00\x01x\xc7D\x85\xd4\ -\x00\x00\x14\x9c\x00\x00\x00\x00\x00\x01\x00\x05\x87N\ +\x00\x00\x14\x9c\x00\x00\x00\x00\x00\x01\x00\x05\x8ef\ \x00\x00\x01x\xc7D\x84\x0b\ -\x00\x00\x14\xba\x00\x00\x00\x00\x00\x01\x00\x05\x88\x98\ +\x00\x00\x14\xba\x00\x00\x00\x00\x00\x01\x00\x05\x8f\xb0\ \x00\x00\x01x\xc7D\x84\x0d\ -\x00\x00\x14\xd8\x00\x00\x00\x00\x00\x01\x00\x05\x89\xe2\ +\x00\x00\x14\xd8\x00\x00\x00\x00\x00\x01\x00\x05\x90\xfa\ \x00\x00\x01x\xc7D\x84\x0e\ -\x00\x00\x15\x14\x00\x00\x00\x00\x00\x01\x00\x05\x8cv\ +\x00\x00\x15\x14\x00\x00\x00\x00\x00\x01\x00\x05\x93\x8e\ \x00\x00\x01x\xc7D\x84\x0e\ -\x00\x00\x15X\x00\x00\x00\x00\x00\x01\x00\x05\x8f\x0a\ +\x00\x00\x15X\x00\x00\x00\x00\x00\x01\x00\x05\x96\x22\ \x00\x00\x01x\xc7D\x84\x0f\ -\x00\x00\x11\xba\x00\x01\x00\x00\x00\x01\x00\x05s6\ +\x00\x00\x11\xba\x00\x01\x00\x00\x00\x01\x00\x05zN\ \x00\x00\x01x\xc7D\x84\xb5\ -\x00\x00\x15\xf0\x00\x00\x00\x00\x00\x01\x00\x05\x93B\ +\x00\x00\x15\xf0\x00\x00\x00\x00\x00\x01\x00\x05\x9aZ\ \x00\x00\x01x\xc7D\x85\xc5\ \x00\x00\x03\xa8\x00\x02\x00\x00\x00\x08\x00\x00\x00Q\ \x00\x00\x00\x00\x00\x00\x00\x00\ -\x00\x00$\x98\x00\x00\x00\x00\x00\x01\x00\x06+\x0e\ +\x00\x00$\x98\x00\x00\x00\x00\x00\x01\x00\x062&\ \x00\x00\x01y+\x8f\x94\x0d\ -\x00\x00#\xa8\x00\x00\x00\x00\x00\x01\x00\x06\x16\xea\ +\x00\x00#\xa8\x00\x00\x00\x00\x00\x01\x00\x06\x1e\x02\ \x00\x00\x01y+\x8f\x94\x0b\ -\x00\x00$\x0a\x00\x00\x00\x00\x00\x01\x00\x06\x1e\xba\ +\x00\x00$\x0a\x00\x00\x00\x00\x00\x01\x00\x06%\xd2\ \x00\x00\x01y+\x8f\x94\x0b\ -\x00\x00$p\x00\x00\x00\x00\x00\x01\x00\x06&\x8c\ +\x00\x00$p\x00\x00\x00\x00\x00\x01\x00\x06-\xa4\ \x00\x00\x01x\xc7F\xf7d\ -\x00\x00#\xe6\x00\x00\x00\x00\x00\x01\x00\x06\x1a8\ +\x00\x00#\xe6\x00\x00\x00\x00\x00\x01\x00\x06!P\ \x00\x00\x01x\xc7F\xf6\xff\ -\x00\x00#D\x00\x00\x00\x00\x00\x01\x00\x06\x0f\x1e\ +\x00\x00#D\x00\x00\x00\x00\x00\x01\x00\x06\x166\ \x00\x00\x01x\xc7F\xf6\xed\ -\x00\x00$J\x00\x00\x00\x00\x00\x01\x00\x06\x22\x0a\ +\x00\x00$J\x00\x00\x00\x00\x00\x01\x00\x06)\x22\ \x00\x00\x01x\xc7F\xf6\xf0\ -\x00\x00#j\x00\x00\x00\x00\x00\x01\x00\x06\x13\xa0\ +\x00\x00#j\x00\x00\x00\x00\x00\x01\x00\x06\x1a\xb8\ \x00\x00\x01y+\x8f\x94\x0c\ \x00\x00\x16\xba\x00\x02\x00\x00\x009\x00\x00\x00Z\ \x00\x00\x00\x00\x00\x00\x00\x00\ -\x00\x00 :\x00\x00\x00\x00\x00\x01\x00\x05\xf1\xf4\ +\x00\x00 :\x00\x00\x00\x00\x00\x01\x00\x05\xf9\x0c\ \x00\x00\x01x\xc7F\xf0|\ -\x00\x00#\x0e\x00\x00\x00\x00\x00\x01\x00\x06\x0a1\ +\x00\x00#\x0e\x00\x00\x00\x00\x00\x01\x00\x06\x11I\ \x00\x00\x01x\xc7F\xed\xa1\ -\x00\x00\x1e\xb2\x00\x00\x00\x00\x00\x01\x00\x05\xe2\xb6\ +\x00\x00\x1e\xb2\x00\x00\x00\x00\x00\x01\x00\x05\xe9\xce\ \x00\x00\x01x\xc7F\xef\xf0\ -\x00\x00\x19\x08\x00\x00\x00\x00\x00\x01\x00\x05\xaf\x1d\ +\x00\x00\x19\x08\x00\x00\x00\x00\x00\x01\x00\x05\xb65\ \x00\x00\x01x\xc7F\xf0\xa1\ -\x00\x00!\xbc\x00\x00\x00\x00\x00\x01\x00\x05\xff\xfd\ +\x00\x00!\xbc\x00\x00\x00\x00\x00\x01\x00\x06\x07\x15\ \x00\x00\x01x\xc7F\xf0W\ -\x00\x00\x17@\x00\x00\x00\x00\x00\x01\x00\x05\x9d\xdd\ +\x00\x00\x17@\x00\x00\x00\x00\x00\x01\x00\x05\xa4\xf5\ \x00\x00\x01x\xc7F\xed\xd6\ -\x00\x00\x19\xae\x00\x00\x00\x00\x00\x01\x00\x05\xb2\xec\ +\x00\x00\x19\xae\x00\x00\x00\x00\x00\x01\x00\x05\xba\x04\ \x00\x00\x01x\xc7F\xe9{\ -\x00\x00\x22b\x00\x00\x00\x00\x00\x01\x00\x06\x04c\ +\x00\x00\x22b\x00\x00\x00\x00\x00\x01\x00\x06\x0b{\ \x00\x00\x01x\xc7F\xef\xab\ -\x00\x00\x1b\xde\x00\x00\x00\x00\x00\x01\x00\x05\xc5\xad\ +\x00\x00\x1b\xde\x00\x00\x00\x00\x00\x01\x00\x05\xcc\xc5\ \x00\x00\x01x\xc7F\xcb~\ -\x00\x00\x18,\x00\x00\x00\x00\x00\x01\x00\x05\xa5\x22\ +\x00\x00\x18,\x00\x00\x00\x00\x00\x01\x00\x05\xac:\ \x00\x00\x01x\xc7F\xf0\x90\ -\x00\x00\x1a\xd6\x00\x00\x00\x00\x00\x01\x00\x05\xba\xbf\ +\x00\x00\x1a\xd6\x00\x00\x00\x00\x00\x01\x00\x05\xc1\xd7\ \x00\x00\x01x\xc7F\xe9@\ -\x00\x00\x1e<\x00\x00\x00\x00\x00\x01\x00\x05\xdcu\ +\x00\x00\x1e<\x00\x00\x00\x00\x00\x01\x00\x05\xe3\x8d\ \x00\x00\x01x\xc7F\xf0=\ -\x00\x00 \xe0\x00\x00\x00\x00\x00\x01\x00\x05\xf6-\ +\x00\x00 \xe0\x00\x00\x00\x00\x00\x01\x00\x05\xfdE\ \x00\x00\x01x\xc7F\xf0\x03\ -\x00\x00\x1c\xba\x00\x00\x00\x00\x00\x01\x00\x05\xd0B\ +\x00\x00\x1c\xba\x00\x00\x00\x00\x00\x01\x00\x05\xd7Z\ \x00\x00\x01x\xc7F\xed\xd8\ -\x00\x00\x1f\x8e\x00\x00\x00\x00\x00\x01\x00\x05\xec\xea\ +\x00\x00\x1f\x8e\x00\x00\x00\x00\x00\x01\x00\x05\xf4\x02\ \x00\x00\x01x\xc7F\xed\xda\ -\x00\x00\x1cT\x00\x00\x00\x00\x00\x01\x00\x05\xcb\x82\ +\x00\x00\x1cT\x00\x00\x00\x00\x00\x01\x00\x05\xd2\x9a\ \x00\x00\x01x\xc7F\xcc\x09\ -\x00\x00\x18b\x00\x00\x00\x00\x00\x01\x00\x05\xa6\x14\ +\x00\x00\x18b\x00\x00\x00\x00\x00\x01\x00\x05\xad,\ \x00\x00\x01x\xc7F\xe9f\ -\x00\x00\x1e\xe8\x00\x00\x00\x00\x00\x01\x00\x05\xe4c\ +\x00\x00\x1e\xe8\x00\x00\x00\x00\x00\x01\x00\x05\xeb{\ \x00\x00\x01x\xc7F\xe9\x86\ -\x00\x00\x1b\x0c\x00\x00\x00\x00\x00\x01\x00\x05\xbc\x0d\ +\x00\x00\x1b\x0c\x00\x00\x00\x00\x00\x01\x00\x05\xc3%\ \x00\x00\x01x\xc7F\xcb\xfe\ -\x00\x00\x1d\x96\x00\x00\x00\x00\x00\x01\x00\x05\xd7\x13\ +\x00\x00\x1d\x96\x00\x00\x00\x00\x00\x01\x00\x05\xde+\ \x00\x00\x01x\xc7F\xe9\x82\ -\x00\x00!\x16\x00\x00\x00\x00\x00\x01\x00\x05\xf7\xeb\ +\x00\x00!\x16\x00\x00\x00\x00\x00\x01\x00\x05\xff\x03\ \x00\x00\x01x\xc7F\xe7N\ -\x00\x00\x17v\x00\x00\x00\x00\x00\x01\x00\x05\x9f\xff\ +\x00\x00\x17v\x00\x00\x00\x00\x00\x01\x00\x05\xa7\x17\ \x00\x00\x01x\xc7F\xe97\ -\x00\x00\x1f\xc4\x00\x00\x00\x00\x00\x01\x00\x05\xee\xfa\ +\x00\x00\x1f\xc4\x00\x00\x00\x00\x00\x01\x00\x05\xf6\x12\ \x00\x00\x01x\xc7F\xe7\xa5\ -\x00\x00\x22\x98\x00\x00\x00\x00\x00\x01\x00\x06\x06c\ +\x00\x00\x22\x98\x00\x00\x00\x00\x00\x01\x00\x06\x0d{\ \x00\x00\x01x\xc7F\xe6\xb8\ -\x00\x00\x19\xe4\x00\x00\x00\x00\x00\x01\x00\x05\xb4\x0b\ +\x00\x00\x19\xe4\x00\x00\x00\x00\x00\x01\x00\x05\xbb#\ \x00\x00\x01x\xc7F\xef\xeb\ -\x00\x00\x1d\x16\x00\x00\x00\x00\x00\x01\x00\x05\xd3\xe3\ +\x00\x00\x1d\x16\x00\x00\x00\x00\x00\x01\x00\x05\xda\xfb\ \x00\x00\x01x\xc7F\xef\xe2\ -\x00\x00\x1bn\x00\x00\x00\x00\x00\x01\x00\x05\xc1)\ +\x00\x00\x1bn\x00\x00\x00\x00\x00\x01\x00\x05\xc8A\ \x00\x00\x01x\xc7F\xef\x93\ -\x00\x00\x1af\x00\x00\x00\x00\x00\x01\x00\x05\xb7h\ +\x00\x00\x1af\x00\x00\x00\x00\x00\x01\x00\x05\xbe\x80\ \x00\x00\x01x\xc7F\xef\xee\ -\x00\x00\x19>\x00\x00\x00\x00\x00\x01\x00\x05\xaf\xef\ +\x00\x00\x19>\x00\x00\x00\x00\x00\x01\x00\x05\xb7\x07\ \x00\x00\x01x\xc7F\xf00\ -\x00\x00\x17\xec\x00\x00\x00\x00\x00\x01\x00\x05\xa2\xf2\ +\x00\x00\x17\xec\x00\x00\x00\x00\x00\x01\x00\x05\xaa\x0a\ \x00\x00\x01x\xc7F\xed\xd3\ -\x00\x00\x16\xd0\x00\x00\x00\x00\x00\x01\x00\x05\x9b\x19\ +\x00\x00\x16\xd0\x00\x00\x00\x00\x00\x01\x00\x05\xa21\ \x00\x00\x01x\xc7F\xef\xe6\ -\x00\x00!\xf2\x00\x00\x00\x00\x00\x01\x00\x06\x01P\ +\x00\x00!\xf2\x00\x00\x00\x00\x00\x01\x00\x06\x08h\ \x00\x00\x01x\xc7F\xf0f\ -\x00\x00 p\x00\x00\x00\x00\x00\x01\x00\x05\xf2\xfb\ +\x00\x00 p\x00\x00\x00\x00\x00\x01\x00\x05\xfa\x13\ \x00\x00\x01x\xc7F\xf0k\ -\x00\x00\x1f\x1e\x00\x00\x00\x00\x00\x01\x00\x05\xe5k\ +\x00\x00\x1f\x1e\x00\x00\x00\x00\x00\x01\x00\x05\xec\x83\ \x00\x00\x01x\xc7F\xed\xc2\ -\x00\x00\x1d\xcc\x00\x00\x00\x00\x00\x01\x00\x05\xd8!\ +\x00\x00\x1d\xcc\x00\x00\x00\x00\x00\x01\x00\x05\xdf9\ \x00\x00\x01x\xc7F\xf0\x0e\ -\x00\x00\x1a&\x00\x00\x00\x00\x00\x01\x00\x05\xb5\xde\ +\x00\x00\x1a&\x00\x00\x00\x00\x00\x01\x00\x05\xbc\xf6\ \x00\x00\x01x\xc7F\xf0%\ -\x00\x00\x18\x98\x00\x00\x00\x00\x00\x01\x00\x05\xa7M\ +\x00\x00\x18\x98\x00\x00\x00\x00\x00\x01\x00\x05\xaee\ \x00\x00\x01x\xc7F\xf0I\ -\x00\x00\x17\xac\x00\x00\x00\x00\x00\x01\x00\x05\xa1s\ +\x00\x00\x17\xac\x00\x00\x00\x00\x00\x01\x00\x05\xa8\x8b\ \x00\x00\x01x\xc7F\xf0,\ -\x00\x00\x22\xce\x00\x00\x00\x00\x00\x01\x00\x06\x08\x1f\ +\x00\x00\x22\xce\x00\x00\x00\x00\x00\x01\x00\x06\x0f7\ \x00\x00\x01x\xc7F\xed\xdd\ -\x00\x00!|\x00\x00\x00\x00\x00\x01\x00\x05\xfe\x91\ +\x00\x00!|\x00\x00\x00\x00\x00\x01\x00\x06\x05\xa9\ \x00\x00\x01x\xc7F\xf04\ -\x00\x00\x1f\xfa\x00\x00\x00\x00\x00\x01\x00\x05\xf0{\ +\x00\x00\x1f\xfa\x00\x00\x00\x00\x00\x01\x00\x05\xf7\x93\ \x00\x00\x01x\xc7F\xf0*\ -\x00\x00\x1er\x00\x00\x00\x00\x00\x01\x00\x05\xdd\xd6\ +\x00\x00\x1er\x00\x00\x00\x00\x00\x01\x00\x05\xe4\xee\ \x00\x00\x01x\xc7F\xed\xc6\ -\x00\x00\x1dV\x00\x00\x00\x00\x00\x01\x00\x05\xd5\xb7\ +\x00\x00\x1dV\x00\x00\x00\x00\x00\x01\x00\x05\xdc\xcf\ \x00\x00\x01x\xc7F\xf0@\ -\x00\x00\x1c\x14\x00\x00\x00\x00\x00\x01\x00\x05\xca)\ +\x00\x00\x1c\x14\x00\x00\x00\x00\x00\x01\x00\x05\xd1A\ \x00\x00\x01x\xc7F\xf0K\ -\x00\x00\x1bB\x00\x00\x00\x00\x00\x01\x00\x05\xbe&\ +\x00\x00\x1bB\x00\x00\x00\x00\x00\x01\x00\x05\xc5>\ \x00\x00\x01x\xc7F\xed\xcb\ -\x00\x00\x1e\x0c\x00\x00\x00\x00\x00\x01\x00\x05\xd9\xa3\ +\x00\x00\x1e\x0c\x00\x00\x00\x00\x00\x01\x00\x05\xe0\xbb\ \x00\x00\x01x\xc7F\xed\xc8\ -\x00\x00 \xb0\x00\x00\x00\x00\x00\x01\x00\x05\xf4(\ +\x00\x00 \xb0\x00\x00\x00\x00\x00\x01\x00\x05\xfb@\ \x00\x00\x01x\xc7F\xef\xa6\ -\x00\x00\x1c\x8a\x00\x00\x00\x00\x00\x01\x00\x05\xcd\x7f\ +\x00\x00\x1c\x8a\x00\x00\x00\x00\x00\x01\x00\x05\xd4\x97\ \x00\x00\x01x\xc7F\xed\xce\ -\x00\x00\x17\x10\x00\x00\x00\x00\x00\x01\x00\x05\x9c\xd8\ +\x00\x00\x17\x10\x00\x00\x00\x00\x00\x01\x00\x05\xa3\xf0\ \x00\x00\x01x\xc7F\xf0\x80\ -\x00\x00\x1f^\x00\x00\x00\x00\x00\x01\x00\x05\xeb\x8c\ +\x00\x00\x1f^\x00\x00\x00\x00\x00\x01\x00\x05\xf2\xa4\ \x00\x00\x01x\xc7F\xf0:\ -\x00\x00\x19~\x00\x00\x00\x00\x00\x01\x00\x05\xb1p\ +\x00\x00\x19~\x00\x00\x00\x00\x00\x01\x00\x05\xb8\x88\ \x00\x00\x01x\xc7F\xf0(\ -\x00\x00\x222\x00\x00\x00\x00\x00\x01\x00\x06\x02\x83\ +\x00\x00\x222\x00\x00\x00\x00\x00\x01\x00\x06\x09\x9b\ \x00\x00\x01x\xc7F\xef\xdd\ -\x00\x00\x1b\xae\x00\x00\x00\x00\x00\x01\x00\x05\xc3\x18\ +\x00\x00\x1b\xae\x00\x00\x00\x00\x00\x01\x00\x05\xca0\ \x00\x00\x01x\xc7F\xed\xd1\ -\x00\x00!L\x00\x00\x00\x00\x00\x01\x00\x05\xf9\xa7\ +\x00\x00!L\x00\x00\x00\x00\x00\x01\x00\x06\x00\xbf\ \x00\x00\x01y+\x8f\x93{\ -\x00\x00\x1a\xa6\x00\x00\x00\x00\x00\x01\x00\x05\xb92\ +\x00\x00\x1a\xa6\x00\x00\x00\x00\x00\x01\x00\x05\xc0J\ \x00\x00\x01x\xc7F\xf0\x0c\ -\x00\x00\x18\xd8\x00\x00\x00\x00\x00\x01\x00\x05\xa8\xa5\ +\x00\x00\x18\xd8\x00\x00\x00\x00\x00\x01\x00\x05\xaf\xbd\ \x00\x00\x01x\xc7F\xed\xa4\ -\x00\x00\x1c\xf0\x00\x00\x00\x00\x00\x01\x00\x05\xd2\x83\ +\x00\x00\x1c\xf0\x00\x00\x00\x00\x00\x01\x00\x05\xd9\x9b\ \x00\x00\x01x\xc7F\xe9<\ -\x00\x00\x0e\xba\x00\x00\x00\x00\x00\x01\x00\x04(&\ +\x00\x00\x0e\xba\x00\x00\x00\x00\x00\x01\x00\x04/>\ \x00\x00\x01y+\x8f\x94\x12\ -\x00\x00\x0d\x0a\x00\x00\x00\x00\x00\x01\x00\x03\x91\x87\ +\x00\x00\x0d\x0a\x00\x00\x00\x00\x00\x01\x00\x03\x98\x9f\ \x00\x00\x01y+\x8f\x93\xdc\ -\x00\x00\x0e\x80\x00\x00\x00\x00\x00\x01\x00\x04#\x0e\ +\x00\x00\x0e\x80\x00\x00\x00\x00\x00\x01\x00\x04*&\ \x00\x00\x01y+\x8f\x94\x08\ -\x00\x00\x0d~\x00\x00\x00\x00\x00\x01\x00\x03\xf4\x82\ +\x00\x00\x0d~\x00\x00\x00\x00\x00\x01\x00\x03\xfb\x9a\ \x00\x00\x01y+\x8f\x93\xcd\ -\x00\x00\x09\xf6\x00\x00\x00\x00\x00\x01\x00\x032\x17\ +\x00\x00\x09\xf6\x00\x00\x00\x00\x00\x01\x00\x039/\ \x00\x00\x01y+\x8f\x94\x10\ -\x00\x00\x09f\x00\x01\x00\x00\x00\x01\x00\x02i(\ +\x00\x00\x09f\x00\x01\x00\x00\x00\x01\x00\x02p@\ \x00\x00\x01x\xc7F\xf5\xfe\ -\x00\x00\x07\x12\x00\x00\x00\x00\x00\x01\x00\x01\x87\xf3\ +\x00\x00\x07\x12\x00\x00\x00\x00\x00\x01\x00\x01\x8f\x0b\ \x00\x00\x01y+\x8f\x93\xce\ -\x00\x00\x08:\x00\x00\x00\x00\x00\x01\x00\x01\xa3\x1b\ +\x00\x00\x08:\x00\x00\x00\x00\x00\x01\x00\x01\xaa3\ \x00\x00\x01y+\x8f\x94\x0a\ -\x00\x00\x0f$\x00\x00\x00\x00\x00\x01\x00\x04jD\ +\x00\x00\x0f$\x00\x00\x00\x00\x00\x01\x00\x04q\x5c\ \x00\x00\x01x\xc7F\xf6G\ -\x00\x00\x0e\x10\x00\x00\x00\x00\x00\x01\x00\x04\x15\xa7\ +\x00\x00\x0e\x10\x00\x00\x00\x00\x00\x01\x00\x04\x1c\xbf\ \x00\x00\x01y+\x8f\x93\xe3\ -\x00\x00\x08\xd2\x00\x00\x00\x00\x00\x01\x00\x02^f\ +\x00\x00\x08\xd2\x00\x00\x00\x00\x00\x01\x00\x02e~\ \x00\x00\x01y+\x8f\x93\xd6\ -\x00\x00\x0eL\x00\x00\x00\x00\x00\x01\x00\x04\x1d\xe2\ +\x00\x00\x0eL\x00\x00\x00\x00\x00\x01\x00\x04$\xfa\ \x00\x00\x01y+\x8f\x94\x09\ -\x00\x00\x09*\x00\x00\x00\x00\x00\x01\x00\x02f\x89\ +\x00\x00\x09*\x00\x00\x00\x00\x00\x01\x00\x02m\xa1\ \x00\x00\x01y+\x8f\x94\x07\ -\x00\x00\x0c \x00\x00\x00\x00\x00\x01\x00\x03u!\ +\x00\x00\x0c \x00\x00\x00\x00\x00\x01\x00\x03|9\ \x00\x00\x01y+\x8f\x93\xd5\ -\x00\x00\x07z\x00\x00\x00\x00\x00\x01\x00\x01\x96/\ +\x00\x00\x07z\x00\x00\x00\x00\x00\x01\x00\x01\x9dG\ \x00\x00\x01y+\x8f\x93\xdf\ -\x00\x00\x08x\x00\x00\x00\x00\x00\x01\x00\x01\xbe\xd0\ +\x00\x00\x08x\x00\x00\x00\x00\x00\x01\x00\x01\xc5\xe8\ \x00\x00\x01x\xc7F\xf6*\ -\x00\x00\x0f\x9c\x00\x00\x00\x00\x00\x01\x00\x04\x87Q\ +\x00\x00\x0f\x9c\x00\x00\x00\x00\x00\x01\x00\x04\x8ei\ \x00\x00\x01y+\x8f\x94\x12\ -\x00\x00\x08T\x00\x00\x00\x00\x00\x01\x00\x01\xa9(\ +\x00\x00\x08T\x00\x00\x00\x00\x00\x01\x00\x01\xb0@\ \x00\x00\x01x\xc7F\xf61\ -\x00\x00\x0cb\x00\x00\x00\x00\x00\x01\x00\x03\x81c\ +\x00\x00\x0cb\x00\x00\x00\x00\x00\x01\x00\x03\x88{\ \x00\x00\x01y+\x8f\x93\xda\ -\x00\x00\x07B\x00\x00\x00\x00\x00\x01\x00\x01\x8c\x97\ +\x00\x00\x07B\x00\x00\x00\x00\x00\x01\x00\x01\x93\xaf\ \x00\x00\x01y+\x8f\x94\x11\ -\x00\x00\x08\xf8\x00\x00\x00\x00\x00\x01\x00\x02b\x14\ +\x00\x00\x08\xf8\x00\x00\x00\x00\x00\x01\x00\x02i,\ \x00\x00\x01y+\x8f\x93\xce\ -\x00\x00\x0a\xc0\x00\x00\x00\x00\x00\x01\x00\x03N\xe6\ +\x00\x00\x0a\xc0\x00\x00\x00\x00\x00\x01\x00\x03U\xfe\ \x00\x00\x01y+\x8f\x93\xd8\ -\x00\x00\x09\xc8\x00\x01\x00\x00\x00\x01\x00\x03\x12$\ +\x00\x00\x09\xc8\x00\x01\x00\x00\x00\x01\x00\x03\x19<\ \x00\x00\x01x\xc7F\xf5\xf8\ -\x00\x00\x06\x92\x00\x00\x00\x00\x00\x01\x00\x01|\xed\ +\x00\x00\x06\x92\x00\x00\x00\x00\x00\x01\x00\x01\x84\x05\ \x00\x00\x01y+\x8f\x93\xcc\ -\x00\x00\x0a\xf8\x00\x00\x00\x00\x00\x01\x00\x03R\x98\ +\x00\x00\x0a\xf8\x00\x00\x00\x00\x00\x01\x00\x03Y\xb0\ \x00\x00\x01y+\x8f\x93\xdb\ -\x00\x00\x0d\x98\x00\x00\x00\x00\x00\x01\x00\x03\xf8 \ +\x00\x00\x0d\x98\x00\x00\x00\x00\x00\x01\x00\x03\xff8\ \x00\x00\x01y+\x8f\x93\xdc\ -\x00\x00\x0c<\x00\x00\x00\x00\x00\x01\x00\x03|B\ +\x00\x00\x0c<\x00\x00\x00\x00\x00\x01\x00\x03\x83Z\ \x00\x00\x01y+\x8f\x93\xde\ -\x00\x00\x0bZ\x00\x00\x00\x00\x00\x01\x00\x03W)\ +\x00\x00\x0bZ\x00\x00\x00\x00\x00\x01\x00\x03^A\ \x00\x00\x01y+\x8f\x93\xd8\ -\x00\x00\x0a\x94\x00\x00\x00\x00\x00\x01\x00\x039\x16\ +\x00\x00\x0a\x94\x00\x00\x00\x00\x00\x01\x00\x03@.\ \x00\x00\x01x\xc7F\xf60\ -\x00\x00\x0f\x00\x00\x00\x00\x00\x00\x01\x00\x04`\xcc\ +\x00\x00\x0f\x00\x00\x00\x00\x00\x00\x01\x00\x04g\xe4\ \x00\x00\x01y+\x8f\x94\x13\ -\x00\x00\x0fN\x00\x00\x00\x00\x00\x01\x00\x04\x7f\x0c\ +\x00\x00\x0fN\x00\x00\x00\x00\x00\x01\x00\x04\x86$\ \x00\x00\x01y+\x8f\x93\xe2\ -\x00\x00\x0dD\x00\x00\x00\x00\x00\x01\x00\x03\x95\x12\ +\x00\x00\x0dD\x00\x00\x00\x00\x00\x01\x00\x03\x9c*\ \x00\x00\x01x\xc7F\xf6\x18\ -\x00\x00\x0a0\x00\x00\x00\x00\x00\x01\x00\x034\xb6\ +\x00\x00\x0a0\x00\x00\x00\x00\x00\x01\x00\x03;\xce\ \x00\x00\x01y+\x8f\x93\xd9\ -\x00\x00\x060\x00\x00\x00\x00\x00\x01\x00\x01\x1a\xc3\ +\x00\x00\x060\x00\x00\x00\x00\x00\x01\x00\x01!\xdb\ \x00\x00\x01x\xc7F\xf6\x1b\ -\x00\x00\x0b\xe2\x00\x00\x00\x00\x00\x01\x00\x03p\xae\ +\x00\x00\x0b\xe2\x00\x00\x00\x00\x00\x01\x00\x03w\xc6\ \x00\x00\x01y+\x8f\x93\xd7\ -\x00\x00\x06\xf0\x00\x00\x00\x00\x00\x01\x00\x01\x84\xba\ +\x00\x00\x06\xf0\x00\x00\x00\x00\x00\x01\x00\x01\x8b\xd2\ \x00\x00\x01y+\x8f\x93\xfd\ -\x00\x00\x07\xb8\x00\x00\x00\x00\x00\x01\x00\x01\x9bR\ +\x00\x00\x07\xb8\x00\x00\x00\x00\x00\x01\x00\x01\xa2j\ \x00\x00\x01y+\x8f\x94\x0f\ -\x00\x00\x0f\xce\x00\x00\x00\x00\x00\x01\x00\x04\x90\xf8\ +\x00\x00\x0f\xce\x00\x00\x00\x00\x00\x01\x00\x04\x98\x10\ \x00\x00\x01y+\x8f\x93\xcb\ -\x00\x00\x0e\xd0\x00\x01\x00\x00\x00\x01\x00\x04/\x99\ +\x00\x00\x0e\xd0\x00\x01\x00\x00\x00\x01\x00\x046\xb1\ \x00\x00\x01x\xc7F\xf5\xf3\ -\x00\x00\x06^\x00\x00\x00\x00\x00\x01\x00\x01zK\ +\x00\x00\x06^\x00\x00\x00\x00\x00\x01\x00\x01\x81c\ \x00\x00\x01y+\x8f\x94\x06\ -\x00\x00\x09\xb4\x00\x00\x00\x00\x00\x01\x00\x03\x0c\xa3\ +\x00\x00\x09\xb4\x00\x00\x00\x00\x00\x01\x00\x03\x13\xbb\ \x00\x00\x01y+\x8f\x93\xd0\ -\x00\x00\x0f\xf8\x00\x00\x00\x00\x00\x01\x00\x04\x93\xb2\ +\x00\x00\x0f\xf8\x00\x00\x00\x00\x00\x01\x00\x04\x9a\xca\ \x00\x00\x01x\xc7F\xf5\xf0\ -\x00\x00\x09\x86\x00\x00\x00\x00\x00\x01\x00\x02\x8c\x97\ +\x00\x00\x09\x86\x00\x00\x00\x00\x00\x01\x00\x02\x93\xaf\ \x00\x00\x01x\xc7F\xf6$\ -\x00\x00\x0d\xe4\x00\x01\x00\x00\x00\x01\x00\x03\xfb\xab\ +\x00\x00\x0d\xe4\x00\x01\x00\x00\x00\x01\x00\x04\x02\xc3\ \x00\x00\x01x\xc7F\xf6\x04\ -\x00\x00\x0c\xec\x00\x00\x00\x00\x00\x01\x00\x03\x8a\xa7\ +\x00\x00\x0c\xec\x00\x00\x00\x00\x00\x01\x00\x03\x91\xbf\ \x00\x00\x01y+\x8f\x94\x0e\ -\x00\x00\x0b\xaa\x00\x00\x00\x00\x00\x01\x00\x03[\xa0\ +\x00\x00\x0b\xaa\x00\x00\x00\x00\x00\x01\x00\x03b\xb8\ \x00\x00\x01x\xc7F\xf6L\ -\x00\x00\x06\xb8\x00\x00\x00\x00\x00\x01\x00\x01\x7f\x95\ +\x00\x00\x06\xb8\x00\x00\x00\x00\x00\x01\x00\x01\x86\xad\ \x00\x00\x01y+\x8f\x93\xe0\ -\x00\x00\x0c\xb0\x00\x00\x00\x00\x00\x01\x00\x03\x86\x07\ +\x00\x00\x0c\xb0\x00\x00\x00\x00\x00\x01\x00\x03\x8d\x1f\ \x00\x00\x01y+\x8f\x93\xdd\ -\x00\x00\x07\xea\x00\x00\x00\x00\x00\x01\x00\x01\x9d\xf4\ +\x00\x00\x07\xea\x00\x00\x00\x00\x00\x01\x00\x01\xa5\x0c\ \x00\x00\x01y+\x8f\x93\xe1\ -\x00\x00\x08\xb4\x00\x00\x00\x00\x00\x01\x00\x02H\xcc\ +\x00\x00\x08\xb4\x00\x00\x00\x00\x00\x01\x00\x02O\xe4\ \x00\x00\x01x\xc7F\xf6D\ -\x00\x00\x11\x06\x00\x00\x00\x00\x00\x01\x00\x05@\xf3\ +\x00\x00\x11\x06\x00\x00\x00\x00\x00\x01\x00\x05H\x0b\ \x00\x00\x01y+\x8f\x93\xd4\ -\x00\x00\x11\x88\x00\x00\x00\x00\x00\x01\x00\x05k\xae\ +\x00\x00\x11\x88\x00\x00\x00\x00\x00\x01\x00\x05r\xc6\ \x00\x00\x01y+\x8f\x93\xd1\ -\x00\x00\x11b\x00\x00\x00\x00\x00\x01\x00\x05Yl\ +\x00\x00\x11b\x00\x00\x00\x00\x00\x01\x00\x05`\x84\ \x00\x00\x01y+\x8f\x93\xcf\ -\x00\x00\x11\xa0\x00\x00\x00\x00\x00\x01\x00\x05o\xc9\ +\x00\x00\x11\xa0\x00\x00\x00\x00\x00\x01\x00\x05v\xe1\ \x00\x00\x01y+\x8f\x93\xd2\ -\x00\x00\x10\x94\x00\x00\x00\x00\x00\x01\x00\x059\xf7\ +\x00\x00\x10\x94\x00\x00\x00\x00\x00\x01\x00\x05A\x0f\ \x00\x00\x01x\xc7F\xf8/\ -\x00\x00\x106\x00\x00\x00\x00\x00\x01\x00\x05!\xc2\ +\x00\x00\x106\x00\x00\x00\x00\x00\x01\x00\x05(\xda\ \x00\x00\x01x\xc7F\xf8,\ -\x00\x00\x11 \x00\x00\x00\x00\x00\x01\x00\x05F\xfd\ +\x00\x00\x11 \x00\x00\x00\x00\x00\x01\x00\x05N\x15\ \x00\x00\x01x\xc7F\xf83\ -\x00\x00\x10\xdc\x00\x00\x00\x00\x00\x01\x00\x05@%\ +\x00\x00\x10\xdc\x00\x00\x00\x00\x00\x01\x00\x05G=\ \x00\x00\x01x\xc7F\xf8(\ -\x00\x00\x10`\x00\x00\x00\x00\x00\x01\x00\x05\x22O\ +\x00\x00\x10`\x00\x00\x00\x00\x00\x01\x00\x05)g\ \x00\x00\x01y+\x8f\x93\xd3\ -\x00\x00\x11J\x00\x00\x00\x00\x00\x01\x00\x05G\xa1\ +\x00\x00\x11J\x00\x00\x00\x00\x00\x01\x00\x05N\xb9\ \x00\x00\x01y+\x8f\x93\xca\ -\x00\x00\x10z\x00\x00\x00\x00\x00\x01\x00\x0509\ +\x00\x00\x10z\x00\x00\x00\x00\x00\x01\x00\x057Q\ \x00\x00\x01y+\x8f\x93\xc9\ -\x00\x00\x10\xbe\x00\x00\x00\x00\x00\x01\x00\x05:\xa6\ +\x00\x00\x10\xbe\x00\x00\x00\x00\x00\x01\x00\x05A\xbe\ \x00\x00\x01y+\x8f\x93\xe3\ " diff --git a/Gems/AWSCore/Code/Tools/ResourceMappingTool/utils/aws_utils.py b/Gems/AWSCore/Code/Tools/ResourceMappingTool/utils/aws_utils.py index b0c3c9c1b3..8152d2addc 100755 --- a/Gems/AWSCore/Code/Tools/ResourceMappingTool/utils/aws_utils.py +++ b/Gems/AWSCore/Code/Tools/ResourceMappingTool/utils/aws_utils.py @@ -12,10 +12,10 @@ WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. import boto3 from botocore.paginate import (PageIterator, Paginator) from botocore.client import BaseClient -from botocore.exceptions import ClientError +from botocore.exceptions import (ClientError, ConfigNotFound, NoCredentialsError, ProfileNotFound) from typing import Dict, List -from model import (constants, error_messages) +from model import error_messages from model.basic_resource_attributes import (BasicResourceAttributes, BasicResourceAttributesBuilder) """ @@ -65,8 +65,11 @@ def _initialize_boto3_aws_client(service: str, region: str = "") -> BaseClient: def setup_default_session(profile: str) -> None: - global default_session - default_session = boto3.session.Session(profile_name=profile) + try: + global default_session + default_session = boto3.session.Session(profile_name=profile) + except (ConfigNotFound, ProfileNotFound) as error: + raise RuntimeError(error) def get_default_account_id() -> str: @@ -76,6 +79,8 @@ def get_default_account_id() -> str: except ClientError as error: raise RuntimeError(error_messages.AWS_SERVICE_REQUEST_CLIENT_ERROR_MESSAGE.format( "get_caller_identity", error.response['Error']['Code'], error.response['Error']['Message'])) + except NoCredentialsError as error: + raise RuntimeError(error) def get_default_region() -> str: diff --git a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/FIR-Weights.cpp b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/FIR-Weights.cpp index 192c82c165..b1d0cfbda0 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/FIR-Weights.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/Code/Source/Converters/FIR-Weights.cpp @@ -88,7 +88,7 @@ namespace ImageProcessingAtom int dstPosition; signed short int n; bool trimZeros = true, stillzero; - int lastnonzero, hWeight, highest; + int lastnonzero = 0, hWeight, highest = 0; signed int sumiWeights, iWeight; signed short int* weightsPtr; signed short int* weightsMem; diff --git a/Gems/Atom/Asset/ImageProcessingAtom/External/CubeMapGen/CCubeMapProcessor.cpp b/Gems/Atom/Asset/ImageProcessingAtom/External/CubeMapGen/CCubeMapProcessor.cpp index 26768f8f2e..df31067af0 100644 --- a/Gems/Atom/Asset/ImageProcessingAtom/External/CubeMapGen/CCubeMapProcessor.cpp +++ b/Gems/Atom/Asset/ImageProcessingAtom/External/CubeMapGen/CCubeMapProcessor.cpp @@ -1106,7 +1106,7 @@ namespace ImageProcessingAtom //fractional amount to apply change in tap intensity along edge to taps // in a perpendicular direction to edge CP_ITYPE fixupFrac = (CP_ITYPE)(fixupDist - iFixup) / (CP_ITYPE)(fixupDist); - CP_ITYPE fixupWeight; + CP_ITYPE fixupWeight = 0.0f; switch(a_FixupType ) { diff --git a/Gems/Atom/Feature/Common/Assets/Materials/Special/ShadowCatcher.azsl b/Gems/Atom/Feature/Common/Assets/Materials/Special/ShadowCatcher.azsl index 942db3ed5f..4a228f076c 100644 --- a/Gems/Atom/Feature/Common/Assets/Materials/Special/ShadowCatcher.azsl +++ b/Gems/Atom/Feature/Common/Assets/Materials/Special/ShadowCatcher.azsl @@ -37,7 +37,7 @@ ShaderResourceGroup MaterialSrg : SRG_PerMaterial } #include -#include +#include #include #include diff --git a/Gems/Atom/Feature/Common/Assets/Passes/TransparentParent.pass b/Gems/Atom/Feature/Common/Assets/Passes/TransparentParent.pass index b278f2bcb4..a9db59c646 100644 --- a/Gems/Atom/Feature/Common/Assets/Passes/TransparentParent.pass +++ b/Gems/Atom/Feature/Common/Assets/Passes/TransparentParent.pass @@ -124,7 +124,7 @@ "DrawListSortType": "KeyThenReverseDepth", "PipelineViewTag": "MainCamera", "PassSrgAsset": { - "FilePath": "shaderlib/atom/features/pbr/transparentpasssrg.azsli:PassSrg" + "FilePath": "shaderlib/atom/features/pbr/forwardpasssrg.azsli:PassSrg" } } } diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/ForwardPassSrg.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/ForwardPassSrg.azsli index 14cff21739..d9367f9d03 100644 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/ForwardPassSrg.azsli +++ b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/ForwardPassSrg.azsli @@ -35,4 +35,5 @@ ShaderResourceGroup PassSrg : SRG_PerPass Texture2D m_tileLightData; StructuredBuffer m_lightListRemapped; + Texture2D m_linearDepthTexture; } diff --git a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/TransparentPassSrg.azsli b/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/TransparentPassSrg.azsli deleted file mode 100644 index d9367f9d03..0000000000 --- a/Gems/Atom/Feature/Common/Assets/ShaderLib/Atom/Features/PBR/TransparentPassSrg.azsli +++ /dev/null @@ -1,39 +0,0 @@ -/* -* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -* its licensors. -* -* For complete copyright and license terms please see the LICENSE at the root of this -* distribution (the "License"). All use of this software is governed by the License, -* or, if provided, by the license below or the license accompanying this file. Do not -* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -* -*/ - -#pragma once - -#include - -ShaderResourceGroup PassSrg : SRG_PerPass -{ - // [GFX TODO][ATOM-2012] adapt to multiple shadowmaps - Texture2DArray m_directionalLightShadowmap; - Texture2DArray m_directionalLightExponentialShadowmap; - Texture2DArray m_projectedShadowmaps; - Texture2DArray m_projectedExponentialShadowmap; - Texture2D m_brdfMap; - - Sampler LinearSampler - { - MinFilter = Linear; - MagFilter = Linear; - MipFilter = Linear; - AddressU = Clamp; - AddressV = Clamp; - AddressW = Clamp; - }; - - Texture2D m_tileLightData; - StructuredBuffer m_lightListRemapped; - Texture2D m_linearDepthTexture; -} diff --git a/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake b/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake index 359c0b9b20..a9ba765329 100644 --- a/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake +++ b/Gems/Atom/Feature/Common/Assets/atom_feature_common_asset_files.cmake @@ -246,7 +246,6 @@ set(FILES ShaderLib/Atom/Features/PBR/Hammersley.azsli ShaderLib/Atom/Features/PBR/LightingOptions.azsli ShaderLib/Atom/Features/PBR/LightingUtils.azsli - ShaderLib/Atom/Features/PBR/TransparentPassSrg.azsli ShaderLib/Atom/Features/PBR/Lighting/DualSpecularLighting.azsli ShaderLib/Atom/Features/PBR/Lighting/EnhancedLighting.azsli ShaderLib/Atom/Features/PBR/Lighting/LightingData.azsli diff --git a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DiskLightFeatureProcessorInterface.h b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DiskLightFeatureProcessorInterface.h index 50c0aa2455..ce911fecf7 100644 --- a/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DiskLightFeatureProcessorInterface.h +++ b/Gems/Atom/Feature/Common/Code/Include/Atom/Feature/CoreLights/DiskLightFeatureProcessorInterface.h @@ -46,8 +46,6 @@ namespace AZ uint16_t m_padding; // Explicit padding. }; - static constexpr size_t size = sizeof(DiskLightData); - //! DiskLightFeatureProcessorInterface provides an interface to acquire, release, and update a disk light. This is necessary for code outside of //! the Atom features gem to communicate with the DiskLightFeatureProcessor. class DiskLightFeatureProcessorInterface diff --git a/Gems/Atom/RHI/DX12/Code/Source/RHI/FrameGraphCompiler.cpp b/Gems/Atom/RHI/DX12/Code/Source/RHI/FrameGraphCompiler.cpp index 7fc1db179a..e4fff11d32 100644 --- a/Gems/Atom/RHI/DX12/Code/Source/RHI/FrameGraphCompiler.cpp +++ b/Gems/Atom/RHI/DX12/Code/Source/RHI/FrameGraphCompiler.cpp @@ -484,6 +484,7 @@ namespace AZ } D3D12_RESOURCE_TRANSITION_BARRIER transition; + memset(&transition, 0, sizeof(D3D12_RESOURCE_TRANSITION_BARRIER)); // C4701 potentially unitialized local variable 'transition' used transition.pResource = image.GetMemoryView().GetMemory(); Scope& firstScope = static_cast(scopeAttachment->GetScope()); diff --git a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Queue.cpp b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Queue.cpp index 5bebc92125..88d103d282 100644 --- a/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Queue.cpp +++ b/Gems/Atom/RHI/Vulkan/Code/Source/RHI/Queue.cpp @@ -45,7 +45,7 @@ namespace AZ Fence* fenceToSignal) { AZStd::vector vkCommandBuffers; - AZStd::vector vkWaitSemaphores; + AZStd::vector vkWaitSemaphoreVector; // vulkan.h has a #define called vkWaitSemaphores, so we name this differently AZStd::vector vkWaitPipelineStages; AZStd::vector vkSignalSemaphores; VkSubmitInfo submitInfo; @@ -65,11 +65,11 @@ namespace AZ return item->GetNativeSemaphore(); }); vkWaitPipelineStages.reserve(waitSemaphoresInfo.size()); - vkWaitSemaphores.reserve(waitSemaphoresInfo.size()); + vkWaitSemaphoreVector.reserve(waitSemaphoresInfo.size()); AZStd::for_each(waitSemaphoresInfo.begin(), waitSemaphoresInfo.end(), [&](auto& item) { vkWaitPipelineStages.push_back(item.first); - vkWaitSemaphores.push_back(item.second->GetNativeSemaphore()); + vkWaitSemaphoreVector.push_back(item.second->GetNativeSemaphore()); // Wait until the wait semaphores has been submitted for signaling. item.second->WaitEvent(); }); @@ -77,8 +77,8 @@ namespace AZ submitInfo = {}; submitInfo.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submitInfo.pNext = nullptr; - submitInfo.waitSemaphoreCount = static_cast(vkWaitSemaphores.size()); - submitInfo.pWaitSemaphores = vkWaitSemaphores.empty() ? nullptr : vkWaitSemaphores.data(); + submitInfo.waitSemaphoreCount = static_cast(vkWaitSemaphoreVector.size()); + submitInfo.pWaitSemaphores = vkWaitSemaphoreVector.empty() ? nullptr : vkWaitSemaphoreVector.data(); submitInfo.pWaitDstStageMask = vkWaitPipelineStages.empty() ? nullptr : vkWaitPipelineStages.data(); submitInfo.commandBufferCount = static_cast(vkCommandBuffers.size()); submitInfo.pCommandBuffers = vkCommandBuffers.empty() ? nullptr : vkCommandBuffers.data(); diff --git a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp index e97805ceb2..3f28805888 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Public/Culling.cpp @@ -244,16 +244,26 @@ namespace AZ void CullingScene::RegisterOrUpdateCullable(Cullable& cullable) { - m_cullDataConcurrencyCheck.soft_lock(); + // Multiple threads can call RegisterOrUpdateCullable at the same time + // since the underlying visScene is thread safe, but if you're inserting or + // updating between BeginCulling and EndCulling, you'll get non-deterministic + // results depending on a race condition if you happen to update before or after + // the culling system starts Enumerating, so use soft_lock_shared here + m_cullDataConcurrencyCheck.soft_lock_shared(); m_visScene->InsertOrUpdateEntry(cullable.m_cullData.m_visibilityEntry); - m_cullDataConcurrencyCheck.soft_unlock(); + m_cullDataConcurrencyCheck.soft_unlock_shared(); } void CullingScene::UnregisterCullable(Cullable& cullable) { - m_cullDataConcurrencyCheck.soft_lock(); + // Multiple threads can call RegisterOrUpdateCullable at the same time + // since the underlying visScene is thread safe, but if you're inserting or + // updating between BeginCulling and EndCulling, you'll get non-deterministic + // results depending on a race condition if you happen to update before or after + // the culling system starts Enumerating, so use soft_lock_shared here + m_cullDataConcurrencyCheck.soft_lock_shared(); m_visScene->RemoveEntry(cullable.m_cullData.m_visibilityEntry); - m_cullDataConcurrencyCheck.soft_unlock(); + m_cullDataConcurrencyCheck.soft_unlock_shared(); } uint32_t CullingScene::GetNumCullables() const diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp index 0a59772d6c..cf655d43f7 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderAsset.cpp @@ -10,6 +10,7 @@ * */ #include +#include #include #include @@ -34,10 +35,6 @@ namespace AZ uint32_t ShaderAsset::MakeAssetProductSubId(uint32_t rhiApiUniqueIndex, uint32_t subProductType) { - static constexpr uint32_t RhiIndexBitPosition = 30; - static constexpr uint32_t RhiIndexNumBits = 32 - RhiIndexBitPosition; - static constexpr uint32_t RhiIndexMaxValue = (1 << RhiIndexNumBits) - 1; - static constexpr uint32_t SubProductTypeBitPosition = 0; static constexpr uint32_t SubProductTypeNumBits = RhiIndexBitPosition - SubProductTypeBitPosition; static constexpr uint32_t SubProductTypeMaxValue = (1 << SubProductTypeNumBits) - 1; diff --git a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderVariantAsset.cpp b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderVariantAsset.cpp index 7864768351..5bcdcbb569 100644 --- a/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderVariantAsset.cpp +++ b/Gems/Atom/RPI/Code/Source/RPI.Reflect/Shader/ShaderVariantAsset.cpp @@ -10,6 +10,7 @@ * */ #include +#include #include #include @@ -24,10 +25,6 @@ namespace AZ uint32_t ShaderVariantAsset::MakeAssetProductSubId( uint32_t rhiApiUniqueIndex, ShaderVariantStableId variantStableId, uint32_t subProductType) { - static constexpr uint32_t RhiIndexBitPosition = 30; - static constexpr uint32_t RhiIndexNumBits = 32 - RhiIndexBitPosition; - static constexpr uint32_t RhiIndexMaxValue = (1 << RhiIndexNumBits) - 1; - static constexpr uint32_t SubProductTypeBitPosition = 17; static constexpr uint32_t SubProductTypeNumBits = RhiIndexBitPosition - SubProductTypeBitPosition; static constexpr uint32_t SubProductTypeMaxValue = (1 << SubProductTypeNumBits) - 1; diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraController.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraController.h index 1318deb355..b88b340926 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraController.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraController.h @@ -51,7 +51,8 @@ namespace AtomToolsFramework void UpdateViewport(const AzFramework::ViewportControllerUpdateEvent& event) override; // ModularViewportCameraControllerRequestBus overrides ... - void InterpolateToTransform(const AZ::Transform& worldFromLocal) override; + void InterpolateToTransform(const AZ::Transform& worldFromLocal, float lookAtDistance) override; + AZStd::optional LookAtAfterInterpolation() const override; private: // AzFramework::ViewportDebugDisplayEventBus overrides ... @@ -71,6 +72,8 @@ namespace AtomToolsFramework AZ::Transform m_transformEnd = AZ::Transform::CreateIdentity(); float m_animationT = 0.0f; CameraMode m_cameraMode = CameraMode::Control; + AZStd::optional m_lookAtAfterInterpolation; //!< The look at point after an interpolation has finished. + //!< Will be cleared when the view changes (camera looks away). bool m_updatingTransform = false; AZ::RPI::ViewportContext::MatrixChangedEvent::Handler m_cameraViewMatrixChangeHandler; diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h index 5b90119372..a7f067cdf4 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Include/AtomToolsFramework/Viewport/ModularViewportCameraControllerRequestBus.h @@ -32,7 +32,12 @@ namespace AtomToolsFramework static const AZ::EBusHandlerPolicy HandlerPolicy = AZ::EBusHandlerPolicy::Single; //! Begin a smooth transition of the camera to the requested transform. - virtual void InterpolateToTransform(const AZ::Transform& worldFromLocal) = 0; + //! @param worldFromLocal The transform of where the camera should end up. + //! @param lookAtDistance The distance between the camera transform and the imagined look at point. + virtual void InterpolateToTransform(const AZ::Transform& worldFromLocal, float lookAtDistance) = 0; + + //! Look at point after an interpolation has finished and no translation has occurred. + virtual AZStd::optional LookAtAfterInterpolation() const = 0; protected: ~ModularViewportCameraControllerRequests() = default; diff --git a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ModularViewportCameraController.cpp b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ModularViewportCameraController.cpp index 896d9f8043..082dc8f272 100644 --- a/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ModularViewportCameraController.cpp +++ b/Gems/Atom/Tools/AtomToolsFramework/Code/Source/Viewport/ModularViewportCameraController.cpp @@ -140,6 +140,18 @@ namespace AtomToolsFramework m_targetCamera = m_cameraSystem.StepCamera(m_targetCamera, event.m_deltaTime.count()); m_camera = AzFramework::SmoothCamera(m_camera, m_targetCamera, event.m_deltaTime.count()); + // if there has been an interpolation, only clear the look at point if it is no longer + // centered in the view (the camera has looked away from it) + if (m_lookAtAfterInterpolation.has_value()) + { + if (const float lookDirection = + (*m_lookAtAfterInterpolation - m_camera.Translation()).GetNormalized().Dot(m_camera.Transform().GetBasisY()); + !AZ::IsCloseMag(lookDirection, 1.0f, 0.001f)) + { + m_lookAtAfterInterpolation = {}; + } + } + viewportContext->SetCameraTransform(m_camera.Transform()); } else if (m_cameraMode == CameraMode::Animation) @@ -148,8 +160,8 @@ namespace AtomToolsFramework { return t * t * t * (t * (t * 6.0f - 15.0f) + 10.0f); }; - const float transitionT = smootherStepFn(m_animationT); + const float transitionT = smootherStepFn(m_animationT); const AZ::Transform current = AZ::Transform::CreateFromQuaternionAndTranslation( m_transformStart.GetRotation().Slerp(m_transformEnd.GetRotation(), transitionT), m_transformStart.GetTranslation().Lerp(m_transformEnd.GetTranslation(), transitionT)); @@ -185,11 +197,17 @@ namespace AtomToolsFramework } } - void ModernViewportCameraControllerInstance::InterpolateToTransform(const AZ::Transform& worldFromLocal) + void ModernViewportCameraControllerInstance::InterpolateToTransform(const AZ::Transform& worldFromLocal, const float lookAtDistance) { m_animationT = 0.0f; m_cameraMode = CameraMode::Animation; m_transformStart = m_camera.Transform(); m_transformEnd = worldFromLocal; + m_lookAtAfterInterpolation = m_transformEnd.GetTranslation() + m_transformEnd.GetBasisY() * lookAtDistance; + } + + AZStd::optional ModernViewportCameraControllerInstance::LookAtAfterInterpolation() const + { + return m_lookAtAfterInterpolation; } } // namespace AtomToolsFramework diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Thumbnails/ThumbnailFeatureProcessorProviderBus.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Thumbnails/ThumbnailFeatureProcessorProviderBus.h new file mode 100644 index 0000000000..71e101bcef --- /dev/null +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Include/AtomLyIntegration/CommonFeatures/Thumbnails/ThumbnailFeatureProcessorProviderBus.h @@ -0,0 +1,37 @@ +/* +* All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +* its licensors. +* +* For complete copyright and license terms please see the LICENSE at the root of this +* distribution (the "License"). All use of this software is governed by the License, +* or, if provided, by the license below or the license accompanying this file. Do not +* remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +* +*/ +#pragma once + +#include +#include + +namespace AZ +{ + namespace LyIntegration + { + namespace Thumbnails + { + //! ThumbnailFeatureProcessorProviderRequests allows registering custom Feature Processors for thumbnail generation + //! Duplicates will be ignored + //! You can check minimal feature processors that are already registered in CommonThumbnailRenderer.cpp + class ThumbnailFeatureProcessorProviderRequests + : public AZ::EBusTraits + { + public: + //! Get a list of custom feature processors to register with thumbnail renderer + virtual const AZStd::vector& GetCustomFeatureProcessors() const = 0; + }; + + using ThumbnailFeatureProcessorProviderBus = AZ::EBus; + } // namespace Thumbnails + } // namespace LyIntegration +} // namespace AZ diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/CommonThumbnailRenderer.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/CommonThumbnailRenderer.cpp index 06d54a20b3..b002a0bb66 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/CommonThumbnailRenderer.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/CommonThumbnailRenderer.cpp @@ -34,12 +34,34 @@ namespace AZ AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler::BusConnect(RPI::MaterialAsset::RTTI_Type()); AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler::BusConnect(RPI::ModelAsset::RTTI_Type()); SystemTickBus::Handler::BusConnect(); + ThumbnailFeatureProcessorProviderBus::Handler::BusConnect(); m_steps[Step::Initialize] = AZStd::make_shared(this); m_steps[Step::FindThumbnailToRender] = AZStd::make_shared(this); m_steps[Step::WaitForAssetsToLoad] = AZStd::make_shared(this); m_steps[Step::Capture] = AZStd::make_shared(this); m_steps[Step::ReleaseResources] = AZStd::make_shared(this); + + m_minimalFeatureProcessors = + { + "AZ::Render::TransformServiceFeatureProcessor", + "AZ::Render::MeshFeatureProcessor", + "AZ::Render::SimplePointLightFeatureProcessor", + "AZ::Render::SimpleSpotLightFeatureProcessor", + "AZ::Render::PointLightFeatureProcessor", + // There is currently a bug where having multiple DirectionalLightFeatureProcessors active can result in shadow + // flickering [ATOM-13568] + // as well as continually rebuilding MeshDrawPackets [ATOM-13633]. Lets just disable the directional light FP for now. + // Possibly re-enable with [GFX TODO][ATOM-13639] + // "AZ::Render::DirectionalLightFeatureProcessor", + "AZ::Render::DiskLightFeatureProcessor", + "AZ::Render::CapsuleLightFeatureProcessor", + "AZ::Render::QuadLightFeatureProcessor", + "AZ::Render::DecalTextureArrayFeatureProcessor", + "AZ::Render::ImageBasedLightFeatureProcessor", + "AZ::Render::PostProcessFeatureProcessor", + "AZ::Render::SkyBoxFeatureProcessor" + }; } CommonThumbnailRenderer::~CommonThumbnailRenderer() @@ -50,6 +72,7 @@ namespace AZ } AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler::BusDisconnect(); SystemTickBus::Handler::BusDisconnect(); + ThumbnailFeatureProcessorProviderBus::Handler::BusDisconnect(); } void CommonThumbnailRenderer::SetStep(Step step) @@ -77,6 +100,11 @@ namespace AZ AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::ExecuteQueuedEvents(); } + const AZStd::vector& CommonThumbnailRenderer::GetCustomFeatureProcessors() const + { + return m_minimalFeatureProcessors; + } + AZStd::shared_ptr CommonThumbnailRenderer::GetData() const { return m_data; diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/CommonThumbnailRenderer.h b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/CommonThumbnailRenderer.h index 50e73f4391..249a1f1343 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/CommonThumbnailRenderer.h +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/CommonThumbnailRenderer.h @@ -17,6 +17,8 @@ #include #include +#include + // Disables warning messages triggered by the Qt library // 4251: class needs to have dll-interface to be used by clients of class // 4800: forcing value to bool 'true' or 'false' (performance warning) @@ -34,9 +36,10 @@ namespace AZ //! Provides custom rendering of material and model thumbnails class CommonThumbnailRenderer - : private AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler + : public ThumbnailRendererContext + , private AzToolsFramework::Thumbnailer::ThumbnailerRendererRequestBus::MultiHandler , private SystemTickBus::Handler - , public ThumbnailRendererContext + , private ThumbnailFeatureProcessorProviderBus::Handler { public: AZ_CLASS_ALLOCATOR(CommonThumbnailRenderer, AZ::SystemAllocator, 0) @@ -57,9 +60,13 @@ namespace AZ //! SystemTickBus::Handler interface overrides... void OnSystemTick() override; + //! Render::ThumbnailFeatureProcessorProviderBus::Handler interface overrides... + const AZStd::vector& GetCustomFeatureProcessors() const override; + AZStd::unordered_map> m_steps; Step m_currentStep = Step::None; AZStd::shared_ptr m_data; + AZStd::vector m_minimalFeatureProcessors; }; } // namespace Thumbnails } // namespace LyIntegration diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/InitializeStep.cpp b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/InitializeStep.cpp index 322c765f1b..8bf157e5f3 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/InitializeStep.cpp +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/Source/Thumbnails/Rendering/ThumbnailRendererSteps/InitializeStep.cpp @@ -11,10 +11,16 @@ */ +#include +#include + +#include + #include #include #include #include + #include #include #include @@ -23,10 +29,11 @@ #include #include #include + #include #include -#include -#include +#include + #include #include #include @@ -37,7 +44,6 @@ namespace AZ { namespace Thumbnails { - InitializeStep::InitializeStep(ThumbnailRendererContext* context) : ThumbnailRendererStep(context) { @@ -50,24 +56,23 @@ namespace AZ data->m_entityContext = AZStd::make_unique(); data->m_entityContext->InitContext(); - // Create and register a scene with minimum required feature processors + // Create and register a scene with all required feature processors RPI::SceneDescriptor sceneDesc; - sceneDesc.m_featureProcessorNames.push_back("AZ::Render::TransformServiceFeatureProcessor"); - sceneDesc.m_featureProcessorNames.push_back("AZ::Render::MeshFeatureProcessor"); - sceneDesc.m_featureProcessorNames.push_back("AZ::Render::SimplePointLightFeatureProcessor"); - sceneDesc.m_featureProcessorNames.push_back("AZ::Render::SimpleSpotLightFeatureProcessor"); - sceneDesc.m_featureProcessorNames.push_back("AZ::Render::PointLightFeatureProcessor"); - // There is currently a bug where having multiple DirectionalLightFeatureProcessors active can result in shadow flickering [ATOM-13568] - // as well as continually rebuilding MeshDrawPackets [ATOM-13633]. Lets just disable the directional light FP for now. - // Possibly re-enable with [GFX TODO][ATOM-13639] - // sceneDesc.m_featureProcessorNames.push_back("AZ::Render::DirectionalLightFeatureProcessor"); - sceneDesc.m_featureProcessorNames.push_back("AZ::Render::DiskLightFeatureProcessor"); - sceneDesc.m_featureProcessorNames.push_back("AZ::Render::CapsuleLightFeatureProcessor"); - sceneDesc.m_featureProcessorNames.push_back("AZ::Render::QuadLightFeatureProcessor"); - sceneDesc.m_featureProcessorNames.push_back("AZ::Render::DecalTextureArrayFeatureProcessor"); - sceneDesc.m_featureProcessorNames.push_back("AZ::Render::ImageBasedLightFeatureProcessor"); - sceneDesc.m_featureProcessorNames.push_back("AZ::Render::PostProcessFeatureProcessor"); - sceneDesc.m_featureProcessorNames.push_back("AZ::Render::SkyBoxFeatureProcessor"); + + AZ::EBusAggregateResults> results; + ThumbnailFeatureProcessorProviderBus::BroadcastResult(results, &ThumbnailFeatureProcessorProviderBus::Handler::GetCustomFeatureProcessors); + + AZStd::set featureProcessorNames; + for (auto& resultCollection : results.values) + { + for (auto& featureProcessorName : resultCollection) + { + if (featureProcessorNames.emplace(featureProcessorName).second) + { + sceneDesc.m_featureProcessorNames.push_back(featureProcessorName); + } + } + } data->m_scene = RPI::Scene::CreateScene(sceneDesc); diff --git a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake index 360511aaea..8259e523f7 100644 --- a/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake +++ b/Gems/AtomLyIntegration/CommonFeatures/Code/atomlyintegration_commonfeatures_editor_files.cmake @@ -10,11 +10,12 @@ # set(FILES + Include/AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h + Include/AtomLyIntegration/CommonFeatures/ReflectionProbe/EditorReflectionProbeBus.h + Include/AtomLyIntegration/CommonFeatures/Thumbnails/ThumbnailFeatureProcessorProviderBus.h Source/Module.cpp Source/Animation/EditorAttachmentComponent.h Source/Animation/EditorAttachmentComponent.cpp - Include/AtomLyIntegration/CommonFeatures/Material/EditorMaterialSystemComponentRequestBus.h - Include/AtomLyIntegration/CommonFeatures/ReflectionProbe/EditorReflectionProbeBus.h Source/EditorCommonFeaturesSystemComponent.h Source/EditorCommonFeaturesSystemComponent.cpp Source/CoreLights/EditorAreaLightComponent.h diff --git a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Code/CMakeLists.txt b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Code/CMakeLists.txt index 76cbf6546d..bad0d743c7 100644 --- a/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Code/CMakeLists.txt +++ b/Gems/AtomLyIntegration/TechnicalArt/DccScriptingInterface/Code/CMakeLists.txt @@ -9,6 +9,10 @@ # WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. # +if(NOT PAL_TRAIT_BUILD_HOST_TOOLS) + return() +endif() + ly_add_target( NAME DccScriptingInterface.Static STATIC NAMESPACE Gem @@ -38,3 +42,9 @@ ly_add_target( PRIVATE Gem::DccScriptingInterface.Static ) + +# Any 'tool' type applications should use Gem::DccScriptingInterface.Editor: +ly_create_alias(NAME DccScriptingInterface.Tools NAMESPACE Gem TARGETS Gem::DccScriptingInterface.Editor) +# Add an empty 'builders' alias to allow the DccScriptInterface root gem path to be added to the generated +# cmake_dependencies..assetprocessor.setreg to allow the asset scan folder for it to be added +ly_create_alias(NAME DccScriptingInterface.Builders NAMESPACE Gem) diff --git a/Gems/EMotionFX/Code/Tests/BoolLogicNodeTests.cpp b/Gems/EMotionFX/Code/Tests/BoolLogicNodeTests.cpp index 30950790d9..218bea2169 100644 --- a/Gems/EMotionFX/Code/Tests/BoolLogicNodeTests.cpp +++ b/Gems/EMotionFX/Code/Tests/BoolLogicNodeTests.cpp @@ -165,8 +165,8 @@ namespace EMotionFX const AZ::Outcome boolYParamIndexOutcome = m_animGraphInstance->FindParameterIndex(nameBoolY); success = boolXParamIndexOutcome.IsSuccess() && boolYParamIndexOutcome.IsSuccess(); - uint32 boolXOutputPortIndex; - uint32 boolYOutputPortIndex; + uint32 boolXOutputPortIndex = InvalidIndex32; + uint32 boolYOutputPortIndex = InvalidIndex32; const int portIndicesTosetCount = 2; int portIndicesFound = 0; const AZStd::vector& parameterNodeOutputPorts = parameterNode->GetOutputPorts(); diff --git a/Gems/FastNoise/Code/External/FastNoise/FastNoise.cpp b/Gems/FastNoise/Code/External/FastNoise/FastNoise.cpp index fce7d6498a..3fe7bf45fe 100644 --- a/Gems/FastNoise/Code/External/FastNoise/FastNoise.cpp +++ b/Gems/FastNoise/Code/External/FastNoise/FastNoise.cpp @@ -612,7 +612,9 @@ FN_DECIMAL FastNoise::SingleValue(unsigned char offset, FN_DECIMAL x, FN_DECIMAL int y1 = y0 + 1; int z1 = z0 + 1; - FN_DECIMAL xs, ys, zs; + FN_DECIMAL xs = 0.0f; + FN_DECIMAL ys = 0.0f; + FN_DECIMAL zs = 0.0f; switch (m_interp) { case Linear: @@ -726,7 +728,8 @@ FN_DECIMAL FastNoise::SingleValue(unsigned char offset, FN_DECIMAL x, FN_DECIMAL int x1 = x0 + 1; int y1 = y0 + 1; - FN_DECIMAL xs, ys; + FN_DECIMAL xs = 0.0f; + FN_DECIMAL ys = 0.0f; switch (m_interp) { case Linear: @@ -840,7 +843,9 @@ FN_DECIMAL FastNoise::SinglePerlin(unsigned char offset, FN_DECIMAL x, FN_DECIMA int y1 = y0 + 1; int z1 = z0 + 1; - FN_DECIMAL xs, ys, zs; + FN_DECIMAL xs = 0.0f; + FN_DECIMAL ys = 0.0f; + FN_DECIMAL zs = 0.0f; switch (m_interp) { case Linear: @@ -962,7 +967,8 @@ FN_DECIMAL FastNoise::SinglePerlin(unsigned char offset, FN_DECIMAL x, FN_DECIMA int x1 = x0 + 1; int y1 = y0 + 1; - FN_DECIMAL xs, ys; + FN_DECIMAL xs = 0.0f; + FN_DECIMAL ys = 0.0f; switch (m_interp) { case Linear: @@ -1699,7 +1705,9 @@ FN_DECIMAL FastNoise::SingleCellular(FN_DECIMAL x, FN_DECIMAL y, FN_DECIMAL z) c int zr = FastRound(z); FN_DECIMAL distance = 999999; - int xc, yc, zc; + int xc = 0; + int yc = 0; + int zc = 0; switch (m_cellularDistanceFunction) { @@ -1923,7 +1931,8 @@ FN_DECIMAL FastNoise::SingleCellular(FN_DECIMAL x, FN_DECIMAL y) const int yr = FastRound(y); FN_DECIMAL distance = 999999; - int xc, yc; + int xc = 0; + int yc = 0; switch (m_cellularDistanceFunction) { diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/Parser.cpp b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/Parser.cpp index 319996949b..db07c3ca8e 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/Parser.cpp +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Styling/Parser.cpp @@ -1072,8 +1072,7 @@ namespace GraphCanvas if (!id.empty()) { - Selector selector = Selector::Get(id); - result.emplace_back(selector); + result.emplace_back(Selector::Get(id)); continue; } @@ -1111,8 +1110,7 @@ namespace GraphCanvas { bits.emplace_back(stateSelector); } - Selector selector = aznew CompoundSelector(std::move(bits)); - nestedSelectors.emplace_back(selector); + nestedSelectors.emplace_back(aznew CompoundSelector(std::move(bits))); } } diff --git a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Utils/GraphUtils.cpp b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Utils/GraphUtils.cpp index 28fb760c20..c2574a69f6 100644 --- a/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Utils/GraphUtils.cpp +++ b/Gems/GraphCanvas/Code/StaticLib/GraphCanvas/Utils/GraphUtils.cpp @@ -1239,7 +1239,7 @@ namespace GraphCanvas bool GraphUtils::IsValidModelConnection(const GraphId& graphId, const Endpoint& sourceEndpoint, const Endpoint& targetEndpoint) { - bool validConnection; + bool validConnection = false; AZStd::unordered_set< Endpoint > finalSourceEndpoints = RemapEndpointForModel(sourceEndpoint); AZStd::unordered_set< Endpoint > finalTargetEndpoints = RemapEndpointForModel(targetEndpoint); diff --git a/Gems/LyShine/Code/Editor/Animation/Controls/UiTimelineCtrl.cpp b/Gems/LyShine/Code/Editor/Animation/Controls/UiTimelineCtrl.cpp index 9f7fae6041..0b18539c54 100644 --- a/Gems/LyShine/Code/Editor/Animation/Controls/UiTimelineCtrl.cpp +++ b/Gems/LyShine/Code/Editor/Animation/Controls/UiTimelineCtrl.cpp @@ -23,10 +23,6 @@ #include #include -static const QColor timeMarkerCol = QColor(255, 0, 255); -static const QColor textCol = QColor(0, 0, 0); -static const QColor ltgrayCol = QColor(110, 110, 110); - QColor InterpolateColor(const QColor& c1, const QColor& c2, float fraction) { const int r = (c2.red() - c1.red()) * fraction + c1.red(); diff --git a/Gems/LyShine/Code/Source/Animation/AnimNode.cpp b/Gems/LyShine/Code/Source/Animation/AnimNode.cpp index 421e9a4fe3..369f080a15 100644 --- a/Gems/LyShine/Code/Source/Animation/AnimNode.cpp +++ b/Gems/LyShine/Code/Source/Animation/AnimNode.cpp @@ -48,7 +48,6 @@ static const EUiAnimCurveType DEFAULT_TRACK_TYPE = eUiAnimCurveType_BezierFloat; // Old serialization values that are no longer // defined in IUiAnimationSystem.h, but needed for conversion: -static const int OLD_APARAM_USER = 100; static const int OLD_ACURVE_GOTO = 21; static const int OLD_APARAM_PARTICLE_COUNT_SCALE = 95; static const int OLD_APARAM_PARTICLE_PULSE_PERIOD = 96; diff --git a/Gems/LyShine/Code/Source/Animation/AzEntityNode.cpp b/Gems/LyShine/Code/Source/Animation/AzEntityNode.cpp index 7ff46f285f..2d24a84c71 100644 --- a/Gems/LyShine/Code/Source/Animation/AzEntityNode.cpp +++ b/Gems/LyShine/Code/Source/Animation/AzEntityNode.cpp @@ -691,7 +691,7 @@ IUiAnimTrack* CUiAnimAzEntityNode::CreateTrackForAzField(const UiAnimParamData& return nullptr; } - EUiAnimValue valueType; + EUiAnimValue valueType = eUiAnimValue_Unknown; switch (numElements) { case 2: diff --git a/Gems/LyShine/Code/Source/LyShineSystemComponent.cpp b/Gems/LyShine/Code/Source/LyShineSystemComponent.cpp index 0eab7705f6..1290683145 100644 --- a/Gems/LyShine/Code/Source/LyShineSystemComponent.cpp +++ b/Gems/LyShine/Code/Source/LyShineSystemComponent.cpp @@ -375,7 +375,7 @@ namespace LyShine } /////////////////////////////////////////////////////////////////////////////////////////////// - void LyShineSystemComponent::OnCrySystemInitialized(ISystem& system, [[maybe_unused]] const SSystemInitParams& startupParams) + void LyShineSystemComponent::OnCrySystemInitialized([[maybe_unused]] ISystem& system, [[maybe_unused]] const SSystemInitParams& startupParams) { #if !defined(AZ_MONOLITHIC_BUILD) // When module is linked dynamically, we must set our gEnv pointer. diff --git a/Gems/LyShine/Code/Source/UiLayoutGridComponent.cpp b/Gems/LyShine/Code/Source/UiLayoutGridComponent.cpp index e39b9cc684..4c5d48911f 100644 --- a/Gems/LyShine/Code/Source/UiLayoutGridComponent.cpp +++ b/Gems/LyShine/Code/Source/UiLayoutGridComponent.cpp @@ -107,7 +107,8 @@ void UiLayoutGridComponent::ApplyLayoutHeight() AZStd::vector childEntityIds; EBUS_EVENT_ID_RESULT(childEntityIds, GetEntityId(), UiElementBus, GetChildEntityIds); int childIndex = 0; - int columnIndex, rowIndex; + int columnIndex = 0; + int rowIndex = 0; for (auto child : childEntityIds) { // Set the anchors @@ -627,7 +628,8 @@ AZ::Vector2 UiLayoutGridComponent::GetChildrenBoundingRectSize(const AZ::Vector2 UiLayoutHelpers::GetSizeInsidePadding(GetEntityId(), m_padding, layoutRectSize); // Calculate number of rows and columns - int numColumns, numRows; + int numColumns = 0; + int numRows = 0; switch (m_startingDirection) { case StartingDirection::HorizontalOrder: diff --git a/Gems/LyShine/Code/Source/UiNavigationHelpers.cpp b/Gems/LyShine/Code/Source/UiNavigationHelpers.cpp index 0c1b6765ce..79084c8bbe 100644 --- a/Gems/LyShine/Code/Source/UiNavigationHelpers.cpp +++ b/Gems/LyShine/Code/Source/UiNavigationHelpers.cpp @@ -151,6 +151,8 @@ namespace UiNavigationHelpers } UiTransformInterface::Rect parentRect; + parentRect.Set(0.0f, 0.0f, 0.0f, 0.0f); + AZ::Matrix4x4 parentTransformFromViewport; if (parentElement.IsValid() && !isCurElementDescendantOfParentElement) { diff --git a/Gems/Maestro/Code/Source/Cinematics/AnimNode.cpp b/Gems/Maestro/Code/Source/Cinematics/AnimNode.cpp index 7fea8097ed..8398ad47fd 100644 --- a/Gems/Maestro/Code/Source/Cinematics/AnimNode.cpp +++ b/Gems/Maestro/Code/Source/Cinematics/AnimNode.cpp @@ -60,7 +60,6 @@ static const EAnimCurveType DEFAULT_TRACK_TYPE = eAnimCurveType_BezierFloat; // Old serialization values that are no longer // defined in IMovieSystem.h, but needed for conversion: -static const int OLD_APARAM_USER = 100; static const int OLD_ACURVE_GOTO = 21; static const int OLD_APARAM_PARTICLE_COUNT_SCALE = 95; static const int OLD_APARAM_PARTICLE_PULSE_PERIOD = 96; diff --git a/Gems/Maestro/Code/Source/Cinematics/AnimPostFXNode.cpp b/Gems/Maestro/Code/Source/Cinematics/AnimPostFXNode.cpp index 67b6ab5cdb..992527d2c4 100644 --- a/Gems/Maestro/Code/Source/Cinematics/AnimPostFXNode.cpp +++ b/Gems/Maestro/Code/Source/Cinematics/AnimPostFXNode.cpp @@ -281,7 +281,6 @@ void CAnimPostFXNode::SerializeAnims(XmlNodeRef& xmlNode, bool bLoading, bool bL paramType.Serialize(trackNode, true); // Don't use APARAM_USER because it could change in newer versions // CAnimNode::SerializeAnims will then take care of that - static const unsigned int OLD_APARAM_USER = 100; paramType = static_cast(static_cast(paramType.GetType()) + OLD_APARAM_USER); paramType.Serialize(trackNode, false); } diff --git a/Gems/Microphone/Code/Source/Platform/Windows/MicrophoneSystemComponent_Windows.cpp b/Gems/Microphone/Code/Source/Platform/Windows/MicrophoneSystemComponent_Windows.cpp index 8b3e3c2ce6..2eb840b253 100644 --- a/Gems/Microphone/Code/Source/Platform/Windows/MicrophoneSystemComponent_Windows.cpp +++ b/Gems/Microphone/Code/Source/Platform/Windows/MicrophoneSystemComponent_Windows.cpp @@ -51,11 +51,11 @@ namespace Audio // To avoid errors, we initialize COM here with the same model. CoInitializeEx(nullptr, COINIT_APARTMENTTHREADED); - const CLSID CLSID_MMDeviceEnumerator = __uuidof(MMDeviceEnumerator); - const IID IID_IMMDeviceEnumerator = __uuidof(IMMDeviceEnumerator); + const CLSID CLSID_MMDeviceEnumerator_UUID = __uuidof(MMDeviceEnumerator); + const IID IID_IMMDeviceEnumerator_UUID = __uuidof(IMMDeviceEnumerator); HRESULT hresult = CoCreateInstance( - CLSID_MMDeviceEnumerator, nullptr, - CLSCTX_ALL, IID_IMMDeviceEnumerator, + CLSID_MMDeviceEnumerator_UUID, nullptr, + CLSCTX_ALL, IID_IMMDeviceEnumerator_UUID, reinterpret_cast(&m_enumerator) ); @@ -133,8 +133,8 @@ namespace Audio AZ_Assert(m_device != nullptr, "Attempting to start a Microphone session while the device is uninitialized - Windows!\n"); // Get the IAudioClient from the device - const IID IID_IAudioClient = __uuidof(IAudioClient); - HRESULT hresult = m_device->Activate(IID_IAudioClient, CLSCTX_ALL, nullptr, reinterpret_cast(&m_audioClient)); + const IID IID_IAudioClient_UUID = __uuidof(IAudioClient); + HRESULT hresult = m_device->Activate(IID_IAudioClient_UUID, CLSCTX_ALL, nullptr, reinterpret_cast(&m_audioClient)); if (FAILED(hresult)) { @@ -182,8 +182,8 @@ namespace Audio } // Get the IAudioCaptureClient - const IID IID_IAudioCaptureClient = __uuidof(IAudioCaptureClient); - hresult = m_audioClient->GetService(IID_IAudioCaptureClient, reinterpret_cast(&m_audioCaptureClient)); + const IID IID_IAudioCaptureClient_UUID = __uuidof(IAudioCaptureClient); + hresult = m_audioClient->GetService(IID_IAudioCaptureClient_UUID, reinterpret_cast(&m_audioCaptureClient)); if (FAILED(hresult)) { diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetBindComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetBindComponent.h index 4fe60f14a3..7d9b7d4086 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetBindComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetBindComponent.h @@ -39,6 +39,7 @@ namespace Multiplayer using EntityMigrationStartEvent = AZ::Event; using EntityMigrationEndEvent = AZ::Event<>; using EntityServerMigrationEvent = AZ::Event; + using EntityPreRenderEvent = AZ::Event; //! @class NetBindComponent //! @brief Component that provides net-binding to a networked entity. @@ -97,6 +98,7 @@ namespace Multiplayer void NotifyMigrationStart(ClientInputId migratedInputId); void NotifyMigrationEnd(); void NotifyServerMigration(HostId hostId, AzNetworking::ConnectionId connectionId); + void NotifyPreRender(float deltaTime, float blendFactor); void AddEntityStopEventHandler(EntityStopEvent::Handler& eventHandler); void AddEntityDirtiedEventHandler(EntityDirtiedEvent::Handler& eventHandler); @@ -104,6 +106,7 @@ namespace Multiplayer void AddEntityMigrationStartEventHandler(EntityMigrationStartEvent::Handler& eventHandler); void AddEntityMigrationEndEventHandler(EntityMigrationEndEvent::Handler& eventHandler); void AddEntityServerMigrationEventHandler(EntityServerMigrationEvent::Handler& eventHandler); + void AddEntityPreRenderEventHandler(EntityPreRenderEvent::Handler& eventHandler); bool SerializeEntityCorrection(AzNetworking::ISerializer& serializer); @@ -152,6 +155,7 @@ namespace Multiplayer EntityMigrationStartEvent m_entityMigrationStartEvent; EntityMigrationEndEvent m_entityMigrationEndEvent; EntityServerMigrationEvent m_entityServerMigrationEvent; + EntityPreRenderEvent m_entityPreRenderEvent; AZ::Event<> m_onRemove; RpcSendEvent::Handler m_handleLocalServerRpcMessageEventHandle; AZ::Event<>::Handler m_handleMarkedDirty; diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkTransformComponent.h b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkTransformComponent.h index f3eb1922fd..1393e7e12f 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkTransformComponent.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/Components/NetworkTransformComponent.h @@ -13,6 +13,7 @@ #pragma once #include +#include #include namespace Multiplayer @@ -32,13 +33,22 @@ namespace Multiplayer void OnDeactivate(Multiplayer::EntityIsMigrating entityIsMigrating) override; private: + void OnPreRender(float deltaTime, float blendFactor); + void OnRotationChangedEvent(const AZ::Quaternion& rotation); void OnTranslationChangedEvent(const AZ::Vector3& translation); void OnScaleChangedEvent(float scale); + void OnResetCountChangedEvent(); + + AZ::Transform m_previousTransform = AZ::Transform::CreateIdentity(); + AZ::Transform m_targetTransform = AZ::Transform::CreateIdentity(); AZ::Event::Handler m_rotationEventHandler; AZ::Event::Handler m_translationEventHandler; AZ::Event::Handler m_scaleEventHandler; + AZ::Event::Handler m_resetCountEventHandler; + + EntityPreRenderEvent::Handler m_entityPreRenderEventHandler; }; class NetworkTransformComponentController diff --git a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/EntityReplication/ReplicationRecord.h b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/EntityReplication/ReplicationRecord.h index 3dfc4b8016..33e1e0bde6 100644 --- a/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/EntityReplication/ReplicationRecord.h +++ b/Gems/Multiplayer/Code/Include/Multiplayer/NetworkEntity/EntityReplication/ReplicationRecord.h @@ -45,10 +45,10 @@ namespace Multiplayer static constexpr uint32_t MaxRecordBits = 2048; ReplicationRecord() = default; - ReplicationRecord(NetEntityRole netEntityRole); + ReplicationRecord(NetEntityRole remoteNetEntityRole); - void SetNetworkRole(NetEntityRole netEntityRole); - NetEntityRole GetNetworkRole() const; + void SetRemoteNetworkRole(NetEntityRole remoteNetEntityRole); + NetEntityRole GetRemoteNetworkRole() const; bool AreAllBitsConsumed() const; void ResetConsumedBits(); @@ -92,6 +92,6 @@ namespace Multiplayer // Sequence number this ReplicationRecord was sent on AzNetworking::PacketId m_sentPacketId = AzNetworking::InvalidPacketId; - NetEntityRole m_netEntityRole = NetEntityRole::InvalidRole;; + NetEntityRole m_remoteNetEntityRole = NetEntityRole::InvalidRole;; }; } diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Common.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Common.jinja index 61dcacaa94..05403a00ef 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Common.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Common.jinja @@ -175,7 +175,7 @@ void Handle{{ PropertyName }}(AzNetworking::IConnection* invokingConnection, {{ //! {{ PropertyName }} Handler //! {{ Property.attrib['Description'] }} //! HandleOn {{ HandleOn }} -virtual void Handle{{ PropertyName }}(AzNetworking::IConnection* invokingConnection, {{ ', '.join(paramDefines) }}) = 0; +virtual void Handle{{ PropertyName }}([[maybe_unused]] AzNetworking::IConnection* invokingConnection, [[maybe_unused]] {{ ', [[maybe_unused]] '.join(paramDefines) }}) {} {% endif %} {% endmacro %} {# diff --git a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja index 5cfd0bfc4d..259f469020 100644 --- a/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja +++ b/Gems/Multiplayer/Code/Source/AutoGen/AutoComponent_Source.jinja @@ -311,7 +311,7 @@ void {{ ClassName }}::Set{{ UpperFirst(Property.attrib['Name']) }}(const {{ Prop {{ AutoComponentMacros.ParseRpcParams(Property, paramNames, paramTypes, paramDefines) }} void {{ ClassName }}::{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(paramDefines) }}) { - constexpr RpcIndex rpcId = static_cast({{ UpperFirst(Component.attrib['Name']) }}Internal::RemoteProcedure::{{ UpperFirst(Property.attrib['Name']) }}); + constexpr Multiplayer::RpcIndex rpcId = static_cast({{ UpperFirst(Component.attrib['Name']) }}Internal::RemoteProcedure::{{ UpperFirst(Property.attrib['Name']) }}); {% if Property.attrib['IsReliable']|booleanTrue %} constexpr AzNetworking::ReliabilityType isReliable = Multiplayer::ReliabilityType::Reliable; {% else %} @@ -368,6 +368,31 @@ void {{ ClassName }}::Signal{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.jo ->Method("{{ UpperFirst(Property.attrib['Name']) }}", [](const {{ ClassName }}* self, {{ ', '.join(paramDefines) }}) { self->m_controller->{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(paramNames) }}); }) + ->Method("{{ UpperFirst(Property.attrib['Name']) }}ByEntity", [](AZ::EntityId id, {{ ', '.join(paramDefines) }}) { + + AZ::Entity* entity = AZ::Interface::Get()->FindEntity(id); + if (!entity) + { + AZ_Warning("Network Property", false, "{{ ClassName }} Get{{ UpperFirst(Property.attrib['Name']) }} failed. The entity with id %s doesn't exist, please provide a valid entity id.", id.ToString().c_str()) + return; + } + + {{ ClassName }}* networkComponent = entity->FindComponent<{{ ClassName }}>(); + if (!networkComponent) + { + AZ_Warning("Network Property", false, "{{ ClassName }} Get{{ UpperFirst(Property.attrib['Name']) }} failed. Entity '%s' (id: %s) is missing {{ ClassName }}, be sure to add {{ ClassName }} to this entity.", entity->GetName().c_str(), id.ToString().c_str()) + return; + } + + {{ ClassName }}Controller* controller = static_cast<{{ ClassName }}Controller*>(networkComponent->GetController()); + if (!controller) + { + AZ_Warning("Network Property", false, "{{ ClassName }} Get{{ UpperFirst(Property.attrib['Name']) }} method failed. Entity '%s' (id: %s) {{ ClassName }} is missing the network controller. This RemoteProcedure can only be invoked from {{InvokeFrom}} network entities, because this entity doesn't have a controller, it must not be a {{InvokeFrom}} entity. Please check your network context before attempting to call {{ UpperFirst(Property.attrib['Name']) }}.", entity->GetName().c_str(), id.ToString().c_str()) + return; + } + + controller->{{ UpperFirst(Property.attrib['Name']) }}({{ ', '.join(paramNames) }}); + }) {% endif %} {% endcall %} {% endmacro %} diff --git a/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp b/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp index 612601883c..97e194dccb 100644 --- a/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/LocalPredictionPlayerInputComponent.cpp @@ -25,6 +25,7 @@ namespace Multiplayer AZ_CVAR(AZ::TimeMs, cl_MaxRewindHistoryMs, AZ::TimeMs{ 2000 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Maximum number of milliseconds to keep for server correction rewind and replay"); #ifndef AZ_RELEASE_BUILD AZ_CVAR(float, cl_DebugHackTimeMultiplier, 1.0f, nullptr, AZ::ConsoleFunctorFlags::Null, "Scalar value used to simulate clock hacking cheats for validating bank time system and anticheat"); + AZ_CVAR(bool, cl_EnableDesyncDebugging, false, nullptr, AZ::ConsoleFunctorFlags::Null, "If enabled, debug logs will contain verbose information on detected state desyncs"); #endif AZ_CVAR(bool, sv_EnableCorrections, true, nullptr, AZ::ConsoleFunctorFlags::Null, "Enables server corrections on autonomous proxy desyncs"); @@ -214,11 +215,12 @@ namespace Multiplayer // Send correction SendClientInputCorrection(GetLastInputId(), correction); -#ifdef _DEBUG - // In debug, show which states caused the correction +#ifndef AZ_RELEASE_BUILD AZStd::string clientStateString; AZStd::string serverStateString; + if (cl_EnableDesyncDebugging) { + // In debug, show which states caused the correction // Write in client state AzNetworking::NetworkOutputSerializer clientStateSerializer(clientState.GetBuffer(), clientState.GetSize()); GetNetBindComponent()->SerializeEntityCorrection(clientStateSerializer); @@ -236,11 +238,13 @@ namespace Multiplayer GetNetBindComponent()->SerializeEntityCorrection(serverValues); AZStd::map> mapComparison; + // put the server value in the first part of the pair for (const auto& pair : serverValues.GetValueMap()) { mapComparison[pair.first].first = pair.second; } + // put the client value in the second part of the pair for (const auto& pair : clientValues.GetValueMap()) { @@ -266,12 +270,13 @@ namespace Multiplayer } } } -#else - const AZStd::string clientStateString = "available in debug only"; - const AZStd::string serverStateString = "available in debug only"; -#endif - + else + { + clientStateString = "available in debug only"; + serverStateString = "available in debug only"; + } AZLOG_ERROR("** Autonomous proxy desync detected! ** clientState=[%s], serverState=[%s]", clientStateString.c_str(), serverStateString.c_str()); +#endif } } } @@ -416,7 +421,7 @@ namespace Multiplayer ClientInputId LocalPredictionPlayerInputComponentController::GetLastInputId() const { - return m_clientInputId; + return m_lastClientInputId; } HostFrameId LocalPredictionPlayerInputComponentController::GetInputFrameId(const NetworkInput& input) const @@ -520,10 +525,13 @@ namespace Multiplayer // In debug, send the entire client output state to the server to make it easier to debug desync issues AzNetworking::PacketEncodingBuffer processInputResult; -#ifdef _DEBUG - AzNetworking::NetworkInputSerializer processInputResultSerializer(processInputResult.GetBuffer(), processInputResult.GetCapacity()); - GetNetBindComponent()->SerializeEntityCorrection(processInputResultSerializer); - processInputResult.Resize(processInputResultSerializer.GetSize()); +#ifndef AZ_RELEASE_BUILD + if (cl_EnableDesyncDebugging) + { + AzNetworking::NetworkInputSerializer processInputResultSerializer(processInputResult.GetBuffer(), processInputResult.GetCapacity()); + GetNetBindComponent()->SerializeEntityCorrection(processInputResultSerializer); + processInputResult.Resize(processInputResultSerializer.GetSize()); + } #endif // Save this input and discard move history outside our client rewind window diff --git a/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp index d91bba2e0c..0847d42dd6 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetBindComponent.cpp @@ -390,6 +390,11 @@ namespace Multiplayer m_entityServerMigrationEvent.Signal(m_netEntityHandle, hostId, connectionId); } + void NetBindComponent::NotifyPreRender(float deltaTime, float blendFactor) + { + m_entityPreRenderEvent.Signal(deltaTime, blendFactor); + } + void NetBindComponent::AddEntityStopEventHandler(EntityStopEvent::Handler& eventHandler) { eventHandler.Connect(m_entityStopEvent); @@ -420,6 +425,11 @@ namespace Multiplayer eventHandler.Connect(m_entityServerMigrationEvent); } + void NetBindComponent::AddEntityPreRenderEventHandler(EntityPreRenderEvent::Handler& eventHandler) + { + eventHandler.Connect(m_entityPreRenderEvent); + } + bool NetBindComponent::SerializeEntityCorrection(AzNetworking::ISerializer& serializer) { m_predictableRecord.ResetConsumedBits(); diff --git a/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp b/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp index 682f7ea988..bb256701ff 100644 --- a/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Components/NetworkTransformComponent.cpp @@ -33,6 +33,8 @@ namespace Multiplayer : m_rotationEventHandler([this](const AZ::Quaternion& rotation) { OnRotationChangedEvent(rotation); }) , m_translationEventHandler([this](const AZ::Vector3& translation) { OnTranslationChangedEvent(translation); }) , m_scaleEventHandler([this](float scale) { OnScaleChangedEvent(scale); }) + , m_resetCountEventHandler([this](const uint8_t&) { OnResetCountChangedEvent(); }) + , m_entityPreRenderEventHandler([this](float deltaTime, float blendFactor) { OnPreRender(deltaTime, blendFactor); }) { ; } @@ -47,6 +49,11 @@ namespace Multiplayer RotationAddEvent(m_rotationEventHandler); TranslationAddEvent(m_translationEventHandler); ScaleAddEvent(m_scaleEventHandler); + ResetCountAddEvent(m_resetCountEventHandler); + GetNetBindComponent()->AddEntityPreRenderEventHandler(m_entityPreRenderEventHandler); + + // When coming into relevance, reset all blending factors so we don't interpolate to our start position + OnResetCountChangedEvent(); } void NetworkTransformComponent::OnDeactivate([[maybe_unused]] Multiplayer::EntityIsMigrating entityIsMigrating) @@ -56,23 +63,40 @@ namespace Multiplayer void NetworkTransformComponent::OnRotationChangedEvent(const AZ::Quaternion& rotation) { - AZ::Transform worldTm = GetTransformComponent()->GetWorldTM(); - worldTm.SetRotation(rotation); - GetTransformComponent()->SetWorldTM(worldTm); + m_previousTransform.SetRotation(m_targetTransform.GetRotation()); + m_targetTransform.SetRotation(rotation); } void NetworkTransformComponent::OnTranslationChangedEvent(const AZ::Vector3& translation) { - AZ::Transform worldTm = GetTransformComponent()->GetWorldTM(); - worldTm.SetTranslation(translation); - GetTransformComponent()->SetWorldTM(worldTm); + m_previousTransform.SetTranslation(m_targetTransform.GetTranslation()); + m_targetTransform.SetTranslation(translation); } void NetworkTransformComponent::OnScaleChangedEvent(float scale) { - AZ::Transform worldTm = GetTransformComponent()->GetWorldTM(); - worldTm.SetUniformScale(scale); - GetTransformComponent()->SetWorldTM(worldTm); + m_previousTransform.SetUniformScale(m_targetTransform.GetUniformScale()); + m_targetTransform.SetUniformScale(scale); + } + + void NetworkTransformComponent::OnResetCountChangedEvent() + { + m_targetTransform.SetRotation(GetRotation()); + m_targetTransform.SetTranslation(GetTranslation()); + m_targetTransform.SetUniformScale(GetScale()); + m_previousTransform = m_targetTransform; + } + + void NetworkTransformComponent::OnPreRender([[maybe_unused]] float deltaTime, float blendFactor) + { + if (!HasController()) + { + AZ::Transform blendTransform; + blendTransform.SetRotation(m_previousTransform.GetRotation().Slerp(m_targetTransform.GetRotation(), blendFactor)); + blendTransform.SetTranslation(m_previousTransform.GetTranslation().Lerp(m_targetTransform.GetTranslation(), blendFactor)); + blendTransform.SetUniformScale(AZ::Lerp(m_previousTransform.GetUniformScale(), m_targetTransform.GetUniformScale(), blendFactor)); + GetTransformComponent()->SetWorldTM(blendTransform); + } } @@ -96,11 +120,8 @@ namespace Multiplayer void NetworkTransformComponentController::OnTransformChangedEvent(const AZ::Transform& worldTm) { - if (IsAuthority()) - { - SetRotation(worldTm.GetRotation()); - SetTranslation(worldTm.GetTranslation()); - SetScale(worldTm.GetUniformScale()); - } + SetRotation(worldTm.GetRotation()); + SetTranslation(worldTm.GetTranslation()); + SetScale(worldTm.GetUniformScale()); } } diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp index f684e1f12f..847d1caadf 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorConnection.cpp @@ -140,7 +140,7 @@ namespace Multiplayer AZ::CVarFixedString remoteAddress; uint16_t remotePort; if (console->GetCvarValue("editorsv_serveraddr", remoteAddress) != AZ::GetValueResult::ConsoleVarNotFound && - console->GetCvarValue("editorsv_port", remotePort) != AZ::GetValueResult::ConsoleVarNotFound) + console->GetCvarValue("sv_port", remotePort) != AZ::GetValueResult::ConsoleVarNotFound) { // Connect the Editor to the editor server for Multiplayer simulation AZ::Interface::Get()->InitializeMultiplayer(MultiplayerAgentType::Client); @@ -149,6 +149,8 @@ namespace Multiplayer const IpAddress ipAddress(remoteAddress.c_str(), remotePort, networkInterface->GetType()); networkInterface->Connect(ipAddress); + + AZ::Interface::Get()->SendReadyForEntityUpdates(true); } } } diff --git a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp index 523ffd90de..3ac4e98d42 100644 --- a/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/Editor/MultiplayerEditorSystemComponent.cpp @@ -165,7 +165,7 @@ namespace Multiplayer // BeginGameMode and Prefab Processing have completed at this point IMultiplayerTools* mpTools = AZ::Interface::Get(); - if (editorsv_enabled && mpTools != nullptr && mpTools->DidProcessNetworkPrefabs()) + if (editorsv_enabled && mpTools != nullptr) { const AZStd::vector>& assetData = prefabEditorEntityOwnershipInterface->GetPlayInEditorAssetData(); diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp index 0818f605df..069ac49d96 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.cpp @@ -12,7 +12,6 @@ #include #include - #include #include #include @@ -24,14 +23,23 @@ #include #include #include +#include #include #include +#include #include #include #include + +#include +#include +#include #include + #include +#include + namespace AZ::ConsoleTypeHelpers { template <> @@ -74,6 +82,7 @@ namespace Multiplayer AZ_CVAR(ProtocolType, sv_protocol, ProtocolType::Udp, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "This flag controls whether we use TCP or UDP for game networking"); AZ_CVAR(bool, sv_isDedicated, true, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Whether the host command creates an independent or client hosted server"); AZ_CVAR(AZ::TimeMs, cl_defaultNetworkEntityActivationTimeSliceMs, AZ::TimeMs{ 0 }, nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "Max Ms to use to activate entities coming from the network, 0 means instantiate everything"); + AZ_CVAR(AZ::TimeMs, sv_serverSendRateMs, AZ::TimeMs{ 50 }, nullptr, AZ::ConsoleFunctorFlags::Null, "Minimum number of milliseconds between each network update"); AZ_CVAR(AZ::CVarFixedString, sv_defaultPlayerSpawnAsset, "prefabs/player.network.spawnable", nullptr, AZ::ConsoleFunctorFlags::DontReplicate, "The default spawnable to use when a new player connects"); void MultiplayerSystemComponent::Reflect(AZ::ReflectContext* context) @@ -156,10 +165,26 @@ namespace Multiplayer AZ::TickBus::Handler::BusDisconnect(); } - void MultiplayerSystemComponent::OnTick([[maybe_unused]] float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time) + void MultiplayerSystemComponent::OnTick(float deltaTime, [[maybe_unused]] AZ::ScriptTimePoint time) { - AZ::TimeMs deltaTimeMs = aznumeric_cast(static_cast(deltaTime * 1000.0f)); - AZ::TimeMs hostTimeMs = AZ::GetElapsedTimeMs(); + const AZ::TimeMs deltaTimeMs = aznumeric_cast(static_cast(deltaTime * 1000.0f)); + const AZ::TimeMs hostTimeMs = AZ::GetElapsedTimeMs(); + const AZ::TimeMs serverRateMs = static_cast(sv_serverSendRateMs); + const float serverRateSeconds = static_cast(serverRateMs) / 1000.0f; + + TickVisibleNetworkEntities(deltaTime, serverRateSeconds); + + if (GetAgentType() == MultiplayerAgentType::ClientServer + || GetAgentType() == MultiplayerAgentType::DedicatedServer) + { + m_serverSendAccumulator += deltaTime; + if (m_serverSendAccumulator < serverRateSeconds) + { + return; + } + m_serverSendAccumulator -= serverRateSeconds; + m_networkTime.IncrementHostFrameId(); + } // Handle deferred local rpc messages that were generated during the updates m_networkEntityManager.DispatchLocalDeferredRpcMessages(); @@ -365,13 +390,21 @@ namespace Multiplayer } EntityReplicationManager& replicationManager = reinterpret_cast(connection->GetUserData())->GetReplicationManager(); - - // Ignore a_Request.GetServerGameTimePoint(), clients can't affect the server gametime + + if ((GetAgentType() == MultiplayerAgentType::Client) && (packet.GetHostFrameId() > m_lastReplicatedHostFrameId)) + { + // Update client to latest server time + m_renderBlendFactor = 0.0f; + m_lastReplicatedHostTimeMs = packet.GetHostTimeMs(); + m_lastReplicatedHostFrameId = packet.GetHostFrameId(); + m_networkTime.AlterTime(m_lastReplicatedHostFrameId, m_lastReplicatedHostTimeMs, AzNetworking::InvalidConnectionId); + } + for (AZStd::size_t i = 0; i < packet.GetEntityMessages().size(); ++i) { const NetworkEntityUpdateMessage& updateMessage = packet.GetEntityMessages()[i]; handledAll &= replicationManager.HandleEntityUpdateMessage(connection, packetHeader, updateMessage); - AZ_Assert(handledAll, "GameServerToClientNetworkRequestHandler EntityUpdates Did not handle all updates"); + AZ_Assert(handledAll, "EntityUpdates did not handle all update messages"); } return handledAll; @@ -440,7 +473,7 @@ namespace Multiplayer // Hosts will spawn a new default player prefab for the user that just connected if (GetAgentType() == MultiplayerAgentType::ClientServer - || GetAgentType() == MultiplayerAgentType::DedicatedServer) + || GetAgentType() == MultiplayerAgentType::DedicatedServer) { NetworkEntityHandle controlledEntity = SpawnDefaultPlayerPrefab(); if (controlledEntity.Exists()) @@ -602,6 +635,74 @@ namespace Multiplayer AZLOG_INFO("Total RPCs received bytes: %llu", aznumeric_cast(rpcsRecv.m_totalBytes)); } + void MultiplayerSystemComponent::TickVisibleNetworkEntities(float deltaTime, float serverRateSeconds) + { + const float targetAdjustBlend = AZStd::clamp(deltaTime / serverRateSeconds, 0.0f, 1.0f); + m_renderBlendFactor += targetAdjustBlend; + + // Linear close to the origin, but asymptote at y = 1 + const float adjustedBlendFactor = 1.0f - (std::pow(0.2f, m_renderBlendFactor)); + AZLOG(NET_Blending, "Computed blend factor of %f", adjustedBlendFactor); + + if (Camera::ActiveCameraRequestBus::HasHandlers()) + { + // If there's a camera, update only what's visible + AZ::Transform activeCameraTransform; + Camera::Configuration activeCameraConfiguration; + Camera::ActiveCameraRequestBus::BroadcastResult(activeCameraTransform, &Camera::ActiveCameraRequestBus::Events::GetActiveCameraTransform); + Camera::ActiveCameraRequestBus::BroadcastResult(activeCameraConfiguration, &Camera::ActiveCameraRequestBus::Events::GetActiveCameraConfiguration); + + const AZ::ViewFrustumAttributes frustumAttributes + ( + activeCameraTransform, + activeCameraConfiguration.m_frustumHeight / activeCameraConfiguration.m_frustumWidth, + activeCameraConfiguration.m_fovRadians, + activeCameraConfiguration.m_nearClipDistance, + activeCameraConfiguration.m_farClipDistance + ); + const AZ::Frustum viewFrustum = AZ::Frustum(frustumAttributes); + + // Unfortunately necessary, as NotifyPreRender can update transforms and thus cause a deadlock inside the vis system + AZStd::vector gatheredEntities; + AzFramework::IEntityBoundsUnion* entityBoundsUnion = AZ::Interface::Get(); + AZ::Interface::Get()->GetDefaultVisibilityScene()->Enumerate(viewFrustum, + [&gatheredEntities, entityBoundsUnion](const AzFramework::IVisibilityScene::NodeData& nodeData) + { + gatheredEntities.reserve(gatheredEntities.size() + nodeData.m_entries.size()); + for (AzFramework::VisibilityEntry* visEntry : nodeData.m_entries) + { + if (visEntry->m_typeFlags & AzFramework::VisibilityEntry::TypeFlags::TYPE_Entity) + { + AZ::Entity* entity = static_cast(visEntry->m_userData); + NetBindComponent* netBindComponent = entity->FindComponent(); + if (netBindComponent != nullptr) + { + gatheredEntities.push_back(netBindComponent); + } + } + } + }); + + for (NetBindComponent* netBindComponent : gatheredEntities) + { + netBindComponent->NotifyPreRender(deltaTime, adjustedBlendFactor); + } + } + else + { + // If there's no camera, fall back to updating all net entities + for (auto& iter : *(m_networkEntityManager.GetNetworkEntityTracker())) + { + AZ::Entity* entity = iter.second; + NetBindComponent* netBindComponent = entity->FindComponent(); + if (netBindComponent != nullptr) + { + netBindComponent->NotifyPreRender(deltaTime, adjustedBlendFactor); + } + } + } + } + void MultiplayerSystemComponent::OnConsoleCommandInvoked ( AZStd::string_view command, diff --git a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h index e8e05a9d4c..3b1faafd40 100644 --- a/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h +++ b/Gems/Multiplayer/Code/Source/MultiplayerSystemComponent.h @@ -102,6 +102,7 @@ namespace Multiplayer private: + void TickVisibleNetworkEntities(float deltaTime, float serverRateSeconds); void OnConsoleCommandInvoked(AZStd::string_view command, const AZ::ConsoleCommandContainer& args, AZ::ConsoleFunctorFlags flags, AZ::ConsoleInvokedFrom invokedFrom); void ExecuteConsoleCommandList(AzNetworking::IConnection* connection, const AZStd::fixed_vector& commands); NetworkEntityHandle SpawnDefaultPlayerPrefab(); @@ -124,6 +125,9 @@ namespace Multiplayer AZ::TimeMs m_lastReplicatedHostTimeMs = AZ::TimeMs{ 0 }; HostFrameId m_lastReplicatedHostFrameId = InvalidHostFrameId; + double m_serverSendAccumulator = 0.0; + float m_renderBlendFactor = 0.0f; + #if !defined(AZ_RELEASE_BUILD) MultiplayerEditorConnection m_editorConnectionListener; #endif diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp index bd30c5e37f..5d0284dfb2 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/EntityReplicationManager.cpp @@ -132,7 +132,7 @@ namespace Multiplayer EntityReplicatorList replicatorUpdatedList; MultiplayerPackets::EntityUpdates entityUpdatePacket; entityUpdatePacket.SetHostTimeMs(hostTimeMs); - entityUpdatePacket.SetHostFrameId(InvalidHostFrameId); + entityUpdatePacket.SetHostFrameId(GetNetworkTime()->GetHostFrameId()); // Serialize everything while (!toSendList.empty()) { diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/PropertyPublisher.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/PropertyPublisher.cpp index dfa324f76b..8af636870b 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/PropertyPublisher.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/PropertyPublisher.cpp @@ -27,7 +27,7 @@ namespace Multiplayer , m_sentRecords(net_EntityReplicatorRecordsMax) { AZ_Assert(m_netBindComponent, "NetBindComponent is nullptr"); - m_pendingRecord.SetNetworkRole(remoteNetworkRole); + m_pendingRecord.SetRemoteNetworkRole(remoteNetworkRole); } bool PropertyPublisher::IsDeleting() const @@ -67,7 +67,7 @@ namespace Multiplayer void PropertyPublisher::SetRebasing() { - AZ_Assert(m_pendingRecord.GetNetworkRole() == NetEntityRole::Autonomous, "Expected to be rebasing on a Autonomous entity"); + AZ_Assert(m_pendingRecord.GetRemoteNetworkRole() == NetEntityRole::Autonomous, "Expected to be rebasing on a Autonomous entity"); m_replicatorState = EntityReplicatorState::Rebasing; } @@ -118,7 +118,7 @@ namespace Multiplayer m_sentRecords.clear(); m_netBindComponent->FillTotalReplicationRecord(m_pendingRecord); // Don't send predictable properties back to the Autonomous unless we correct them - if (m_pendingRecord.GetNetworkRole() == NetEntityRole::Autonomous) + if (m_pendingRecord.GetRemoteNetworkRole() == NetEntityRole::Autonomous) { m_pendingRecord.Subtract(m_netBindComponent->GetPredictableRecord()); } @@ -137,7 +137,7 @@ namespace Multiplayer // We need to clear out old records, and build up a list of everything that has changed since the last acked packet m_sentRecords.push_front(m_pendingRecord); auto iter = m_sentRecords.begin(); - ++iter; // consider everything after the record we are going to send + ++iter; // Consider everything after the record we are going to send for (; iter != m_sentRecords.end(); ++iter) { // Sequence wasn't acked, so we need to send these bits again @@ -145,7 +145,7 @@ namespace Multiplayer } // Don't send predictable properties back to the Autonomous unless we correct them - if (m_pendingRecord.GetNetworkRole() == NetEntityRole::Autonomous) + if (m_pendingRecord.GetRemoteNetworkRole() == NetEntityRole::Autonomous) { m_pendingRecord.Subtract(m_netBindComponent->GetPredictableRecord()); } diff --git a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/ReplicationRecord.cpp b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/ReplicationRecord.cpp index 7fe0efd323..47360bfba6 100644 --- a/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/ReplicationRecord.cpp +++ b/Gems/Multiplayer/Code/Source/NetworkEntity/EntityReplication/ReplicationRecord.cpp @@ -49,19 +49,19 @@ namespace Multiplayer } ReplicationRecord::ReplicationRecord(NetEntityRole netEntityRole) - : m_netEntityRole(netEntityRole) + : m_remoteNetEntityRole(netEntityRole) { ; } - void ReplicationRecord::SetNetworkRole(NetEntityRole netEntityRole) + void ReplicationRecord::SetRemoteNetworkRole(NetEntityRole remoteNetEntityRole) { - m_netEntityRole = netEntityRole; + m_remoteNetEntityRole = remoteNetEntityRole; } - NetEntityRole ReplicationRecord::GetNetworkRole() const + NetEntityRole ReplicationRecord::GetRemoteNetworkRole() const { - return m_netEntityRole; + return m_remoteNetEntityRole; } bool ReplicationRecord::AreAllBitsConsumed() const @@ -196,26 +196,26 @@ namespace Multiplayer bool ReplicationRecord::ContainsAuthorityToClientBits() const { - return (m_netEntityRole != NetEntityRole::Authority) - || (m_netEntityRole == NetEntityRole::InvalidRole); + return (m_remoteNetEntityRole != NetEntityRole::Authority) + || (m_remoteNetEntityRole == NetEntityRole::InvalidRole); } bool ReplicationRecord::ContainsAuthorityToServerBits() const { - return (m_netEntityRole == NetEntityRole::Server) - || (m_netEntityRole == NetEntityRole::InvalidRole); + return (m_remoteNetEntityRole == NetEntityRole::Server) + || (m_remoteNetEntityRole == NetEntityRole::InvalidRole); } bool ReplicationRecord::ContainsAuthorityToAutonomousBits() const { - return (m_netEntityRole == NetEntityRole::Autonomous || m_netEntityRole == NetEntityRole::Server) - || (m_netEntityRole == NetEntityRole::InvalidRole); + return (m_remoteNetEntityRole == NetEntityRole::Autonomous || m_remoteNetEntityRole == NetEntityRole::Server) + || (m_remoteNetEntityRole == NetEntityRole::InvalidRole); } bool ReplicationRecord::ContainsAutonomousToAuthorityBits() const { - return (m_netEntityRole == NetEntityRole::Authority) - || (m_netEntityRole == NetEntityRole::InvalidRole); + return (m_remoteNetEntityRole == NetEntityRole::Authority) + || (m_remoteNetEntityRole == NetEntityRole::InvalidRole); } uint32_t ReplicationRecord::GetRemainingAuthorityToClientBits() const diff --git a/Gems/PhysX/Code/NumericalMethods/Source/Optimization/Constants.h b/Gems/PhysX/Code/NumericalMethods/Source/Optimization/Constants.h index d480fb9cd0..feb626aa50 100644 --- a/Gems/PhysX/Code/NumericalMethods/Source/Optimization/Constants.h +++ b/Gems/PhysX/Code/NumericalMethods/Source/Optimization/Constants.h @@ -27,6 +27,6 @@ namespace NumericalMethods::Optimization const double epsilon = 1e-7; // values recommended in Nocedal and Wright for constants in the Wolfe conditions for satisfactory solution improvement - const double c1 = 1e-4; - const double c2 = 0.9; + const double WolfeConditionsC1 = 1e-4; + const double WolfeConditionsC2 = 0.9; } // namespace NumericalMethods::Optimization diff --git a/Gems/PhysX/Code/NumericalMethods/Source/Optimization/LineSearch.cpp b/Gems/PhysX/Code/NumericalMethods/Source/Optimization/LineSearch.cpp index abc2154228..06152446d6 100644 --- a/Gems/PhysX/Code/NumericalMethods/Source/Optimization/LineSearch.cpp +++ b/Gems/PhysX/Code/NumericalMethods/Source/Optimization/LineSearch.cpp @@ -72,7 +72,7 @@ namespace NumericalMethods::Optimization for (AZ::u32 iteration = 0; iteration < lineSearchIterations; iteration++) { - ScalarVariable alphaNew; + ScalarVariable alphaNew = 0.0; if (iteration > 0) { // first try selecting a new alpha value based on cubic interpolation through the most recent points @@ -162,16 +162,16 @@ namespace NumericalMethods::Optimization { // if the value of f corresponding to alpha1 isn't sufficiently small compared to f at x0, // then the interval [alpha0 ... alpha1] must bracket a suitable point. - if ((f_alpha1 > f_x0 + c1 * alpha1 * df_x0) || (iteration > 0 && f_alpha1 > f_alpha0)) + if ((f_alpha1 > f_x0 + WolfeConditionsC1 * alpha1 * df_x0) || (iteration > 0 && f_alpha1 > f_alpha0)) { return SelectStepSizeFromInterval(alpha0, alpha1, f_alpha0, f_alpha1, df_alpha0, - f, x0, searchDirection, f_x0, df_x0, c1, c2); + f, x0, searchDirection, f_x0, df_x0, WolfeConditionsC1, WolfeConditionsC2); } // otherwise, if the derivative corresponding to alpha1 is large enough, alpha1 already // satisfies the Wolfe conditions and so return alpha1. double df_alpha1 = DirectionalDerivative(f, x0 + alpha1 * searchDirection, searchDirection); - if (fabs(df_alpha1) <= -c2 * df_x0) + if (fabs(df_alpha1) <= -WolfeConditionsC2 * df_x0) { LineSearchResult result; result.m_outcome = LineSearchOutcome::Success; @@ -184,7 +184,7 @@ namespace NumericalMethods::Optimization if (df_alpha1 >= 0.0) { return SelectStepSizeFromInterval(alpha1, alpha0, f_alpha1, f_alpha0, df_alpha1, - f, x0, searchDirection, f_x0, df_x0, c1, c2); + f, x0, searchDirection, f_x0, df_x0, WolfeConditionsC1, WolfeConditionsC2); } // haven't found an interval which is guaranteed to bracket a suitable point, diff --git a/Gems/PhysX/Code/NumericalMethods/Tests/OptimizationTest.cpp b/Gems/PhysX/Code/NumericalMethods/Tests/OptimizationTest.cpp index b02f13d8a4..92d7e0dcba 100644 --- a/Gems/PhysX/Code/NumericalMethods/Tests/OptimizationTest.cpp +++ b/Gems/PhysX/Code/NumericalMethods/Tests/OptimizationTest.cpp @@ -158,12 +158,12 @@ namespace NumericalMethods::Optimization double f_x0 = f_alpha0; double df_x0 = df_alpha0; LineSearchResult lineSearchResult = SelectStepSizeFromInterval(alpha0, alpha1, f_alpha0, f_alpha1, df_alpha0, - testFunctionRosenbrock, x0, searchDirection, f_x0, df_x0, c1, c2); + testFunctionRosenbrock, x0, searchDirection, f_x0, df_x0, WolfeConditionsC1, WolfeConditionsC2); EXPECT_TRUE(lineSearchResult.m_outcome == LineSearchOutcome::Success); // check that the Wolfe conditions are satisfied by the returned step size - EXPECT_TRUE(lineSearchResult.m_functionValue < f_x0 + c1 * df_x0 * lineSearchResult.m_stepSize); - EXPECT_TRUE(fabs(lineSearchResult.m_derivativeValue) <= -c2 * df_x0); + EXPECT_TRUE(lineSearchResult.m_functionValue < f_x0 + WolfeConditionsC1 * df_x0 * lineSearchResult.m_stepSize); + EXPECT_TRUE(fabs(lineSearchResult.m_derivativeValue) <= -WolfeConditionsC2 * df_x0); } TEST(OptimizationTest, LineSearch_VariousSearchDirections_SatisfiesWolfeCondition) @@ -180,8 +180,8 @@ namespace NumericalMethods::Optimization EXPECT_TRUE(lineSearchResult.m_outcome == LineSearchOutcome::Success); // check that the Wolfe conditions are satisfied by the returned step size - EXPECT_TRUE(lineSearchResult.m_functionValue < f_x0 + c1 * df_x0 * lineSearchResult.m_stepSize); - EXPECT_TRUE(fabs(lineSearchResult.m_derivativeValue) <= -c2 * df_x0); + EXPECT_TRUE(lineSearchResult.m_functionValue < f_x0 + WolfeConditionsC1 * df_x0 * lineSearchResult.m_stepSize); + EXPECT_TRUE(fabs(lineSearchResult.m_derivativeValue) <= -WolfeConditionsC2 * df_x0); } } diff --git a/Gems/PhysX/Code/Tests/Benchmarks/PhysXBenchmarksUtilities.h b/Gems/PhysX/Code/Tests/Benchmarks/PhysXBenchmarksUtilities.h index ab9bd58148..0971226644 100644 --- a/Gems/PhysX/Code/Tests/Benchmarks/PhysXBenchmarksUtilities.h +++ b/Gems/PhysX/Code/Tests/Benchmarks/PhysXBenchmarksUtilities.h @@ -112,9 +112,9 @@ namespace PhysX::Benchmarks for (double percentile : percentiles) { //ensure the percentile is between 0.0 and 1.0 - const double epsilon = 0.001; - AZ::ClampIfCloseMag(percentile, 0.0, epsilon); - AZ::ClampIfCloseMag(percentile, 1.0, epsilon); + const double testEpsilon = 0.001; + AZ::ClampIfCloseMag(percentile, 0.0, testEpsilon); + AZ::ClampIfCloseMag(percentile, 1.0, testEpsilon); size_t idx = aznumeric_cast(std::round(percentile * (values.size() - 1))); std::nth_element(values.begin(), values.begin() + idx, values.end()); diff --git a/Gems/PhysXDebug/Code/Source/SystemComponent.cpp b/Gems/PhysXDebug/Code/Source/SystemComponent.cpp index 08bf71753d..34315eb11e 100644 --- a/Gems/PhysXDebug/Code/Source/SystemComponent.cpp +++ b/Gems/PhysXDebug/Code/Source/SystemComponent.cpp @@ -15,6 +15,7 @@ #include #include #include +#include #include #include @@ -183,9 +184,7 @@ namespace PhysXDebug void SystemComponent::OnCrySystemInitialized([[maybe_unused]] ISystem& system, const SSystemInitParams&) { InitPhysXColorMappings(); - RegisterCommands(); ConfigurePhysXVisualizationParameters(); - } void SystemComponent::Reflect(AZ::ReflectContext* context) @@ -537,12 +536,13 @@ namespace PhysXDebug } } - static void CmdEnableWireFrame([[maybe_unused]] IConsoleCmdArgs* args) + static void physx_CullingBox([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments) { PhysXDebug::PhysXDebugRequestBus::Broadcast(&PhysXDebug::PhysXDebugRequestBus::Events::ToggleCullingWireFrame); } + AZ_CONSOLEFREEFUNC(physx_CullingBox, AZ::ConsoleFunctorFlags::DontReplicate, "Enables physx wireframe view"); - static void CmdConnectToPvd([[maybe_unused]] IConsoleCmdArgs* args) + static void physx_PvdConnect([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments) { auto* debug = AZ::Interface::Get(); if (debug) @@ -550,8 +550,9 @@ namespace PhysXDebug debug->ConnectToPvd(); } } + AZ_CONSOLEFREEFUNC(physx_PvdConnect, AZ::ConsoleFunctorFlags::DontReplicate, "Connects to the physx visual debugger"); - static void CmdDisconnectFromPvd([[maybe_unused]] IConsoleCmdArgs* args) + static void physx_PvdDisconnect([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments) { auto* debug = AZ::Interface::Get(); if (debug) @@ -559,13 +560,14 @@ namespace PhysXDebug debug->DisconnectFromPvd(); } } + AZ_CONSOLEFREEFUNC(physx_PvdDisconnect, AZ::ConsoleFunctorFlags::DontReplicate, "Disconnects from the physx visual debugger"); - static void CmdSetPhysXDebugCullingBoxSize(IConsoleCmdArgs* args) + static void physx_CullingBoxSize([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments) { - const int argumentCount = args->GetArgCount(); + const int argumentCount = arguments.size(); if (argumentCount == 2) { - float newCullingBoxSize = (float)strtol(args->GetArg(1), nullptr, 10); + float newCullingBoxSize = (float)strtol(AZ::CVarFixedString(arguments[1]).c_str(), nullptr, 10); PhysXDebug::PhysXDebugRequestBus::Broadcast(&PhysXDebug::PhysXDebugRequestBus::Events::SetCullingBoxSize, newCullingBoxSize); } else @@ -574,16 +576,17 @@ namespace PhysXDebug "Please use physx_SetDebugCullingBoxSize e.g. physx_SetDebugCullingBoxSize 100."); } } + AZ_CONSOLEFREEFUNC(physx_CullingBoxSize, AZ::ConsoleFunctorFlags::DontReplicate, "Sets physx debug culling box size"); - static void CmdTogglePhysXDebugVisualization(IConsoleCmdArgs* args) + static void physx_Debug([[maybe_unused]] const AZ::ConsoleCommandContainer& arguments) { using namespace CryStringUtils; - const int argumentCount = args->GetArgCount(); + const int argumentCount = arguments.size(); if (argumentCount == 2) { - const auto userPreference = static_cast(strtol(args->GetArg(1), nullptr, 10)); + const auto userPreference = static_cast(strtol(AZ::CVarFixedString(arguments[1]).c_str(), nullptr, 10)); switch (userPreference) { @@ -609,29 +612,7 @@ namespace PhysXDebug AZ_Warning("PhysXDebug", false, "Invalid physx_Debug Arguments. Please use physx_Debug 1 to enable, physx_Debug 0 to disable or physx_Debug 2 to enable all configuration settings."); } } - - void SystemComponent::RegisterCommands() - { - if (m_registered) - { - return; - } - - if (gEnv) - { - IConsole* console = gEnv->pSystem->GetIConsole(); - if (console) - { - console->AddCommand("physx_Debug", CmdTogglePhysXDebugVisualization); - console->AddCommand("physx_CullingBox", CmdEnableWireFrame); - console->AddCommand("physx_CullingBoxSize", CmdSetPhysXDebugCullingBoxSize); - console->AddCommand("physx_PvdConnect", CmdConnectToPvd); - console->AddCommand("physx_PvdDisconnect", CmdDisconnectFromPvd); - } - - m_registered = true; - } - } + AZ_CONSOLEFREEFUNC(physx_Debug, AZ::ConsoleFunctorFlags::DontReplicate, "Toggles physx debug visualization"); void SystemComponent::ConfigurePhysXVisualizationParameters() { diff --git a/Gems/PhysXDebug/Code/Source/SystemComponent.h b/Gems/PhysXDebug/Code/Source/SystemComponent.h index 631354c034..f4d033fd4c 100644 --- a/Gems/PhysXDebug/Code/Source/SystemComponent.h +++ b/Gems/PhysXDebug/Code/Source/SystemComponent.h @@ -161,9 +161,6 @@ namespace PhysXDebug /// Initialise the PhysX debug draw colors based on defaults. void InitPhysXColorMappings(); - /// Register debug drawing PhysX commands with Open 3D Engine console during game mode. - void RegisterCommands(); - /// Draw the culling box being used by the viewport. /// @param cullingBoxAabb culling box Aabb to debug draw. void DrawDebugCullingBox(const AZ::Aabb& cullingBoxAabb); diff --git a/Templates/DefaultProject/Template/Code/enabled_gems.cmake b/Templates/DefaultProject/Template/Code/enabled_gems.cmake index dfb7d93233..ec45be0743 100644 --- a/Templates/DefaultProject/Template/Code/enabled_gems.cmake +++ b/Templates/DefaultProject/Template/Code/enabled_gems.cmake @@ -10,7 +10,7 @@ # {END_LICENSE} set(ENABLED_GEMS - Project::${Name} + ${Name} Atom_AtomBridge Camera CameraFramework diff --git a/cmake/Gems.cmake b/cmake/Gems.cmake index d418d5dcd1..169210d991 100644 --- a/cmake/Gems.cmake +++ b/cmake/Gems.cmake @@ -29,9 +29,6 @@ function(ly_create_alias) message(FATAL_ERROR "Provide the namespace of the alias to create using the NAMESPACE keyword") endif() - if (NOT ly_create_alias_TARGETS) - message(FATAL_ERROR "Provide the name of the targets the alias be associated with, using the TARGETS keyword") - endif() if(TARGET ${ly_create_alias_NAMESPACE}::${ly_create_alias_NAME}) message(FATAL_ERROR "Target already exists, cannot create an alias for it: ${ly_create_alias_NAMESPACE}::${ly_create_alias_NAME}\n" @@ -78,8 +75,11 @@ function(ly_create_alias) list(APPEND final_targets ${de_aliased_target_name}) endforeach() - ly_parse_third_party_dependencies("${final_targets}") - ly_add_dependencies(${ly_create_alias_NAME} ${final_targets}) + # add_dependencies must be called with at least one dependent target + if(final_targets) + ly_parse_third_party_dependencies("${final_targets}") + ly_add_dependencies(${ly_create_alias_NAME} ${final_targets}) + endif() # now add the final alias: add_library(${ly_create_alias_NAMESPACE}::${ly_create_alias_NAME} ALIAS ${ly_create_alias_NAME}) diff --git a/cmake/Platform/Common/Install_common.cmake b/cmake/Platform/Common/Install_common.cmake index 710a8b266f..11260ab018 100644 --- a/cmake/Platform/Common/Install_common.cmake +++ b/cmake/Platform/Common/Install_common.cmake @@ -443,7 +443,6 @@ function(ly_setup_others) install(DIRECTORY ${LY_ROOT_FOLDER}/scripts/bundler - ${LY_ROOT_FOLDER}/scripts/project_manager ${LY_ROOT_FOLDER}/scripts/o3de DESTINATION ./scripts COMPONENT ${LY_DEFAULT_INSTALL_COMPONENT} diff --git a/cmake/Platform/Common/MSVC/Configurations_msvc.cmake b/cmake/Platform/Common/MSVC/Configurations_msvc.cmake index 25b6e63ab9..f53b8aa769 100644 --- a/cmake/Platform/Common/MSVC/Configurations_msvc.cmake +++ b/cmake/Platform/Common/MSVC/Configurations_msvc.cmake @@ -74,8 +74,6 @@ ly_append_configurations_options( /wd4436 # the result of unary operator may be unaligned /wd4450 # declaration hides global declaration /wd4457 # declaration hides function parameter - /wd4459 # declaration hides global declaration - /wd4701 # potentially unintialized local variable # Enabling warnings that are disabled by default from /W4 # https://docs.microsoft.com/en-us/cpp/preprocessor/compiler-warnings-that-are-off-by-default?view=vs-2019 diff --git a/cmake/SettingsRegistry.cmake b/cmake/SettingsRegistry.cmake index 4d932601b4..d7ef91d284 100644 --- a/cmake/SettingsRegistry.cmake +++ b/cmake/SettingsRegistry.cmake @@ -25,14 +25,15 @@ set(gems_json_template [[ @target_gem_dependencies_names@ } } -}]] +} +]] ) -set(gem_module_template [[ - "@stripped_gem_target@": - { - "Modules":["$"], - "SourcePaths":["@gem_module_root_relative_to_engine_root@"] - }]] + string(APPEND gem_module_template +[=[ "@stripped_gem_target@":]=] "\n" +[=[ {]=] "\n" +[=[$<$,INTERFACE_LIBRARY>>: "Modules":["$"]]=] "$\n>" +[=[ "SourcePaths":["@gem_module_root_relative_to_engine_root@"]]=] "\n" +[=[ }]=] ) #!ly_get_gem_load_dependencies: Retrieves the list of "load" dependencies for a target @@ -161,10 +162,12 @@ function(ly_delayed_generate_settings_registry) message(FATAL_ERROR "Dependency ${gem_target} from ${target} does not exist") endif() + get_property(has_manually_added_dependencies TARGET ${gem_target} PROPERTY MANUALLY_ADDED_DEPENDENCIES SET) get_target_property(target_type ${gem_target} TYPE) - if (target_type STREQUAL "INTERFACE_LIBRARY") - # don't use interface libraries here, we only want ones which produce actual binaries. - # we have still already recursed into their dependencies - they'll show up later. + if (target_type STREQUAL "INTERFACE_LIBRARY" AND has_manually_added_dependencies) + # don't use interface libraries here, we only want ones which produce actual binaries unless the target + # is empty. We have still already recursed into their dependencies - they'll show up later. + # When the target has no dependencies however we want to add the gem root path to the generated setreg continue() endif() diff --git a/scripts/CMakeLists.txt b/scripts/CMakeLists.txt index d3c9640665..6df0f4b77f 100644 --- a/scripts/CMakeLists.txt +++ b/scripts/CMakeLists.txt @@ -12,5 +12,4 @@ add_subdirectory(detect_file_changes) add_subdirectory(commit_validation) add_subdirectory(o3de) -add_subdirectory(project_manager) add_subdirectory(ctest) diff --git a/scripts/o3de.py b/scripts/o3de.py index 8d7532878c..85d49ec268 100755 --- a/scripts/o3de.py +++ b/scripts/o3de.py @@ -32,7 +32,7 @@ def add_args(parser, subparsers) -> None: # add the scripts/o3de directory to the front of the sys.path sys.path.insert(0, str(o3de_package_dir)) from o3de import engine_template, global_project, register, print_registration, get_registration, \ - enable_gem, disable_gem, sha256 + enable_gem, disable_gem, project_properties, sha256 # Remove the temporarily added path sys.path = sys.path[1:] @@ -55,7 +55,10 @@ def add_args(parser, subparsers) -> None: # remove a gem from a project disable_gem.add_args(subparsers) - + + # modify project properties + project_properties.add_args(subparsers) + # sha256 sha256.add_args(subparsers) diff --git a/scripts/o3de/o3de/manifest.py b/scripts/o3de/o3de/manifest.py index 2a7e5bba11..edcd44c525 100644 --- a/scripts/o3de/o3de/manifest.py +++ b/scripts/o3de/o3de/manifest.py @@ -573,9 +573,7 @@ def get_registered(engine_name: str = None, return engine_path elif isinstance(project_name, str): - enging_projects = get_engine_projects() - projects = json_data['projects'].copy() - projects.extend(engine_object['projects']) + projects = get_all_projects() for project_path in projects: project_path = pathlib.Path(project_path).resolve() project_json = project_path / 'project.json' @@ -605,9 +603,7 @@ def get_registered(engine_name: str = None, return gem_path elif isinstance(template_name, str): - engine_templates = get_engine_templates() - templates = json_data['templates'].copy() - templates.extend(engine_templates) + templates = get_all_templates() for template_path in templates: template_path = pathlib.Path(template_path).resolve() template_json = template_path / 'template.json' @@ -622,9 +618,7 @@ def get_registered(engine_name: str = None, return template_path elif isinstance(restricted_name, str): - engine_restricted = get_engine_restricted() - restricted = json_data['restricted'].copy() - restricted.extend(engine_restricted) + restricted = get_all_restricted() for restricted_path in restricted: restricted_path = pathlib.Path(restricted_path).resolve() restricted_json = restricted_path / 'restricted.json' diff --git a/scripts/o3de/o3de/project_properties.py b/scripts/o3de/o3de/project_properties.py new file mode 100644 index 0000000000..69bd1b9406 --- /dev/null +++ b/scripts/o3de/o3de/project_properties.py @@ -0,0 +1,104 @@ +# +# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or +# its licensors. +# +# For complete copyright and license terms please see the LICENSE at the root of this +# distribution (the "License"). All use of this software is governed by the License, +# or, if provided, by the license below or the license accompanying this file. Do not +# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, +# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. +# + +import argparse +import json +import os +import pathlib +import sys +import logging + +from o3de import manifest + +logger = logging.getLogger() +logging.basicConfig() + +def get_project_props(name: str = None, path: pathlib.Path = None) -> dict: + proj_json = manifest.get_project_json_data(project_name=name, project_path=path) + if not proj_json: + param = name if name else path + logger.error(f'Could not retrieve project.json file for {param}') + return None + return proj_json + +def edit_project_props(proj_path, proj_name, new_origin, new_display, + new_summary, new_icon, new_tag, remove_tag) -> int: + proj_json = get_project_props(proj_name, proj_path) + + if not proj_json: + return 1 + + if new_origin: + proj_json['origin'] = new_origin + if new_display: + proj_json['display_name'] = new_display + if new_summary: + proj_json['summary'] = new_summary + if new_icon: + proj_json['icon_path'] = new_icon + if new_tag: + proj_json.setdefault('user_tags', []).append(new_tag) + if remove_tag: + if 'user_tags' in proj_json: + if remove_tag in proj_json['user_tags']: + proj_json['user_tags'].remove(remove_tag) + else: + logger.warn(f'{remove_tag} not found in user_tags for removal.') + else: + logger.warn(f'user_tags property not found for removal of tag {remove_tag}.') + + manifest.save_o3de_manifest(proj_json, pathlib.Path(proj_path) / 'project.json') + return 0 + +def _edit_project_props(args: argparse) -> int: + return edit_project_props(args.project_path, + args.project_name, + args.project_origin, + args.project_display, + args.project_summary, + args.project_icon, + args.project_tag, + args.remove_tag) + +def add_parser_args(parser): + group = parser.add_mutually_exclusive_group(required=True) + group.add_argument('-pp', '--project-path', type=pathlib.Path, required=False, + help='The path to the project.') + group.add_argument('-pn', '--project-name', type=str, required=False, + help='The name of the project.') + group = parser.add_argument_group('properties', 'arguments for modifying individual project properties.') + group.add_argument('-po', '--project-origin', type=str, required=False, + help='Sets description or url for project origin (such as project host, repository, owner...etc).') + group.add_argument('-pd', '--project-display', type=str, required=False, + help='Sets the project display name.') + group.add_argument('-ps', '--project-summary', type=str, required=False, + help='Sets the summary description of the project.') + group.add_argument('-pi', '--project-icon', type=str, required=False, + help='Sets the path to the projects icon resource.') + group.add_argument('-pt', '--project-tag', type=str, required=False, + help='Adds a tag to user_tags property. These tags are intended for documentation and filtering.') + group.add_argument('-rt', '--remove-tag', type=str, required=False, + help='Removes a tag from the user_tags property.') + parser.set_defaults(func=_edit_project_props) + +def add_args(subparsers) -> None: + enable_project_props_subparser = subparsers.add_parser('edit-project-properties') + add_parser_args(enable_project_props_subparser) + +def main(): + the_parser = argparse.ArgumentParser() + add_parser_args(the_parser) + the_args = the_parser.parse_args() + ret = the_args.func(the_args) if hasattr(the_args, 'func') else 1 + sys.exit(ret) + +if __name__ == "__main__": + main() diff --git a/scripts/project_manager/CMakeLists.txt b/scripts/project_manager/CMakeLists.txt deleted file mode 100644 index 36c9ad0360..0000000000 --- a/scripts/project_manager/CMakeLists.txt +++ /dev/null @@ -1,22 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -ly_download_associated_package(pyside2) - -ly_add_pytest( - NAME test_pyside - PATH ${CMAKE_CURRENT_LIST_DIR}/tests/test_pyside.py -) - -ly_add_pytest( - NAME test_projects - PATH ${CMAKE_CURRENT_LIST_DIR}/tests/test_projects.py -) \ No newline at end of file diff --git a/scripts/project_manager/__init__.py b/scripts/project_manager/__init__.py deleted file mode 100755 index 4d5680a30d..0000000000 --- a/scripts/project_manager/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# diff --git a/scripts/project_manager/projects.py b/scripts/project_manager/projects.py deleted file mode 100755 index f9f40aa4bf..0000000000 --- a/scripts/project_manager/projects.py +++ /dev/null @@ -1,804 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -# PySide project and gem selector GUI - -import os -import pathlib -import sys -import argparse -import json -import logging -import subprocess -from logging.handlers import RotatingFileHandler -from typing import List -from pyside import add_pyside_environment, is_pyside_ready, uninstall_env - -engine_path = os.path.realpath(os.path.join(os.path.dirname(__file__), '..', '..')) -sys.path.append(engine_path) -executable_path = '' - -logger = logging.getLogger() -logger.setLevel(logging.INFO) - -from o3de import disable_gem, enable_gem, cmake, engine_template, manifest, register - -o3de_folder = manifest.get_o3de_folder() -o3de_logs_folder = manifest.get_o3de_logs_folder() -project_manager_log_file_path = o3de_logs_folder / "project_manager.log" -log_file_handler = RotatingFileHandler(filename=project_manager_log_file_path, maxBytes=1024 * 1024, backupCount=1) -formatter = logging.Formatter('%(asctime)s | %(levelname)s : %(message)s') -log_file_handler.setFormatter(formatter) -logger.addHandler(log_file_handler) - -logger.info("Starting Project Manager") - - -def initialize_pyside_from_parser(): - # Parse arguments up top. We need to know the path to our binaries and QT libs in particular to load up - # PySide - parser = argparse.ArgumentParser() - parser.add_argument('--executable-path', required=True, help='Path to Executable to launch with project') - parser.add_argument('--binaries-path', default=None, help='Path to QT Binaries necessary for PySide. If not' - ' provided executable_path folder is assumed') - parser.add_argument('--parent-pid', default=0, help='Process ID of launching process') - - args = parser.parse_args() - - logger.info(f"parent_pid is {args.parent_pid}") - global executable_path - executable_path = args.executable_path - binaries_path = args.binaries_path or os.path.dirname(executable_path) - - # Initialize PySide before imports below. This adds both PySide python modules to the python system interpreter - # path and adds the necessary paths to binaries for the DLLs to be found and load their dependencies - add_pyside_environment(binaries_path) - - -if not is_pyside_ready(): - initialize_pyside_from_parser() - -try: - from PySide2.QtWidgets import QApplication, QDialogButtonBox, QPushButton, QComboBox, QMessageBox, QFileDialog - from PySide2.QtWidgets import QListView, QLabel - from PySide2.QtUiTools import QUiLoader - from PySide2.QtCore import QFile, QObject, Qt, Signal, Slot - from PySide2.QtGui import QIcon, QStandardItemModel, QStandardItem -except ImportError as e: - logger.error(f"Failed to import PySide2 with error {e}") - exit(-1) - -logger.error(f"PySide2 imports successful") - - -class DialogLoggerSignaller(QObject): - send_to_dialog = Signal(str) - - def __init__(self, dialog_logger): - super(DialogLoggerSignaller, self).__init__() - - self.dialog_logger = dialog_logger - - -# Independent class to handle log forwarding. Logger and qt signals both use emit method. -# This class's job is to receive the logger record and then emit the formatted message through -# DialogLoggerSignaller which is what the ProjectDialog handler listens for -class DialogLogger(logging.Handler): - - def __init__(self, log_dialog, log_level=logging.INFO, forward_log_level=logging.WARNING, - message_box_log_level=logging.ERROR): - super(DialogLogger, self).__init__() - - self.log_dialog = log_dialog - self.log_level = log_level - self.forward_log_level = forward_log_level - self.message_box_log_level = message_box_log_level - self.log_records = [] - self.formatter = logging.Formatter('%(levelname)s : %(message)s') - self.setFormatter(self.formatter) - self.signaller = DialogLoggerSignaller(self) - - def emit(self, record): - self.log_records.append(record) - if record.levelno >= self.message_box_log_level: - QMessageBox.warning(None, record.levelname, record.message) - elif record.levelno >= self.forward_log_level: - self.signaller.send_to_dialog.emit(self.format(record)) - - -class ProjectManagerDialog(QObject): - """ - Main project manager dialog is responsible for displaying the project selection list and output pane - """ - - def __init__(self, parent=None): - super(ProjectManagerDialog, self).__init__(parent) - - self.ui_path = (pathlib.Path(__file__).parent / 'ui').resolve() - self.home_folder = manifest.get_home_folder() - - self.log_display = None - self.dialog_logger = DialogLogger(self) - logger.addHandler(self.dialog_logger) - logger.setLevel(logging.INFO) - - self.dialog_logger.signaller.send_to_dialog.connect(self.handle_log_message) - self.mru_file_path = o3de_folder / 'mru.json' - - self.create_from_template_ui_file_path = self.ui_path / 'create_from_template.ui' - self.create_gem_ui_file_path = self.ui_path / 'create_gem.ui' - self.create_project_ui_file_path = self.ui_path / 'create_project.ui' - self.manage_project_gem_targets_ui_file_path = self.ui_path / 'manage_gem_targets.ui' - self.project_manager_icon_file_path = self.ui_path / 'project_manager.ico' - self.project_manager_ui_file_path = self.ui_path / 'project_manager.ui' - - self.project_manager_ui_file = QFile(self.project_manager_ui_file_path.as_posix()) - self.project_manager_ui_file.open(QFile.ReadOnly) - - loader = QUiLoader() - self.dialog = loader.load(self.project_manager_ui_file) - self.dialog.setWindowIcon(QIcon(self.project_manager_icon_file_path.as_posix())) - self.dialog.setFixedSize(self.dialog.size()) - - self.project_list_box = self.dialog.findChild(QComboBox, 'projectListBox') - self.refresh_project_list() - mru = self.get_mru_list() - if len(mru): - last_mru = pathlib.Path(mru[0]).resolve() - for this_slot in range(self.project_list_box.count()): - item_text = self.project_list_box.itemText(this_slot) - if last_mru.as_posix() in item_text: - self.project_list_box.setCurrentIndex(this_slot) - break - - self.create_project_button = self.dialog.findChild(QPushButton, 'createProjectButton') - self.create_project_button.clicked.connect(self.create_project_handler) - self.create_gem_button = self.dialog.findChild(QPushButton, 'createGemButton') - self.create_gem_button.clicked.connect(self.create_gem_handler) - self.create_template_button = self.dialog.findChild(QPushButton, 'createTemplateButton') - self.create_template_button.clicked.connect(self.create_template_handler) - self.create_from_template_button = self.dialog.findChild(QPushButton, 'createFromTemplateButton') - self.create_from_template_button.clicked.connect(self.create_from_template_handler) - - self.add_project_button = self.dialog.findChild(QPushButton, 'addProjectButton') - self.add_project_button.clicked.connect(self.add_project_handler) - self.add_gem_button = self.dialog.findChild(QPushButton, 'addGemButton') - self.add_gem_button.clicked.connect(self.add_gem_handler) - self.add_template_button = self.dialog.findChild(QPushButton, 'addTemplateButton') - self.add_template_button.clicked.connect(self.add_template_handler) - self.add_restricted_button = self.dialog.findChild(QPushButton, 'addRestrictedButton') - self.add_restricted_button.clicked.connect(self.add_restricted_handler) - - self.remove_project_button = self.dialog.findChild(QPushButton, 'removeProjectButton') - self.remove_project_button.clicked.connect(self.remove_project_handler) - self.remove_gem_button = self.dialog.findChild(QPushButton, 'removeGemButton') - self.remove_gem_button.clicked.connect(self.remove_gem_handler) - self.remove_template_button = self.dialog.findChild(QPushButton, 'removeTemplateButton') - self.remove_template_button.clicked.connect(self.remove_template_handler) - self.remove_restricted_button = self.dialog.findChild(QPushButton, 'removeRestrictedButton') - self.remove_restricted_button.clicked.connect(self.remove_restricted_handler) - - self.manage_project_gem_targets_button = self.dialog.findChild(QPushButton, 'manageRuntimeGemTargetsButton') - self.manage_project_gem_targets_button.clicked.connect(self.manage_project_gem_targets_handler) - - self.log_display = self.dialog.findChild(QLabel, 'logDisplay') - - self.ok_cancel_button = self.dialog.findChild(QDialogButtonBox, 'okCancel') - self.ok_cancel_button.accepted.connect(self.accepted_handler) - - self.dialog.show() - - def refresh_project_list(self) -> None: - projects = manifest.get_all_projects() - self.project_list_box.clear() - for this_slot in range(len(projects)): - display_name = f'{os.path.basename(os.path.normpath(projects[this_slot]))} ({projects[this_slot]})' - self.project_list_box.addItem(display_name) - self.project_list_box.setItemData(self.project_list_box.count() - 1, projects[this_slot], - Qt.ToolTipRole) - - def accepted_handler(self) -> None: - """ - Override for handling "Ok" on main project dialog to first check whether the user has selected a project and - prompt them to if not. If a project is selected will attempt to open it. - :return: None - """ - if not self.project_list_box.currentText(): - msg_box = QMessageBox(parent=self.dialog) - msg_box.setWindowTitle("O3DE") - msg_box.setText("Please select a project") - msg_box.exec() - return - self.launch_with_project_path(self.get_selected_project_path()) - - def get_launch_project(self) -> str: - return os.path.normpath(self.get_selected_project_path()) - - def get_executable_launch_params(self) -> list: - """ - Retrieve the necessary launch parameters to make the subprocess launch call with - this is the path - to the executable such as the Editor and the path to the selected project - :return: list of params - """ - launch_params = [executable_path, - f'-regset="/Amazon/AzCore/Bootstrap/project_path={self.get_launch_project()}"'] - return launch_params - - def launch_with_project_path(self, project_path: str) -> None: - """ - Launch the desired application given the selected project - :param project_path: Path to currently selected project - :return: None - """ - logger.info(f'Attempting to open {project_path}') - self.update_mru_list(project_path) - launch_params = self.get_executable_launch_params() - logger.info(f'Launching with params {launch_params}') - subprocess.run(launch_params, env=uninstall_env()) - - def get_selected_project_path(self) -> str: - if self.project_list_box.currentIndex() == -1: - logger.warning("No project selected") - return "" - return self.project_list_box.itemData(self.project_list_box.currentIndex(), Qt.ToolTipRole) - - def get_selected_project_name(self) -> str: - project_data = manifest.get_project_json_data(project_path=self.get_selected_project_path()) - return project_data['project_name'] - - def create_project_handler(self): - """ - Opens the Create Project pane. Retrieves a list of available templates for display - :return: None - """ - loader = QUiLoader() - self.create_project_file = QFile(self.create_project_ui_file_path.as_posix()) - - if not self.create_project_file: - logger.error(f'Failed to create project UI file at {self.create_project_file}') - return - - self.create_project_dialog = loader.load(self.create_project_file) - - if not self.create_project_dialog: - logger.error(f'Failed to load create project dialog file at {self.create_project_file}') - return - - self.create_project_ok_button = self.create_project_dialog.findChild(QDialogButtonBox, 'okCancel') - self.create_project_ok_button.accepted.connect(self.create_project_accepted_handler) - - self.create_project_template_list = self.create_project_dialog.findChild(QListView, 'projectTemplates') - self.refresh_create_project_template_list() - - self.create_project_dialog.exec() - - def create_project_accepted_handler(self) -> None: - """ - Searches the available gems list for selected gems and attempts to add each one to the current project. - Updates UI after completion. - :return: None - """ - - selected_item = self.create_project_template_list.selectionModel().currentIndex() - project_template_path = self.create_project_template_list.model().data(selected_item) - if not project_template_path: - return - - folder_dialog = QFileDialog(self.dialog, "Select a Folder and Enter a New Project Name", - manifest.get_o3de_projects_folder().as_posix()) - folder_dialog.setFileMode(QFileDialog.AnyFile) - folder_dialog.setOptions(QFileDialog.ShowDirsOnly) - project_count = 0 - project_name = "MyNewProject" - while os.path.exists(os.path.join(engine_path, project_name)): - project_name = f"MyNewProject{project_count}" - project_count += 1 - folder_dialog.selectFile(project_name) - project_folder = None - if folder_dialog.exec(): - project_folder = folder_dialog.selectedFiles() - if project_folder: - if engine_template.create_project(project_path=project_folder[0], - template_path=project_template_path) == 0: - # Success - register.register(project_path=project_folder[0]) - self.refresh_project_list() - msg_box = QMessageBox(parent=self.dialog) - msg_box.setWindowTitle("O3DE") - msg_box.setText(f"Project {project_folder[0]} created.") - msg_box.exec() - return - - def create_gem_handler(self): - """ - Opens the Create Gem pane. Retrieves a list of available templates for display - :return: None - """ - loader = QUiLoader() - self.create_gem_file = QFile(self.create_gem_ui_file_path.as_posix()) - - if not self.create_gem_file: - logger.error(f'Failed to create gem UI file at {self.create_gem_file}') - return - - self.create_gem_dialog = loader.load(self.create_gem_file) - - if not self.create_gem_dialog: - logger.error(f'Failed to load create gem dialog file at {self.create_gem_file}') - return - - self.create_gem_ok_button = self.create_gem_dialog.findChild(QDialogButtonBox, 'okCancel') - self.create_gem_ok_button.accepted.connect(self.create_gem_accepted_handler) - - self.create_gem_template_list = self.create_gem_dialog.findChild(QListView, 'gemTemplates') - self.refresh_create_gem_template_list() - - self.create_gem_dialog.exec() - - def create_gem_accepted_handler(self) -> None: - """ - Searches the available gems list for selected gems and attempts to add each one to the current gem. - Updates UI after completion. - :return: None - """ - selected_item = self.create_gem_template_list.selectionModel().currentIndex() - gem_template_path = self.create_gem_template_list.model().data(selected_item) - if not gem_template_path: - return - - folder_dialog = QFileDialog(self.dialog, "Select a Folder and Enter a New Gem Name", - manifest.get_o3de_gems_folder().as_posix()) - folder_dialog.setFileMode(QFileDialog.AnyFile) - folder_dialog.setOptions(QFileDialog.ShowDirsOnly) - gem_count = 0 - gem_name = "MyNewGem" - while os.path.exists(os.path.join(engine_path, gem_name)): - gem_name = f"MyNewGem{gem_count}" - gem_count += 1 - folder_dialog.selectFile(gem_name) - gem_folder = None - if folder_dialog.exec(): - gem_folder = folder_dialog.selectedFiles() - if gem_folder: - if engine_template.create_gem(gem_path=gem_folder[0], - template_path=gem_template_path) == 0: - # Success - register.register(gem_path=gem_folder[0]) - msg_box = QMessageBox(parent=self.dialog) - msg_box.setWindowTitle("O3DE") - msg_box.setText(f"Gem {gem_folder[0]} created.") - msg_box.exec() - return - - def create_template_handler(self): - """ - Opens a foldr select dialog and lets the user select the source folder they want to make a template - out of, then opens a second folder select dialog to get where they want to put the template and it name - :return: None - """ - - source_folder = QFileDialog.getExistingDirectory(self.dialog, - "Select a Folder to make a template out of.", - manifest.get_o3de_folder().as_posix()) - if not source_folder: - return - - destination_template_folder_dialog = QFileDialog(self.dialog, - "Select where the template is to be created and named.", - manifest.get_o3de_templates_folder().as_posix()) - destination_template_folder_dialog.setFileMode(QFileDialog.AnyFile) - destination_template_folder_dialog.setOptions(QFileDialog.ShowDirsOnly) - destination_folder = None - if destination_template_folder_dialog.exec(): - destination_folder = destination_template_folder_dialog.selectedFiles() - if not destination_folder: - return - - if engine_template.create_template(source_path=source_folder, - template_path=destination_folder[0]) == 0: - # Success - register.register(template_path=destination_folder[0]) - msg_box = QMessageBox(parent=self.dialog) - msg_box.setWindowTitle("O3DE") - msg_box.setText(f"Template {destination_folder[0]} created.") - msg_box.exec() - return - - def create_from_template_handler(self): - """ - Opens the Create from_template pane. Retrieves a list of available from_templates for display - :return: None - """ - loader = QUiLoader() - self.create_from_template_file = QFile(self.create_from_template_ui_file_path.as_posix()) - - if not self.create_from_template_file: - logger.error(f'Failed to create from_template UI file at {self.create_from_template_file}') - return - - self.create_from_template_dialog = loader.load(self.create_from_template_file) - - if not self.create_from_template_dialog: - logger.error(f'Failed to load create from_template dialog file at {self.create_from_template_file}') - return - - self.create_from_template_ok_button = self.create_from_template_dialog.findChild(QDialogButtonBox, 'okCancel') - self.create_from_template_ok_button.accepted.connect(self.create_from_template_accepted_handler) - - self.create_from_template_list = self.create_from_template_dialog.findChild(QListView, 'genericTemplates') - self.refresh_create_from_template_list() - - self.create_from_template_dialog.exec() - - def create_from_template_accepted_handler(self) -> None: - """ - Searches the available gems list for selected gems and attempts to add each one to the current gem. - Updates UI after completion. - :return: None - """ - create_gem_item = self.get_selected_gem_template() - if not create_gem_item: - return - - folder_dialog = QFileDialog(self.dialog, "Select a Folder and Enter a New Gem Name", - manifest.get_o3de_gems_folder().as_posix()) - folder_dialog.setFileMode(QFileDialog.AnyFile) - folder_dialog.setOptions(QFileDialog.ShowDirsOnly) - gem_count = 0 - gem_name = "MyNewGem" - while os.path.exists(os.path.join(engine_path, gem_name)): - gem_name = f"MyNewGem{gem_count}" - gem_count += 1 - folder_dialog.selectFile(gem_name) - gem_folder = None - if folder_dialog.exec(): - gem_folder = folder_dialog.selectedFiles() - if gem_folder: - if engine_template.create_gem(gem_folder[0], create_gem_item[1]) == 0: - # Success - msg_box = QMessageBox(parent=self.dialog) - msg_box.setWindowTitle("O3DE") - msg_box.setText(f"gem {os.path.basename(os.path.normpath(gem_folder[0]))} created." - " Build your\nnew gem before hitting OK to launch.") - msg_box.exec() - return - - def add_project_handler(self): - """ - Open a file search dialog looking for a folder which contains a valid project. If valid - will update the mru list with the new entry, if invalid will warn the user. - :return: None - """ - project_folder = QFileDialog.getExistingDirectory(self.dialog, "Select Project Folder", - manifest.get_o3de_projects_folder().as_posix()) - if project_folder: - if register.register(project_path=project_folder) == 0: - # Success - self.refresh_project_list() - - msg_box = QMessageBox(parent=self.dialog) - msg_box.setWindowTitle("O3DE") - msg_box.setText(f"Added Project {project_folder}.") - msg_box.exec() - return - - def add_gem_handler(self): - """ - Open a file search dialog looking for a folder which contains a gem. If valid - will update the mru list with the new entry, if invalid will warn the user. - :return: None - """ - gem_folder = QFileDialog.getExistingDirectory(self.dialog, "Select Gem Folder", - manifest.get_o3de_gems_folder().as_posix()) - if gem_folder: - if register.register(gem_path=gem_folder) == 0: - # Success - msg_box = QMessageBox(parent=self.dialog) - msg_box.setWindowTitle("O3DE") - msg_box.setText(f"Added Gem {gem_folder}.") - msg_box.exec() - return - - def add_template_handler(self): - """ - Open a file search dialog looking for a folder which contains a valid template. If valid - will update the mru list with the new entry, if invalid will warn the user. - :return: None - """ - template_folder = QFileDialog.getExistingDirectory(self.dialog, "Select Template Folder", - manifest.get_o3de_templates_folder().as_posix()) - if template_folder: - if register.register(template_path=template_folder) == 0: - # Success - msg_box = QMessageBox(parent=self.dialog) - msg_box.setWindowTitle("O3DE") - msg_box.setText(f"Added Template {template_folder}.") - msg_box.exec() - return - - def add_restricted_handler(self): - """ - Open a file search dialog looking for a folder which contains a valid template. If valid - will update the mru list with the new entry, if invalid will warn the user. - :return: None - """ - restricted_folder = QFileDialog.getExistingDirectory(self.dialog, "Select Restricted Folder", - manifest.get_o3de_restricted_folder().as_posix()) - if restricted_folder: - if register.register(restricted_path=restricted_folder) == 0: - # Success - msg_box = QMessageBox(parent=self.dialog) - msg_box.setWindowTitle("O3DE") - msg_box.setText(f"Added Restricted {restricted_folder}.") - msg_box.exec() - return - - def remove_project_handler(self): - """ - Open a file search dialog looking for a folder which contains a valid project. If valid - will update the mru list with the new entry, if invalid will warn the user. - :return: None - """ - project_folder = QFileDialog.getExistingDirectory(self.dialog, "Select Project Folder", - manifest.get_o3de_projects_folder().as_posix()) - if project_folder: - if register.register(project_path=project_folder, remove=True) == 0: - # Success - self.refresh_project_list() - - msg_box = QMessageBox(parent=self.dialog) - msg_box.setWindowTitle("O3DE") - msg_box.setText(f"Removed Project {project_folder}.") - msg_box.exec() - return - - def remove_gem_handler(self): - """ - Open a file search dialog looking for a folder which contains a gem. If valid - will update the mru list with the new entry, if invalid will warn the user. - :return: None - """ - gem_folder = QFileDialog.getExistingDirectory(self.dialog, "Select Gem Folder", - manifest.get_o3de_gems_folder().as_posix()) - if gem_folder: - if register.register(gem_path=gem_folder, remove=True) == 0: - # Success - msg_box = QMessageBox(parent=self.dialog) - msg_box.setWindowTitle("O3DE") - msg_box.setText(f"Removed Gem {gem_folder}.") - msg_box.exec() - return - - def remove_template_handler(self): - """ - Open a file search dialog looking for a folder which contains a valid template. If valid - will update the mru list with the new entry, if invalid will warn the user. - :return: None - """ - template_folder = QFileDialog.getExistingDirectory(self.dialog, "Select Template Folder", - manifest.get_o3de_templates_folder().as_posix()) - if template_folder: - if register.register(template_path=template_folder, remove=True) == 0: - # Success - msg_box = QMessageBox(parent=self.dialog) - msg_box.setWindowTitle("O3DE") - msg_box.setText(f"Removed Template {template_folder}.") - msg_box.exec() - return - - def remove_restricted_handler(self): - """ - Open a file search dialog looking for a folder which contains a valid template. If valid - will update the mru list with the new entry, if invalid will warn the user. - :return: None - """ - restricted_folder = QFileDialog.getExistingDirectory(self.dialog, "Select Restricted Folder", - manifest.get_o3de_restricted_folder().as_posix()) - if restricted_folder: - if register.register(restricted_path=restricted_folder, remove=True) == 0: - # Success - msg_box = QMessageBox(parent=self.dialog) - msg_box.setWindowTitle("O3DE") - msg_box.setText(f"Removed Restricted {restricted_folder}.") - msg_box.exec() - return - - def manage_project_gem_targets_handler(self): - """ - Opens the Gem management pane. Waits for the load thread to complete if still running and displays all - active gems for the current project as well as all available gems which aren't currently active. - :return: None - """ - - if not self.get_selected_project_path(): - msg_box = QMessageBox(parent=self.dialog) - msg_box.setWindowTitle("O3DE") - msg_box.setText("Please select a project") - msg_box.exec() - return - - loader = QUiLoader() - self.manage_project_gem_targets_file = QFile(self.manage_project_gem_targets_ui_file_path.as_posix()) - - if not self.manage_project_gem_targets_file: - logger.error(f'Failed to load manage gem targets UI file at {self.manage_project_gem_targets_ui_file_path}') - return - - self.manage_project_gem_targets_dialog = loader.load(self.manage_project_gem_targets_file) - - if not self.manage_project_gem_targets_dialog: - logger.error(f'Failed to load gems dialog file at {self.manage_project_gem_targets_ui_file_path.as_posix()}') - return - - self.manage_project_gem_targets_dialog.setWindowTitle(f"Manage Gems for Project:" - f" {self.get_selected_project_name()}") - - self.add_gem_button = self.manage_project_gem_targets_dialog.findChild(QPushButton, 'addGemTargetsButton') - self.add_gem_button.clicked.connect(self.add_project_gem_targets_handler) - - self.available_gem_targets_list = self.manage_project_gem_targets_dialog.findChild(QListView, - 'availableGemTargetsList') - self.refresh_project_gem_targets_available_list() - - self.remove_project_gem_targets_button = self.manage_project_gem_targets_dialog.findChild(QPushButton, - 'removeGemTargetsButton') - self.remove_project_gem_targets_button.clicked.connect(self.remove_project_gem_targets_handler) - - self.enabled_gem_targets_list = self.manage_project_gem_targets_dialog.findChild(QListView, - 'enabledGemTargetsList') - self.refresh_project_gem_targets_enabled_list() - - self.manage_project_gem_targets_dialog.exec() - - - def manage_project_gem_targets_get_selected_available_gems(self) -> list: - selected_items = self.available_gem_targets_list.selectionModel().selectedRows() - return [(self.available_gem_targets_list.model().data(item)) for item in selected_items] - - def manage_project_gem_targets_get_selected_enabled_gems(self) -> list: - selected_items = self.enabled_gem_targets_list.selectionModel().selectedRows() - return [(self.enabled_gem_targets_list.model().data(item)) for item in selected_items] - - def add_project_gem_targets_handler(self) -> None: - gem_paths = manifest.get_all_gems() - for gem_target in self.manage_project_gem_targets_get_selected_available_gems(): - for gem_path in gem_paths: - enable_gem.enable_gem_in_project(gem_path=gem_path, - project_path=self.get_selected_project_path()) - self.refresh_project_gem_targets_available_list() - self.refresh_project_gem_targets_enabled_list() - return - self.refresh_project_gem_targets_available_list() - self.refresh_project_gem_targets_enabled_list() - - def remove_project_gem_targets_handler(self): - gem_paths = manifest.get_all_gems() - for gem_target in self.manage_project_gem_targets_get_selected_enabled_gems(): - for gem_path in gem_paths: - disable_gem.disable_gem_in_project(gem_path=gem_path, - project_path=self.get_selected_project_path()) - self.refresh_project_gem_targets_available_list() - self.refresh_project_gem_targets_enabled_list() - return - self.refresh_project_gem_targets_available_list() - self.refresh_project_gem_targets_enabled_list() - - def refresh_project_gem_targets_enabled_list(self) -> None: - enabled_project_gem_targets_model = QStandardItemModel() - enabled_project_gems = cmake.get_project_gems(project_path=self.get_selected_project_path()) - for gem_target in sorted(enabled_project_gems): - model_item = QStandardItem(gem_target) - enabled_project_gem_targets_model.appendRow(model_item) - self.enabled_gem_targets_list.setModel(enabled_project_gem_targets_model) - - - def refresh_project_gem_targets_available_list(self) -> None: - available_project_gem_targets_model = QStandardItemModel() - enabled_project_gem_targets = cmake.get_project_gems(project_path=self.get_selected_project_path()) - all_gem_targets = manifest.get_all_gems() - for gem_target in sorted(all_gem_targets): - if gem_target not in enabled_project_gem_targets: - model_item = QStandardItem(gem_target) - available_project_gem_targets_model.appendRow(model_item) - self.available_gem_targets_list.setModel(available_project_gem_targets_model) - - - def refresh_create_project_template_list(self) -> None: - self.create_project_template_model = QStandardItemModel() - for project_template_path in manifest.get_project_templates(): - model_item = QStandardItem(project_template_path) - self.create_project_template_model.appendRow(model_item) - self.create_project_template_list.setModel(self.create_project_template_model) - - def refresh_create_gem_template_list(self) -> None: - self.create_gem_template_model = QStandardItemModel() - for gem_template_path in manifest.get_gem_templates(): - model_item = QStandardItem(gem_template_path) - self.create_gem_template_model.appendRow(model_item) - self.create_gem_template_list.setModel(self.create_gem_template_model) - - def refresh_create_from_template_list(self) -> None: - self.create_from_template_model = QStandardItemModel() - for generic_template_path in manifest.get_generic_templates(): - model_item = QStandardItem(generic_template_path) - self.create_from_template_model.appendRow(model_item) - self.create_from_template_list.setModel(self.create_from_template_model) - - def update_mru_list(self, used_project: str) -> None: - """ - Promote a supplied project name to the "most recent" in a given MRU list. - :param used_project: path to project to promote - :param file_path: path to mru list file - :return: None - """ - used_project = os.path.normpath(used_project) - if not os.path.exists(os.path.dirname(self.mru_file_path)): - os.makedirs(os.path.dirname(self.mru_file_path), exist_ok=True) - mru_data = {} - try: - with open(self.mru_file_path, 'r') as mru_file: - mru_data = json.loads(mru_file.read()) - except FileNotFoundError: - pass - except json.JSONDecodeError: - pass - - recent_list = mru_data.get('Projects', []) - recent_list = [item for item in recent_list if item.get('Path') != used_project and - self.is_project_folder(item.get('Path'))] - - new_list = [{'Path': used_project}] - new_list.extend(recent_list) - - mru_data['Projects'] = new_list - try: - with open(self.mru_file_path, 'w') as mru_file: - mru_file.write(json.dumps(mru_data, indent=1)) - except PermissionError as e: - logger.warning(f"Failed to write {self.mru_file_path} with error {e}") - - def get_mru_list(self) -> List[str]: - """ - Retrieve the current MRU list. Does not perform validation that the projects still appear valid - :return: list of full path strings to project folders - """ - if not os.path.exists(os.path.dirname(self.mru_file_path)): - return [] - try: - with open(self.mru_file_path, 'r') as mru_file: - mru_data = json.loads(mru_file.read()) - except FileNotFoundError: - return [] - except json.JSONDecodeError: - logger.error(f'MRU list at {self.mru_file_path} is not valid JSON') - return [] - - recent_list = mru_data.get('Projects', []) - return [item.get('Path') for item in recent_list if item.get('Path') is not None] - - @Slot(str) - def handle_log_message(self, message: str) -> None: - """ - Signal handler for messages from the logger. Displays the most recent warning/error - :param message: formatted log message from DialogLoggerSignaller - :return: - """ - if not self.log_display: - return - self.log_display.setText(message) - self.log_display.setToolTip(message) - - -if __name__ == "__main__": - dialog_app = QApplication(sys.argv) - my_dialog = ProjectManagerDialog() - dialog_app.exec_() - sys.exit(0) diff --git a/scripts/project_manager/pyside.py b/scripts/project_manager/pyside.py deleted file mode 100755 index 80bf327f9b..0000000000 --- a/scripts/project_manager/pyside.py +++ /dev/null @@ -1,61 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -import os -import logging - -from pathlib import Path - -logger = logging.getLogger() - -pyside_initialized = False -old_env = os.environ.copy() - -# Helper to extend OS PATH for pyside to locate our QT binaries based on our build folder -def add_pyside_environment(bin_path): - if is_pyside_ready(): - # No need to reinitialize currently - logger.info("Pyside environment already initialized") - return - global old_env - old_env = os.environ.copy() - binaries_path = Path(os.path.normpath(bin_path)) - platforms_path = binaries_path.joinpath("platforms") - logger.info(f'Adding binaries path {binaries_path}') - os.environ["QT_QPA_PLATFORM_PLUGIN_PATH"] = str(platforms_path) - - path = os.environ['PATH'] - - new_path = os.pathsep.join([str(binaries_path), str(platforms_path), path]) - os.environ['PATH'] = new_path - - global pyside_initialized - pyside_initialized = True - - -def is_pyside_ready(): - return pyside_initialized - - -def is_configuration_valid(workspace): - return os.path.basename(workspace.paths.build_directory()) != "debug" - - -def uninstall_env(): - if not is_pyside_ready(): - logger.warning("Pyside not initialized") - return os.environ - - global old_env - if old_env.get("QT_QPA_PLATFORM_PLUGIN_PATH"): - old_env.pop("QT_QPA_PLATFORM_PLUGIN_PATH") - os.environ = old_env - return old_env diff --git a/scripts/project_manager/tests/__init__.py b/scripts/project_manager/tests/__init__.py deleted file mode 100755 index 4d5680a30d..0000000000 --- a/scripts/project_manager/tests/__init__.py +++ /dev/null @@ -1,10 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# diff --git a/scripts/project_manager/tests/test_projects.py b/scripts/project_manager/tests/test_projects.py deleted file mode 100755 index d073cf6c7a..0000000000 --- a/scripts/project_manager/tests/test_projects.py +++ /dev/null @@ -1,255 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - - -import pytest -''' -import os -import sys -import tempfile -import logging -import pathlib -from unittest.mock import MagicMock - -logger = logging.getLogger() - -# Code lives one folder above -project_manager_path = os.path.realpath(os.path.join(os.path.dirname(__file__), '..')) -sys.path.append(project_manager_path) - -from pyside import add_pyside_environment, is_configuration_valid -from ly_test_tools import WINDOWS - -sys.path.append(os.path.realpath(os.path.join(os.path.dirname(__file__), '..', '..', '..'))) -executable_path = '' -from cmake.Tools import registration -from cmake.Tools import engine_template - - -class ProjectHelper: - def __init__(self): - self._temp_directory = pathlib.Path(tempfile.TemporaryDirectory().name).resolve() - self._temp_directory.mkdir(parents=True, exist_ok=True) - - self.home_path = self._temp_directory - registration.override_home_folder = self.home_path - self.engine_path = registration.get_this_engine_path() - if registration.register(engine_path=self.engine_path): - assert True, f"Failed to register the engine." - - if registration.register_shipped_engine_o3de_objects(): - assert True, f"Failed to register shipped engine objects." - - self.projects_folder = registration.get_o3de_projects_folder() - if not self.projects_folder.is_dir(): - assert True - - self.application = None - self.dialog = None - - def create_empty_projects(self): - self.project_1_dir = self.projects_folder / "Project1" - if engine_template.create_project(project_manager_path=self.project_1_dir): - assert True, f"Failed to create Project1." - - self.project_2_dir = self.projects_folder / "Project2" - if engine_template.create_project(project_manager_path=self.project_2_dir): - assert True, f"Failed to create Project2." - - self.project_3_dir = self.projects_folder / "Project3" - if engine_template.create_project(project_manager_path=self.project_3_dir): - assert True, f"Failed to create Project3." - - self.invalid_project_dir = self.projects_folder / "InvalidProject" - self.invalid_project_dir.mkdir(parents=True, exist_ok=True) - - def setup_dialog_test(self, workspace): - add_pyside_environment(workspace.paths.build_directory()) - - if not is_configuration_valid(workspace): - # This is essentially skipif debug. Our debug tests use our profile version of python, but that means we'd - # need to use the profile version of PySide which works with the profile QT libs which aren't in the debug - # folder we've built. - return None - - from PySide2.QtWidgets import QApplication, QMessageBox - - if QApplication.instance(): - self.application = QApplication.instance() - else: - self.application = QApplication(sys.argv) - assert self.application - - from projects import ProjectManagerDialog - - try: - self.dialog = ProjectManagerDialog(settings_folder=self.home_path) - return self.dialog - except Exception as e: - logger.error(f'Failed to create ProjectManagerDialog with error {e}') - return None - - def create_project_from_template(self, project_name) -> bool: - """ - Uses the dialog to create a temporary project based on the first template found - :param project_name: Name of project to create. Will be created under temp_project_root - :return: True for Success, False for failure - """ - from PySide2.QtWidgets import QWidget, QFileDialog - from projects import ProjectManagerDialog - - QWidget.exec = MagicMock() - self.dialog.create_project_handler() - QWidget.exec.assert_called_once() - - assert len(self.dialog.project_templates), 'Failed to find any project templates' - ProjectManagerDialog.get_selected_project_template = MagicMock(return_value=self.dialog.project_templates[0]) - - QFileDialog.exec = MagicMock() - create_project_path = self.projects_folder / project_name - QFileDialog.selectedFiles = MagicMock(return_value=[create_project_path]) - self.dialog.create_project_accepted_handler() - if create_project_path.is_dir(): - assert True, f"Expected project creation folder not found at {create_project_path}" - - if QWidget.exec.call_count == 2: - assert True, "Message box confirming project creation failed to show" - - -@pytest.fixture -def project_helper(): - return ProjectHelper() - - -@pytest.mark.skipif(not WINDOWS, reason="PySide2 only works on windows currently") -@pytest.mark.parametrize('project', ['']) # Workspace wants a project, but this test is not project dependent -def test_logger_handler(workspace, project_helper): - my_dialog = project_helper.setup_dialog_test(workspace) - if not my_dialog: - return - - from PySide2.QtWidgets import QMessageBox - QMessageBox.warning = MagicMock() - logger.error(f'Testing logger') - QMessageBox.warning.assert_called_once() - - -@pytest.mark.skipif(not WINDOWS, reason="PySide2 only works on windows currently") -@pytest.mark.parametrize('project', ['']) # Workspace wants a project, but this test is not project dependent -def test_mru_list(workspace, project_helper): - my_dialog = project_helper.setup_dialog_test(workspace) - if not my_dialog: - return - - project_helper.create_empty_projects() - - from PySide2.QtWidgets import QMessageBox - - mru_list = my_dialog.get_mru_list() - assert len(mru_list) == 0, f'MRU list unexpectedly had entries: {mru_list}' - - QMessageBox.warning = MagicMock() - my_dialog.add_project(project_helper.invalid_project_dir) - mru_list = my_dialog.get_mru_list() - assert len(mru_list) == 0, f'MRU list unexpectedly added an invalid project : {mru_list}' - QMessageBox.warning.assert_called_once() - - my_dialog.add_project(project_helper.project_1_dir) - mru_list = my_dialog.get_mru_list() - assert len(mru_list) == 1, f'MRU list failed to add project at {project_helper.project_1_dir}' - - my_dialog.add_project(project_helper.project_1_dir) - mru_list = my_dialog.get_mru_list() - assert len(mru_list) == 1, f'MRU list added project at {project_helper.project_1_dir} a second time : {mru_list}' - - my_dialog.update_mru_list(project_helper.project_1_dir) - mru_list = my_dialog.get_mru_list() - assert len(mru_list) == 1, f'MRU list added project at {project_helper.project_1_dir} a second time : {mru_list}' - - my_dialog.add_project(project_helper.project_2_dir) - mru_list = my_dialog.get_mru_list() - assert len(mru_list) == 2, f'MRU list failed to add project at {project_helper.project_2_dir}' - - assert mru_list[0] == project_helper.project_2_dir, f"{project_helper.project_2_dir} wasn't first item" - assert mru_list[1] == project_helper.project_1_dir, f"{project_helper.project_1_dir} wasn't second item" - - my_dialog.update_mru_list(project_helper.project_1_dir) - mru_list = my_dialog.get_mru_list() - assert len(mru_list) == 2, f'MRU list added wrong items {mru_list}' - assert mru_list[0] == project_helper.project_1_dir, f"{project_helper.project_1_dir} wasn't first item" - assert mru_list[1] == project_helper.project_2_dir, f"{project_helper.project_2_dir} wasn't second item" - - my_dialog.add_project(project_helper.invalid_project_dir) - mru_list = my_dialog.get_mru_list() - assert len(mru_list) == 2, f'MRU list added invalid item {mru_list}' - assert mru_list[0] == project_helper.project_1_dir, f"{project_helper.project_1_dir} wasn't first item" - assert mru_list[1] == project_helper.project_2_dir, f"{project_helper.project_2_dir} wasn't second item" - - my_dialog.add_project(project_helper.project_3_dir) - mru_list = my_dialog.get_mru_list() - assert len(mru_list) == 3, f'MRU list failed to add {project_helper.project_3_dir} : {mru_list}' - assert mru_list[0] == project_helper.project_3_dir, f"{project_helper.project_3_dir} wasn't first item" - assert mru_list[1] == project_helper.project_1_dir, f"{project_helper.project_1_dir} wasn't second item" - assert mru_list[2] == project_helper.project_2_dir, f"{project_helper.project_2_dir} wasn't third item" - - -@pytest.mark.skipif(not WINDOWS, reason="PySide2 only works on windows currently") -@pytest.mark.parametrize('project', ['']) # Workspace wants a project, but this test is not project dependent -def test_create_project(workspace, project_helper): - my_dialog = project_helper.setup_dialog_test(workspace) - if not my_dialog: - return - - project_helper.create_project_from_template("TestCreateProject") - - -@pytest.mark.skipif(not WINDOWS, reason="PySide2 only works on windows currently") -@pytest.mark.parametrize('project', ['']) # Workspace wants a project, but this test is not project dependent -def test_add_remove_gems(workspace, project_helper): - my_dialog = project_helper.setup_dialog_test(workspace) - if not my_dialog: - return - - my_project_name = "TestAddRemoveGems" - - project_helper.create_project_from_template(project_manager_path=my_project_name) - my_project_path = project_helper.projects_folder / my_project_name - - from PySide2.QtWidgets import QWidget, QFileDialog - from projects import ProjectManagerDialog - - assert my_dialog.get_selected_project_path() == my_project_path, "TestAddRemoveGems project not selected" - QWidget.exec = MagicMock() - my_dialog.manage_gems_handler() - assert my_dialog.manage_gem_targets_dialog, "No gem management dialog created" - QWidget.exec.assert_called_once() - - if not len(my_dialog.all_gems_list): - assert True, 'Failed to find any gems' - - my_test_gem_path = my_dialog.all_gems_list[0] - gem_data = registration.get_gem_data(my_test_gem_path) - my_test_gem_selection = (my_test_gem_name, my_test_gem_path) - ProjectManagerDialog.get_selected_add_gems = MagicMock(return_value=[my_test_gem_selection]) - - assert my_test_gem_name, "No Name set in test gem" - assert my_test_gem_name not in my_dialog.project_gem_list, f'Gem {my_test_gem_name} already in project gem list' - - my_dialog.add_gems_handler() - assert my_test_gem_name in my_dialog.project_gem_list, f'Gem {my_test_gem_name} failed to add to gem list' - - ProjectManagerDialog.get_selected_project_gems = MagicMock(return_value=[my_test_gem_name]) - my_dialog.remove_gems_handler() - assert my_test_gem_name not in my_dialog.project_gem_list, f'Gem {my_test_gem_name} still in project gem list' -''' - -def test_project_place_holder(): - pass \ No newline at end of file diff --git a/scripts/project_manager/tests/test_pyside.py b/scripts/project_manager/tests/test_pyside.py deleted file mode 100755 index c9be313f2b..0000000000 --- a/scripts/project_manager/tests/test_pyside.py +++ /dev/null @@ -1,54 +0,0 @@ -# -# All or portions of this file Copyright (c) Amazon.com, Inc. or its affiliates or -# its licensors. -# -# For complete copyright and license terms please see the LICENSE at the root of this -# distribution (the "License"). All use of this software is governed by the License, -# or, if provided, by the license below or the license accompanying this file. Do not -# remove or modify any license notices. This file is distributed on an "AS IS" BASIS, -# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. -# - -import pytest -import sys -import os - -pyside_path = os.path.realpath(os.path.join(os.path.dirname(__file__), '..')) -sys.path.append(pyside_path) - -from pyside import add_pyside_environment, is_pyside_ready, is_configuration_valid - -from ly_test_tools import WINDOWS - -import logging -logger = logging.getLogger() - -@pytest.mark.skipif(not WINDOWS, reason="PySide2 only works on windows currently") -@pytest.mark.parametrize('project', ['']) # Workspace wants a project, but this test is not project dependent -def test_add_pyside_environment(workspace): - import_failed = False - try: - import PySide2 - except ImportError: - import_failed = True - - if not import_failed: - cur_path = sys.path - logger.warning(f"Expected to fail initial import but passed. Sys path was {cur_path}") - - assert is_pyside_ready() is False, "Expected pyside not to be initialized yet" - add_pyside_environment(workspace.paths.build_directory()) - assert is_pyside_ready() is True, "Expected pyside to be initialized yet" - - try: - import PySide2 - if not is_configuration_valid(workspace): - return - from PySide2.QtWidgets import QApplication - except ImportError as e: - assert False, f"Failed to import PySide2 with error {e}" - try: - from PySide2.QtWidgets import QApplication - except ImportError as e: - assert False, f"Failed to import QApplication from PySide2.QtWidgets with error {e}" - diff --git a/scripts/project_manager/ui/create_from_template.ui b/scripts/project_manager/ui/create_from_template.ui deleted file mode 100644 index b6a8740594..0000000000 --- a/scripts/project_manager/ui/create_from_template.ui +++ /dev/null @@ -1,94 +0,0 @@ - - - createFromTemplateDialog - - - - 0 - 0 - 467 - 288 - - - - Create From Template - - - - - 50 - 250 - 400 - 32 - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - 10 - 20 - 449 - 221 - - - - QAbstractItemView::SingleSelection - - - - - - 10 - 0 - 300 - 16 - - - - Available Templates - - - - - - - okCancel - accepted() - createFromTemplateDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - okCancel - rejected() - createFromTemplateDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/scripts/project_manager/ui/create_gem.ui b/scripts/project_manager/ui/create_gem.ui deleted file mode 100644 index 5a5fd14a6d..0000000000 --- a/scripts/project_manager/ui/create_gem.ui +++ /dev/null @@ -1,94 +0,0 @@ - - - createGemDialog - - - - 0 - 0 - 467 - 288 - - - - Create Gem - - - - - 50 - 250 - 400 - 32 - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - 10 - 20 - 449 - 221 - - - - QAbstractItemView::SingleSelection - - - - - - 10 - 0 - 300 - 16 - - - - Available Templates - - - - - - - okCancel - accepted() - createGemDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - okCancel - rejected() - createGemDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/scripts/project_manager/ui/create_project.ui b/scripts/project_manager/ui/create_project.ui deleted file mode 100644 index 67a13325b0..0000000000 --- a/scripts/project_manager/ui/create_project.ui +++ /dev/null @@ -1,94 +0,0 @@ - - - createProjectDialog - - - - 0 - 0 - 467 - 288 - - - - Create Project - - - - - 50 - 250 - 400 - 32 - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - 10 - 20 - 449 - 221 - - - - QAbstractItemView::SingleSelection - - - - - - 10 - 0 - 300 - 16 - - - - Available Templates - - - - - - - okCancel - accepted() - createProjectDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - okCancel - rejected() - createProjectDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/scripts/project_manager/ui/manage_gem_targets.ui b/scripts/project_manager/ui/manage_gem_targets.ui deleted file mode 100644 index ea9e607d42..0000000000 --- a/scripts/project_manager/ui/manage_gem_targets.ui +++ /dev/null @@ -1,165 +0,0 @@ - - - manageGemTargetsDialog - - - - 0 - 0 - 702 - 297 - - - - Manage Gem Targets - - - - - 310 - 260 - 71 - 32 - - - - Qt::Horizontal - - - QDialogButtonBox::Close - - - - - - 440 - 50 - 250 - 221 - - - - - 0 - 0 - - - - QAbstractItemView::ExtendedSelection - - - - - - 10 - 50 - 250 - 221 - - - - QAbstractItemView::ExtendedSelection - - - - - - 440 - 30 - 251 - 16 - - - - Available Gem Targets - - - - - - 10 - 30 - 251 - 16 - - - - Enabled Gem Targets - - - - - - 264 - 130 - 171 - 23 - - - - Remove Gem Targets >> - - - - - - 264 - 100 - 171 - 23 - - - - << Add Gem Targets - - - - - - 10 - 5 - 400 - 21 - - - - Adding new Gem Targets may require rebuilding - - - - - - - close - accepted() - manageGemTargetsDialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - close - rejected() - manageGemTargetsDialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - - diff --git a/scripts/project_manager/ui/project_manager.ico b/scripts/project_manager/ui/project_manager.ico deleted file mode 100644 index 597266946a..0000000000 --- a/scripts/project_manager/ui/project_manager.ico +++ /dev/null @@ -1,3 +0,0 @@ -version https://git-lfs.github.com/spec/v1 -oid sha256:113be7ded1969e0d535722382b6d3730eed052a10430cf23804ae7f20414b999 -size 108278 diff --git a/scripts/project_manager/ui/project_manager.ui b/scripts/project_manager/ui/project_manager.ui deleted file mode 100644 index 6b3b5f758c..0000000000 --- a/scripts/project_manager/ui/project_manager.ui +++ /dev/null @@ -1,407 +0,0 @@ - - - Dialog - - - - 0 - 0 - 712 - 395 - - - - - 1 - 0 - - - - O3DE - - - Select and manage your projects for O3DE - - - - - 540 - 360 - 161 - 31 - - - - Qt::Horizontal - - - QDialogButtonBox::Cancel|QDialogButtonBox::Ok - - - - - - 10 - 30 - 691 - 31 - - - - Current project to launch or manage gems for. - - - - - - 10 - 10 - 47 - 13 - - - - Project - - - - - - 270 - 220 - 431 - 141 - - - - QFrame::Panel - - - QFrame::Sunken - - - - - - true - - - Qt::LinksAccessibleByMouse|Qt::TextSelectableByMouse - - - - - - 10 - 70 - 241 - 151 - - - - Create - - - - - 10 - 110 - 221 - 31 - - - - Create a new O3DE object from a pre configured template. - - - Create From Template - - - - - - 10 - 20 - 221 - 31 - - - - Create a new O3DE object from a pre configured template. - - - Create Project - - - - - - 11 - 50 - 221 - 31 - - - - Create a new O3DE object from a pre configured template. - - - Create Gem - - - - - - 11 - 80 - 221 - 31 - - - - Create a new O3DE object from a pre configured template. - - - Create Template - - - - - - - 260 - 70 - 451 - 141 - - - - Registration - - - - - 10 - 80 - 211 - 31 - - - - Browse for an existing O3DE project. - - - Add Template - - - - - - 10 - 20 - 211 - 31 - - - - Browse for an existing O3DE project. - - - Add Project - - - - - - 10 - 50 - 211 - 31 - - - - Browse for an existing O3DE project. - - - Add Gem - - - - - - 10 - 110 - 211 - 31 - - - - Browse for an existing O3DE project. - - - Add Restricted - - - - - - 230 - 110 - 211 - 31 - - - - Browse for an existing O3DE project. - - - Remove Restricted - - - - - - 230 - 20 - 211 - 31 - - - - Browse for an existing O3DE project. - - - Remove Project - - - - - - 230 - 50 - 211 - 31 - - - - Browse for an existing O3DE project. - - - Remove Gem - - - - - - 230 - 80 - 211 - 31 - - - - Browse for an existing O3DE project. - - - Remove Template - - - - - - - 10 - 230 - 241 - 121 - - - - Manage Project - - - - - 10 - 80 - 221 - 31 - - - - Add or remove gems from your selected project. Gems add and remove additional assets and features to projects. - - - Manage Server Gem Targets - - - - - - 10 - 50 - 221 - 31 - - - - Add or remove gems from your selected project. Gems add and remove additional assets and features to projects. - - - Manage Tool Gem Targets - - - - - - 10 - 20 - 221 - 31 - - - - Add or remove gems from your selected project. Gems add and remove additional assets and features to projects. - - - Manage Runtime Gem Targets - - - - - - - - okCancel - accepted() - Dialog - accept() - - - 248 - 254 - - - 157 - 274 - - - - - okCancel - rejected() - Dialog - reject() - - - 316 - 260 - - - 286 - 274 - - - - -