From 7ce7e6e763ba99f4098852f0a23ee28dc8c1171d Mon Sep 17 00:00:00 2001 From: peelz Date: Wed, 3 Aug 2022 21:34:41 -0400 Subject: [PATCH 01/35] Support resolving methods with ref/out parameters --- .../LuaCs/Lua/LuaClasses/LuaUserData.cs | 15 +++++++++++---- 1 file changed, 11 insertions(+), 4 deletions(-) diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaClasses/LuaUserData.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaClasses/LuaUserData.cs index 6736d9723..94b74963e 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaClasses/LuaUserData.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaClasses/LuaUserData.cs @@ -11,20 +11,27 @@ namespace Barotrauma { public static Type GetType(string typeName) { + var byRef = false; + if (typeName.StartsWith("out ") || typeName.StartsWith("ref ")) + { + typeName = typeName.Remove(0, 4); + byRef = true; + } + var type = Type.GetType(typeName); - if (type != null) return type; + if (type != null) return byRef ? type.MakeByRefType() : type; foreach (var a in AppDomain.CurrentDomain.GetAssemblies()) { if (CsScriptBase.LoadedAssemblyName.Contains(a.GetName().Name)) - { + { var attrs = a.GetCustomAttributes(); var revision = attrs.FirstOrDefault(attr => attr.Key == "Revision")?.Value; if (revision != null && int.Parse(revision) != (int)CsScriptBase.Revision[a.GetName().Name]) { continue; } - } + } type = a.GetType(typeName); if (type != null) { - return type; + return byRef ? type.MakeByRefType() : type; } } return null; From a5bffce33b06c281ef04970d01e231c34f4fe87e Mon Sep 17 00:00:00 2001 From: peelz Date: Wed, 3 Aug 2022 21:34:41 -0400 Subject: [PATCH 02/35] Move prohibitedHooks check to a method --- .../SharedSource/LuaCs/LuaCsHook.cs | 13 +++++++++---- .../SharedSource/LuaCs/LuaCsHookCompat.cs | 6 +----- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsHook.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsHook.cs index 0516f6344..738425070 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsHook.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsHook.cs @@ -57,6 +57,14 @@ namespace Barotrauma "ContentPackageManager", }; + private static void ValidatePatchTarget(MethodInfo methodInfo) + { + if (prohibitedHooks.Any(h => methodInfo.DeclaringType.FullName.StartsWith(h))) + { + throw new ArgumentException("Hooks into the modding environment are prohibited."); + } + } + private Harmony harmony; private Dictionary> hookFunctions; @@ -300,10 +308,7 @@ namespace Barotrauma { throw new ArgumentNullException("Identifier, Method and Patch arguments must not be null."); } - if (prohibitedHooks.Any(h => method.DeclaringType.FullName.StartsWith(h))) - { - throw new ArgumentException("Hooks into Modding Environment are prohibited."); - } + ValidatePatchTarget(method); var funcAddr = (long)method.MethodHandle.GetFunctionPointer(); var patches = Harmony.GetPatchInfo(method); diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsHookCompat.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsHookCompat.cs index dee8089e3..3aa07f90a 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsHookCompat.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsHookCompat.cs @@ -157,11 +157,7 @@ namespace Barotrauma GameMain.LuaCs.HandleException(new ArgumentNullException("Identifier, Method and Patch arguments must not be null."), exceptionType: ExceptionType.Both); return; } - if (prohibitedHooks.Any(h => method.DeclaringType.FullName.StartsWith(h))) - { - GameMain.LuaCs.HandleException(new ArgumentException("Hooks into Modding Environment are prohibited."), exceptionType: ExceptionType.Both); - return; - } + ValidatePatchTarget(method); var funcAddr = ((long)method.MethodHandle.GetFunctionPointer()); var patches = Harmony.GetPatchInfo(method); From 768abd5ce18f7101a1a8668868707dd0a4d41cc2 Mon Sep 17 00:00:00 2001 From: peelz Date: Sun, 7 Aug 2022 12:58:15 -0400 Subject: [PATCH 03/35] Remove LuaResult --- .../LuaCs/Lua/LuaClasses/LuaGame.cs | 8 +- .../LuaCs/Lua/LuaClasses/LuaResult.cs | 105 ------------------ .../LuaCs/Lua/LuaCustomConverters.cs | 48 ++++---- .../SharedSource/LuaCs/LuaCsHook.cs | 78 +++++-------- .../SharedSource/LuaCs/LuaCsHookCompat.cs | 8 +- .../SharedSource/LuaCs/LuaCsSetup.cs | 2 +- 6 files changed, 62 insertions(+), 187 deletions(-) delete mode 100644 Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaClasses/LuaResult.cs diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaClasses/LuaGame.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaClasses/LuaGame.cs index cfe0d0418..381e6a081 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaClasses/LuaGame.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaClasses/LuaGame.cs @@ -367,13 +367,11 @@ namespace Barotrauma public void AddCommand(string name, string help, LuaCsAction onExecute, LuaCsFunc getValidArgs = null, bool isCheat = false) { - var cmd = new DebugConsole.Command(name, help, (string[] arg1) => { onExecute(new object[] { arg1 }); }, + var cmd = new DebugConsole.Command(name, help, (string[] arg1) => onExecute(new object[] { arg1 }), () => { - if (getValidArgs == null) { return null; } - var obj = getValidArgs(); - if (obj is LuaResult lr) { return lr.DynValue().ToObject(); } - return (string[][])obj; + if (getValidArgs == null) return null; + return getValidArgs().ToObject(); }, isCheat); luaAddedCommand.Add(cmd); diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaClasses/LuaResult.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaClasses/LuaResult.cs deleted file mode 100644 index c71cb9e50..000000000 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaClasses/LuaResult.cs +++ /dev/null @@ -1,105 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Text; -using MoonSharp.Interpreter; -using Microsoft.Xna.Framework; -using Barotrauma.Networking; -using Barotrauma.Items.Components; -using System.IO; -using System.Net; -using System.Linq; -using System.Xml.Linq; -using FarseerPhysics.Dynamics; -using System.Reflection; -using HarmonyLib; -using MoonSharp.Interpreter.Interop; -using System.Diagnostics; - -namespace Barotrauma -{ - public class LuaResult - { - object result; - public LuaResult(object arg) - { - result = arg; - } - - public bool IsNull() - { - if (result == null) - return true; - - if (result is DynValue dynValue) - return dynValue.IsNil(); - - return false; - } - - public bool Bool() - { - if (result is DynValue dynValue) - { - return dynValue.CastToBool(); - } - - return false; - } - - public float Float() - { - if (result is DynValue dynValue) - { - var num = dynValue.CastToNumber(); - if (num == null) { return 0f; } - return (float)num.Value; - } - - return 0f; - } - - public double Double() - { - if (result is DynValue dynValue) - { - var num = dynValue.CastToNumber(); - if (num == null) { return 0f; } - return num.Value; - } - - return 0f; - } - - public string String() - { - if (result is DynValue dynValue) - { - var str = dynValue.CastToString(); - if (str == null) { return ""; } - return str; - } - - return ""; - } - - public object Object() - { - if (result is DynValue dynValue) - { - return dynValue.ToObject(); - } - - return null; - } - - public DynValue DynValue() - { - if (result is DynValue dynValue) - { - return dynValue; - } - - return null; - } - } -} \ No newline at end of file diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaCustomConverters.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaCustomConverters.cs index ecae3a6cb..581becdc0 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaCustomConverters.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaCustomConverters.cs @@ -21,42 +21,50 @@ namespace Barotrauma RegisterFunc(); RegisterFunc(); - Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion(DataType.Function, typeof(LuaCsAction), v => (LuaCsAction)( args => GameMain.LuaCs.CallLuaFunction(v.Function, args) )); - Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion(DataType.Function, typeof(LuaCsFunc), v => (LuaCsFunc)( args => new LuaResult(GameMain.LuaCs.CallLuaFunction(v.Function, args)) )); - Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion(DataType.Function, typeof(LuaCsPatch), v => (LuaCsPatch)( (self, args) => new LuaResult(GameMain.LuaCs.CallLuaFunction(v.Function, self, args)) )); - Script.GlobalOptions.CustomConverters.SetClrToScriptCustomConversion(typeof(LuaResult), (Script s, object v) => (v as LuaResult).DynValue()); + Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion( + DataType.Function, + typeof(LuaCsAction), + v => (LuaCsAction)(args => GameMain.LuaCs.CallLuaFunction(v.Function, args))); + Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion( + DataType.Function, + typeof(LuaCsFunc), + v => (LuaCsFunc)(args => GameMain.LuaCs.CallLuaFunction(v.Function, args))); + Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion( + DataType.Function, + typeof(LuaCsPatch), + v => (LuaCsPatch)((self, args) => GameMain.LuaCs.CallLuaFunction(v.Function, self, args))); #if CLIENT RegisterAction(); RegisterAction(); { - object Call(object function, params object[] arguments) => GameMain.LuaCs.CallLuaFunction(function, arguments); + DynValue Call(object function, params object[] arguments) => GameMain.LuaCs.CallLuaFunction(function, arguments); void RegisterHandler(Func converter) => Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion(DataType.Function, typeof(T), v => converter(v.Function)); RegisterHandler(f => (GUIComponent.SecondaryButtonDownHandler)( - (a1, a2) => new LuaResult(Call(f, a1, a2)).Bool())); + (a1, a2) => Call(f, a1, a2).CastToBool())); RegisterHandler(f => (GUIButton.OnClickedHandler)( - (a1, a2) => new LuaResult(Call(f, a1, a2)).Bool())); + (a1, a2) => Call(f, a1, a2).CastToBool())); RegisterHandler(f => (GUIButton.OnButtonDownHandler)( - () => new LuaResult(Call(f)).Bool())); + () => Call(f).CastToBool())); RegisterHandler(f => (GUIButton.OnPressedHandler)( - () => new LuaResult(Call(f)).Bool())); + () => Call(f).CastToBool())); RegisterHandler(f => (GUIColorPicker.OnColorSelectedHandler)( - (a1, a2) => new LuaResult(Call(f, a1, a2)).Bool())); + (a1, a2) => Call(f, a1, a2).CastToBool())); RegisterHandler(f => (GUIDropDown.OnSelectedHandler)( - (a1, a2) => new LuaResult(Call(f, a1, a2)).Bool())); + (a1, a2) => Call(f, a1, a2).CastToBool())); RegisterHandler(f => (GUIListBox.OnSelectedHandler)( - (a1, a2) => new LuaResult(Call(f, a1, a2)).Bool())); + (a1, a2) => Call(f, a1, a2).CastToBool())); RegisterHandler(f => (GUIListBox.OnRearrangedHandler)( (a1, a2) => Call(f, a1, a2))); RegisterHandler(f => (GUIListBox.CheckSelectedHandler)( - () => new LuaResult(Call(f)).Object())); + () => Call(f).ToObject())); RegisterHandler(f => (GUINumberInput.OnValueEnteredHandler)( (a1) => Call(f, a1))); @@ -64,28 +72,28 @@ namespace Barotrauma (a1) => Call(f, a1))); RegisterHandler(f => (GUIProgressBar.ProgressGetterHandler)( - () => new LuaResult(Call(f)).Float())); + () => (float)(Call(f).CastToNumber() ?? 0))); RegisterHandler(f => (GUIRadioButtonGroup.RadioButtonGroupDelegate)( (a1, a2) => Call(f, a1, a2))); RegisterHandler(f => (GUIScrollBar.OnMovedHandler)( - (a1, a2) => new LuaResult(Call(f, a1, a2)).Bool())); + (a1, a2) => Call(f, a1, a2).CastToBool())); RegisterHandler(f => (GUIScrollBar.ScrollConversion)( - (a1, a2) => new LuaResult(Call(f, a1, a2)).Float())); + (a1, a2) => (float)(Call(f, a1, a2).CastToNumber() ?? 0))); RegisterHandler(f => (GUITextBlock.TextGetterHandler)( - () => new LuaResult(Call(f, new object[] { })).String())); + () => Call(f, new object[0]).CastToString())); RegisterHandler(f => (GUITextBox.OnEnterHandler)( - (a1, a2) => new LuaResult(Call(f, a1, a2)).Bool())); + (a1, a2) => Call(f, a1, a2).CastToBool())); RegisterHandler(f => (GUITextBox.OnTextChangedHandler)( - (a1, a2) => new LuaResult(Call(f, a1, a2)).Bool())); + (a1, a2) => Call(f, a1, a2).CastToBool())); RegisterHandler(f => (TextBoxEvent)( (a1, a2) => Call(f, a1, a2))); RegisterHandler(f => (GUITickBox.OnSelectedHandler)( - (a1) => new LuaResult(Call(f, a1)).Bool())); + (a1) => Call(f, a1).CastToBool())); } #endif diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsHook.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsHook.cs index 738425070..4558c8afa 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsHook.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsHook.cs @@ -13,7 +13,7 @@ using System.Diagnostics; namespace Barotrauma { public delegate void LuaCsAction(params object[] args); - public delegate object LuaCsFunc(params object[] args); + public delegate DynValue LuaCsFunc(params object[] args); public delegate object LuaCsPatch(object self, Dictionary args); public partial class LuaCsHook @@ -157,18 +157,18 @@ namespace Barotrauma [MoonSharpHidden] public T Call(string name, params object[] args) { - if (GameMain.LuaCs == null) { return default(T); } - if (name == null) { throw new ScriptRuntimeException("Hook.Call: name must not be null."); } - if (args == null) { args = new object[] { }; } + if (GameMain.LuaCs == null) return default; // FIXME: should this throw an exception? + if (name == null) throw new ArgumentNullException(name); + if (args == null) args = new object[0]; name = name.ToLower(); if (!hookFunctions.ContainsKey(name)) { - return default(T); + return default; } - T lastResult = default(T); + T lastResult = default; if (!hookFunctions.ContainsKey(name)) { @@ -192,30 +192,9 @@ namespace Barotrauma } var result = tuple.Item1.func(args); - if (result != null) + if (result != null && !result.IsNil()) { - if (typeof(object) != typeof(T)) - { - if (result is LuaResult lRes) - { - if (!lRes.IsNull()) { lastResult = lRes.DynValue().ToObject(); } - } - else if (result is T cRes && cRes != null) - { - lastResult = cRes; - } - } - else - { - if (result is LuaResult lRes) - { - if (!lRes.IsNull()) { lastResult = (T)(object)lRes.DynValue(); } - } - else - { - lastResult = (T)result; - } - } + lastResult = result.ToObject(); } if (GameMain.LuaCs.PerformanceCounter.EnablePerformanceCounter) @@ -239,10 +218,14 @@ namespace Barotrauma return lastResult; } - public object Call(string name, params object[] args) => Call(name, args); + public object Call(string name, params object[] args) + { + if (name == null) throw new ScriptRuntimeException("Hook.Call: name must not be null."); + return Call(name, args); + } - private static bool PatchPrefix(MethodBase __originalMethod, object[] __args, object __instance) + private static bool PatchPrefix(MethodBase __originalMethod, object[] __args, object __instance) { ExecutePatch(__originalMethod, __args, __instance, out object result, HookMethodType.Before); return result == null; @@ -415,31 +398,22 @@ namespace Barotrauma continue; } - object[] args = new object[] { __instance }.Concat(__args).ToArray(); - object _result = tuple.Item2(args); + var args = Enumerable.Empty() + .Concat(__args) + .Prepend(__instance) + .ToArray(); + var _result = tuple.Item2(args); - if (_result == null) + if (_result != null && !_result.IsNil()) { - continue; - } - - if (_result is LuaResult res) - { - if (!res.IsNull()) + if (__originalMethod is MethodInfo mi && mi.ReturnType != typeof(void)) { - if (__originalMethod is MethodInfo mi && mi.ReturnType != typeof(void)) - { - result = res.DynValue().ToObject(mi.ReturnType); - } - else - { - result = res.DynValue().ToObject(); - } + result = _result.ToObject(mi.ReturnType); + } + else + { + result = _result.ToObject(); } - } - else - { - result = _result; } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsHookCompat.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsHookCompat.cs index 3aa07f90a..c009a8d24 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsHookCompat.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsHookCompat.cs @@ -58,17 +58,17 @@ namespace Barotrauma var _result = tuple.Item2(__instance, args); if (_result != null) { - if (_result is LuaResult res) + if (_result is DynValue res) { - if (!res.IsNull()) + if (!res.IsNil()) { if (__originalMethod is MethodInfo mi && mi.ReturnType != typeof(void)) { - result = res.DynValue().ToObject(mi.ReturnType); + result = res.ToObject(mi.ReturnType); } else { - result = res.DynValue().ToObject(); + result = res.ToObject(); } } } diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsSetup.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsSetup.cs index 42b9865e3..9df6d4931 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsSetup.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsSetup.cs @@ -284,7 +284,7 @@ namespace Barotrauma return lua.LoadFile(file, globalContext, codeStringFriendly); } - public object CallLuaFunction(object function, params object[] arguments) + public DynValue CallLuaFunction(object function, params object[] arguments) { lock (lua) { From 08836088fb4df84a67115854adeddb57ef41213a Mon Sep 17 00:00:00 2001 From: peelz Date: Wed, 3 Aug 2022 21:34:41 -0400 Subject: [PATCH 04/35] Refactor hooking API This completely changes how method patching works under the hood. Unlike the previous API (`Hook.HookMethod`), the new API (`Hook.Patch`) generates a Harmony patch method at runtime, using IL generation. This fixes methods with ByRef (out/ref) parameters getting silently corrupted due to the ByRef semantics being lost when passed through the `object[] __args` parameter. This new API also makes it possible to: - modify parameters (including ByRef params) - change the return value to `null` (old API would use `return nil` for Harmony control flow) - prevent execution of the original method (and other harmony patches), independently of modifying the return value --- .../BarotraumaClient/LinuxClient.csproj | 3 +- Barotrauma/BarotraumaClient/MacClient.csproj | 3 +- .../BarotraumaClient/WindowsClient.csproj | 1 + .../BarotraumaServer/LinuxServer.csproj | 1 + Barotrauma/BarotraumaServer/MacServer.csproj | 1 + .../BarotraumaServer/WindowsServer.csproj | 1 + .../LuaCs/Lua/LuaCustomConverters.cs | 10 +- .../SharedSource/LuaCs/LuaCsHook.cs | 1587 ++++++++++++----- .../SharedSource/LuaCs/LuaCsHookCompat.cs | 94 +- .../SharedSource/LuaCs/LuaCsSetup.cs | 4 +- 10 files changed, 1223 insertions(+), 482 deletions(-) diff --git a/Barotrauma/BarotraumaClient/LinuxClient.csproj b/Barotrauma/BarotraumaClient/LinuxClient.csproj index d51ae3431..01106d746 100644 --- a/Barotrauma/BarotraumaClient/LinuxClient.csproj +++ b/Barotrauma/BarotraumaClient/LinuxClient.csproj @@ -142,6 +142,7 @@ + @@ -222,4 +223,4 @@ - \ No newline at end of file + diff --git a/Barotrauma/BarotraumaClient/MacClient.csproj b/Barotrauma/BarotraumaClient/MacClient.csproj index fb9a8c51f..97be07204 100644 --- a/Barotrauma/BarotraumaClient/MacClient.csproj +++ b/Barotrauma/BarotraumaClient/MacClient.csproj @@ -134,6 +134,7 @@ + @@ -224,4 +225,4 @@ - \ No newline at end of file + diff --git a/Barotrauma/BarotraumaClient/WindowsClient.csproj b/Barotrauma/BarotraumaClient/WindowsClient.csproj index 1c96d533e..2114560b0 100644 --- a/Barotrauma/BarotraumaClient/WindowsClient.csproj +++ b/Barotrauma/BarotraumaClient/WindowsClient.csproj @@ -141,6 +141,7 @@ + diff --git a/Barotrauma/BarotraumaServer/LinuxServer.csproj b/Barotrauma/BarotraumaServer/LinuxServer.csproj index 0828ce53f..9d7662827 100644 --- a/Barotrauma/BarotraumaServer/LinuxServer.csproj +++ b/Barotrauma/BarotraumaServer/LinuxServer.csproj @@ -89,6 +89,7 @@ + diff --git a/Barotrauma/BarotraumaServer/MacServer.csproj b/Barotrauma/BarotraumaServer/MacServer.csproj index 731599318..518422501 100644 --- a/Barotrauma/BarotraumaServer/MacServer.csproj +++ b/Barotrauma/BarotraumaServer/MacServer.csproj @@ -86,6 +86,7 @@ + diff --git a/Barotrauma/BarotraumaServer/WindowsServer.csproj b/Barotrauma/BarotraumaServer/WindowsServer.csproj index d1e747e39..b4e15a59c 100644 --- a/Barotrauma/BarotraumaServer/WindowsServer.csproj +++ b/Barotrauma/BarotraumaServer/WindowsServer.csproj @@ -88,6 +88,7 @@ + diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaCustomConverters.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaCustomConverters.cs index 581becdc0..a0a4fd308 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaCustomConverters.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaCustomConverters.cs @@ -4,10 +4,10 @@ using System.Text; using MoonSharp.Interpreter; using Microsoft.Xna.Framework; using FarseerPhysics.Dynamics; +using LuaCsCompatPatchFunc = Barotrauma.LuaCsPatch; namespace Barotrauma { - public static class LuaCustomConverters { public static void RegisterAll() @@ -31,8 +31,12 @@ namespace Barotrauma v => (LuaCsFunc)(args => GameMain.LuaCs.CallLuaFunction(v.Function, args))); Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion( DataType.Function, - typeof(LuaCsPatch), - v => (LuaCsPatch)((self, args) => GameMain.LuaCs.CallLuaFunction(v.Function, self, args))); + typeof(LuaCsCompatPatchFunc), + v => (LuaCsCompatPatchFunc)((self, args) => GameMain.LuaCs.CallLuaFunction(v.Function, self, args))); + Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion( + DataType.Function, + typeof(LuaCsPatchFunc), + v => (LuaCsPatchFunc)((self, args) => GameMain.LuaCs.CallLuaFunction(v.Function, self, args))); #if CLIENT RegisterAction(); diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsHook.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsHook.cs index 4558c8afa..1d6f53405 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsHook.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsHook.cs @@ -1,61 +1,524 @@ using System; -using System.Linq; -using System.Reflection; -using MoonSharp.Interpreter; -using HarmonyLib; +using System.Collections; using System.Collections.Generic; -using System.Text; -using MoonSharp.Interpreter.Interop; -using static Barotrauma.LuaCsSetup; -using System.Threading; using System.Diagnostics; +using System.Linq; +using System.Linq.Expressions; +using System.Reflection; +using System.Reflection.Emit; +using System.Text; +using System.Text.RegularExpressions; +using HarmonyLib; +using Microsoft.Xna.Framework; +using MoonSharp.Interpreter; +using MoonSharp.Interpreter.Interop; +using Sigil; +using Sigil.NonGeneric; +using static Barotrauma.LuaCsSetup; namespace Barotrauma { - public delegate void LuaCsAction(params object[] args); - public delegate DynValue LuaCsFunc(params object[] args); - public delegate object LuaCsPatch(object self, Dictionary args); + public delegate void LuaCsAction(params object[] args); + public delegate DynValue LuaCsFunc(params object[] args); + public delegate DynValue LuaCsPatchFunc(object instance, LuaCsHook.ParameterTable ptable); - public partial class LuaCsHook - { - public enum HookMethodType - { - Before, After - } + internal static class SigilExtensions + { + /// + /// Puts a type on the stack, as a object instead of a + /// runtime type token. + /// + /// The IL emitter. + /// The type to put on the stack. + public static void LoadType(this Emit il, Type type) + { + if (type == null) throw new ArgumentNullException(nameof(type)); + il.LoadConstant(type); // ldtoken + // This converts the type token into a Type object + il.Call(typeof(Type).GetMethod( + name: nameof(Type.GetTypeFromHandle), + bindingAttr: BindingFlags.Public | BindingFlags.Static, + binder: null, + types: new Type[] { typeof(RuntimeTypeHandle) }, + modifiers: null)); + } - private class LuaHookFunction - { - public string name; - public string hookName; - public object function; + /// + /// Converts the value on the stack to . + /// + /// The IL emitter. + /// The type of the value on the stack. + public static void ToObject(this Emit il, Type type) + { + if (type == null) throw new ArgumentNullException(nameof(type)); + il.DerefIfByRef(ref type); + if (type.IsValueType) + { + il.Box(type); + } + else if (type != typeof(object)) + { + il.CastClass(); + } + } - public LuaHookFunction(string n, string hn, object func) - { - name = n; - hookName = hn; - function = func; - } - } - private class LuaCsHookCallback - { - public string name; - public string hookName; - public LuaCsFunc func; + /// + /// Deferences the value on stack if the provided type is ByRef. + /// + /// The IL emitter. + /// The type to check if ByRef. + public static void DerefIfByRef(this Emit il, Type type) => il.DerefIfByRef(ref type); - public LuaCsHookCallback(string name, string hookName, LuaCsFunc func) - { - this.name = name; - this.hookName = hookName; - this.func = func; - } - } + /// + /// Deferences the value on stack if the provided type is ByRef. + /// + /// The IL emitter. + /// The type to check if ByRef. + public static void DerefIfByRef(this Emit il, ref Type type) + { + if (type == null) throw new ArgumentNullException(nameof(type)); + if (type.IsByRef) + { + type = type.GetElementType(); + if (type.IsValueType) + { + il.LoadObject(type); + } + else + { + il.LoadIndirect(type); + } + } + } - private const BindingFlags DefaultBindingFlags = BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; - private static readonly string[] prohibitedHooks = { - "Barotrauma.Lua", - "Barotrauma.Cs", - "ContentPackageManager", - }; + // Copied from https://github.com/evilfactory/moonsharp/blob/5264656c6442e783f3c75082cce69a93d66d4cc0/src/MoonSharp.Interpreter/Interop/Converters/ScriptToClrConversions.cs#L79-L99 + private static MethodInfo HasImplicitConversion(Type baseType, Type targetType) + { + try + { + return Expression.Convert(Expression.Parameter(baseType, null), targetType).Method; + } + catch + { + if (baseType.BaseType != null) + { + return HasImplicitConversion(baseType.BaseType, targetType); + } + + if (targetType.BaseType != null) + { + return HasImplicitConversion(baseType, targetType.BaseType); + } + + return null; + } + } + + /// + /// Loads a local variable and casts it to the target type. + /// + /// The IL emitter. + /// The value to cast. Must be of type . + /// The type to cast into. + public static void LoadLocalAndCast(this Emit il, Local value, Type targetType) + { + if (value == null) throw new ArgumentNullException(nameof(value)); + if (targetType == null) throw new ArgumentNullException(nameof(targetType)); + if (value.LocalType != typeof(object)) + { + throw new ArgumentException($"Expected local type {typeof(object)}; got {value.LocalType}.", nameof(value)); + } + + var guid = Guid.NewGuid().ToString("N"); + + if (targetType.IsByRef) + { + targetType = targetType.GetElementType(); + } + + // IL: var baseType = value.GetType(); + var baseType = il.DeclareLocal(typeof(Type), $"cast_baseType_{guid}"); + il.LoadLocal(value); + il.Call(typeof(object).GetMethod("GetType")); + il.StoreLocal(baseType); + + // IL: var implicitConversionMethod = SigilExtensions.HasImplicitConversion(baseType, ); + var implicitConversionMethod = il.DeclareLocal(typeof(MethodInfo), $"cast_implicitConversionMethod_{guid}"); + il.LoadLocal(baseType); + il.LoadType(targetType); + il.Call(typeof(SigilExtensions).GetMethod(nameof(HasImplicitConversion), BindingFlags.NonPublic | BindingFlags.Static)); + il.StoreLocal(implicitConversionMethod); + + // IL: castValue; + var castValue = il.DeclareLocal(targetType, $"cast_castValue_{guid}"); + + // IL: if (implicitConversionMethod != null) + il.LoadLocal(implicitConversionMethod); + il.Branch((il) => + { + // IL: var methodInvokeParams = new object[1]; + var methodInvokeParams = il.DeclareLocal(typeof(object[]), $"cast_methodInvokeParams_{guid}"); + il.LoadConstant(1); + il.NewArray(typeof(object)); + il.StoreLocal(methodInvokeParams); + + // IL: methodInvokeParams[0] = value; + il.LoadLocal(methodInvokeParams); + il.LoadConstant(0); + il.LoadLocal(value); + il.StoreElement(); + + // IL: castValue = ()implicitConversionMethod.Invoke(null, methodInvokeParams); + il.LoadLocal(implicitConversionMethod); + il.LoadNull(); // first parameter is null because implicit cast operators are static + il.LoadLocal(methodInvokeParams); + il.Call(typeof(MethodInfo).GetMethod("Invoke", new[] { typeof(object), typeof(object[]) })); + if (targetType.IsValueType) + { + il.UnboxAny(targetType); + } + else + { + il.CastClass(targetType); + } + il.StoreLocal(castValue); + }, + (il) => + { + // IL: castValue = ()value; + il.LoadLocal(value); + if (targetType.IsValueType) + { + il.UnboxAny(targetType); + } + else + { + il.CastClass(targetType); + } + il.StoreLocal(castValue); + }); + + il.LoadLocal(castValue); + } + + /// + /// Emits a call to . + /// + /// The IL emitter. + /// The string format. + /// The local variables passed to string.Format. + public static void FormatString(this Emit il, string format, params Local[] args) + { + if (format == null) throw new ArgumentNullException(nameof(format)); + if (args == null) throw new ArgumentNullException(nameof(args)); + + var guid = Guid.NewGuid().ToString("N"); + + var listType = typeof(List<>).MakeGenericType(typeof(object)); + var list = il.DeclareLocal(listType, $"formatString_list_{guid}"); + il.NewObject(listType); + il.StoreLocal(list); + + foreach (var arg in args) + { + il.LoadLocal(list); + il.LoadLocal(arg); + il.ToObject(arg.LocalType); + il.CallVirtual(listType.GetMethod("Add", new[] { typeof(object) })); + } + + var arr = il.DeclareLocal($"formatString_arr_{guid}"); + il.LoadLocal(list); + il.CallVirtual(listType.GetMethod("ToArray", new Type[0])); + il.StoreLocal(arr); + + il.LoadConstant(format); + il.LoadLocal(arr); + il.Call(typeof(string).GetMethod("Format", new[] { typeof(string), typeof(object[]) })); + } + + /// + /// Emits a call to . + /// + /// The IL emitter. + /// The message to print. + public static void NewMessage(this Emit il, string message) + { + var newMessage = typeof(DebugConsole).GetMethod( + name: nameof(DebugConsole.NewMessage), + bindingAttr: BindingFlags.Public | BindingFlags.Static, + binder: null, + types: new Type[] { typeof(string), typeof(Color?), typeof(bool) }, + modifiers: null); + il.LoadConstant(message); + il.Call(typeof(Color).GetProperty(nameof(Color.LightBlue), BindingFlags.Public | BindingFlags.Static).GetGetMethod()); + il.LoadConstant(false); + il.Call(newMessage); + } + + /// + /// Emits a call to , + /// using the string on the stack. + /// + /// The IL emitter. + public static void NewMessage(this Emit il) + { + var newMessage = typeof(DebugConsole).GetMethod( + name: nameof(DebugConsole.NewMessage), + bindingAttr: BindingFlags.Public | BindingFlags.Static, + binder: null, + types: new Type[] { typeof(string), typeof(Color?), typeof(bool) }, + modifiers: null); + il.Call(typeof(Color).GetProperty(nameof(Color.LightBlue), BindingFlags.Public | BindingFlags.Static).GetGetMethod()); + il.LoadConstant(false); + il.Call(newMessage); + } + + /// + /// Emits a foreach loop that iterates over an local variable. + /// + /// The type of elements in the enumerable. + /// The IL emitter. + /// The enumerable. + /// The body of code to run on each iteration. + public static void ForEachEnumerable(this Emit il, Local enumerable, Action action) + { + if (enumerable == null) throw new ArgumentNullException(nameof(enumerable)); + if (action == null) throw new ArgumentNullException(nameof(action)); + if (!typeof(IEnumerable).IsAssignableFrom(enumerable.LocalType)) + { + throw new ArgumentException($"Expected local type {typeof(IEnumerator)}; got {enumerable.LocalType}.", nameof(enumerable)); + } + + var guid = Guid.NewGuid().ToString("N"); + + var enumerator = il.DeclareLocal>($"forEachEnumerable_enumerator_{guid}"); + il.LoadLocal(enumerable); + il.CallVirtual(typeof(IEnumerable).GetMethod("GetEnumerator")); + il.StoreLocal(enumerator); + ForEachEnumerator(il, enumerator, action); + } + + /// + /// Emits a foreach loop that iterates over an local variable. + /// + /// The type of elements in the enumerable. + /// The IL emitter. + /// The enumerator. + /// The body of code to run on each iteration. + public static void ForEachEnumerator(this Emit il, Local enumerator, Action action) + { + if (enumerator == null) throw new ArgumentNullException(nameof(enumerator)); + if (action == null) throw new ArgumentNullException(nameof(action)); + if (!typeof(IEnumerator).IsAssignableFrom(enumerator.LocalType)) + { + throw new ArgumentException($"Expected local type {typeof(IEnumerator)}; got {enumerator.LocalType}.", nameof(enumerator)); + } + + var guid = Guid.NewGuid().ToString("N"); + var labelLoopStart = il.DefineLabel($"forEach_loopStart_{guid}"); + var labelMoveNext = il.DefineLabel($"forEach_moveNext_{guid}"); + var labelLeave = il.DefineLabel($"forEach_leave_{guid}"); + + il.BeginExceptionBlock(out var exceptionBlock); + il.Branch(labelMoveNext); // MoveNext() needs to be called at least once before iterating + il.MarkLabel(labelLoopStart); + + // IL: var current = enumerator.Current; + var current = il.DeclareLocal($"forEachEnumerator_current_{guid}"); + il.LoadLocal(enumerator); + il.CallVirtual(enumerator.LocalType.GetProperty("Current").GetGetMethod()); + il.StoreLocal(current); + + action(il, current, labelLeave); + + il.MarkLabel(labelMoveNext); + il.LoadLocal(enumerator); + il.CallVirtual(typeof(IEnumerator).GetMethod("MoveNext")); + il.BranchIfTrue(labelLoopStart); // loop if MoveNext() returns true + + // IL: finally { enumerator.Dispose(); } + il.BeginFinallyBlock(exceptionBlock, out var finallyBlock); + il.LoadLocal(enumerator); + il.CallVirtual(typeof(IDisposable).GetMethod("Dispose")); + il.EndFinallyBlock(finallyBlock); + + il.EndExceptionBlock(exceptionBlock); + + il.MarkLabel(labelLeave); + } + + /// + /// Emits a branch that only executes if the last value on the stack + /// is truthy (e.g. non-null references, 1, etc). + /// + /// The IL emitter. + /// The body of code to run if the value is truthy. + public static void If(this Emit il, Action action) + { + if (action == null) throw new ArgumentNullException(nameof(action)); + il.Branch(@if: action); + } + + /// + /// Emits a branch that only executes if the last value on the stack + /// is falsy (e.g. null references, 0, etc). + /// + /// The IL emitter. + /// The body of code to run if the value is falsy. + public static void IfNot(this Emit il, Action action) + { + if (action == null) throw new ArgumentNullException(nameof(action)); + il.Branch(@else: action); + } + + /// + /// Emits two branches that diverge based on a condition -- analogous + /// to an if-else statement. If either + /// or are omitted, it behaves the same as + /// + /// and . + /// + /// The IL emitter. + /// The body of code to run if the value is truthy. + /// The body of code to run if the value is falsy. + public static void Branch(this Emit il, Action @if = null, Action @else = null) + { + if (@if == null && @else == null) throw new ArgumentException("At least one of the two branches must be defined."); + + var guid = Guid.NewGuid().ToString("N"); + var labelEnd = il.DefineLabel($"branch_end_{guid}"); + if (@if != null && @else != null) + { + var labelElse = il.DefineLabel($"branch_else_{guid}"); + il.BranchIfFalse(labelElse); + @if(il); + il.Branch(labelEnd); + il.MarkLabel(labelElse); + @else(il); + } + else if (@if != null) + { + il.BranchIfFalse(labelEnd); + @if(il); + } + else + { + il.BranchIfTrue(labelEnd); + @else(il); + } + il.MarkLabel(labelEnd); + } + } + + public partial class LuaCsHook + { + public enum HookMethodType + { + Before, After + } + + private class LuaCsHookCallback + { + public string name; + public string hookName; + public LuaCsFunc func; + + public LuaCsHookCallback(string name, string hookName, LuaCsFunc func) + { + this.name = name; + this.hookName = hookName; + this.func = func; + } + } + + private class LuaCsPatch + { + public string Identifier { get; set; } + + public LuaCsPatchFunc PatchFunc { get; set; } + } + + private class PatchedMethod + { + public PatchedMethod(MethodInfo harmonyPrefix, MethodInfo harmonyPostfix) + { + HarmonyPrefixMethod = harmonyPrefix; + HarmonyPostfixMethod = harmonyPostfix; + Prefixes = new Dictionary(); + Postfixes = new Dictionary(); + } + + public MethodInfo HarmonyPrefixMethod { get; } + + public MethodInfo HarmonyPostfixMethod { get; } + + public IEnumerator GetPrefixEnumerator() => Prefixes.Values.GetEnumerator(); + + public IEnumerator GetPostfixEnumerator() => Postfixes.Values.GetEnumerator(); + + public Dictionary Prefixes { get; } + + public Dictionary Postfixes { get; } + } + + public class ParameterTable + { + private readonly Dictionary parameters; + private bool returnValueModified; + private object returnValue; + + public ParameterTable(Dictionary dict) + { + parameters = dict; + } + + public object this[string paramName] + { + get + { + if (ModifiedParameters.TryGetValue(paramName, out var value)) + { + return value; + } + return OriginalParameters[paramName]; + } + set + { + ModifiedParameters[paramName] = value; + } + } + + public object OriginalReturnValue { get; private set; } + + public object ReturnValue + { + get + { + if (returnValueModified) return returnValue; + return OriginalReturnValue; + } + set + { + returnValueModified = true; + returnValue = value; + } + } + + public bool PreventExecution { get; set; } + + public Dictionary OriginalParameters => parameters; + + [MoonSharpHidden] + public Dictionary ModifiedParameters { get; } = new Dictionary(); + } + + private static readonly string[] prohibitedHooks = + { + "Barotrauma.Lua", + "Barotrauma.Cs", + "ContentPackageManager", + }; private static void ValidatePatchTarget(MethodInfo methodInfo) { @@ -65,409 +528,705 @@ namespace Barotrauma } } - private Harmony harmony; - - private Dictionary> hookFunctions; - private Dictionary> hookPrefixMethods; - private Dictionary> hookPostfixMethods; - - private static LuaCsHook instance; - - public LuaCsHook() { - instance = this; - - hookFunctions = new Dictionary>(); - - hookPrefixMethods = new Dictionary>(); - hookPostfixMethods = new Dictionary>(); - - compatHookPrefixMethods = new Dictionary>(); - compatHookPostfixMethods = new Dictionary>(); - } - - public void Initialize() + private static string NormalizeIdentifier(string identifier) { - harmony = new Harmony("LuaCsForBarotrauma"); - - var hookType = UserData.RegisterType(); - var hookDesc = (StandardUserDataDescriptor)hookType; - typeof(LuaCsHook).GetMethods(BindingFlags.NonPublic | BindingFlags.Instance).ToList().ForEach(m => { - if ( - m.Name.Contains("HookMethod") || - m.Name.Contains("UnhookMethod") || - m.Name.Contains("EnqueueFunction") || - m.Name.Contains("EnqueueTimedFunction") - ) - { - hookDesc.AddMember(m.Name, new MethodMemberDescriptor(m, InteropAccessMode.Default)); - } - }); - } - - public void Add(string name, string hookName, LuaCsFunc hook, ACsMod owner = null) - { - if (name == null || hookName == null || hook == null) - { - throw new ScriptRuntimeException("Hook.Add: name, hookName and hook must not be null."); - } - - name = name.ToLower(); - - if (!hookFunctions.ContainsKey(name)) - { - hookFunctions.Add(name, new Dictionary()); - } - - hookFunctions[name][hookName] = (new LuaCsHookCallback(name, hookName, hook), owner); - } - - public void Remove(string name, string hookName) - { - if (name == null || hookName == null) { return; } - - name = name.ToLower(); - - if (hookFunctions.ContainsKey(name) && hookFunctions[name].ContainsKey(hookName)) - { - hookFunctions[name].Remove(hookName); - } - } - - public void Clear() - { - hookFunctions.Clear(); - - hookPrefixMethods.Clear(); - hookPostfixMethods.Clear(); - - compatHookPrefixMethods.Clear(); - compatHookPostfixMethods.Clear(); - - harmony?.UnpatchAll(); - } - - - public void Update() - { - - } - - private Stopwatch performanceMeasurement = new Stopwatch(); - - [MoonSharpHidden] - public T Call(string name, params object[] args) - { - if (GameMain.LuaCs == null) return default; // FIXME: should this throw an exception? - if (name == null) throw new ArgumentNullException(name); - if (args == null) args = new object[0]; - - name = name.ToLower(); - - if (!hookFunctions.ContainsKey(name)) - { - return default; - } - - T lastResult = default; - - if (!hookFunctions.ContainsKey(name)) - { - return lastResult; - } - - var hooksToRemove = new List(); - foreach ((var key, var tuple) in hookFunctions[name]) - { - if (tuple.Item2 != null && tuple.Item2.IsDisposed) - { - hooksToRemove.Add(key); - continue; - } - - try - { - if (GameMain.LuaCs.PerformanceCounter.EnablePerformanceCounter) - { - performanceMeasurement.Start(); - } - - var result = tuple.Item1.func(args); - if (result != null && !result.IsNil()) - { - lastResult = result.ToObject(); - } - - if (GameMain.LuaCs.PerformanceCounter.EnablePerformanceCounter) - { - performanceMeasurement.Stop(); - GameMain.LuaCs.PerformanceCounter.SetHookElapsedTicks(name, key, performanceMeasurement.ElapsedTicks); - performanceMeasurement.Reset(); - } - } - catch (Exception e) - { - StringBuilder argsSb = new StringBuilder(); - foreach (var arg in args) argsSb.Append(arg + " "); - GameMain.LuaCs.HandleException(e, $"Error in Hook '{name}'->'{key}', with args '{argsSb}':\n{e}", ExceptionType.Both); - } - } - foreach (var key in hooksToRemove) - { - hookFunctions[name].Remove(key); - } - - return lastResult; - } - - public object Call(string name, params object[] args) - { - if (name == null) throw new ScriptRuntimeException("Hook.Call: name must not be null."); - return Call(name, args); + return identifier?.Trim().ToLowerInvariant(); } - private static bool PatchPrefix(MethodBase __originalMethod, object[] __args, object __instance) - { - ExecutePatch(__originalMethod, __args, __instance, out object result, HookMethodType.Before); - return result == null; - } - private static void PatchPostfix(MethodBase __originalMethod, object[] __args, object __instance) => - ExecutePatch(__originalMethod, __args, __instance, out object _, HookMethodType.After); + private Harmony harmony; - private static bool PatchPrefixWithReturn(MethodBase __originalMethod, object[] __args, ref object __result, object __instance) - { - ExecutePatch(__originalMethod, __args, __instance, out object result, HookMethodType.Before); - if (result != null) - { - __result = result; - return false; - } - else { return true; } - } - private static void PatchPostfixWithReturn(MethodBase __originalMethod, object[] __args, ref object __result, object __instance) - { - ExecutePatch(__originalMethod, __args, __instance, out object result, HookMethodType.After); - if (result != null) { __result = result; } - } + private Lazy patchModuleBuilder; + private readonly Dictionary> hookFunctions = new Dictionary>(); - private static readonly MethodInfo miPatchPrefix = typeof(LuaCsHook).GetMethod("PatchPrefix", BindingFlags.NonPublic | BindingFlags.Static); - private static readonly MethodInfo miPatchPostfix = typeof(LuaCsHook).GetMethod("PatchPostfix", BindingFlags.NonPublic | BindingFlags.Static); - private static readonly MethodInfo miPatchPrefixWithReturn = typeof(LuaCsHook).GetMethod("PatchPrefixWithReturn", BindingFlags.NonPublic | BindingFlags.Static); - private static readonly MethodInfo miPatchPostfixWithReturn = typeof(LuaCsHook).GetMethod("PatchPostfixWithReturn", BindingFlags.NonPublic | BindingFlags.Static); + private readonly Dictionary registeredPatches = new Dictionary(); - private static MethodInfo ResolveMethod(string className, string methodName, string[] parameterNames) - { - var classType = LuaUserData.GetType(className); + private static LuaCsHook instance; - if (classType == null) - { - throw new ArgumentNullException($"Invalid class name '{className}'."); - } + private struct MethodKey : IEquatable + { + public ModuleHandle ModuleHandle { get; set; } - MethodInfo methodInfo = null; + public int MetadataToken { get; set; } - if (parameterNames != null) - { - Type[] parameterTypes = parameterNames.Select(x => LuaUserData.GetType(x)).ToArray(); - methodInfo = classType.GetMethod(methodName, DefaultBindingFlags, null, parameterTypes, null); - } - else - { - methodInfo = classType.GetMethod(methodName, DefaultBindingFlags); - } + public override bool Equals(object obj) + { + return obj is MethodKey key && Equals(key); + } - if (methodInfo == null) - { - string parameterNamesStr = parameterNames == null ? "" : string.Join(", ", parameterNames); - throw new ArgumentNullException($"Method '{methodName}' with parameters '{parameterNamesStr}' not found in class '{className}'"); - } + public bool Equals(MethodKey other) + { + return ModuleHandle.Equals(other.ModuleHandle) && MetadataToken == other.MetadataToken; + } - return methodInfo; - } + public override int GetHashCode() + { + return HashCode.Combine(ModuleHandle, MetadataToken); + } - public void Patch(string identifier, MethodInfo method, LuaCsFunc patch, HookMethodType hookType = HookMethodType.Before, ACsMod owner = null) - { - if (identifier == null || method == null || patch == null) - { - throw new ArgumentNullException("Identifier, Method and Patch arguments must not be null."); - } - ValidatePatchTarget(method); + public static bool operator ==(MethodKey left, MethodKey right) + { + return left.Equals(right); + } - var funcAddr = (long)method.MethodHandle.GetFunctionPointer(); - var patches = Harmony.GetPatchInfo(method); + public static bool operator !=(MethodKey left, MethodKey right) + { + return !(left == right); + } - if (hookType == HookMethodType.Before) - { - if (method.ReturnType != typeof(void)) - { - if (patches == null || patches.Prefixes == null || patches.Prefixes.Find(patch => patch.PatchMethod == miPatchPrefixWithReturn) == null) - { - harmony.Patch(method, prefix: new HarmonyMethod(miPatchPrefixWithReturn)); - } - } - else - { - if (patches == null || patches.Prefixes == null || patches.Prefixes.Find(patch => patch.PatchMethod == miPatchPrefix) == null) - { - harmony.Patch(method, prefix: new HarmonyMethod(miPatchPrefix)); - } - } + public static MethodKey Create(MethodInfo method) => new MethodKey + { + ModuleHandle = method.Module.ModuleHandle, + MetadataToken = method.MetadataToken, + }; + } - if (hookPrefixMethods.TryGetValue(funcAddr, out HashSet<(string, LuaCsFunc, ACsMod)> methodSet)) - { - if (identifier != "") - { - methodSet.RemoveWhere(tuple => tuple.Item1 == identifier); - } + public LuaCsHook() + { + instance = this; + } - methodSet.Add((identifier, patch, owner)); - } - else if (patch != null) - { - hookPrefixMethods.Add(funcAddr, new HashSet<(string, LuaCsFunc, ACsMod)>() { (identifier, patch, owner) }); - } + public void Initialize() + { + harmony = new Harmony("LuaCsForBarotrauma"); + patchModuleBuilder = new Lazy(CreateModuleBuilder); - } - else if (hookType == HookMethodType.After) - { - if (method.ReturnType != typeof(void)) - { - if (patches == null || patches.Postfixes == null || patches.Postfixes.Find(patch => patch.PatchMethod == miPatchPostfixWithReturn) == null) - { - harmony.Patch(method, postfix: new HarmonyMethod(miPatchPostfixWithReturn)); - } - } - else - { - if (patches == null || patches.Postfixes == null || patches.Postfixes.Find(patch => patch.PatchMethod == miPatchPostfix) == null) - { - harmony.Patch(method, postfix: new HarmonyMethod(miPatchPostfix)); - } - } + UserData.RegisterType(); + var hookType = UserData.RegisterType(); + var hookDesc = (StandardUserDataDescriptor)hookType; + typeof(LuaCsHook).GetMethods(BindingFlags.NonPublic | BindingFlags.Instance).ToList().ForEach(m => { + if ( + m.Name.Contains("HookMethod") || + m.Name.Contains("UnhookMethod") || + m.Name.Contains("EnqueueFunction") || + m.Name.Contains("EnqueueTimedFunction") + ) + { + hookDesc.AddMember(m.Name, new MethodMemberDescriptor(m, InteropAccessMode.Default)); + } + }); + } - if (hookPostfixMethods.TryGetValue(funcAddr, out HashSet<(string, LuaCsFunc, ACsMod)> methodSet)) - { - if (identifier != "") - { - methodSet.RemoveWhere(tuple => tuple.Item1 == identifier); - } + private ModuleBuilder CreateModuleBuilder() + { + var assemblyName = $"LuaCsHookPatch-{Guid.NewGuid():N}"; + var assemblyBuilder = AssemblyBuilder.DefineDynamicAssembly(new AssemblyName(assemblyName), AssemblyBuilderAccess.RunAndCollect); + var moduleBuilder = assemblyBuilder.DefineDynamicModule("LuaCsHookPatch"); - methodSet.Add((identifier, patch, owner)); - } - else if (patch != null) - { - hookPostfixMethods.Add(funcAddr, new HashSet<(string, LuaCsFunc, ACsMod)>() { (identifier, patch, owner) }); - } + // This code emits the Roslyn attribute + // "IgnoresAccessChecksToAttribute" so we can freely access + // the Barotrauma assembly from our dynamic patches. + // This is important because the generated IL references + // non-public types/members. - } + // class IgnoresAccessChecksToAttribute { + var typeBuilder = moduleBuilder.DefineType( + name: "System.Runtime.CompilerServices.IgnoresAccessChecksToAttribute", + attr: TypeAttributes.NotPublic | TypeAttributes.Sealed | TypeAttributes.Class, + parent: typeof(Attribute)); - } + // [AttributeUsage(AllowMultiple = true)] + var attributeUsageAttribute = new CustomAttributeBuilder( + con: typeof(AttributeUsageAttribute).GetConstructor(new[] { typeof(AttributeTargets) }), + constructorArgs: new object[] { AttributeTargets.Assembly }, + namedProperties: new[] { typeof(AttributeUsageAttribute).GetProperty("AllowMultiple") }, + propertyValues: new object[] { true }); + typeBuilder.SetCustomAttribute(attributeUsageAttribute); - private static void ExecutePatch(MethodBase __originalMethod, object[] __args, object __instance, out object result, HookMethodType hookMethodType) - { - result = null; + // private readonly string assemblyName; + var attributeTypeFieldBuilder = typeBuilder.DefineField( + fieldName: "assemblyName", + type: typeof(string), + attributes: FieldAttributes.Private | FieldAttributes.InitOnly); - try - { - long funcAddr = (long)__originalMethod.MethodHandle.GetFunctionPointer(); + var ctor = Emit.BuildConstructor( + parameterTypes: new[] { typeof(string) }, + type: typeBuilder, + attributes: MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, + callingConvention: CallingConventions.Standard | CallingConventions.HasThis); + // IL: this.assemblyName = arg; + ctor.LoadArgument(0); + ctor.LoadArgument(1); + ctor.StoreField(attributeTypeFieldBuilder); + ctor.Return(); + ctor.CreateConstructor(); - HashSet<(string, LuaCsFunc, ACsMod)> methodSet = null; - switch (hookMethodType) - { - case HookMethodType.Before: - instance.hookPrefixMethods.TryGetValue(funcAddr, out methodSet); - break; - case HookMethodType.After: - instance.hookPostfixMethods.TryGetValue(funcAddr, out methodSet); - break; - default: - break; - } + // public string AttributeName => this.assemblyName; + var attributeNameGetter = Emit.BuildMethod( + returnType: typeof(string), + parameterTypes: new Type[0], + type: typeBuilder, + name: "get_AttributeName", + attributes: MethodAttributes.Public | MethodAttributes.SpecialName | MethodAttributes.RTSpecialName, + callingConvention: CallingConventions.Standard | CallingConventions.HasThis); + attributeNameGetter.LoadArgument(0); + attributeNameGetter.LoadField(attributeTypeFieldBuilder); + attributeNameGetter.Return(); - if (methodSet == null) - { - return; - } + var attributeName = typeBuilder.DefineProperty( + name: "AttributeName", + attributes: PropertyAttributes.None, + returnType: typeof(string), + parameterTypes: null); + attributeName.SetGetMethod(attributeNameGetter.CreateMethod()); + // } - var patchesToRemove = new HashSet<(string, LuaCsFunc, ACsMod)>(); - foreach (var tuple in methodSet) - { - if (tuple.Item3 != null && tuple.Item3.IsDisposed) - { - patchesToRemove.Add(tuple); - continue; - } + var type = typeBuilder.CreateTypeInfo().AsType(); - var args = Enumerable.Empty() - .Concat(__args) - .Prepend(__instance) - .ToArray(); - var _result = tuple.Item2(args); + // The assembly names are hardcoded, otherwise it would + // break unit tests. + var assembliesToExpose = new[] { "Barotrauma", "DedicatedServer" }; + foreach (var name in assembliesToExpose) + { + var attr = new CustomAttributeBuilder( + con: type.GetConstructor(new[] { typeof(string)}), + constructorArgs: new[] { name }); + assemblyBuilder.SetCustomAttribute(attr); + } - if (_result != null && !_result.IsNil()) - { - if (__originalMethod is MethodInfo mi && mi.ReturnType != typeof(void)) - { - result = _result.ToObject(mi.ReturnType); - } - else - { - result = _result.ToObject(); - } - } - } + return moduleBuilder; + } - foreach (var tuple in patchesToRemove) - { - methodSet.Remove(tuple); - } - } - catch (Exception ex) - { - GameMain.LuaCs.HandleException(ex, $"Error in {__originalMethod.Name}:", exceptionType: LuaCsSetup.ExceptionType.Both); - } - } + public void Add(string name, string identifier, LuaCsFunc func, ACsMod owner = null) + { + if (name == null) throw new ArgumentNullException(nameof(name)); + if (identifier == null) throw new ArgumentNullException(nameof(identifier)); + if (func == null) throw new ArgumentNullException(nameof(func)); + name = NormalizeIdentifier(name); + identifier = NormalizeIdentifier(identifier); - public void Patch(string identifier, string className, string methodName, string[] parameterNames, LuaCsFunc patch, HookMethodType hookMethodType = HookMethodType.Before) - { - MethodInfo methodInfo = ResolveMethod(className, methodName, parameterNames); - if (methodInfo == null) return; - Patch(identifier, methodInfo, patch, hookMethodType); - } - public void Patch(string identifier, string className, string methodName, LuaCsFunc patch, HookMethodType hookMethodType = HookMethodType.Before) => - Patch(identifier, className, methodName, null, patch, hookMethodType); - public void Patch(string className, string methodName, LuaCsFunc patch, HookMethodType hookMethodType = HookMethodType.Before) => - Patch("", className, methodName, null, patch, hookMethodType); - public void Patch(string className, string methodName, string[] parameterNames, LuaCsFunc patch, HookMethodType hookMethodType = HookMethodType.Before) => - Patch("", className, methodName, parameterNames, patch, hookMethodType); + if (!hookFunctions.ContainsKey(name)) + { + hookFunctions.Add(name, new Dictionary()); + } + hookFunctions[name][identifier] = (new LuaCsHookCallback(name, identifier, func), owner); + } - public void RemovePatch(string identifier, MethodInfo method, HookMethodType hookType = HookMethodType.Before) - { - var funcAddr = (long)method.MethodHandle.GetFunctionPointer(); + public void Remove(string name, string identifier) + { + if (name == null) throw new ArgumentNullException(nameof(name)); + if (identifier == null) throw new ArgumentNullException(nameof(identifier)); - Dictionary> methods; - if (hookType == HookMethodType.Before) { methods = hookPrefixMethods; } - else if (hookType == HookMethodType.After) { methods = hookPostfixMethods; } - else { throw new NotImplementedException(); } + name = NormalizeIdentifier(name); + identifier = NormalizeIdentifier(identifier); - if (methods.ContainsKey(funcAddr)) - { - methods[funcAddr]?.RemoveWhere(t => t.Item1 == identifier); - } - } + if (hookFunctions.ContainsKey(name) && hookFunctions[name].ContainsKey(identifier)) + { + hookFunctions[name].Remove(identifier); + } + } - public void RemovePatch(string identifier, string className, string methodName, string[] parameterNames, HookMethodType hookType = HookMethodType.Before) - { - MethodInfo methodInfo = ResolveMethod(className, methodName, parameterNames); + public void Clear() + { + harmony?.UnpatchAll(); - if (methodInfo == null) - { - return; - } + hookFunctions.Clear(); + registeredPatches.Clear(); + patchModuleBuilder = null; - RemovePatch(identifier, methodInfo, hookType); - } - } -} \ No newline at end of file + compatHookPrefixMethods.Clear(); + compatHookPostfixMethods.Clear(); + } + + public void Update() { } + + private Stopwatch performanceMeasurement = new Stopwatch(); + + [MoonSharpHidden] + public T Call(string name, params object[] args) + { + if (GameMain.LuaCs == null) throw new InvalidOperationException("Can't call hooks before LuaCsHook is initialized."); + if (name == null) throw new ArgumentNullException(name); + if (args == null) args = new object[0]; + + name = NormalizeIdentifier(name); + if (!hookFunctions.ContainsKey(name)) return default; + + T lastResult = default; + + var hooksToRemove = new List(); + foreach ((var key, var tuple) in hookFunctions[name]) + { + if (tuple.Item2 != null && tuple.Item2.IsDisposed) + { + hooksToRemove.Add(key); + continue; + } + + try + { + if (GameMain.LuaCs.PerformanceCounter.EnablePerformanceCounter) + { + performanceMeasurement.Start(); + } + + var result = tuple.Item1.func(args); + // TODO(BREAKING): change this to !result.IsVoid() + if (result != null && !result.IsNil()) + { + lastResult = result.ToObject(); + } + + if (GameMain.LuaCs.PerformanceCounter.EnablePerformanceCounter) + { + performanceMeasurement.Stop(); + GameMain.LuaCs.PerformanceCounter.SetHookElapsedTicks(name, key, performanceMeasurement.ElapsedTicks); + performanceMeasurement.Reset(); + } + } + catch (Exception e) + { + var argsSb = new StringBuilder(); + foreach (var arg in args) + { + argsSb.Append(arg + " "); + } + GameMain.LuaCs.HandleException(e, $"Error in Hook '{name}'->'{key}', with args '{argsSb}':\n{e}", ExceptionType.Both); + } + } + foreach (var key in hooksToRemove) + { + hookFunctions[name].Remove(key); + } + + return lastResult; + } + + public object Call(string name, params object[] args) => Call(name, args); + + private static MethodInfo ResolveMethod(string className, string methodName, string[] parameterNames) + { + var classType = LuaUserData.GetType(className); + if (classType == null) throw new InvalidOperationException($"Invalid class name '{className}'"); + + const BindingFlags BINDING_FLAGS = BindingFlags.Static | BindingFlags.Instance | BindingFlags.Public | BindingFlags.NonPublic; + MethodInfo methodInfo = null; + if (parameterNames != null) + { + var parameterTypes = parameterNames.Select(x => LuaUserData.GetType(x)).ToArray(); + methodInfo = classType.GetMethod(methodName, BINDING_FLAGS, null, parameterTypes, null); + } + else + { + methodInfo = classType.GetMethod(methodName, BINDING_FLAGS); + } + + if (methodInfo == null) + { + var parameterNamesStr = parameterNames == null ? "" : string.Join(", ", parameterNames); + throw new InvalidOperationException($"Method '{methodName}({parameterNamesStr})' not found in class '{className}'"); + } + + return methodInfo; + } + + private class DynamicParameterMapping + { + public DynamicParameterMapping(string name, Type originalMethodParamType, Type harmonyPatchParamType) + { + ParameterName = name; + OriginalMethodParamType = originalMethodParamType; + HarmonyPatchParamType = harmonyPatchParamType; + } + + public string ParameterName { get; set; } + + public Type OriginalMethodParamType { get; set; } + + public Type HarmonyPatchParamType { get; set; } + } + + private static readonly Regex InvalidIdentifierCharsRegex = new Regex(@"[^\w\d]", RegexOptions.Compiled); + + // If you need to debug this: + // - use https://sharplab.io ; it's a very useful for resource for writing IL by hand. + // - use il.NewMessage("") or il.WriteLine("") to see where the IL crashes at runtime. + private MethodInfo CreateDynamicHarmonyPatch(string identifier, MethodInfo original, HookMethodType hookType) + { + var parameters = new List + { + new DynamicParameterMapping("__originalMethod", null, typeof(MethodBase)), + new DynamicParameterMapping("__instance", null, typeof(object)), + }; + + var hasReturnType = original.ReturnType != typeof(void); + if (hasReturnType) + { + parameters.Add(new DynamicParameterMapping("__result", null, typeof(object).MakeByRefType())); + } + + foreach (var parameter in original.GetParameters()) + { + var paramName = parameter.Name; + var originalMethodParamType = parameter.ParameterType; + var harmonyPatchParamType = originalMethodParamType.IsByRef + ? originalMethodParamType + // Make all parameters modifiable by the harmony patch + : originalMethodParamType.MakeByRefType(); + parameters.Add(new DynamicParameterMapping(paramName, originalMethodParamType, harmonyPatchParamType)); + } + + static string MangleName(object o) => InvalidIdentifierCharsRegex.Replace(o?.ToString(), "_"); + + var moduleBuilder = patchModuleBuilder.Value; + var mangledName = original.DeclaringType != null + ? $"{MangleName(original.DeclaringType)}-{MangleName(original)}" + : MangleName(original); + var typeBuilder = moduleBuilder.DefineType($"Patch_{identifier}_{Guid.NewGuid():N}_{mangledName}", TypeAttributes.Public); + + var methodName = hookType == HookMethodType.Before ? "HarmonyPrefix" : "HarmonyPostfix"; + var il = Emit.BuildMethod( + returnType: hookType == HookMethodType.Before ? typeof(bool) : typeof(void), + parameterTypes: parameters.Select(x => x.HarmonyPatchParamType).ToArray(), + type: typeBuilder, + name: methodName, + attributes: MethodAttributes.Public | MethodAttributes.Static, + callingConvention: CallingConventions.Standard); + + var labelReturn = il.DefineLabel("endOfFunction"); + + il.BeginExceptionBlock(out var exceptionBlock); + + // IL: var harmonyReturnValue = true; + var harmonyReturnValue = il.DeclareLocal("harmonyReturnValue"); + il.LoadConstant(true); + il.StoreLocal(harmonyReturnValue); + + // IL: var patchKey = MethodKey.Create(__originalMethod); + var patchKey = il.DeclareLocal("patchKey"); + il.LoadArgument(0); // load __originalMethod + il.CastClass(); + il.Call(typeof(MethodKey).GetMethod(nameof(MethodKey.Create))); + il.StoreLocal(patchKey); + + // IL: var patchExists = instance.registeredPatches.TryGetValue(patchKey, out MethodPatches patches) + var patchExists = il.DeclareLocal("patchExists"); + var patches = il.DeclareLocal("patches"); + il.LoadField(typeof(LuaCsHook).GetField(nameof(instance), BindingFlags.NonPublic | BindingFlags.Static)); + il.LoadField(typeof(LuaCsHook).GetField(nameof(registeredPatches), BindingFlags.NonPublic | BindingFlags.Instance)); + il.LoadLocal(patchKey); + il.LoadLocalAddress(patches); // out parameter + il.Call(typeof(Dictionary).GetMethod("TryGetValue")); + il.StoreLocal(patchExists); + + // IL: if (!patchExists) + il.LoadLocal(patchExists); + il.IfNot((il) => + { + // XXX: if we get here, it's probably because a patched + // method was running when `reloadlua` was executed. + // This can happen with a postfix on + // `Barotrauma.Networking.GameServer#Update`. + il.Leave(labelReturn); + }); + + // IL: var parameterDict = new Dictionary(); + var parameterDict = il.DeclareLocal>("parameterDict"); + il.LoadConstant(parameters.Count(x => x.OriginalMethodParamType != null)); // preallocate the dictionary using the # of args + il.NewObject(typeof(Dictionary), typeof(int)); + il.StoreLocal(parameterDict); + + for (ushort i = 0; i < parameters.Count; i++) + { + // Skip parameters that don't exist in the original method + if (parameters[i].OriginalMethodParamType == null) continue; + + // IL: parameterDict.Add(, ); + il.LoadLocal(parameterDict); + il.LoadConstant(parameters[i].ParameterName); + il.LoadArgument(i); + il.ToObject(parameters[i].HarmonyPatchParamType); + il.Call(typeof(Dictionary).GetMethod("Add")); + } + + // IL: var ptable = new ParameterTable(parameterDict); + var ptable = il.DeclareLocal("ptable"); + il.LoadLocal(parameterDict); + il.NewObject(typeof(ParameterTable), typeof(Dictionary)); + il.StoreLocal(ptable); + + if (hasReturnType && hookType == HookMethodType.After) + { + // IL: ptable.OriginalReturnValue = __result; + il.LoadLocal(ptable); + il.LoadArgument(2); // ref __result + il.ToObject(parameters[2].HarmonyPatchParamType); + il.Call(typeof(ParameterTable).GetProperty(nameof(ParameterTable.OriginalReturnValue)).GetSetMethod(nonPublic: true)); + } + + // IL: var enumerator = patches.GetPrefixEnumerator(); + var enumerator = il.DeclareLocal>("enumerator"); + il.LoadLocal(patches); + il.CallVirtual(typeof(PatchedMethod).GetMethod( + name: hookType == HookMethodType.Before + ? nameof(PatchedMethod.GetPrefixEnumerator) + : nameof(PatchedMethod.GetPostfixEnumerator), + bindingAttr: BindingFlags.Public | BindingFlags.Instance)); + il.StoreLocal(enumerator); + + var labelUpdateParameters = il.DefineLabel("updateParameters"); + + // Iterate over prefixes/postfixes + il.ForEachEnumerator(enumerator, (il, current, labelLeave) => + { + // IL: var luaReturnValue = current.PatchFunc.Invoke(__instance, ptable); + var luaReturnValue = il.DeclareLocal("luaReturnValue"); + il.LoadLocal(current); + il.Call(typeof(LuaCsPatch).GetProperty(nameof(LuaCsPatch.PatchFunc)).GetGetMethod()); + il.LoadArgument(1); // __instance + il.LoadLocal(ptable); + il.CallVirtual(typeof(LuaCsPatchFunc).GetMethod("Invoke")); + il.StoreLocal(luaReturnValue); + + if (hasReturnType) + { + // IL: var ptableReturnValue = ptable.ReturnValue; + var ptableReturnValue = il.DeclareLocal("ptableReturnValue"); + il.LoadLocal(ptable); + il.Call(typeof(ParameterTable).GetProperty(nameof(ParameterTable.ReturnValue)).GetGetMethod()); + il.StoreLocal(ptableReturnValue); + + // IL: if (ptableReturnValue != null) + il.LoadLocal(ptableReturnValue); + il.If((il) => + { + // IL: __result = ptableReturnValue; + il.LoadArgument(2); // ref __result + il.LoadLocal(ptableReturnValue); + il.StoreIndirect(typeof(object)); + il.Break(); + }); + + // IL: if (luaReturnValue != null) + il.LoadLocal(luaReturnValue); + il.If((il) => + { + // IL: if (!luaReturnValue.IsVoid()) + il.LoadLocal(luaReturnValue); + il.Call(typeof(DynValue).GetMethod(nameof(DynValue.IsVoid))); + il.IfNot((il) => + { + // IL: var csReturnType = Type.GetTypeFromHandle(); + var csReturnType = il.DeclareLocal("csReturnType"); + il.LoadType(original.ReturnType); + il.StoreLocal(csReturnType); + + // IL: var csReturnValue = luaReturnValue.ToObject(csReturnValueType); + var csReturnValue = il.DeclareLocal("csReturnValue"); + il.LoadLocal(luaReturnValue); + il.LoadLocal(csReturnType); + il.Call(typeof(DynValue).GetMethod( + name: nameof(DynValue.ToObject), + bindingAttr: BindingFlags.Public | BindingFlags.Instance, + binder: null, + types: new Type[] { typeof(Type) }, + modifiers: null)); + il.StoreLocal(csReturnValue); + + // IL: __result = csReturnValue; + il.LoadArgument(2); // ref __result + il.LoadLocal(csReturnValue); + il.StoreIndirect(typeof(object)); + }); + }); + } + + // IL: if (ptable.PreventExecution) + il.LoadLocal(ptable); + il.Call(typeof(ParameterTable).GetProperty(nameof(ParameterTable.PreventExecution)).GetGetMethod()); + il.If((il) => + { + // IL: harmonyReturnValue = false; + il.LoadConstant(false); + il.StoreLocal(harmonyReturnValue); + + // IL: break; + il.Leave(labelLeave); + }); + }); + + // IL: var modifiedParameters = ptable.ModifiedParameters; + var modifiedParameters = il.DeclareLocal>("modifiedParameters"); + il.LoadLocal(ptable); + il.Call(typeof(ParameterTable).GetProperty(nameof(ParameterTable.ModifiedParameters)).GetGetMethod()); + il.StoreLocal(modifiedParameters); + // IL: object modifiedValue; + var modifiedValue = il.DeclareLocal("modifiedValue"); + + // Update the parameters + for (ushort i = 0; i < parameters.Count; i++) + { + // Skip parameters that don't exist in the original method + if (parameters[i].OriginalMethodParamType == null) continue; + + // IL: if (modifiedParameters.TryGetValue("parameterName", out modifiedValue)) + il.LoadLocal(modifiedParameters); + il.LoadConstant(parameters[i].ParameterName); + il.LoadLocalAddress(modifiedValue); // out parameter + il.Call(typeof(Dictionary).GetMethod(nameof(Dictionary.TryGetValue))); + il.If((il) => + { + // XXX: GetElementType() gets the "real" type behind + // the ByRef. This is safe because all the parameters + // are made into ByRef to support modification. + var paramType = parameters[i].HarmonyPatchParamType.GetElementType(); + + // IL: ref argName = modifiedValue; + il.LoadArgument(i); + il.LoadLocalAndCast(modifiedValue, paramType); + if (paramType.IsValueType) + { + il.StoreObject(paramType); + } + else + { + il.StoreIndirect(paramType); + } + }); + } + + il.MarkLabel(labelReturn); + + // IL: catch (Exception exception) + il.BeginCatchAllBlock(exceptionBlock, out var catchBlock); + var exception = il.DeclareLocal("exception"); + il.StoreLocal(exception); + + // IL: var luaCsSetup = GameMain.LuaCs; + var luaCsSetup = il.DeclareLocal("luaCsSetup"); + il.LoadField(typeof(GameMain).GetField(nameof(GameMain.LuaCs), BindingFlags.Public | BindingFlags.Static)); + il.StoreLocal(luaCsSetup); + + // IL: luaCsSetup.HandleException(exception, "", ExceptionType.Lua); + il.LoadLocal(luaCsSetup); + il.LoadLocal(exception); + il.LoadConstant(""); + il.LoadConstant((int)ExceptionType.Lua); // underlying enum type is int + il.Call(typeof(LuaCsSetup).GetMethod(nameof(LuaCsSetup.HandleException))); + il.EndCatchBlock(catchBlock); + + il.EndExceptionBlock(exceptionBlock); + + // Only prefixes return a bool + if (hookType == HookMethodType.Before) + { + il.LoadLocal(harmonyReturnValue); + } + il.Return(); + + var method = il.CreateMethod(); + for (var i = 0; i < parameters.Count; i++) + { + method.DefineParameter(i + 1, ParameterAttributes.None, parameters[i].ParameterName); + } + + var type = typeBuilder.CreateType(); + return type.GetMethod(methodName, BindingFlags.Public | BindingFlags.Static); + } + + private string Patch(string identifier, MethodInfo method, LuaCsPatchFunc patch, HookMethodType hookType = HookMethodType.Before) + { + if (method == null) throw new ArgumentNullException(nameof(method)); + if (patch == null) throw new ArgumentNullException(nameof(patch)); + ValidatePatchTarget(method); + + identifier ??= Guid.NewGuid().ToString("N"); + identifier = NormalizeIdentifier(identifier); + + var patchKey = MethodKey.Create(method); + if (!registeredPatches.TryGetValue(patchKey, out var methodPatches)) + { + var harmonyPrefix = CreateDynamicHarmonyPatch(identifier, method, HookMethodType.Before); + var harmonyPostfix = CreateDynamicHarmonyPatch(identifier, method, HookMethodType.After); + harmony.Patch(method, prefix: new HarmonyMethod(harmonyPrefix), postfix: new HarmonyMethod(harmonyPostfix)); + methodPatches = registeredPatches[patchKey] = new PatchedMethod(harmonyPrefix, harmonyPostfix); + } + + if (hookType == HookMethodType.Before) + { + if (methodPatches.Prefixes.Remove(identifier)) + { + PrintLogMessage($"Replacing existing prefix: {identifier}"); + } + + methodPatches.Prefixes.Add(identifier, new LuaCsPatch + { + Identifier = identifier, + PatchFunc = patch, + }); + } + else if (hookType == HookMethodType.After) + { + if (methodPatches.Postfixes.Remove(identifier)) + { + PrintLogMessage($"Replacing existing postfix: {identifier}"); + } + + methodPatches.Postfixes.Add(identifier, new LuaCsPatch + { + Identifier = identifier, + PatchFunc = patch, + }); + } + + return identifier; + } + + public string Patch(string identifier, string className, string methodName, string[] parameterTypes, LuaCsPatchFunc patch, HookMethodType hookType = HookMethodType.Before) + { + var methodInfo = ResolveMethod(className, methodName, parameterTypes); + return Patch(identifier, methodInfo, patch, hookType); + } + + public string Patch(string identifier, string className, string methodName, LuaCsPatchFunc patch, HookMethodType hookType = HookMethodType.Before) + { + var methodInfo = ResolveMethod(className, methodName, null); + return Patch(identifier, methodInfo, patch, hookType); + } + + public string Patch(string className, string methodName, string[] parameterTypes, LuaCsPatchFunc patch, HookMethodType hookType = HookMethodType.Before) + { + var methodInfo = ResolveMethod(className, methodName, parameterTypes); + return Patch(null, methodInfo, patch, hookType); + } + + public string Patch(string className, string methodName, LuaCsPatchFunc patch, HookMethodType hookType = HookMethodType.Before) + { + var methodInfo = ResolveMethod(className, methodName, null); + return Patch(null, methodInfo, patch, hookType); + } + + private bool RemovePatch(string identifier, MethodInfo method, HookMethodType hookType) + { + if (identifier == null) throw new ArgumentNullException(nameof(identifier)); + identifier = NormalizeIdentifier(identifier); + + var patchKey = MethodKey.Create(method); + if (!registeredPatches.TryGetValue(patchKey, out var methodPatches)) + { + return false; + } + + return hookType switch + { + HookMethodType.Before => methodPatches.Prefixes.Remove(identifier), + HookMethodType.After => methodPatches.Postfixes.Remove(identifier), + _ => throw new ArgumentException($"Invalid {nameof(HookMethodType)} enum value.", nameof(hookType)), + }; + } + + public bool RemovePatch(string identifier, string className, string methodName, string[] parameterTypes, HookMethodType hookType) + { + var methodInfo = ResolveMethod(className, methodName, parameterTypes); + return RemovePatch(identifier, methodInfo, hookType); + } + + public bool RemovePatch(string identifier, string className, string methodName, HookMethodType hookType) + { + var methodInfo = ResolveMethod(className, methodName, null); + return RemovePatch(identifier, methodInfo, hookType); + } + } +} diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsHookCompat.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsHookCompat.cs index c009a8d24..0967b6165 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsHookCompat.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsHookCompat.cs @@ -1,31 +1,31 @@ using System; using System.Linq; using System.Reflection; -using MoonSharp.Interpreter; using HarmonyLib; using System.Collections.Generic; -using System.Text; -using MoonSharp.Interpreter.Interop; +using MoonSharp.Interpreter; using static Barotrauma.LuaCsSetup; -using System.Threading; -using System.Diagnostics; +using LuaCsCompatPatchFunc = Barotrauma.LuaCsPatch; namespace Barotrauma { - partial class LuaCsHook - { - private Dictionary> compatHookPrefixMethods; - private Dictionary> compatHookPostfixMethods; + // XXX: this can't be renamed because of backward compatibility with C# mods + public delegate object LuaCsPatch(object self, Dictionary args); - private static void _hookLuaCsPatch(MethodBase __originalMethod, object[] __args, object __instance, out object result, HookMethodType hookMethodType) + partial class LuaCsHook + { + private Dictionary> compatHookPrefixMethods = new Dictionary>(); + private Dictionary> compatHookPostfixMethods = new Dictionary>(); + + private static void _hookLuaCsPatch(MethodBase __originalMethod, object[] __args, object __instance, out object result, HookMethodType hookType) { result = null; try { var funcAddr = ((long)__originalMethod.MethodHandle.GetFunctionPointer()); - HashSet<(string, LuaCsPatch, ACsMod)> methodSet = null; - switch (hookMethodType) + HashSet<(string, LuaCsCompatPatchFunc, ACsMod)> methodSet = null; + switch (hookType) { case HookMethodType.Before: instance.compatHookPrefixMethods.TryGetValue(funcAddr, out methodSet); @@ -34,7 +34,7 @@ namespace Barotrauma instance.compatHookPostfixMethods.TryGetValue(funcAddr, out methodSet); break; default: - break; + throw new ArgumentException($"Invalid {nameof(HookMethodType)} enum value.", nameof(hookType)); } if (methodSet != null) @@ -46,7 +46,7 @@ namespace Barotrauma args.Add(@params[i].Name, __args[i]); } - var outOfSocpe = new HashSet<(string, LuaCsPatch, ACsMod)>(); + var outOfSocpe = new HashSet<(string, LuaCsCompatPatchFunc, ACsMod)>(); foreach (var tuple in methodSet) { if (tuple.Item3 != null && tuple.Item3.IsDisposed) @@ -94,6 +94,7 @@ namespace Barotrauma _hookLuaCsPatch(__originalMethod, __args, __instance, out object result, HookMethodType.Before); return result == null; } + private static void HookLuaCsPatchPostfix(MethodBase __originalMethod, object[] __args, object __instance) => _hookLuaCsPatch(__originalMethod, __args, __instance, out object _, HookMethodType.After); @@ -107,50 +108,19 @@ namespace Barotrauma } else return true; } + private static void HookLuaCsPatchRetPostfix(MethodBase __originalMethod, object[] __args, ref object __result, object __instance) { _hookLuaCsPatch(__originalMethod, __args, __instance, out object result, HookMethodType.After); if (result != null) __result = result; } - private static MethodInfo _miHookLuaCsPatchPrefix = typeof(LuaCsHook).GetMethod("HookLuaCsPatchPrefix", BindingFlags.NonPublic | BindingFlags.Static); private static MethodInfo _miHookLuaCsPatchPostfix = typeof(LuaCsHook).GetMethod("HookLuaCsPatchPostfix", BindingFlags.NonPublic | BindingFlags.Static); private static MethodInfo _miHookLuaCsPatchRetPrefix = typeof(LuaCsHook).GetMethod("HookLuaCsPatchRetPrefix", BindingFlags.NonPublic | BindingFlags.Static); private static MethodInfo _miHookLuaCsPatchRetPostfix = typeof(LuaCsHook).GetMethod("HookLuaCsPatchRetPostfix", BindingFlags.NonPublic | BindingFlags.Static); - private static MethodInfo ResolveMethod(string where, string className, string methodName, string[] parameterNames) - { - var classType = LuaUserData.GetType(className); - - if (classType == null) - { - GameMain.LuaCs.HandleException(new Exception($"Tried to use {where} with an invalid class name '{className}'.")); - return null; - } - - MethodInfo methodInfo = null; - - if (parameterNames != null) - { - Type[] parameterTypes = parameterNames.Select(x => LuaUserData.GetType(x)).ToArray(); - methodInfo = classType.GetMethod(methodName, DefaultBindingFlags, null, parameterTypes, null); - } - else - { - methodInfo = classType.GetMethod(methodName, DefaultBindingFlags); - } - - if (methodInfo == null) - { - string parameterNamesStr = parameterNames == null ? "" : string.Join(", ", parameterNames); - GameMain.LuaCs.HandleException(new Exception($"Method '{methodName}' with parameters '{parameterNamesStr}' not found in class '{className}'")); - } - - return methodInfo; - } - - public void HookMethod(string identifier, MethodInfo method, LuaCsPatch patch, HookMethodType hookType = HookMethodType.Before, ACsMod owner = null) + public void HookMethod(string identifier, MethodInfo method, LuaCsCompatPatchFunc patch, HookMethodType hookType = HookMethodType.Before, ACsMod owner = null) { if (identifier == null || method == null || patch == null) { @@ -179,7 +149,7 @@ namespace Barotrauma } } - if (compatHookPrefixMethods.TryGetValue(funcAddr, out HashSet<(string, LuaCsPatch, ACsMod)> methodSet)) + if (compatHookPrefixMethods.TryGetValue(funcAddr, out HashSet<(string, LuaCsCompatPatchFunc, ACsMod)> methodSet)) { if (identifier != "") { @@ -190,7 +160,7 @@ namespace Barotrauma } else if (patch != null) { - compatHookPrefixMethods.Add(funcAddr, new HashSet<(string, LuaCsPatch, ACsMod)>() { (identifier, patch, owner) }); + compatHookPrefixMethods.Add(funcAddr, new HashSet<(string, LuaCsCompatPatchFunc, ACsMod)>() { (identifier, patch, owner) }); } } @@ -211,7 +181,7 @@ namespace Barotrauma } } - if (compatHookPostfixMethods.TryGetValue(funcAddr, out HashSet<(string, LuaCsPatch, ACsMod)> methodSet)) + if (compatHookPostfixMethods.TryGetValue(funcAddr, out HashSet<(string, LuaCsCompatPatchFunc, ACsMod)> methodSet)) { if (identifier != "") { @@ -222,25 +192,25 @@ namespace Barotrauma } else if (patch != null) { - compatHookPostfixMethods.Add(funcAddr, new HashSet<(string, LuaCsPatch, ACsMod)>() { (identifier, patch, owner) }); + compatHookPostfixMethods.Add(funcAddr, new HashSet<(string, LuaCsCompatPatchFunc, ACsMod)>() { (identifier, patch, owner) }); } - } - } - - protected void HookMethod(string identifier, string className, string methodName, string[] parameterNames, LuaCsPatch patch, HookMethodType hookMethodType = HookMethodType.Before) + protected void HookMethod(string identifier, string className, string methodName, string[] parameterNames, LuaCsCompatPatchFunc patch, HookMethodType hookMethodType = HookMethodType.Before) { - - MethodInfo methodInfo = ResolveMethod("HookMethod", className, methodName, parameterNames); + var methodInfo = ResolveMethod(className, methodName, parameterNames); if (methodInfo == null) return; + if (methodInfo.GetParameters().Any(x => x.ParameterType.IsByRef)) + { + throw new InvalidOperationException($"{nameof(HookMethod)} doesn't support ByRef parameters; use {nameof(Patch)} instead."); + } HookMethod(identifier, methodInfo, patch, hookMethodType); } - protected void HookMethod(string identifier, string className, string methodName, LuaCsPatch patch, HookMethodType hookMethodType = HookMethodType.Before) => + protected void HookMethod(string identifier, string className, string methodName, LuaCsCompatPatchFunc patch, HookMethodType hookMethodType = HookMethodType.Before) => HookMethod(identifier, className, methodName, null, patch, hookMethodType); - protected void HookMethod(string className, string methodName, LuaCsPatch patch, HookMethodType hookMethodType = HookMethodType.Before) => + protected void HookMethod(string className, string methodName, LuaCsCompatPatchFunc patch, HookMethodType hookMethodType = HookMethodType.Before) => HookMethod("", className, methodName, null, patch, hookMethodType); - protected void HookMethod(string className, string methodName, string[] parameterNames, LuaCsPatch patch, HookMethodType hookMethodType = HookMethodType.Before) => + protected void HookMethod(string className, string methodName, string[] parameterNames, LuaCsCompatPatchFunc patch, HookMethodType hookMethodType = HookMethodType.Before) => HookMethod("", className, methodName, parameterNames, patch, hookMethodType); @@ -248,7 +218,7 @@ namespace Barotrauma { var funcAddr = ((long)method.MethodHandle.GetFunctionPointer()); - Dictionary> methods; + Dictionary> methods; if (hookType == HookMethodType.Before) methods = compatHookPrefixMethods; else if (hookType == HookMethodType.After) methods = compatHookPostfixMethods; else throw null; @@ -257,7 +227,7 @@ namespace Barotrauma } protected void UnhookMethod(string identifier, string className, string methodName, string[] parameterNames, HookMethodType hookType = HookMethodType.Before) { - MethodInfo methodInfo = ResolveMethod("UnhookMathod", className, methodName, parameterNames); + var methodInfo = ResolveMethod(className, methodName, parameterNames); if (methodInfo == null) return; UnhookMethod(identifier, methodInfo, hookType); } diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsSetup.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsSetup.cs index 9df6d4931..e5ca284cd 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsSetup.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsSetup.cs @@ -12,6 +12,7 @@ using System.Runtime.CompilerServices; using System.Linq; using System.Reflection; using System.Threading; +using LuaCsCompatPatchFunc = Barotrauma.LuaCsPatch; [assembly: InternalsVisibleTo(Barotrauma.CsScriptBase.CsScriptAssembly, AllInternalsVisible = true)] [assembly: InternalsVisibleTo(Barotrauma.CsScriptBase.CsOneTimeScriptAssembly, AllInternalsVisible = true)] @@ -396,7 +397,8 @@ namespace Barotrauma UserData.RegisterType(); UserData.RegisterType(); UserData.RegisterType(); - UserData.RegisterType(); + UserData.RegisterType(); + UserData.RegisterType(); UserData.RegisterType(); UserData.RegisterType(); UserData.RegisterType(); From fb1005d255e8651a73e64b995432cabad0b18530 Mon Sep 17 00:00:00 2001 From: peelz Date: Wed, 3 Aug 2022 21:34:41 -0400 Subject: [PATCH 05/35] Clean up dependencies --- .gitmodules | 3 + .../BarotraumaClient/LinuxClient.csproj | 17 +--- Barotrauma/BarotraumaClient/MacClient.csproj | 16 +-- .../BarotraumaClient/WindowsClient.csproj | 17 +--- .../BarotraumaServer/LinuxServer.csproj | 17 +--- Barotrauma/BarotraumaServer/MacServer.csproj | 16 +-- .../BarotraumaServer/WindowsServer.csproj | 17 +--- .../BarotraumaShared/LuaCsDependencies.props | 8 ++ Libraries/0Harmony.dll | Bin 235008 -> 0 bytes Libraries/MoonSharp.Interpreter.dll | Bin 377344 -> 0 bytes Libraries/moonsharp | 1 + LinuxSolution.sln | 21 +++- MacSolution.sln | 21 +++- WindowsSolution.sln | 96 +++++++++++++++++- 14 files changed, 171 insertions(+), 79 deletions(-) create mode 100644 .gitmodules create mode 100644 Barotrauma/BarotraumaShared/LuaCsDependencies.props delete mode 100644 Libraries/0Harmony.dll delete mode 100644 Libraries/MoonSharp.Interpreter.dll create mode 160000 Libraries/moonsharp diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 000000000..dd5e75d54 --- /dev/null +++ b/.gitmodules @@ -0,0 +1,3 @@ +[submodule "Libraries/moonsharp"] + path = Libraries/moonsharp + url = https://github.com/evilfactory/moonsharp.git diff --git a/Barotrauma/BarotraumaClient/LinuxClient.csproj b/Barotrauma/BarotraumaClient/LinuxClient.csproj index 01106d746..e0ac1dffb 100644 --- a/Barotrauma/BarotraumaClient/LinuxClient.csproj +++ b/Barotrauma/BarotraumaClient/LinuxClient.csproj @@ -1,5 +1,10 @@ + + + + + WinExe netcoreapp3.1 @@ -138,20 +143,8 @@ - - - - - - - - ..\..\Libraries\0Harmony.dll - - - ..\..\Libraries\MoonSharp.Interpreter.dll - diff --git a/Barotrauma/BarotraumaClient/MacClient.csproj b/Barotrauma/BarotraumaClient/MacClient.csproj index 97be07204..30b6ad713 100644 --- a/Barotrauma/BarotraumaClient/MacClient.csproj +++ b/Barotrauma/BarotraumaClient/MacClient.csproj @@ -1,5 +1,10 @@ + + + + + WinExe netcoreapp3.1 @@ -130,11 +135,8 @@ - - - @@ -144,14 +146,6 @@ PreserveNewest - - - ..\..\Libraries\0Harmony.dll - - - ..\..\Libraries\MoonSharp.Interpreter.dll - - PreserveNewest diff --git a/Barotrauma/BarotraumaClient/WindowsClient.csproj b/Barotrauma/BarotraumaClient/WindowsClient.csproj index 2114560b0..200e77b7b 100644 --- a/Barotrauma/BarotraumaClient/WindowsClient.csproj +++ b/Barotrauma/BarotraumaClient/WindowsClient.csproj @@ -1,5 +1,10 @@  + + + + + WinExe netcoreapp3.1 @@ -137,20 +142,8 @@ - - - - - - - - ..\..\Libraries\0Harmony.dll - - - ..\..\Libraries\MoonSharp.Interpreter.dll - diff --git a/Barotrauma/BarotraumaServer/LinuxServer.csproj b/Barotrauma/BarotraumaServer/LinuxServer.csproj index 9d7662827..97aca5a84 100644 --- a/Barotrauma/BarotraumaServer/LinuxServer.csproj +++ b/Barotrauma/BarotraumaServer/LinuxServer.csproj @@ -1,5 +1,10 @@ + + + + + Exe netcoreapp3.1 @@ -86,19 +91,7 @@ - - - - - - - - ..\..\Libraries\0Harmony.dll - - - ..\..\Libraries\MoonSharp.Interpreter.dll - diff --git a/Barotrauma/BarotraumaServer/MacServer.csproj b/Barotrauma/BarotraumaServer/MacServer.csproj index 518422501..90d94a8cf 100644 --- a/Barotrauma/BarotraumaServer/MacServer.csproj +++ b/Barotrauma/BarotraumaServer/MacServer.csproj @@ -1,5 +1,10 @@ + + + + + Exe netcoreapp3.1 @@ -83,10 +88,7 @@ - - - @@ -96,14 +98,6 @@ PreserveNewest - - - ..\..\Libraries\0Harmony.dll - - - ..\..\Libraries\MoonSharp.Interpreter.dll - - diff --git a/Barotrauma/BarotraumaServer/WindowsServer.csproj b/Barotrauma/BarotraumaServer/WindowsServer.csproj index b4e15a59c..85c7e124e 100644 --- a/Barotrauma/BarotraumaServer/WindowsServer.csproj +++ b/Barotrauma/BarotraumaServer/WindowsServer.csproj @@ -1,5 +1,10 @@ + + + + + Exe netcoreapp3.1 @@ -85,19 +90,7 @@ - - - - - - - - ..\..\Libraries\0Harmony.dll - - - ..\..\Libraries\MoonSharp.Interpreter.dll - diff --git a/Barotrauma/BarotraumaShared/LuaCsDependencies.props b/Barotrauma/BarotraumaShared/LuaCsDependencies.props new file mode 100644 index 000000000..21439206a --- /dev/null +++ b/Barotrauma/BarotraumaShared/LuaCsDependencies.props @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/Libraries/0Harmony.dll b/Libraries/0Harmony.dll deleted file mode 100644 index d6f00001f7f5cf6efa1a2d20201ee4bc887bbd6d..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 235008 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

g~WRQH?cHZj25T- ztG1f9*eG30WrSuVWz@Q%Y|!pX%B-s08t@h&mny;-Tgm@Z1dp75>n^JTM@&|rA!%bA z&Pstp&BqL_>6Q#yJ(P)*i!;Fw+OmEx&`DFKRdv#`mj7AjhKi4oGnpHI95oY}pAYLq z+4iKFq-JlZIR3wg9jy4Is+ccLDdPF1vft6Bi<_cPOU-tjOCgV3Ux)sg|HvHicG12b zxQ=k6yr6`mYzCRFV$j_yPE^f+Y=;PRbm)I^eh+Dkaiz>CinA)l>lWt91v8-vlgd&P zt8i(|(MlcL=v+0K!_yhV+55JzBaWWkS|SOLs1e#hnLYHOH`+G|=)$n!@F4oqGPg zsvm1@_hWK5S?J515&Y%O7{#^DSbfpg#fL?DiOw z4n?idT0cBYaaGxvbS`F{i4^TZ2%QvKmc%;A>rW2p97#Gygmv1OpW&Z1QE2>?r9eKj zVg$uSucTf=rBte=G)9vitNF)ietJC< z(muiFE02dYeyC~0WT-8DSo81I{4Z&KT9y^k{<`LWOY>ub*p}I+`CrrgwB#zJ2h-KI z|0B(h$yl2|qWNFY{Ir}Zq~}%5|AywL#Zw`f_cZ^0&3{1iW5{I7zhnC=sjpRSGTvr0 zxf2U%r_L+nr%o&6r_L(mr%o#5r_L$lr%oy4r_Lzkr%ov3pRM_+0}Aof|AhS1&xHKc z!$^PS3FJQ1k2HWCqZ0A#f&q#@6;>RRQ_Y3R1o-?l;i!3qlem0bUd%+~xV^jRS?Vg2 zrvAJbbLOPpXyJZ>tvh%vhqG((2$#cGBA$tIym;`CbBLx$Ls~CVS}}ZNHpg|bxRj!` z$ZH@2BTFbtr>tNdGJaxn(j)zK;HhR|8ax9Y{M5ydzaE@OIq~r^pO+aaR~oyjdy?Ea zo$;xG4N608sUa!C!Uq6YMZ`raj_E3BpBe2o!=|&sUDz*Po7O8=7mbVMJc=p$3!LX0 zgY5DeY8tu20PTF&N6un%8sM1p2Jlt0Tq@`OhJc~$HzL2`&BRac`|<>I7+mO-c6H`w zb%8z^bX8Ekz0=TrV}l%B8do9rRn@6^u_#fuaY&yjy#Gy~>CZ+ns%VjOYf&Pl zPFP0)bf9CrsC=9!(-#x2*b5GLMZA!{I+G#2lQ@3$sQ7(K~{*ML!dGLW~il5xCj;GOQwb7d@W5p?;QS#E0 z3NV{CHr!x3@nFzON*~4Kzb|D%NhKt z!&`V!S>vg!QKCsfs7!8tHal>MGMOi;@nlu?RD8@~QZzrxVp?h^Qo|YNd+-z0W97}1 zR$~;1&AEC)WNJPgLYtEinK9ZIkr3%Bsu3*@S6^cyUQQy;BpRJtEjm0eQ=Oia5w8>< z=ZJI_-0HfKbmG8V?@)9K32u+eTZCeGi{YTHo8c!Zg6D_mgC0>i^%E@Yv5i_yu>x<< zrQRGO@*z9{x@og8Z+Sf&cn6{Q&h;n#dh%CUH~3=jLaO`=e-E1e7D!>iFGvuZ zI|5%pt2`dT-x55XbvUmz?RKPeiNEIYk5F?jJ0ZdT3&qM zLj(ml{0Ef(Dw@B+mP>pd!SZbIK895o<#a|OVCsf)I$loArMc+)JhZP$+hdBG)c42( zo$HP%ni!uE+VPhY>3tc>tKqd#gDEr)v7Hvc2#38ac@+byn zKVWn8Ol8p=slcEK@8b60=6Z;4x=oNYa?*)%5}4f%FXH5M8D7?eHb3JQ6%*bO@81dK zp*_Kt+6v4YfU*1MN6K%Fs0vySYWlk)Vop{RPap8UXP2*96uqd$y>~3aqAKXYLCQsi z_xtj0-$R&qmB-ohR_FNy<=Dfbv$Xh6mVKTxV+8yfBwL ztLBm*)pdlpJ(NeT6Ew#C$dPO_(~J4D9L~0^_|Qh*=BN{XFG`2L2j0A(2zGOE3&_o{ zaw9g^ex_FG7v>TZp?X~s#GL1)PiDxJji?_iBJ150K9mXL40^BNlBQV~pSdMtqtA%a ziv@U5)GzI#TG<~dqtNxqQim&F>WU5g**;W7bXjMiHtgTl_KCl(a23=0bNcl^M}tJX zb0XkciGg;y`gZQ!6)Cv-&RK8LJZ@`*R-VGGs^V)nxiMw9_N_|tLx0%{>P9+ixw%&M zD`i*?%pcpL%UVi>auv?aH>6~Vow;uTP5w+N-e*6G0+h4BXa6ohhp4%|?@-S{qFB%H z9sNXB+%%V3yb z_`FJnfRy3=bC_eGOnBQ+O}B-ENR*~8P5w$zDa4W41%|`>nXZqVq5XWA>vh$hBXL!w2VO@RWKQ|Z8+<2LWy=0r&k3fws7b=7{-NH& zZ4?TnHsW*tcz?XXGx~UNMle_XLQNy~#3x(wYR0$2sJf*|Yie<& ztRlxns3~)QNdazVIxv!HwX%8OgIlnE zSb}2JwTAfAk50lbhKm>K@nTIo;;@2HM83KunW5LqsZj4 zm6RWvFk4U6)RBz6yUB@g5_~oS#t9MPGUNx74gVMU$*htenU6@fOc~F}Ztv}vP8z$J z_#8=#*F~X^`(M(=;i-rgt5-=R>I8kN#h)bL`j>f#;*%xaW!SZ#(iws>VglxWNk>n> ztr7@Vx+)|m5Frz&Qmfc3s{S;vMf>bZf9bP@j!>6u%vQF`<=Eh8qInyUWwG z_%+F|KDk~?*Y)cRw6LfJb?SGuOFwx+9&uIt@P>Y{QTCSC8bE?0SmrmHT;e_NL8 z{Y%EDa$y22q3i>dmwHn{I9w`Bwa_8u7;YfMw+fnUDtP-$}bR5 z=Oj;{mM`E!H`*0u5CeI%K&tOtFWR)u^_-UC?mlHQ=SW43o!b)!3#fIL4}bHqULP z7R89;I4I*)w7INGVUFA2UeEzY|Z1j?jB6jMxFEE)}m&< z%;@s!lz~((U24qnW}%X*LOz8;wvNSzJ7t|q_FM{zknV}0me@}%K`-_?71O+t5Eusv zbSDB8C@0I{N1w@mw7#GIb7O&-5^4;t(Q_VB%M)?vGJ=#b98Ix~hHN2-I5ht|62Ev4 zg#M12vhKMeJPeTKuniT(njYE2F*%W*vVF)PeXlhh;pNoi@F$X=S_ys+`KcA)PbNRL z6?YKW#S`u*INb?c3LMUJs4;aYP0*VBxamtYgS8ON45q{7T`k$J@JE<0kimRPNQS#P zAIc$P*RexFeX$&WE=IbaPXI}#hPSEhmASb4Aw;$#POL#WQzp7 ztr4Y~3m=^8MTW5rnddW-sz>7)DVlXzzB({{5WC-^JBd;s0pYJ4B>%(>JQ?Ka~_N%3_in!UJPJq78c8tVP}z5*{Ev9Xe^b1cqb6g zmF&F?$zX=d<(-5~VXO}?ZilD{t(m!G?qe(Y+(!bLBlyZl9zo^02>xE3%ioZt#p;vzAFJ_v;F z+t~PHl?{Q0%Juf!FsvDi>X^A^Jo@tvPnngEptL4*7*y(s{B1@Q9k{5I&ra;G{o#ld6`6wE> zz>jvt7&h5?+fhm8!YC@R2n|(`npM#Po^#euR14=J znpsI9^riD0I@62j5s2)434~o5&J%hbfjcMaf-)1tMTP5tMtUAPMVmft#XRN53}=z^ zWBKo;BIcE0Pli=VIj~i4b@7p*6)iz(`THT9KMJAVD<`|V%MUA#2~5T1DZfLHG3MRW z1fh_KSS^Ish0^Gg;_$U<^0(c-qHC4O7@7ZAE6@+qq;i~zMMYL1t}3*((xwxa|8JBf z7azC7PB~mjshDi=?Jk<*20BfB2K`*>w?%_xM;({DqH9PdCeBENIYb#6vDPZcT3VPm3!|;;+Qz5JgzBDp zE{*0xz_=fW`SUfr6uQE1}4Hz?dJy3PB3NENIexM`!z8d$~Z`i(jLbi&1|9}dV_RUs@h9ePJT`&A3 zr{V>gGB}RO=6aFAb%dy9aM&OaGB^sc4URNOy=-uiWN=}vWN_RSpb5gPG&sf=8eCXN zHZf)R0)vC9KAbl>{czqkxN7CZM``AELDl&N$&1Om4+e?RVUn!iXzsE3HDQk7!M9pc z%X>pncC;@c5bRv_Rvu{3r7A)5?k*9wA-e#o!2jKbZ0SqbP&kb0OScofF8|erxDR{< zHu5doNMDqwij7=@f`kSi{SfHGOf(CTIe3!GFchYt5#8HEMnu6dqRa4;>|$FBhh1cAd1csqVpR@ALG8Y4iHk?K@GqF<7tHvJm1bhi_s7dFZRJ zQ^;=1ijdoK%xQ$YlPC*MyB5sFol?D053(e?!A;15DZS8*krvXXzEQ5TKOy9Zq!5F*!dj|8(ROk6uy%0~`R$&<>Ls zmtk~I*@5!XpS#r~a9yoX!@?DO0EC-eZtzyCh~@fohilOnba8{Y*)5vw3R0W6*)1CD z3jPOZ)pWP|E$ZhAKBO4=ThvC%NQraSD=?k1#;9NKtMK{iNO?EMx-b+ii06w_WM83c zJoLNF3{FLLN-Lo}z{m+xnY@$+4hPpHC@&XD_j<^s+lvt!z52xLnEvWBR2I6Xafjx2JZ6w0xOhTazV5Mn zURPix_1bj1%*Dz1%kWr~l7|UcN+-CAp5465u5q=Yq zm)bX8huY2bn8ACXVL=)IEwYkO-rcJ6yV4x2S*1A>`U=BfR2qE?EQlEgoGcX>4UAdc z$xPSeEwCFiYnj2j5v=ZzDsDROjFn$(;CFppkq`Qlx_lXE3yS5(EMN@$lqv5xl%;GQ z+3CNA2J{{vk47t2C1#bvRYLR2b&w8~*%c~gHTii2`7T%%54Tv?G}?;pj^YlEI}uE$hCiS2SztCQ3EPU;z=qJg zHm-^?c@5%@XH{aL9?muT@rTh#Q-6NR--||&G?Flb4^genC#C2%{_m*JwK~*=?(Sje zD>Z|U;HM1zZuq+E9qKY4r4;FUMOShq6=O39+aB?>xd6EdT#d5x7>hb!+#46la+gzt zZF+>z(3u8jhq6NT8?Mvr2Ds;RM4&Qpf_PdBL8=xbJJ03A$1ei3$T7Pxumx$m_>x8z zucH((C=6#2HB2{Os_9Grs+$FUbLtJ)#?P>gif0i6yQL$sU&4>n)mw+-F<9yyDHnKK z!FwTN2A@Y-p*!O$BwmIWAvEnze6;rf?u=IOK9=08Nv_r;i4RF`WXTsa$u*iJ@gd3k zS@K0qa;+vwyp&XXIig_ja^3R^Gx#h*Wo<|!;z?ycYA42jIW^q>XCidJpuY1o>Kym5 zE0DM!Mix`?GM5X})-S_$uWE zZv-(3OcTV-p^6VqFoUllL>`C`hhX{i5q@WW0HNxg`5TnO3*MPi@CEP8??!1Z`Oe%F zf201eVg>geil^S0)B78|Ge3bJyvySBf^Q-i--A7h;s>gC!_3GJ$Qq{M-qQHgFpq=V z5a7oxF{b=o_~_*_Dax6S6WJ)F0Ya)p@n*^eIk>x+oPvB&mxFiYq}P8n>Jn3nT_B68 zMZ8Opr$4G-d3az{`Rk-VmDS@tc5Wysl@M-SF}W-7aX3^0P8XrX<@@V9saEKH9PM9m zqpUywEMHI0u?tPYK0H5hj#Hrs<`AUhw71g?ac`iVRYLq~*QIe>nlS9VfUt*v;NY9#dhr?0KB1lBDnJE3^7kywu)U*;F$cLx4Hg8#6Y zL-;}UapDz~xR2qeIYi)7hRP4ahlZlS)R>RaV}wPii4WmomUqlR9axh3D7Y3iO+`IE zJCpH?h$ufh4rJ0#gGU=M3_-rGbM=8ZN@}}vf3V@AJOlc$kp&HR` z;WKWJ zQc(E&KA0SlACY1{)ED9o%^lgjc%d(cIn~s|)%4UVPgm$q=`M~%m&mt#sscrEbt=?p z(ptIiA;@c^tFDzBgN0fr5fr5TE|&y2|Gu-Fj1+{d9keRFyd z0&j%GAAG%>g_L}D%g0BhZ~+m)CltB(T{!IK9GKiFxjYcZXfd|j5&r8bGZB#y{&jdz zQ8a(#O0JQjEy8Z^X}o-$zZpFcoDwP#%k!9hLV3hw_IV${%qk z|Di+qj~vP$btr$#q5Q`V<&Qg*$84&j_C4WH9&@dZ^nd11{&R=&UpSQi(xLoU4&}dg zDF2N^`EMP{f9FvCdx!ET9m=0_D35nzj_mD6hw?u;l*e4Pqx_t9D35tXNBS{$=13m% zD30Xu-0Dak&z+9s@x16r{x65}e>;>v<52z|hw^6~%Aa#6f8L?Iz)2`hp2rP`@}@(1 zmqU5CL-`1Y@|HvSNQd%K4&|dA%EvgAk98}mP7e;hw`-@%GYrypW#rxu0#2H4(01RlyBfrzM(_; zMh@jO9m;1plyB@%-s?~vYk3^qud^M>=Qx!2Ih4d9%6D`q-^rnTXNU4# z9LjffDBsPYe0PWPJsisSbSU4;p?q(L@_ii2_jM@W&!PNf4(0nhlpo+wexO77K@Q~y zJCq;dP`<>W{7{GT!yL+&I+P#oP=16%`H>FgM>&)q?NEMKXaY&Wm$X`FXjNgpG(qh+|tFeQ)Gw-@~eDY`93 z`v9@FkNSNuH&{kZ!CZ8T#UFdLR?~18__y;v~xroLIY+BR6JZ)e9CRQ?Bg-J&3b%uy`ZJ zldn#i!y@1tP)lZTC0w`vM!51kt-Lbq!iuTH2CMLj>eh7b9M(Ri%l&|PWv@`p(zjOa z1hBD^&y5Pm9YONLp|$YgP3C>=mBcsTj6s&5^Lv$q8KeVzL*iAEzuOGbLA^+x&giw}>5$%#JZC_3 z9^4yKEe6|PHMk3l^YZhG@{0;u5fve3!QG9QMGY&2XoQzQI~NMkaPa)V^f^<@Na6@C z{Epqgl?)bXZyO>WT*DqEy}IJp3%qkIaOYY62_%d^6I+0WeX=npIs@QuqlgY1q}1_# zUfcr@awu?muy_>zlfWUDq8B(6o8?Fr@BV3DsQ3!tH2y<{z7l;}7g{_Jj z_%B3vOv%unC0=OTX0#>lcF}crx$u&f3y8)9e@j;uh3oS8qX3HLlqV?c@ zvlGQSxR&VGs$~yLeAb^d^co-RlPG#Jf5!mw``BO6mSj$}Cim!Ua=UaP_hHCb;yZSq zWDT+HTlVmXt$oi;tR-9{`pwP|H|A3+e@_VZX&^rHOz>JFx;4cbl}H*gdXl@P4b{L+ z9`esgoZxLBnkSKD-NLJp^UMV#Zwx1YmqL;mhuV8oygZ!h>Rr~E0Qpy#$r2BvCOzWQ z;V*WpCB}54)F&dXT4DmqQcFBn(r$i*`tBTfqVn;5bB}KpfdW|wL}u@ zKMNk;BxysM3ooNsqfzIUST&v8pTHN2XA*0ou4eZk|D@jJ|BKshRBy^#T+ee8Em1PS zgSf}$dHOUL_jf9o41UD9iP>Ut8!G!RP09VK<{%7tKilWer9$t^V(qI$6eO4M~2uo zn%XNLxot1r96>n@axK*xLOSE;Q>)DxMt%?EJB!@HYJ)7{Ln@Z&%`JG#ycfH9#Hb!G zcFPbGy7!x%Ep9C(3wd%r+euIUCYXkI&vYWwFw#5C$b?%iyAUf7;Td|YyF;-g|qAF}W#VQ-dj^(J>uKhnRg zDY;*AZkP8W|J*_3uHw{Ja_#NqntT(b^9ZviS!2ZB5;W=*olzx<}gnV*$Ay?hS7e1=hZ;*#a#f_Y|^PHc9Q|bC$jT%T4In(>i_J|sj z&rPf$zL-m?eAAioa1bRzn-;h1ktdRywis!NO^DlEoDURCZZ3WwNbX&I$TeCQz+#^n zYxLQ&?Z|0Hd1}!j3AG zp>C24!~P_o{hbK)6WHwobnP%g1Mw{>%1h!TLc{3G*a*9_j8Lh-J_;bg>5UWzxXw;D z%g3QHwND@psyM2K&~n5FGgQ=ljv_o-C+e3c81YPcxxr{`uZc#*LyCOI9GCC1SPcVvzML z;r81g`f}JyC|`-#AW95s+agZy9x;ivG#Nl)_ljwZs-fmf#3pei$2-inyG1NzG=2<) zJuX%=dYr>{iM53-uAht7FoljwJi#U_& zdNo?*5YTBxLs-jU@ux{zwsE|VMQazKuNCUd=p9C%im{9~C^Q8~h*-ArPsLOYdx68g z5@jy3!>pc4_I6unIe&fs~(6#+E9rNCA5BajPu zc@<0v_HawI1Y_3{?5y0%VR-ZaIwN+tT=nC{7%!po;t38Lj8Sh1EaF*4-bg}`#`BDP zoL)6!A0wGUE#n{~nL>tfNYh#0ct=Bxj1M%)OyfgFcq<~B8Ck|rmkXs`G*pOr!b=Ec zc#ZSvHI8Xoni`*JVMWH*j3VPGpUsSKHOUsn_Z)Up1ch}r{$TVFqb|l@jPQ99g!M4~ zVYH1)i}!-=aJ>vPTyDyXtd}81q&ounyo~Ft#E8};M;fsj8e_z{sYKBzQ3T`k7 zoKem|LRSD?i?9}LNXsH1D&L?)Lghva)+udbvC)>%<9J{x5lfBsjPB)Hy4L8TrMJxJ zrlI9VPYta!`f^Z8omZ&^BX{hISjvINoiX>t~IXTG&g*Iu4We@~Uw=qi677i{8e#gOM!ld&VY4 zqww%pB0ex4bfbe2_W+fPw)Q^s42WZ8c(-ewfw7W>h7ml0N!BWI_L!!jQCD8CvXGCIjN@tbj!(LRp% zyYVrjpalXGWvzN%RY&-xd0eQKR}Y><>oURlL6#^-+>% z7=5A8Sw@K}-g!n(sdz>N+2;@?>0)$Bp$JClDqbX`=?!J-(TwUV6w4@}P<2K*3dJ!> zRVaZ`u|oJV2~uCHY9fVEtV%tV(Ss_VH5qkMVd;!MQm782D3#B;jJ{B)KBGSrYRD+2 zfhI`OIO|TxrQ=l&>V4GOAEv1&ppxVMUCL#xlKTjQ&wp(1Ouv zg<3IMtZJeSqg;jBG3ueTbYQeo<*XBbVG*qE(jJ7D$gHbz$dNF!hp+1bdD%6kB z3kvmTv|6EojOf!0WY>ck-L6mxqiYo!#^`>9hBJy+Wf;lmW_5LpX7nG0#xk;$oIu<49usoI^%Xp};;89k(spV1_R<}uo>P#L34)eZ|7y{;_s zaz-9ygI6;0D->k3O`&o|?UjXG&FCqG7Bgz8>}4sVcNMyp(K01@9ivJWc0HpfRoQP~ z^nyY+GJ0R-b2XzYRbJLI>Zx?DW7JvcyoJ#k6?QA56)Nm@MmZ{lI~dha=q^ULt6I8; zQKf2!`xw2d>hpd^cPq&U7~Q7A{=;aB()lo>WToX%M)YA{vbrsd7AY;;7&TO3k1@Je z>3p0~qLO@q(Ksc!i;=176$dq(dnbc)e0 z3jN3^N#*)yMlY$bUl@I^&~J=fO7agz4Hf!}QISGt81+-=ETdag+0Qe|RmiaD4)mdt zbTNuhC5m8lQe8)pjB=HQL^E2WP%NYU3RPz`R#`zDqeBWMFdD9GB8kz@3Z*dGpd?cn z-K@fDGI~ShES=FA6;_AQA1a@98Qr1sQlHTbrL!TUNh)3@qX$%2V@CI=uqKR-sPu9e z-LKjqm(imNHD$C*)n@^tPgGut7}ZmGX~t-&LM<3QrBEwIgH&GHFnUR$c8vO}oONLI zxI%~N^l!ssrQ>u!u*R5|Oxh)!grtGE}Vw^izW7`0TWAEO9m<^36bsnQ$B=v5Uq zm=PUXPC82%RjL$*F^X1U!x^nqVIvv+q4GJJ(R>v)meGAG*W(#&Q<4)IjZ>*lW^_#D zY$~JoRM>PzjaAr8M#&1zW@ISjXY_-r>v@cF6)IzNUe(J2MpG2JoY5C5XIC=vt6T>e z#VSJ-O4CU>85Q!=b*%XUOZH+^-F!)l7iaFb=!yNRB6>zoGGEuiQV1bT z&PaI7gN!=zb5)voh|xq&FWr2Hk$hImFyCWz9C^j8t@#0?w@g9}%_9~T1kl70(#tYG zvg(U=MR8dPV#=_s^#@jdJ|QWqK>RQvEvvqmIx-XP(J?LHMvv+RcOf%BFjEtZOEexb z9&VFsJ0?$t%(t_qy6THvQ~l7hYxEUhKJRud+&iYMfIFenRM)`D<;9IN23NM| zy|sRU=-lQONZv8=zN{kAgPGFlTcGpRSzGJ7#LoPNhD$8*?Ev%e9CH7d_ypXWCq9o@ zkIkU8z8mx!G*gQ8MS~GRirxX=aK>S{N$j2QuLr=i%G3^Vu z)u(<7x5boS;I19}PnIF>9cwi%5dEfJL3J{^8vN(s4y=sMXq+*y@|QZ~4rPBIb{nwU zj$I$S>*|tBIb2!x`r^l~N8;*>KKbN6IDzuKhh3Tf<0*%kk4^-;g22I2=0VdO%S@FsBwlN=8Y+AY=~B4 z+QN;SL;m*j3L&$ztUGj4izJBIqx&{a5<6#3btQ@Tfm2;Y;^y`x(D3erQn>pkOh9OF z@CBl{>yE4>@jWC9L|^zV>sZ6B^@mnI)pbVWp_M-lBiAY=_lXhYc47DB;WHYCCFg+u zssp(<%p-TqusL?<*=_+azA|#hv%9Q>{GCe4eX3Kymi(&gLNL?kF0`4n?m;lca4oI} zL)?^2HE<+nYkfm(hu>n0ve=?5wk5nZf-Qye_H~y^XxPW@8Fmx964QX)_rX}=%@*Va zTQsR|hzjWRh}-K-MaMuEZitPrTMHJL4jBtFmRN>Xu*BJB*MfO*+~F)s3|&Yv8(OUb z^V?8z`?k0V?u1Tv!hN$fX`7ov?)B{U%h`lh+mrJ!+=84PxKf@S_824|oX{Qa9(H$4 zCI9ki-5Y0#zhFOEVs}a7j4Vzoi_^;Dw6eIx7mL?()1mFDUQ{!e!;9lny0h7wuJnB zN*gsU65EID$QoMdnn%_$9yuIZIhozD8!lbJ4kb zQdV7xv%8Ajd%En%@`wqY$r5G^ zN~`A)_jKD@KLLGX5j0P2J-=Rom|D;V{z0wiS`NG^-^s(^5wK3qR$O{7CeE%|)L& z0i;;VeL38u_@!`TQ^?H_zb1Obrs}KUzuovX5i2`7-UdC#2I5K=T_@ZJ|Gp-~jGsvi zT|0(o*M_Vp1LZP=uL=3@n6e!*>*1o$fkol{EnGus=m0&2h?qicA>bNfPzt#(rc&I8 z)1E-ww}w(E_2+ao9f`A)PN_M}Q(2xxZabSV!X4~-!%NIzXt;ImF}U9i`h@&F-hrOZ zC11c#{ih|SOneQ@KHN<#aWM58@K@A24L2kGG;8<=+Fq;ShFcb4H1UY5;$oU$70J|O zxKk$9g*&^9uJH*;4VzGkUdW6|&4s(k)dKEoHOTE=iI#KQK5h;%2U96j=I87bVr1ydLh@5w%G*q& z(%6^_*Ht43d9oiD--%pe?3}k!UFb>n!QI2|uBqf-K5btr#TD^nCqKtw5<0>cPs}pt z5fL}Q?HzYB-20O6g8N*=LvVM;J=UZl`kA(k8=}u4_wPh=>HMncz zKLAfX2Ic>A_z$Lj4R_uc>c_TmpLsm~299+a@*?67xV_`dY)Y$b62>h_)v|Fv^^p5u z8o3uU$7)r}E)rb};<72{=V9CJBFVzr#CwuFq9~{?gP=}J{#YI`4KGh81g?jFuxwwEv?f8%a}iXZyW^(Ae>9T(YvZRw&!1hXO*agotEpeB9efqfhsm81D6#nNTPQcwANA6R}CT1hjJ2|gKH-}9_}1BwbZXs>F~cG(joI^=g|E$+~?%xg!-JfdXOz0 zfQymaK=NM$m-?c~xU$y6HN>!_Lg=AB-xBltv_;%d-xb#L*F0*kci>(emvY!0`Kjxo zx)Kr8Pd*pX2h6Wgqd1?l;chiAhr8Xqgm~jdxL=y&URi@mR41+l>_qnA(v2N>D{^(Q zfA!Qm!S|R(V}^{0NpPiKzYDBsy$WSn)0%XyX-)RLru9a|I)wW*R#eUCfY4#w0|(|( zxxO#mf>65CSz=PRJ@8LRdVy@lKC6j=Jz=d zw+>$g^ZRr`uB5-0VxoN)GEU?N;OSb3WogZ^k+%~$rAj217quB-7K0JQmFBA`tUyLYT7c_lb zhr$*W6kyd&2wh&#!xu)g3kLc;Vt#!JT`vv~8SYCJX_#jqbb81{AbK+*QNWib4k`5J zu*JTbq9vZnDa9Ya(8>(J^0+ctfH5?%(+G z#9>Bj#nL9f`U=D;h01`6gd0!gq{TDmtgo5)hS6HFeP%>%3sLH&FhY%T+lZ5lmWs#w z_s#7nCN-h3rQ+)W!*jceV+tMUKP9)Pm_`RiDVyjgo@KN)t?h_2vHip;MoZB~H>LCw z|41Y*Z{IumGU3gUI!}(@8+)0UqR=hr)2j6s_bRk5Dc|ZZqI^>Fm9!I4{l#8|zAs#u z+h4@z%CKPJRY2nyEfytBBJeGNoiYrmSL6;5HS(n7%qA;x2Z`E@)`=;7*W?ZsqZGPw zz&*J`#qNBPTqn{dJeNCC+*=?~_bG>S$B2E5)`}HFf6JX9zENSdrku&0D6T1#l69sf z=S{&%MnX$P#2jzl3{i<0398*TQ(NWD65mL~wKQ9NQcPj{#et5WVGY$+jN~;oTl~O? zYOr(OY?0iIB$tY@Q+nt5MQw#X?=uvrxkC9<^R0QJ3!`9rSZkvXj1A%i8#Rr*Q~aY)e)ms|yG7sjQp?fKog?oPn{6~O z@&S?Gfx_gq@`%Xq$VjYhetq5}Vn8QC>qORoRY3PD^u@?q^Bxr^74i(&2sEp+l>DIU z*1WA^r$X)KJejvmT+@ZZ$a;q6Z5O*G5~uq-pSMFqb>%QIxkJ9SL+oTEZE%OUvm1r2 z70tWtgX9TD>qM~48+kiLX?F@+hm|vj^PUtZ6!O;iEN_<>(nE&zNd69JuRgB~W-7S*)NXg9H5D$uKgH^!5y(WQVde)$gFtr ziWsL*X32`&SH&EKmUigY^fhq}qjk9cMm2pyWL!pC2+e8wmgvcdQmAP9ws^09IE8n` z#|lvj?}={|x^J=}-WLM~NXh)M%bUJ0mNQx`>WneO2cqvl3R^5rO(C>qkd&+#y{730 z;=Lh+){1A^Z*BUK7*axLu~_Ka)%2LiA1P7KzRx!OSQLyRgr2?N<))vAu8bC=Cwsl= z2~n!hgG1kG`l*Nvs%Frw0a-Sl%YcPxc175(Pa z$p234o=j++*zT*7|Gk(sO`<7dz4A z(Q_7|#p3ay?ec#W%V$e;d2W{YU3lk6G-GJj{NKf)D+sL>d%Moa|3}oL$!5gcFkx=~ zSuswbZ4)Z<&x^eZT{W#T-!Q6OB_&Um8Cco5CMc0He^tKA*t3X`v>vySU(QJMm~ltG z+qglY-`j1@Lo1tKkHXWmdyXk4Yx=z%vBG&k;K zBum@c*u+SdwzaWaBC)o^qQurl?UhnXMA^*+ZH#G*){3)(?z+8b{$k}0$|>aWqmimRZuHmWDu8v|^#xuAnF*G5kibTZc1Xiq^G zV~>sY7j!qiw9&f-y^WZ)q4bUx^flVp=&OSM#&jF~S}@qyV51Z<)YxjHvjxM9gEopN z9BzDPqxiy6M%+!I6jBSv7+xFIDI9C`vr$&zcw>%@3JWJ0OKjAtaI$fejk*+0HC|=3 zSQN}?RXD>Ky)KkOJu%CeVx!FkvyHhndZNH@TxFv@1@nw+ZM45&zHyU{`V?MaJZhsM zg;yE-Y&5R0-1yW+{=&t^85^aDYYg|zp}d?exYnp)qlm)mjG8vOvT%h_Xrsl2D~(<@ zT3&dgF~LSR6|OcG*{HDaCgWNgwJKa^thUiTg|`?F*~k#R?d{&`=e& zI*@N|Fvh5`+XE3|gE3Ww6&HUd?lAl+tYdKs&=oQa{Zfj!(^z7o2&{5lX`_{{yN!o! zw7KwJV?QJ6&+z8K_`(hwnzzx2UmvQM9fg~WGKnyJMNb+#8Od?oZo_?Ns0+3J;urR6&*4ydoK zNMLjg&>KY`85wri2Svw>wv6cR`*qPL#;c5$iT9@eRrIOxk3wUo#1wyOjM_*#mx*<= z@~uyeNeX?0tK(DSW{KE#KQ%TnS}aE9t;qeaXE zq)=hM+ltQ^n-%KW?{1(c6#97T1I7Os&nk3s>Qum9IL4hDpc5Vckx-{2qRfb z=ZwA&Q5n{WpPD{fe9qXz=u@MAoAFrxFzR6ngJlhSrPwg}`+sU&V4A*1WW3oO4i%f` zT!k+0aHQB}?o_B~=!s&td5)1BK}4Eqk5as4QQbObiAeJZqoo)Ne_b4D{v^Xhjk2GL zqs_XTSyH5xrC?W15u>GI8zke+o+_*pBoocc73vGgH1h@}dHd9_i))(qs<6zGpNiAX zw-g#!l7hY6pDW3$rhQ%9!2DB%)hYd{xRKd-i%dPcG(}{aZ58^W(~8_2^8}-{VrG-G z#Xhs|R+3yRx_7AFtf{%4ksLi0n1>{ax-++xQD9En#yX=m=dQ>tFn3A>E3etC(Co0C z!+;t$Yh})2MAv41v$p0j87A`c+BfTJ&UlO@_lpN6=EQU}S2Oy`SlGXNv+m~oD$Jjs zZ}l*@Gg>V2lKV94VIE^dIUCfhr#awpPESdqW80)Zq12tyzV1VRR;40~tT z8AKqFDL^0~1PBsHPy`f6Kv6(I5Tl?#KtVwPK|w(Y2`C;=!JwibLqO#S4jfQX(EHX? zl}>v-p8LPw`qui^x7Kkjf9rkfd8@j*y1Tl%H`!aQgIeTTJK;>Pq3Zg#c_#1mWl|m9 zX;im*6{vlLqVq+I8>Ob1$}?`XnkQ8Xd4=jIQ+dXXQA?!ikgvs!Rqr>IXIzoGNUD&b zTHH8wxv4zk#;fb0o>KUHeXn|6dY-9?wtLmiHRze*ml_avulkXxJmV&+jzioN_kdz` zi&T9ILgFT=yQSiD`4qKlt;qavSae*8T41VJQdgz=W_-W6sVe>ilG^-c{9SR=RR20r z@4kBrfOf@~OT;#a8yrJ6f)eEd`DDpO63e_A~w)wv-__UJ9~ z`_;Rp3W(noe?YB~s(jF!@o%f=rCKz|*Zm!};3H8le|Sy&L3N8%dq~x)0VjmFe&X@? zBkJQ){WRiy{CnySsSc028h=zhB~|{2Tk-F!ze=^Tmyz%fHS(lrWmmp;!ZCG@RNM2L zCw!oOCe_5~4hbKru^)@f9m8W2PO8hLdS+%?LcMxLs(pp`Bz&TV)r(BeiK7xeRd+}g zTs$%1wA%fY@PdnHCVZ}5GS$3PSL^>Nwr#ok+N>ZZXw`gs;@@p9{}c{9VE~s>4)2C0tQAnMz6gRy}4a*Tk!8 z_b(dj`6XUcE2Y}dJ1p@#_3W3z+t9m9;*aVNQeE%eGx3I6cush0^Lr=WRO_Vrn&_o@d?ojx^Am~QT4$+7b$=?cnYIH;%)&m}eP1KfbyDQ|L?3OYRPPUb zfz+c?b+B(E^^8>CcoA(gk^N2H8z z#MY-a|AbUasiwC)L#m5ZXFM;G>MK<%zi&w0CDnt?zav#B)k7_BkeV*lxzJxpRZ8`6 zsM6aZk0-^Kq}wXnWUyl^-b%$NX?TfIXI8hN~zj} z3@5cos_~(Pq+XY*RnWbp-jixr@Dx&?OLc$HbW&HPdOi37Qol=eZs0?teEuo+=;Xjk zQthPLJMb}5u~OO6my=4D>O|^Oq=rh>e9BrX zo86>#Nwq2TO;UAIWw$v<>JzD62|YsU8>#Z!93%CsRK9IalJfjk?8DEYpOFfcDzwd) zr06v~{r8kBYjcTIs#IOu{F79kR4oVnK^D>H(>|lWvoGOsd-h)jmGjTB%NU zcO~_TR6kGgCUsD%ikJXWC#5PaX+`RiRGKr4)K5}f%j`(X^{VLSKxZVWR#N?v*@IN1 zR1=&Dr20$MDz!hUyQT8-a*!G;Rf$&?sWPcH``=A!kyJnX-$UwYsX7D|ka|g~x}dS7 z_DeM`cp|9}qYq z^_f&J1qYM*r&N1`+L5{~)z875NqK)S_UQYdXi{NP`G>@k>LJzDX}w7~r1~{AnbbW} z{g9eYYNAv>4$mR=pj55wgGeor>Z6H6Nj)c3S?MTJJES_EIgZpJspij^M5yvshIs4Qsbm* zpShmYOsSliFOYgvs{1mxk$Og|)tS3UZIdc4b1$j4r7FmLo76{AEy%1Rby2Dw`J45=|TR9~r1IKLrvmsD!!??@F&74LO})O4vH z^ZJ!krBomKEB)yHCsjazL28Rs1A;tBy(!g&AU{&aq*@l-lGK+{9SI2~^@CJvgThIv z*X2G0cOexZ)xo6hq&iCVaY!7g1gX-z`jX0$>a2e%sRF4+24#|(D%INHyGYHG>ZPDO zQY)pp5OrrENcsFr z?9n^^m89B9#R4896)ROz&~j4gQk@8Tiquf49tvJdYLZl1$VO6gr0Sfuh14>s9_#Z8 zsr8Mjd^f3GQXO`_Nvck&b^!-TeInJWz?-q&#nkeHhc`Gg6^a z1tfn-iatQ5|DJLUab6;oDpgqWKS||D73ute)V)%08%HVn%1TjsY_Dj28WUQNh-hKj-*^~iheF=9Z9N{ROedt zAQdUq?v@Fp`b*{3qCcs-rJC;VAT?I1eD{CjNyP@;L+WX%{+Log>LsZP zoMTDtmugnYL{c9}HQhOt)Oo2!3@9UYU8?Z|9wfzn68qqvGLKYqslMy9h*W2(x}+>2 z)my4}JFg^_Bh|4^Pm>xY)mNRLBQ;H`)15bwS|C-_ke5iUlIo3!9i(26iuHV*)Ly9) z)Ay5lUn+0=AyQ|g`Y7T(Qr}56)$su-`dS~HFAq5CN%=`Nd-~_3!lk;Cbe>e4R6`TL zB9$rCsPe0%hC@{-p;NAtnghk({`}hCPrGd@*Q7wLQ^Qf=)Xl@y}+-4HLWO()Nr9_ zk8HGsd8Qgj-Zw(gXJ>^;ZM3*MqRo%#UnRBCoI)vACf!TkD5)0HrypV3y;8lIRgx5@ z%`m+ONbQiyEv7Q5t!DcjZ7QD@S0=UBf~5MUXhl*7Ek~*iiJOu-YKx@u?Xokev$jL3 zs+7G+5!xB3`AStvZBkb)_7BnKuIRl<(OR-p-Ryglx@$R7we5X8sfYHkR6}ykCdF#2 zq`JS`*`!|Dn^J{N`6?++tCwndx37{CwA)hUbo(Kxx7L~dg)RTAC%)UQq(0hRQr(rN zB=^tkxLzA7_JgJ`RmYkfe)tM?QIY(=$iZ%cq6dqag$5#$s;uW zw_!Zr&Tkx)vZ+@QBv*QhvRu+NYFj`=x3>A}Zy6?Ubn! zQXbTPkSZi4EoH8D+g-GIt5aIa!`j^*LdB9dU)w0viOz#kDzu|g{XlA=_JdU6qY6?U z(ZW4NJX{tFXOSIMWS4@1A+D)xYS*HENR7+BpYiFeTzUxyd zE46XnBJG&n^L8HAl2tZM^m2FzLILqfKOA_YClP}Gvm{g z=QKut&%n1nyQm>$z2X^m+GS#BU5*2yP=*`dc;gh z-K|aGe`HLb4%AIrmHN6?5A~F?xolO;UM)C)dt&SRw0S})A1A+-x=&jL^_0?m>Kmla zOLZ%vb=aF)clzFEzVEjp_NTt3l}Y8FcO-ScRwq?R-u~1B8fz{xzZm?R$J^RoshXv< z4trY*Zy~$_QtxQ7QVpK+o5w+IiBvz0`6%_Ewxy-We1E_vsfV;3rfNv7)%Kd|W@?>w z&{S^r!`cm~Cl!}iU;7a)FNnADr1DX(miBkG&!h@24zs_ft#2i~=EWxx-q#$hg|Zd< zCH_OZB2_+prg2Pb87w^4(humn_U@L-h>Wp+q&)(~@5MIdg!Tl~e5ux$-e+Mav=@YO zo!37`IjOxS)nomyYbUjLNrlOy^0D?kyr<|B{zUu7+Rvs+vDa&^^dq4BI6Tukla!BC zFUJqCpVC60#MVF2IzaKBg(;tCv8LJ^`>B?1X5M4}Oj~BELi-omUXiJM)jv-8QY)ap z!{Kw?SN)6aUuqSmstf*7d&yMy+0SX8N%ejI`|anok~X5={LdI2T4sB`J?>qwU)NSirH-z(-_+_&^^yH&Ei7DQs-rL1f7Obi#M%XuG8<@a5`Tsh$tKMe27;z27yX1K&c0^0T|m@w*l&)oNd^ zilnLwbRjhpN^I{B?IG#yc7NaX4{fVd_xW;F3nk`|KeP}3RPR%n*@rcA{GpwbD%hum zLt)pX3J7fBu(4mH8mzebtBn3(C;IuWS5I$^xk^=SbM zl}(b~=k8tIUD*Rtxwq`9+H}I8yFH*Z`zAJzBCI(V{P%TE1ljv4c{*(`rFjw`NzML_dSsO(@aNV0K4le(ZgZBbYVoCfb~hC0c36Izm;@ce^?r?bslxCbpR9XwSw; zb=BMH2xn8Iy1UgxM+f$hR1dJJj*e`RR9CbzM<=#isyEq0M`z{|Blf7e*+fSK3zF() zz(hwE)>WzpeI`1(vVKsaXOS#TdPhCJ>=wyJS-dDVQF@o@s6?^lJ;fGo2R`VCW^H1* z60LM&ouFtA@o+l2u}rB>`#u)coz?de_4>7Z&=JFm;+pF9U{jzf6n8p5d$8mLk=dd7 zgN~l;LntwxvFr;dvAtN@jyU>V6p^_# zWq6Btw%wAMz%D?E?Io~lfAVh1dQRr%lfWYAI_FnMf4#3yB8!0%^?I{@P-5%7S(?mr z)4RI&W@Drp+-#;xA2v&>mja%0^kwsMPskqCJju_LWrkb$`c^$-aXU`{!go{Ym`}RiV7>d&J>nVf{p#p9dasWU*8zF-qC& zE-2ATHoHe=dbk{SWV2CH?eO`;k;Cq_WahG|(tFVJoFkXbk!nKABaXY+6H*Oqe$Fv~ zt+Uj-n{Ad}U(c@{ceA|~Zy-A;y>o6($3XUrRJVP)x({ML{rNtKUJqu$P@>m^SvXXM z(o+AKB;dKa^Ei>J4L$OYa8l-!Qht;tgjzq?hcw(J`ETAl0(KjgArQs#IUM*yt!= z?n&Z^J=|)eVo*&(P%cnk~x|Uk(ueQoh3 zvLB>pkT;PzQ<~16VwMjjwouGQLRBa~=}$R|*?6fwX%?L}iA|R3lFL($$!vyH&-g^A zO<{AO#0s#4u~c!y&UfpTR>Jb2#1^Kq0w^(NQ`y8ndDCRQDrKExDq9P6M9E3$pLQQ} zw>OPMDf5F8t(3CRKY1OYX!bU~c9gPcss7pQYsYjJC)J^VuN^a3f2pckeC;S>=~BJn z8l6_oa-c-7XRcDN;&o5jL(CmY)O(mU zhZ0+Vn1#yB9JQKuTY>QN{wwRIj7~26QT6vu9mEL>q100XD^=V=xcF`QNgw;y*3(X-**;i66r#WO9 zb4wSQ9a?X6Ji#KRs_=aeOS=uW0Ih1H+HM=N1FIttXW@IN-Ey7Dz+u7sb^KJ4odW_iX9UQJ*#3LOON-gik*=j z?^zY=n%&g1Dn^|&zfYi1@$VCOy7jZtJ7VMCv#{1wB{4hFp8r$5^`RJ<)4%zHDbnP+~l{vka&TrL#65 zZ96NF>K?tP_bY6yRNag((q3hgr1En6CT$0sE7d=gn`t}QLaFwuzoqSBOQkx^)b!Wb zYN>v=xux%BFH7~k>XyET?Uky++tvSdc2KHL&76)m*aaxj&%Nx5^t!wE^xn&EOBLeV z(|aFlmcx&V$b6HvfT~cAxZSe9$+}p)w^$F0_ZG{tc>CEP={@b~;@Ho|Tf76T#Nr)b z3oYK;Y^n54x;0CGo2{4XzQAVb@34bXeecsO{UAGUsaM0kk>1@N&C_d`mfN%shnNQx zeO~36;eUvQTD)4;QF?7XLepzmqQ$FYDbkDb=;>X@23WkqY>4!3&}!{48*lNBuoCHw z^~mr)!e(2%cUgt>Ui9pc{w`Z#@!n%=rRUu|GW|XFmBl;CzLVbFEqi(&WwyJT_V0b> z300vi^z4!TJ}VcV>(%sr>Bm{6CG$hJ(e%>NKV-Wt-U)Wn^m5ZruuB&2W2O$k_NdML z^p9D9Q1l!?f9K75nBK_rQ_NxUK4oJ~Z+!Zvtjyw_W-CpvB>gnoWbwXWhfJ?L{R>ua z@y@dArZ+eJEMs>!jl?EE;67Vk%P()5m}|Hv*`Jo-I@L5+PmmHsalAQbv?ll3sYv*|aP!{YtS z#+u&c^q*Oo#k<8;n%=eaTWpiX`;8qky_@O3v3iSlhg~yY81->`UYx?f)7 zm_=o{>ES|Q%sli|)9aPtq32mVFMYb{^~>cZg3%$(Z1?ekIuOuT#-(>Mx>xWFQ zJfpQ!vq1BSdHSG#$S-dOM-`^_S5`kF|Jh^+Be$G^4FP&f>M#7nt7ajQ08} zi`PNlWqRu}I_Py4uakb!^fqU7(r;M22;FZ;1Pu9nQx6vkW0tL_nqIHWY(3B7 zPnuqC=0N?D#T%@vBO3dXpE+0$5DI-<`BKi;tkVRnqEof zFnyE78=)UEz4FWvdcDOPsb4p}xtSw%R?u|(M(gc_;@4m1Xg${AjnN00-qOr5`Z$YM zq%SbN)tN>5DvLK>-(`C1GRNz67H@)n(eyTFPS9^yykgyNWaF4^&n(u%g~FIk)>BPy zPv&Gj&*GKn(@pO{W{F;D@$S<%n%?2e`}ExwuT(#2ddD+M^-C6ShOUlk?8~Xl8G3+F z=u5dyU*90lv$L7yy2Ik#ua7mo%bEAnz?Q`bE?0;Cw{CVeuC0eua(W66IX1hYN)ZiL*-IWbvNW51C%M^I5&#;;qxKo8DaKI{H|z>G(abw-btAf6nLi zSc|toA7pwa)%q%nw^`q1dh48<^*W39qJGizHalO`Z&M{iNv~cka|L zS-jVDbzEa#PB~xG1B60f_UQDbD2>;jbC2$@cyH)qP4BYv4ZY0b?bBD9-ZkeweUrs| zOFv|KH=S?k^%n1de%!=o4n+?yz{D>SIlBeAcIWnZ-M;uQa`qtke1?i}!_o$n?syzR>F}-dX*+>CMeL ztFwts$M2lpPAGo;Wu4PwE#3uvkm)VWx}cAa-uw|bt%yQWV!y#ra-^h%5Oy}r@( z4rhI@@3we9>L*R_c-D{lC5!hjU7gg}ms45)(gTD-UvBF3RYQ%}U)D|CVex*}$C}>d zte^EVi+4+3X?oYPZt0sW-f#LL)4Q4Vn_h46?&#M|?@rboolR~!et+oggyPp<_8)qz z#j_cMOwTLZW{k6Vnz6w20<$$^mBrJIU8Wb3ts8X~&&9ZCdL6P|j2jlu&G4JjI4)7y zZbrCJ7&8we)%1F0dl-2Z&&!x@di}D!j7p2w%-CppY1z$;-4@T+IB9yh*}ldli|22s zC5?T_&-OP0ghF2ejUJ{qGCR<4SiBa-SkoJy-NGodctOTW(<{jiGB#Pf*2W>zE6;9i z)LXm|fx)TZOt#%L!Lzy7k@7_kt8q&Dd@6VvLifcRV}BxMcBq8tSyhzMRVLX#@y`zVtGBnBLj!UWUWs z#T#Qy?{ap$QD*THjg_W%Ej!WJWbyhKhfMEgb|0hO;`KAGo8F!5eg-RTI(|t;JE8dX zmy=|~TD%lvkm-5lq!{BYp50hrdVx81W0l2AGj^F?NKTqjXYn$Oi>B8hC&Rd5@tlU= z^u{rZ%5fUuLSf9Zja1X?m6L7cS-f0hy6N@H$u%l1-T-5x>80fiFm_wKfyPPG%gq^R zT(WqB4RuCiU-EMX8v#P0FZo6f(;JzSZ#XR85M!+Ajn5fklv%uC#!AyG$r)yBvUnqm zL#9`rGs391cq5JLrZ+cdq`}IXj^AjbolyMx%NcFNTD&pFAk$l#GsYNa@rsNErnfq$ z$XI3Z#v8j#Z(YuKqt4<@FfN+j=9~$}4U1Q7_?0(~+4h`bBU~tq*<>Tt^!DUTHu5Z9 zi80;u4&;;=l@{+lW25OE&biOnZShKtlcsk(r_{J)@n#t6%*MW)%9&vV2!+0s8$C?# zY)-l1uz2?yV@>aJ&izK2#hYcUG`(v%vy4p^Z?+vf@#Y(YOwTKKzA?_?RTv9QFEF>lSY`1R8oNv{BzK`vXYn2}E}CA4 z+((QX7H_fP_dw&AMddCw!iB<^J#M6$Ua#E8jXaCD)R=C1{c@KYl@{*_W25P%9^qA%-> z$1R!bjVEPhcUxNMdSjy{bA$1UC3A!Ey37nxr#dzmZ%fsll{q#VUr5zmclEC}8lc4X zHW}YTiC%9qewLX*E;DmC8OH3UUcX>?LsclVJOY&$j7}EsMWdU=d(p_Vcw3ADi?_vC zVDYvZk4bMQwYk;UWbs}ywpqNFjE^ne%f=TL?`1mD^b0-FoKq*$*OSROyE`x8RF#2t zex7ib|Mu0t%TyKDq^5ixYpBKkwia(w{geCi-Moc^130}3IjtpQ6M2cCwVzQ_csm&r zx!V-~yRBav;NznzSCQF}#%t}yJ~qYtmlb$B){<-Jk0`Oa)>!O&6-JMj^WSDr6sA<< z@z(xYK4KF6kb`u2zDoH%^yi0D5`27^GO)aHPybUn-^z$dJfGLnWvx!w;+9n2!iZ^{ z@%Qrfuh1yjlok0+EzinPsfEK4yyVP4o^x_KZ&RmyrYs`r^gBaM`3XIEz9@MXZ8l42 z%5TaPTTvBje!JnkeZF-KXBVSi6X7k3a&hiikHy#&?(!0qeq)Ts@1g%Qaqfvs;U=W; zar5rXbGA<9$5+JT==j={B8N&XG{%i>HnzON(O9A??XeZn=Fde<<)W@Aze9V`SU17a z!~ffO5spq{|C`#pgmX`n`)4(kZ?p8rx-EWYYRZ$iQba8e9KjSiE=+mJQa&@6XEv4V zvX&V6?`a%V`UNANlXN%VE{|=>$8-+!{pF)7jshP6mByKuugTzX2aLRE=YMaFzHpMx zK+&>|eiNuE7MZr0yyO+^>C$L^q}t+I^N8l<*N5>Vn=+bjYb-|lL(4fFIF#ocF5>6p zY&uV@=aQyunegWlF~6xe-x^yJSA=L^wDtzoqAP;hWU@_uRw8f6pf}z}p(I}zzZOBoP z(->!tYAU~jtCF{4Q!b3*{O}%5alFKg$4gY@vYEr@D85}$!ZU>{=0F?$T2fQVfpp%Q z*xypxZsY!n5^?T{cn_U9jc4O;PTm49*OY0P!9;&}%QpI5K<-XKZ+V+ECKyAW&wsU% z^DP{yQ)S#0bM=OJ9=}R;ZRmgFoy9hSmn^2SBA3q3KgYi$@$zeQ6=+H{o%u{D!1@1o zKh3l@`bEP=s>&f;r@TbPz6e*u+Fx8zLUTUO98v2|ucp>`{O4LtC4a8P z$4#T(Q{;7d+&Ge=KVnpQOm`)0OO%NGCN}n;mx#DAU$nzXM+Ysepsn!z;@7ok!Rr3M zJ0?$CMwrKc9+Re$e>Wzg?*DvDM1B(+$Ap*sr!n#IX*}!h!I}09=Kq_x;>1~MSM8&#AqhBZGCF%@bBIa=Z3q+0cjNS`Fcu76n3H^DF=)a)YQ$g#Q^Vhf^&Y$-% z8}pcczYP>KunXO-Y)V_K&-vM<(Z2AKz4?6W2MTyBW_4a7Tt<7!?{WO>rQbrso^tp9 z54FVEBKGdS2);+{r#1Cl#9|D^UW+l|`3xgN-%3yW&VPee+}U|ueiT%!4K$3KSSP56 z|8B%w=yzoKuUFh}p2^}Un9SrYM|a~biz9v!_qb^-!g&kMPrF@d5Ku{HC+Xb zwZw?=mNoj-+ooApWQzMTzt%K)t%;K5y?IGlHt&NdxrKRvuXY+|EwNvRFxr2OMdmGB zS;GBm$>Vf={dxZU&-s6C;s0p~A2I7);1=DN|9lr{?DPLQQ;a0f7w1Xiyu?TT&#_q3 zS}E4I;_ma59Lc7hSW6n?rZ&YEXX89+>}{i~(l0pvPh)ZZi);3!NxarH95r#~h*-2Q zVsVVbn+lO5;=k7VyPUskP2`I^NOU(o^5K}J#nEXj;peazTk*ssTJys7%j3o|Z+wR% zo?>`w-wx&Xqv@F6#Je3&+&!)3))xMAEc(#+oWi&JGu<(5N-a1WYXiRa;Oi6K=f-DA z-V<@&&dTA(TEz4{n`Z2RmAqKPh`IMIc_s23aqWwD0^%*lr?f{j!_c;D$}4ntR+W2k zkLFiy<8186MiCCnEzl&ePSt|P0w90CHMix)vV#U?C`r`AO zc#{y3%a4VK-<#4Di+2tY^qj8J@2T@%iFRJe;!n6DU+jxuqsue@uKYibj2Jy}jWkh= z1I_9fnSam5u^NHV9+u6INEVKcIF902h`J}TxI23YKjI#=da=p-jySKxnd2^2IOTlb z`7;{L`Ih*JQr?1d5HEkddm|g4<9NxRV?HvPd|x5lKbP>VeN z#!bgdv@GU5Yp-56`>86{_%V8N6#eJ#n)qmx;_49Xh__E77DxYoj~nYgj&oSJ)_4`p z8ByL8|9!1gjGMT-iFjd7)BR4A7<5mxDSwS)#_{qeCi9W6z+NmuT#tNlOuOJ35pT@G z(KkCrM!cUC<%0ZaT2(%!m5)vN>l244vCg-AhE$a=C-QtTTMF_sN|p1rYaEZo6Qyt` z;9ekR5^?U$nZ{cXXRNh^m#c~gos*h;whgS=4GQ`BAWFoXERKta`8(~#b;#e9{Lk}mV+|wNn~petN7}?+aU}{@JRJ?nRZcc&{biEQwg`g|%Fi{AY@HccNBLoU0=KYl(2ho1zYQ(h)6-m91EN z3yK`U<9GA^i&&f|BA-8JHLmbyVb&FV9{alrw7!oL`MgBDoBE%zZ6?p>l_oA_79{=?-IMGjW{fc-b-oS`{T92a`b8$Ax zIFq+&oniiOV=>yI)|0 zJ&CKzd8~@XYFVtn1;tZ__0vP^y8Yk9eUt;X{>oh2aK&92t{kQR4f_8C{eMcCM&+f7 zx3XGkPX8aJ|NZFy6ZHQnWet_Bp|UkpwuZ{qP??*;zxTqIs4GE;+Y;TC7^07oNDNd` zKqoj590ra7i@{QG7C0YV3|0|aDI173{Eh!E>mih(>*pQJC(cQwkoHU;^D6;1DQ+FA!V2?apF-LoAwDGQu(B) zdgY43%P%Mn`o-%j%J6%>qVgH^t-3YJhT*>|^U;2d%H2e!P4=?IP~ZHC%J2}|V){<6 z_O?XZgp$q_x0)j29^`uVNwghRcJxlO1=*sa2ia=W8`S1vcGa^zWyYq9%+VA_MT)r8 zR-?3ve3ZCs=1Qv7n`$j)UHo`{F3+K@ziAt0d!chj>g@;A{~G1Xq>jWEi9FsG*OA&c zI`jOxB+l}3a0X|?K)#hq$6<=kqYob=rylV_%E~8i){e5Q()Wm6dz>eCa>RA*400|Z=c=+cDV^Ow-_C%H&LUnnf7)c`DdW15 zrR*qsZQvGmRSAiCi{d$x-zCnU#yPwEJn?nPxvD%(t<@<1%;CrVr!rT)M!AaLYiEhx z6#r2iK%CaAm41TpUl`n~Fq|RXqdV(2(5oBNL%TYR{!Mu_WzOr>k>j;W}kHD{0;Gs^rg;=*#KnzHigIS5a&C2ybC#x#PHY^ z<+qVP3~_l0FYktUGV%ivyCB|=x;=XHoc9s?CGj{NIlEZ)aygc9*d|h|8~(blpkfE&DcnFy|Z>3Th-U;cf+@;pT_gd`5819vEgZN8CTfX z_7lb&%D=8%VTbKuRQ{XYM)Y%VwgXS0WI{;~+Z8sUr$$Bn*1Y`+#B~Zm4p=j?y`!G*L5vX%^Kn7 z8li@F4{@DEt#xo+i}J<#*oZ{eO50nLrV;u4umns|yGOt8I?TqRIx21H9A4s*qCXz< zk!zy<&a~66PIjW@dDkVVwFyUU8}{Nhu8S1g#a=eI6kBdmcVj2cjT9TdQ^c_MliRrs zqu$23l@j|A@9&@Gb`>*2sk$M3s9Tj@>=@-XP(2z~;#Q-a$9&!s*J;nh61T0?^FX>= zZS1kgZI(K({}MMR>shkgZNB<>_lpXTH@jVtIj@!NcH5~wIitpHKlSapw%@j*Bu3e) z_3CoOEm3cu^cm`Y>Gq@kOz(eE$$%ceP&~O@bx+g_C{EN9VtC2H?!4p+r?2}hJtV7* zyUqAAql5ca{Pwn+!TWPextgBieoPrSt-Dd9`t}^_ehj1GD_n|o>NB5zcB+F%Pjjz_ zyHmYZ_?Y_{#K&+fE>V0;Jw|g#z57*~Lt>Pxm@#fsNs7W$tPRvR>AKsXP6MB!>+Uv2 zZ-aVzY%`Ay>H?H^lmGXP_(PeAoR! zkG?kE|Gu`9lXN8jnKkOjgjMvFUXkolW6)iWJXd*w!%{AGi6`S|DBBqsL`>)}y3G zSv6v)XNoN(ZY*(L|B0Sk)$#J=T+rB92i;_t7YWfvwp?o@@@g4?U`Ravz8y(=~z!n`U zA0LN$c3L~H8s#fmu{cydo-ww%gb1$~+Y>oaUNOem2|c}XsI?sT6eG81AgxB+$7FaV z8bM=nz49<(4wa8^4p<;F`E^l1`@&mpnb}b(wgtpjdM#!jb>jW;qn<2gvuSOxn7xUW z!$>d!C5ynrC@IA{&R@A!dp8^ z_lp{JBhA(0uoqL%|0(GI6!qusZr)Q=KF(zr)kGFKvxWC!b|aP7{f+h_QswKGM0TFe zkfVB1Nr?9xv~!f*NgL)7!-n>b_O4Oxp>g2X%Mz_E)^dwkJgx2)v#&4?Ul41QMHxN4 zV;KGXuy>`(k98%rb6w*r(oEW7@c^Dbns`B3NXL3Hn@RVtC6q&J9+l65WjNw%<<@5= z&iB5cv`*uhr-$>p7bo(qbdP?=dlPcDfftk~#$2KA>8qpbrAFm(BD-(qO)8mBtB##) zZIae(7_M|a8!XXwt9+i>FUk9POyTEGotoO`)n>;q8uiq&O{rJEcYH=A{7gQkdW}8X z?2Ouq{YcCXQGCv*{F}iqp$}*1AIL{i3IBHRt0*~!7K*Xm8yJln*y3%tjGh9Hsh(f} z7)*T7BUl?f?wrpJ%DJwU(%-3G_6dg@so_e|_zcq*C9xX6ULv(y(`y<6~8$%q{rAmmjHHzWb;=ke+U|a@DUAZ7u;TF&e87&sSf* zE6>kKXXbEX&yqsFwHiMs7aR8^&7qjDzBXZG)@r<;eKAXHLw^>tWi9!+@mO=t2CRrG zz&KnT-&4H8c)s-kzisIATH2yb*(Aw(9)#F|GjKmfemCgUHb*AfoLajnamsG3pUVxu z-CB0YA4L95-Fyyg=D%C(!NZ?@0zuh z;(*>4l`5QLRmP-LeuvFXdewh};WzCy|E)&x^nLz2$+amvji+M|``63OPq0HZnooDZM>Z3<^bz&^B=$i_xqRBDXslwko!jhy0+N~tz+?wA zsywcw^+SyEqrvCfVw-bvNMIsMpt&m+IrFKXKfBD=5{i73`C2}hnL8@*Dn_+0<5$Xj zjnDas)aRdFaxmt78NX^)P+j`VGOb6#&n_#_!V0voLgOv0KrOxsqQAD&N|NRW4paT} zYOpR^8Ca#=pY(KK44XM%3&kR5=iu#u4)lsYRUHpJN-NBFna$;j^Y=iT%eGOhc@Eq> zne%XYWb=Z?xO6Gwcj58PYt$E=-RT~k5;47bIK!1lIdpYn_MU=08prtcH3fT7#`v5t z2jz26vXk9E^J4RQoPqVYa?2RMe#_8WC0eUw*`3o`EMffD5Gq;65rr*Qp`Ep8X9?po z-deO)$-XaMOqnl?J=`Lk*8EP7C2T*%OV}F|I3JzJIeO}cE$T52^%|c=H(|fFfxE$a zx_)VW%Q8xwE%&2j9OFm7j?Ia=;u(y($50-uy_))EoeHaif>C~yRhJwM3P;Rmr=Nq4D%T_3 zTScP8mwJ*B5GynDr;TlOls#Np;?Y;<&xXFV#(toc18xqO2in*MS_22!Qm_WDobYa| z0+f#<(!aaI%)c9Fa*eWg;;*fypu9#2812=%4DlS~SAt8xRUp6HW(2IIeR;aIuM59( zZKC)_>up5;;N758+tGVSaEfj+u-1zhLtYAJf_|VMYUsy%%zoIWHK`yr5>w|+_%IW(U+R_#Ssm%?;UkUc5C+@o7 z2$vp{+tG7(G=F=e4X0#nz z19~I0m_=aS<>$xa>6rOv;w})1l7~>T042+DFIWqHg1Y;`b6Ed`g4KB2GdY5{Hk?+c zi`hDQkF=P%V6{0P&*LXil7x5&RzB?q@tnm&IbTiV9ELaxIo*?a$#{AQ_8tuG_T4ks;5L;{xF2Z}i2D}N}i*v>c?*?b0x8Gr0Pp}|Hc8)tS%Un2h|g>cF_ zMw$Hk7a|cKWon=Cp^0oCz1j1{obSsXi(C^r%*JPoVKy&%B8WBk%)?!so=hK{@J=Yt zsS90UoM?HHxTwdcq5KWNnb7@s>fBFHsWHk5W1auS(7uKkE50h;X5;J0+gShecOwsa z+*Yu{M9=#ge0}IJ_|tt3t*K&^JnZ99y3e+56N{VzxZ~hXG58zBDYX9aGhz)sAI6gV znO;Wj5BeO8UM#g~SIg*|dOFf7?t#<~+YEFGp7}|eF)o?Cyy&SmZur?Y1$dq77C^DEFto(sn=kRtGw;O1wayQ#7=t zZxJuK);3Z-T6WIo7~EA@q17W^qVkbnLibreqh1v&7Jk*#t9->$uksa3y~Ab;NI!FNjx_pNKywzflWrHnoGQc-b;L zxGF)2Ll8$HjzRn=#jeT{n+tKd&6Bvw=1Y9q)||N3)|$8pIa`pk4LLiIvlr$2QGO8R zbtr!ib&sKLJ#s#y96$PWj&JKc(T{$igU6Rp*Hh)OkIMTHpz=PnRCynQRo;g%wKe4* zM$RwbZ8eO_6|Do2XBSLGJmt?oQ$JGckj2Ob8Gf+xUJpzgtY>*m3G>x(!L zaR|kJmjW_S->So+s2krz*{CG((f6hd(AD9BBfle?N z90=xv!@!Z?7;rpT43>bUU^zGooD0qe7lMnyrQiy1HCP3%12=%1!L8tSa3{D2+y@>2 zYrwQz{}tj@EZ6dcoVz@-T`d^e8hCn4fFzi!9Xww3<2AM9l!`M z3XB1Jfr(&0Fa=BlonS6F5X=XMfg`~&;CQeYECEZwa&Q(n7n~0+1Q&x#!4=?YunJrU zZU8rfTfyz%PH+#n4?Fz|~+CxDMO^ZU(o4+rgdS9&jIc0IUHIgGa&R;0f>)cp5wlUH~tHSHNrF zkKj%47I+7=HOKgaZlD+F3kHHgU;Oi9QD6+%3rqz2fhk}b=mc}YfnYv33>*oL z0mp;IUjvx!`t+5$p%1 zfN7u;%moL6`QR{cBsc~f4;F(ZU@2G*&I0Fx^TCDSVsI(A0$dGNf$P8x;AU_uxEbUU^zGooD0qe7lMnyrQiy1 zHCP3%12=%1!L8tSa3{D2+y@>2YrwQz{}tj@EZ6dcoVz@-T`ev z7=O?W^a6dsKrjdl0o#Hdzz8r3i~)OriC{l41xy2-U@kZi%m;^oBf&A?c(52O0ZYMh za27ZhoDVJp7lTW|72s;H3S0+n05^kM!R_Eqa1Xc-JOI{!hry%Zaqt9q3Oo&-1uuY? z!7Jc3@JH|_cniD(+FD`!K{wC~^aTUKATR`M3w8h_z$h>V>;)!*{lFA34RjJaDNiac z9ikNv&|k?K9<8)i1{2#W(}`V`*~Fg8eDFzPALT8gUAaumR$7mURt76E#1Tq=Vv&+h zoUE)R&H!gCTPdzkULR4UJgf8>RYBY_N>QIx>PCbS0}GOgSp|i}?+WIjq*~?u3D>HL zb`3G(aS_vOokmv>Q$|-4M~to|mXB^AJ~Ud>IopGFu!#7`=nAkJtOXlD&ERf(&<++6 zmybSgyr!g#w7c-_RtUQCxEgHl#$!8J1XhR=_m_D-SPj;K7uo{`H_s0PlR-OJ2o`}AU^Q3^ zHh^QA@w!Fee6RxC09J#wU<0W6pl_fZECMUQYOoe;0NeZW{4rn=SOHdpwO|9N`JrXd z4i3~_tJ$%yTU3lSF~o`<*saWz;AHh@|?^Z~SkMPLP34c3AU zpw=GwpdBm%E5K^77Hj~uaO8u@;k@T|#D$295LbZJU@h1HY8_A)w1Y)p1y~K%f(@Y7 z5&57UECMUQYOoeO-x1@9Sm}iE?1Z|Y9V`MXz-q7-Yyh>+$Or9U5m*6MgPS_@J6$c} z22hJYEzk}YffeBT2oK7yMtl%)E#kTe-p+ZHG@wN5f_6YVSOivp>$~thsz!VeaV_HW zh#L?qU9s0)c{{8tZ$Atr?NO49*p9dmaS`Hqh$|4UM_i5gAmUoY4WJf@mLqw~?Gf9- zB5+@r8cEm+s1-KsN)rf1s22hFS`C2qu0PSEQ%8L+> zi{`zmKwJ&hf(@Y74Rt{~SOivpmECyVYQ(i*1E_UJEikz|A6q-(BCrCi25Z5(?!27_ z#99pMf_AV7tN^RQgE73#TEulRsEZP<2et(kffZmiSPNe2!Sl7AJd^e0aeKseun4RG ztHD}ueXrM)k4LWW0e*<7*z<) z1H)5!xx>ym&cRuk#aRbl8pUH}G}-|j<9W#?F#KK~Cr`lE!Fk|%@E~{|R3`GAFfbV` z1m{WeUad!bP?DFNM|^1_`cRA(z+|ux99PWUd5G782f_28G6}W7WUvsN2d)PXg6BbH zGV;Oj$=JL9#oV{R$5oVl&pF9%o0haq(zH#}+#0a7rRn`nTXJm|lBUgtay4v{O|oRO zo9?A;Kx_dK0RaKA0wQ-25V^^VfLsI-0TBVQ0`gWsL_p*wA_DULpXZr5=jid4* z?`wba-;EFU8OA&kc{?qa;0@n0JF2>6l?`Ir~cf`JU0KNpNl)B71Reck0V>MYPTHm)mvW~Ft zvVUj4Z8v3Y&g#ngN7m%*zhtlR?eKloce8J`e~148|MD?iV}3ZMX6#X8zclt+V{aP! z(bzFL6LLy&X64l7Y|LrN>B&jt?8^C8&UbUJ&-rCe&bXp+3&ypK`|h~=#=SPKaQr#r zFCM>Qzoz{n`@Oy2@wq#4&&s_pH#OnJ371T`V!~|`-kR{w3AqzjOzfQa-HCmZelY2+ zNlWss%DXLZPu{C}2jz$IPsu+w|J(W3wYzTk&bdmlxkz{CM&6#rBMf8C5e*o^j5M z>t;MU>)UftXH|JK}}K{)O$FU`~+_h{tIsk zDr>j0tskgt>qh0XeyIG`O=^sFvl?sNf-}ck)i~=mHQxG>+RwTjr;q=oCRlf^oY66@!x z)OuKzS-()_cr&iTdQ{D{9#gojq4u{PR|i@Gt>v^@<`n_6Wy`Yv_|E-o;d)0F54{C+=BHjsj zNv*P8R;#T);+E?xYK`?Lwbpu7t+W2D4zXUt+dHqT8tV;JYyCylS#PR(>#wT8dP}Xh z-c}o|cT~W7R~>4-rw+6JrW&pHF^2xGHd-I3X6qknll7t6Z2eQUSRbiY>toeseWKc} zf2qT*Pt_KDOy~&9!h3qQb(Dp5xs{D~ihS0wmf!lKHOA_&##+Z&IabgbXLVZRtuAXn ztJ})8Le>PU$C_yMT9d3kE6)mB`PT7Pfz@wKwjx%cHDFD#qSjO^W=*pO@pj-=Yq}M; zimik-!%A8uR>~^1wpnG?cB|alVO3Z|)=cXJs}duBf4nbnfOV4f1?$V!ENiEAptZ}I zZJlh*u}-n(TBlkESzoc{S*KZ5)>p0h*6G#)>uY#_@eFH`b*8o0I?GyOecf7Wooy|% z&aswT=UOYQZ&)j>^Q=|Y`PORd0_$Mwo7NiZLTjz{Eo+^%+d9PhHr^z<$f~g}wrZ_Q z@XptFta|HGtHJuNwcfhS+F)I71*|KqL#->V!>p^UM(b*;$+`xA9R7P&vvsYt3A$>t zb)D5>U2nBoH&|`f53F|UM(c3vht?MBCUEy=aQ7B)_f~NCHgNYx;O_0M>2LdeVr(M=L46mxCD6kAryPxyvu+O_g)QLu=GaY6h@2jgoADYe-zz?!l1FI)i1HV0a zJ@E9YO~BanR$yWAk-)zccL3{Vgn*SL{lIYPR$yt-cHpryP6D1-bSm(!8D{`r-h3|b zx0^2n{$%qdz$Fb=0Qc5h3!EOg5!g_68}KKkcLE-G?4kkS zFNzYtz8OQnt4ekPzglz}@Sz!J0l&ZQJm7z=+YKzuy_Ea~R{@t5T?b6fxCwYc(e1#e zX50@>d0-tKw3tSg?1-Rv)H-MEb?*Pv@;sfAK^`8Kb zJR%EUPdhY_16**(L||yJ5V$3`82DB}1u#}L3;4qs^MF4oS`7SX#!BFOMf4T3sU`Km zw+9ac{;Os)@NYF+fbSo5EU>h;3;0rB7`S6=4EW@R6!5gfiNI9+WZ=AmP6s|e>ug}t zK^Fj*A9NA$PqQuq)*N&VaBS@jz*!q^0XA*81DM!w5AdQ5^hMNF8y*23x8VukS2sKZ z{K1Cjf%k2A5x955tH4y^P2jUfy$5_I@FCED;HSXVxjuZS?3`Tsn%L6?dBACtrvh7x zN`SZp3Os+tY~Z6M^MRKaEd}nKu^RZZb=AN&g%OJQx5?j2|Nb;!sI7`-^twr{A0-rz`ILc28POB15P{SEnr39ec=9w zd<0w&u<$*;{SFxe%n#%OSEmYqCF_cSdrHfIHY%;7!064!j+>aLe7myJ!CtICk;Fz%>nz1M_R2 z2L5LDv%q-WUf`GNUIFfmzX7xkc?WpR$`62@sZW5*dr3=QQA3zBhcxj1jHyG2a^r%m zR;5ljXdLkEu8F|2yNZDWjkAFEx_Q7)qNKf7#|UqY5`DPeTE7T%YyB$58pcBzTNsaG z>|{KiG0u1b<0*`1GM>+PG2@kt*E8PAco*XXjE^!t#rPcKON_5GzRUPeMw`BTx_%sE zKI3%8nT&H77cs74tYJKqv4!y{#!klL8RLv6FrLDACgb^x7c*YTcs=8-jCV0U!1yTR zQ;g3szQp)Cq>5MZO=P)i}T*X+! zcqn5F<57&AjK?#^8Bbt5h4D z)7OXBk7LYdoX$9taSr1m##M|pjE6F|FdoI&$#^_tobd$4Qy9-=JfHDm#w!`GXS|j1 zF2)BKA7y-s@j1qq7++_6m+_yBHhqV2{W!*a#_5bR8RsxAVqC>o!+0oT3*%9Yos7pb z#u-mwJcaR0#`763X1tQ|dd6EB?_zv_@lnR77@uQ&iSc#DcNzc5XdlV-XUu1u&N!2C z4&x%mRg5)^hcdP>9>v(ncsygA@dU5Tj>^7V7rzld=aW6e=E*0@<%CyxcoM=}7C)t(2Jo^A(zC8@C2#_L0DY?4yD6>|=pdb_a029Rx10yMPPr z5O9&*3tVi6flKUu;8J@4xXg|Lm)l!`E9?YtrJVw{Ef&_G!Qx`*a|_CjzXq&jQxlX9FATbAjva^MD)d3xEOpLg1nHZs1||MZiX! zuG^}~z7)98z6{uGUjf`?Uj^K3UjuBhuLZW+*8$t?8-VThjlje0n}A#FTYyK{w*il| zZwDS_-vK<@z7u$ieK+t}`ySvI?fZZoc$*i0>%)E!7_=V(cG?dEyX;4R-S%U^ko`EY z$9@9XYd;C>v!4cr?Pq|;+k1fh_Orl<{XB5MegPP@_X1<~i@-tqW#Cr(6=2+c6_~JJ z119Y^fGPV;;5Pd$;CA~R;12se;E?@3@C5q<;EDE!z%SV!0Z+0&0e;#36u8s2EL-ie zvw$btKHw?#7~rWk?x*7o_3^;d>|Eei?TNtC?L6Su>;m8!b|LUgdn)iO8+X|8hCwm# zY`X+_j$H;k*RBA5!>$CLXCDAO-<}1$z@82Krac#Up^ZE5>Ra}F;BI>%@Z0uc;6?UQ z;KlZG;3fE0f~~$|uLfRfuK|A7UI)C)t_EIi*8;Dw>w#C=>w#C<0pQj4VZdwbCgAt% zX5h8=5u)yBBzy z9R~i$?g!p(4*>tmjsfqmw*r4`CxCa_Dd1i9cHrIi5b!7ViNJg8lYsZyJAwDvCj;-d zPX+$eJ`MPQeLC<#`wZaE?6ZIm*=GZPZl4Q$*gg;V3;P1#Bld;BNA2Cf$MBw~t$t}= z0({)Q6!b+BX6B*tYwFIbZ}2?= zywUd<@Fw5mz*~Gz0B`d>3B29+H1H1JGr&82dw_TQo(10Hdmeb7?*-sbeS3iq`d$P+ zEUYmM<6hj&CCHJzpO1eP03a z10UYz!5e#1fgkybfS>q^fuH(HfR?`unB}hk`uvr^G5!O9IsRF|@&4JsT>o6)ME^Ws zo_{{Dz`qb!=wA$+>R$>h@-GJ#`&R->{HuXw{x!e~|2kl$zZ!UezZN*lUk{w^Uk{w? z4*=)+4+GBkHvt#=n}Lh{n}JLHt-$5}cHm0?7T{|Ck-# z`ga1m{3ioL{!@Xy{?mYA|LMSf{~5pm|5?D8|7_q^|GB_~|2$yIe*tj2|3cu9e>d<% z|3$!){FeZC`Y#2Z?7s|ns{ab$Y5uE#r~9t~p5eb1c$WV<;Mx8gfam&e1fJ)=33!43 z7T|^c+km_Mw*xQo-vPYDe<$!#|J}gL{PzH_@ZSf#%KuZ~HU0;I*ZLmNyW_3L7aDIRjq&}QxT!WK?3VW4(nEiG8O8XZ3LHkAfLwjsiQP!NS>a6p# zF3$Q_mOp!4c4PKY*%sec$w5>-(kekG_w6Q~hE8*Zt@Df8jrQ%(uqm zj9oDHk7M5*Tay(7f$^C#QP`ydg5ynCr{ciY1gE$ zO}cQ>gOh$e>35SpnKUi0B5zLK;d$Y_c;0Dw=j2_N_p7|UdAa$e`E~h;{IBKzDE}|{ z2NoEz`o&c22u^+H=$XIPJ}8A51GRI;?1G(Km{IT=YUw z$@GTl(dnm5KX3Y#(_fhW&h+f!sl{I?-ca09+*^E3@$1FoXB5r&a7I>1V@Z3-F(s*z zvrDchxuN8t605YVbV=!9rAL=uQ~GLYR@wNng0e{2iDhS&-CFjOvYhfr`B3?n%70yc zTt#2SlNHZZw3Ddyi=HuYCWPmXI%zV&X@7{OGK0|%fqYXBk(WIJZQx?Yun)3;SPt} zg0>z3cO=|Va7V)(19vRWw!erk;da0shcoV=Do~wpU2xrMvI@cV!1coQ!G+O{CN;k5lExG%%) zgxjT7sFU$E-BaLBh5HKJY0w{Eg*zSYYdD!d1MW<9usRDT^k0WN8}1yqb8%Y#4Yf|4 zrw&o)<6FEJz`fO>j5E-2!(j+--0_g1a5=zu@kG`!U>|aCgDo z4fhkcd*JScyASSuxSzs30QVr=&)^<{`#IdhaKC_i1nyC|$KZYm_c+|I;GTf{HQbYM zPr*G6_Zzrp;C>6Y2kv)p&%!+i_dMM1;a-6IZ@9g1e}H=t?j^XF;rJ!@U9b7q~a!aQ3g>f_oe89k_Sl-h=xa-1~5Uhx-8TA8;SS{S)pZxR2pJf%_NS zr*O(@P!^mGmj#y%=Y#XZje#2rmjgErZamz6aJg_3;3mRNg3E);hbw@a3|9y@1#T+b zG`J$T>2SqxGvG?#O5w`j%Hb;DX2Mm%?GJYV+!x?x!5s)U8*UEVT)2ba=D}6L&4*h6 zw-9a-++w&Ta7*Eq!7Ybd0k;xv72ImLgW=Y|t%X|$cL-cHTn$_;Tpe6JTm#&CxD9Xt zxI^I%gKLCqg4+n!47UkxGh7Q?D_k2~JKW)LTi}j>I}+|FxTE2YfjbuNi*Oxq$H4{R zI^nwDy5T}_J#f8neQ;s8 zJRP2=!&L~=^Xv8Rdi{I3{(VmW{$Bt7oqw^jXh}U5SiA8TYo|d*CA^5kIPcfLOKmBC zIew|$%k8ImE?TYWQ?rg``qV5*SA;Ot=Rh5v!@t=otbhA8{aXEdo&J3ozf}IiSyUgK zqU!J?I{cXaeO&)Oq2r&>;U{(YNe)j_PwVj0I{b_dKcmBYba;;rKdZyfau{deI{ds2 zzo38j>faZ2{EIsLvi^M~>nqEs{;%ra*Yxj4NKdo%C;Y3_r}{T9Tlkjp%kNIV@N=gQ zpR9lH)4xB}zYpr)hxG5m`u7q48zscYNBk#pKRl+xkLmDhI^7%irF3ujr9a>C3*X<< z^!GSCO}($f@9X#vH2o7z|3uSs$4LFl^lzp9ov(kF;&%=-&P=#+xP##8uqRfBeJE)7 ztO6^ZRbkzqRcSq!bpYH_YiIUK>&ooa{JS%|62I%ftJ^M&C;77+)Ca@AV(! zyT_X2zty)1`FRyqFmF#5{a%dU2Gnm!nlqYxcr*`>dE8EUm@4EuutNP*O3hS4XKhF9d zp7*^;N&n95(lJ*Terh+2v$7k;UEq7I@IoK?3#VM>Yn*bGuVczRzMa`qvR<1qE$i(m zpV~XK!zjA~pG2FIRSdUy+PbWr*?Z9!*vA0<41A}mVchq!?5yv@-Rmdk$G5bO@l{w0 zi_rceEBhGVWxkV(j?dbY-9;Eqn%+Aszp76}roBewrb9=@%Kpq>$t+c*Xne_j)vRGYXmBM1! zAHN4-CY}ek0^wC~2g9v}TaUO6Z~?d=!rgEf)*T(K$zU?vRUMB9hXT=XvTbNE)EYh^ zv}Vx)b@1Ai9UTi7bimgeN_K1tCcFBYLdm{Z_u5Vq!NlrF#HG~6qT538WLs=~C>n|f zBY|il8BcX3!?9?>O|`bGqoXdI7>opmY9ql!LLNg8T*MD-i?t@>;b<@A5K3kaO+}8y zoMU|`DV5t890>7~gx(Sgc1uJ}tb2&c6MANOOSqIw>QbTNDeO5k7LHO896+--1mgp- z=umaMH#HE7Ci$@=mczurz+fz%tPMsYt>I*7ZEr`1Te;S7Z#0-p#Y10`{KWc&#Co)= zEgp;}2E&n1obql8#?iuLC@$>eM48o=teMm$k`_fTb&5uRb2&oc9;c_)g%Vxy@F4XX zcN)<$i*rg_xJXyDCKc%yUQ6AW-Wclzry65j!N}pkcsSS@kta&7VTE#@OzI+`QfHfI zHH1TvZnv*T%(leIwv>I>6w*~Vt7z+d~G}wOomWwB-j-qd84e0M$D?I#4wJp z9|*JHIl@C(B`Wi&E2TinO08%}MIsr}!1SuFWGv35=*~Yh*2%pt*)ztsQ|6*2lw4X; zHxvyHgu6syxGkQ@K%*!S{S?VZyjaYMHpN3d;T_dIJ)y2-qB$P!4M&5lg(MoIW>Y*i z2t_pH$_Qm7P1e}m)e+!Qs65XzsSX^Z?5?`wW-*=AqZ>jhPiLgjIblOQHlW3Z6_4}C zMN5eZH29lQ#bA^bst_}KV`S=LE`Vy!9#q7zz-6Y1;f7FssC9-&RZF80U+O}gsa`OW zwJxWu>N35|L5yRE15BV?&D){px$#WOY=96^(}l~Z(OP5j=;cg?oT!ctaaFZ;;MAFg zNn(})j?bK27LS-~Wa<)0i<+^=k!Kw;4Ii;YC+ouHEY48NwUJl?>YJD>#9?*kq?y!3 zLPaeWv(MY2K(r+o?d2jRGBfkY)Fn=l?16AlZ@Ypa(HR5OB$Y1EGp2OV3Tk5JqZf`y zT|6RniBQo-_Mk%ER&EI=`Yjj9PPp#kv9#CnW2stRe)v~*kTy#P@pRT<&MvzJmg5g#c;=lx% zEuB(`5=$g8fqF^i`J&8`5p2@sc6(aDvW?i<*uWsh9rtM?gx}DSt0k03MR@ce9g6Mt zOp}^ZtuT$Ww<*{kstbh%Yh!~vO(Ku!9WUoIXd(l;;P~2ZlKx(v+#|6r?Qc!G{>JWL zyq88udvuTl6LT3^;DNf@zED^HmrQ09nIT%Ok`tsC=wblq1L1~ z#A$UQ+SX{2A{)D@$aV=hMe49F(i;oES%twz&C3|iST`YpO8T5N7ZsGDi6k#+FfIfP z8F@K_tRt^nNCM&^*u~xLfoO8^A}n2y7X9yC2Y|;7!EmHGDnjuknEg~2JQX%Je52Up zlqRFTl0K6PBUzURn-@MK#hy_l5u;hpBRokmP#s|mBR*3?nJU7@mZYx05=twNA;IM! zxZ&wC9wSVr!eQGX!*DZD7Y8E`zKqc;L}nkwn&MqsPKfFD9cWbB4Es(~z4jYORP-_` zdUOrS(UcB`4ieI$G~N24Vlp~Ul2RrZEr>_6Oiziv5i%vgdLL^9^>_Cvoj=NJA=+Pze1hYXP<)=+uLQ0>y zK+BlAB$d&1W_AMAF;?K`nN?IbLK}jawzEi$SHnxO?({aoMuJCs8vRGw(53-%#rYK zPK8qR%lvE!^}t)dgH;bEeFUIG#KU7L8X2mMMIu-j3Xxu0~*$u=~>b#;uq`8&`G~660NaFVe=PT#?EkGWJdTR z1(v{cHlr9RGs1_3h7J!#QZP%y@#7Y$x-e^jV0;LH&P5}SSgC>xOH_QgG~=N(8PtV( zn&VJEuyYnWk_;cBhfA_f0aF6Lk=aF2y*#>E+;xtnl!3?@Vy&rOu?7jFIJ9Feq)g|= zPljNDk}g;vWJnPxqf0#^M-U)ThmE6=C5Q^w)3#|Y$$qshthY{q zLlXEhYRg2@CVC-?6rm!ed`uP|J|>4Jpv@SaPOs5*hzs-j+5C}0WQ>^7S!P$D2hAe8ITcTv;xV}) zdhJTg@3HPwB(#?HFT&ekx?(25CIJp=*z~LmC9x1rtW^V?Y<+v6PHn>OQ4fxT;O&#M zMEDw1U42dadN1qPuQ|tlt|RY9=w!kvoz4^BgaLdi7gKSdQ8l*&)(19LH+D4Dw{2*y z>)2G?Qr)Bi8(Z6|H`dncVCzsK85*d|3e66~!WDR$i0F=HPpqW7;WM&Y5O>an3Ya)&>-UEx; z4Z&hCfyNM45#I8hB58tgi)@dY07`WOQE#9(>4}MW1EH-Zciec8SeK5Jt++@_ZFhRA z-3_+ma;?MjhCqEII-;e%A#jA^{^$s7tgAo5kj!yahidF5{w(N#eWX2lSBs)Z@B<4e z&iI@s6WJ^bIEBauydoVO&2rwvUMis7#PYykGUsyWc}aijwVTfqq$;+BnuWNSG%E;M^}t}R55s@Voafm!6Ox8iWp#&k5Vr- zso@b0!y~Dg!l@C%%~|3!2R^qs2$<$@3Tci>g5yU-lLP9V+Y6pmMf zjiX6>n{vpfTh26TE)yNEv>Y^t!R;nM9$i=Van($T0Z$J2O^;BtR7=Nm9CvFgrGgMZ zCy`IIlco_#>!l9GN(n!tk?>>G5qU)yA(JQ~U5?~t6(qSy4R>nd5BsCI6Q{Q54T!6VDj_?qeDs9 zp42|k>#DZDCyX0OOk##=cH%KSUP3+eB*&|}7DW+R1c&@0^;qbFjKh@}{i2~XWVli6 zX-Y+evN6<4?VwIVDe|*DjN<~d7z_zhWUTXega-MiHOW78b&5S3L)-bcCLRj*Q>xAg zekhtf(ol2_O78ljP90a6HN0Lk9*s)W#TK6G2@H zP=kS`Y6&p5R5M(Cw7Vx9!9IgjK^+l;4_zCK;}=q%#7}K7k;K7sB0<$nA}{L373A7+ z)$kyqp=as&Xl}eWg1tk?Ln4gPgU6%Zs;w^`+s=iErzW<8Mp`HRa7B27kP-yDW7yj? zk+?4buJuE*F?KpUmX-F>DA!&d@!C&=9u*MHrfDQ%L4^bFcu;M`nB0b~j$|wquL~xF zs+%DcfmfdoHHJGetYTd_`Y~Iiv@e65Qgu8W89f+cFp0aAwj|yC*hCdy>j`(MrdTvq zHBv+(6pVNE;gXcxP2}#(Jxc93X}Sf!Gvhm-YOa2 z)*A0p2{=j_=vKIWKtB|!O~vCd*>SiV>h=mR;S7 zD_0xDc~`I-dK5sZAnGazM;Ecs!-1ZSp-^{7!ra*og=>^(4Fw~JMwX7|sO%fkJ~h;( zl;@~om5M?LSu0ERUBxVoOKb|`5F16&Jubc~LDlN5N#X7k_SkhIY?_6;MHT}+fhbfI zf>IR|3M3AXfrxRiK80Uu8t>34Q>=v6%k_?6v`db+p}Jym?Zp{jEV4}p4i0p$?eFNQ z33m14&XHt3*jzr~O9#VZ@$8W!0*Q^GWG$Z4lEO|u?kfz-$|)M?>g4uVy#F(jkz9@% zjiH{3M`@R2_!D@7i1w0&0{OvN!|)_HMQG~_Cx%DhI3p77!u{J3V{uqNJd1OcZ-lr2 z?+Rw}fO;$;eC9sUe(rjVD$k}X4QEN%L$@zj5O|W;K2j*`m8o8P4G|MQKHe9C@=|T_ z@BsaiBxw_X6`AqJENKv)esW^VK=xYFP*4+eD-NDQuqV4h5IVj!%Yw)^LI;Ver>DAbus4e1(r{Oz z3Z-=Q;~WyPB5rbgh#<|`q}AxmLCY^By}EmwOYFkM9S;pzaV?`Jjs$%kGV)WQ`c`~; zgeN4qJp@uaxJpB6o2nsJ1)euP~`U17VuD+1!l6#*Lz;vyTrX*b5MMjeNA=8*iVV}a_#3ftp zrPks4j?oP} z7c*HRO!=tC>tZSLw#BqZ&$>ME2pe@jlFHEpoknCookpUqDQGT*+(UVOUMtKDexCL; zz?$MGn3bqJj;9`U0BLM9dk9Swg3L{N7nie;LgBfKJ?NqibM1RGT`|BM%5N$__vv>Q z6x9wRo70QpV{Z~<_UO&v>LlKR=}aYs7)%S%V@yAD9-1N22r+#vo?|IF-Q{$~+S#p$cL^>8Op*EO-M!{>T;m-2Y%Mo$_}U&-XwJc+H~Rw zn)Rg_%{@#YuGb8Ki%vPRV1YJZN;j{HObAtIhxHtf3>gYBqL!=;POXidme4l3sKh21 z)8m;06y(P0TrL4On{zl6Yx|kh>G-I^JS8w4^>A%J zL!oiR{xC0Qu)1B`(LwX8XC~x$D$8B;@QC*Wq1E+rNY%h>7kfhuIyuF@;NSq88tiR> z(GW)GLXnbgj5D#BUy=$&(unl*dRk41UYC?zF&wC2r0IP0`ib*E$ADo9_53EhvIjGQ z!?a~U3o|bb?BTdL-&tlaNcD1V$3dh1I6y`6+malj`kBi&Oz21&S%tjmJx#N1f}Kdr ztRc0f57rj!+;~5CUJV;OCXo(_6@*!fULm9Vvv{?Hcaro)OJjCmYmtP24)IBFy^F1lbgIBBQbB;n-%w zgzJ#7rwE%K8+8svk3%P1-P;>y)rYABas*{#I>MuWRDCzLcjGm%U)a!LrGz1~u%iQt z23e>${Lth8@vUmdu6Cjd_MyD!sY{ND$6d zZyEBof^6i4nqV$NRNYL?oe)*<&83Ka>ul1v5mG_i`wDl%NOtM3+5YSy^~sYs&nw~^ z2=<1%#EXiOQtg&dST7izIN=p_rI56Bz+(yPOd&!Wp&0RF-R)%7{Fd-GvMRKGqoXGF z^5Gr(WJ{dAfdsC0VEYDJRra@fIHZ#3Sd#OCDkly>uLu&j1;TIx;O7NU-I@QO-#ixE;}{`9w@I%Apwh zu~MdU8TRXQ8TR9J%xHemQBd6+;apXuy%9IiEkYgWp&87i1-meNQ;4QO?AL6>MQ6kk zMeoOGajMcIlVY5moTlL5AQ8+&$Dawn8WOvgl8#o(llDbwt_*CeGp$*U=bbt`sPFs1bD>elai6oZvDa6pY$0C(BcWD2vPtk%7rT3{w zte1aTifJ!{Ll}__u?UQ84w1Dhd=Eu?wO8AFjY7d;9!0cAp=-$mrgO0bd#F#6;>Gqk zmqJr)n9o_nHyBp!@vy=fobFs4&`Lr&!et+y;m(OrLOaBznfq5vRo&BxI5@QE9T=G-bFU-c925 z37eC|8^^v93a1#I2GK|Ho}>*l6M4h(j)G9Ian}|~=vVx3_!W)O%MJY)CSE#DV}#tv zk*%Rnzvem4!5vPMUmqT!oxupig%Eln(T87>I#}*~!RSVsts^*c+MX5`jh68d0Mfr^ z(?H>X)(x2b5R757+JNF{#3~-&DUnKHYGxBshp^Pa+`s{`=P4v-75ttrYEQdF;+G|w z_(`>iFGBAF8*Sk|?riQn9;Y(ZY`(k{P_11D;L%7CeqjK?CZ>F7ozj@}cPLGijS1@tUVUaSHV2_}-dz{JBv&_V?IJQN(D zw6XY*4h&LzSQV*3`Xq|(CHa+W6HJoJAnb)bI>4p|d!SVL7yUnAIw;UwMKggYcg{l= zhz{~b3MSuP9VUHO9qENwCHn?Q>|llMz+PW#Uw8no2|0E@he-qIK^%v$_qRrns13#h zq!ES|t*I6)RG2_Bys7!)6`-+L%C7F*E-S+`t!bH7vtzIl>P;>#!LB))%=PU0&l=_I~|l1}1_ zDCwjCZhyEGx{l(8%#9SL$fXqV^1T%C^3@dfnyW#&1Z@bJ0^K_*oETH9iIJNsQW{@U zVXxd*5h`C*5vuEziz})ohJ{ctN+w4*i4_!7co;$2kHHMcnXp2a{Y1Lpf+G#u5^rNa zb(X&P!YTRgi^8ElDXO{{8C&vLx!0g6Sh(ULQCK0Ne$keS-d)5qqcYEOp5|6#C*?L_w{Ot3LH(AgbyU)RGRrn-|p@eo7K>wMA%%mbt$a3=-Fc2V); zop=mB@q4zZ^tps2*RStUR&Ag`Z|he9t4MuSIpflkW_={qNv64!Z8FMDf+}OTOC)Pi zT63h6qcK=CDVzrK{*oGCT`PXGn$%HXmgmWC?G!IX+zCRhhiygs^1Aj4Z#H>C-Pk~r zk^+q$W-#}>VN!DLStBVavNF9Sk}_{t3`Q>{3{}wGj9VcvrsYdsD(wBP7nSOb#2UaG zB=vz~P%kO$DGCyY?G@q)Nit8U669TMPuQHru>5%FW>?xn!jy>X10Diu$m%%UMfyBF zf+iRbg{KtyzLtlE6E3%kB)XUcxLJJ9gQt|?R9Dc`Q+Ex^?@9sHj;?7q)*9U*qbZSJ zBf#6TX6KCPt*JrU9`sV1Xum>Vrg771s#M4&)9yVUEoZ+j*=!Cpr;^P*eC+9o!;x-p zDwayHvEZSbBO(;(p$^im79Kb*QC}z0lPRss^k$&g*G^sOWZI1*+2C()xFlY~xIrwk zFznp0X@*OY-BfRw7BJggf;MDaLddCG#%Hjcy*?~`21wcPGL!c|;^BmrW!#uaic~jt zlNm*!x)`sPg7HvW49YioD5h}u+<9IHWR^#O=Xm_mMh<=;$UH7c1<83L`#_f7H6h&; z4{?CU4ac*wZH5g~^oB6|rxGT9Bhpzdu9%I@XS`6*&gXF`M)p9`tm0l|bnm9)_$5nD*P}VAt7z*I}BKfGeIL;vmx29rr zD-%A4^R0B)5yZARlVvHx-ax|K7ZlTqRCI=phjP-sPQEIN4j@Uy>)crNVpj{hW#q>Wk3%Alvxx=2@QgJH#h?@D z&K!&ZeQ*k5PY9X?zcJjEi@T#9{N34cd z8={*=Lmq71hVcC-`9=oreTQki>wGi=dzvtY+yh9+IMlt~7jXpM*^YB5bko%h1rix% zsV5Gc@FbKQ*x0uQcU*E3rF9I_HUL>}?%`@RT_H0HPy&3bjPBy6=uQjP=9~n(Fto3a z#26IVYvBF0xN5_tcJ<2#zCS{gM>cYZ+64&4poMH+i(B=4e zco6SFZEA9R%_t}=CUD(@vXed&D~H0J8DSY-Y#g8xl)`9LGx?1|7Rl!>qFf=cfqo(P z=5Qje&p6z$7G0NsZ7@FEkXZxEVQk8T<9taRCmC|L4|NB3al0>#LVU#w7Q6*jACJQq zXheP==>684na@I160j9&HqQ3CWl@g!+1e!%wVX z0`RFgilXH!-f4!9KP>{JFJz)WVbyhcCNf9r(rJ(n-1u^=IK6{`%RU|&NdhSXHx(-A z4Q75!k48~~4K~ySMi-sZv(gPgA<}YZp2gj$*2Vs*$%R5(=A;5EywW zg0t&%&KSd`!35j?IwB^Q=1~!zBbs|^XzPKqR@3*PX+||)@TA%grb5fjj|Q02`@LizZPp zwIhs54w>+Pu8FKfquq2ILwCf9Ep)QxhB*bF6uGpFO&(`i1G8#d7`n%FvN?)m2E+zO zi>CPz^JLtKcl5MF!C4Y^Y@pv|y`8{>E-F)hmPdpcAF87DKkpb|!%KC-BIx4l(+P+q zX`BQd#F5c2fe2E=9G5@>1AxASDj_!0=zG4frjQLM3mu)X5If`wbwVY#$C!|CC@{P+ z;^abZh&U=#XVk2&p_FJdkda>g-0GF$G{~B#CMy+Cy@L+C$T0&qKj;JfU7F z4DeIFT5eREO9<&RWtT!3(w5f=lPiM?^BE1yAi}_PuX`u(4zGDKYDHqfcjmD0NZ`UY zErME;Lli(Ps|Gqt=8%jL37S(c$5?Vw>2}H&viZzbiXtnD)LpWJ7B)pAg5_}xNyV@K#QeGSm5t)~<+?Ief>iw7Em%{U2# zqd11v5n5YFD6ZS%40AT_X^`>7Gij`Ij-KFkmM6%}^@N4J9s)hZ6J$Qn`Lu@y_IN^4 z0&VPhh-g)93PX6nL&FRwmmNJchc_M?@*0!YLk*gnHwg@9xpb4@m;ay!64=D01tW9} zBlS1$LDO85xnqmL+>KWb`J75$$Vtf7h5P!U&TKTRyYKE?nQ^qL*%Pm-jSmebV>l)s z>>H}`q=WG(hr6gAZUyp}YWWs~`KY0ywUm$(vP7m!Ht0Dx2d7Y=@ZqC-NV-v`En^ux=_?@IF5q)<#$)aFH0*(430`arxTq6Q5GYTT_=N? zOPyPLG)?dy(2(Kap$mbOi-#x&s@PxFm;R{WfzFth;9BpVR5TlUP9Hom+*~tmJajF? zv>8@~zJ&UqTYyeOXjNF%66(dbpRq+fEZULoP0j^EOxjvg@l-*_9ZqR(oi+c>0#XL8 zr{<7?^rlb>x_p+FhL>V#6etHd<4gZ@EHZ$Nq^0H18eqh~;*x&&<7kVvSjLBuXj)1u z6yaJq984qQ&5^V)pJj)&Vb6>jcD901VRq)zHM!jD0Bd>FwqjV~RXyQ0v?fWDK5PIh zNz^^zkQ+r~8kZD!DAO+q5dTW;$*cDr*+L~_42j8D6))xIT~<=pI&i#4Q3uDi_=Y)vh4tJ9X6+=o&Rq{n$egKn`#`HysVTIJZp+(2XgqTwvDP~5@}Q9LmT7}E{{X;ym7 zv$QZ4jdXQ{^{ew;2E37keJdEPSlS>?4orFP8karg8G7}~(>qL1hv@zIq$XDD*O&CZ ztftc5qxMptx29NU>cuTOkrW5L3T?cdL&hho0qA+!_oS~>f+uFO*01^Ly${ymq}uf# zNf=?sdt=dU-qDm1rNx$ZzL<^rP}HbEh#Ie|+;EaC8?CxXJz)&#EoqkuTTkn8mlkyL z=&eImwfvoU`HU~$yu>8L&(i@iqqNu9raH)$9p)cg^^11n8a{V5PQ(-RmVw-N;gG1q z>ZF%<2zfYy5c)tSMoJfpLJ`n##^y9#)WlMS3&*V-x;%=BnS-)DLn;lo5S=~9ZcT>n zAnnRXBSmfxaT0tr#u#_#mpc62 zA!MUzW|Yhdw-)H$319r=C*q4v@?`wVi>S?X)qw6?QlGIE?NaLHq};qa-TI@~)m-#gk8#66&BFU@0WyT?!0h(V(ELTKqLPuTWxKc0cr zGu8qLlSID%gvUbPa~@nsP9?D?jqQufi=$YT;IIjs9DJxi_eAI~6B+5Hm59EO%wD#g zlbnE$u~1jca7@5thI1_s0qd4+{B+zx#8-p{vm16SI4O99} z#t7wLio+P7WG-$~qC=R55blKx;nFrgWuyM(cN>_3Nrvv<;IwXs3U}`iPC&WrV8ff# z%nsHv;=?1vLrmsfPH}O`3wy<IQGreW6sZ*=$24`q>k2$M&LOeP&1)W1}9n4qc^`=_1wr7(Z}37;Lqp?=K}4TV5s zHQw66dvWwfIU&Z7I6hjFFb>6Vbf6cmlmKFkx0#sf#+=6}#p1(! z=E0%jDZFt=m#!URP%iP_Z zVWyCof%8a-FyC@>F4QaDAfk(37$WZ9NRsa$!um1IH2W$zYH;sac~)Cha&*jACDzk8 zLxI^%H|e2i`KJyQq4)o(%?+@4DM}6#o$EaK?n(#mMB(h8{DCN5;)YlvWMp0644@@1 zv^F9#dI7Tq-8gKH*AEOL)~xJ|rB7Y!_*peNp+S9N3xBazsks|eP{q}Nim9kVF{%w8 zOs-Z~;VOLVMRlqOs3)og>LjIRjg+;44=L z!YAdAmSO;>%x2uVFvuF!NS3#rDGt2L;* zD)PphsHmMK+vr)C9EsaX~R-t5-*T{7Xp$(Ep<+>EKC+i^R*(8;QLn><~%Bn&; z;^vELNW20`iBO%^aDub;jASqDeMPljMs(>ULuLr`(^N{|4 zbd}JGQe_n5lVo^?j5FdPxITWS<{fwfHDUS>-1boqs@8Uti;W!uqm}t@lrgah)O$)-^zrdBHYU#>{HM0>Eb?Rw8dY(#-11VpZe z^Q!D}Fqn!I22u5?v!v5V?NZ~sl{Ac{$=e8e8?%-Ki1Ka)n`F`&KjeozJqB($`#&A|EL^lJBhJcXnL}ROU|q(^{WhHdS(|w zD(gU$NoI@6!gN_dHOmy7G(}S^Z6kA_8Ey2cc~wYpMp&soseNh;QDJ5@lXOY{kQyc) zQ}0npMm-ShIb6lqc&uy%-ZyFq~^piszG{M=}%IC6z3Yj z>HrE6->8o1HSnY=5?<2iOfOgYnD>Pw>!f-lmZo>~_&PGFW8e>sKhs((PQ_+H}}}_R$ncMu#zI z)SC3(*tc;l`USk2DDpw2Q7zQG^l()M-(^y9jKk%wuF347F0P~DzAQf+$!0^q7v89-kmTv(hV`SvWBH92y>g-)eFv3@6t+*+G<9zXj|;kfi9y{ zjTfCwas+*J|%7vjCMHqMH-d3CNlzYF?~D=Ur9sLY?N-!QXFxU`fT`&*9HTRdXYB#f-_H&3G>B~58XNei<$|A*?j-qbbhQ%o{F3#mqHwo)$hQ{G57)rZJl#3SK z5=SGE%;FQk)K&1)+)uJjBYi#`l}b65BFB9EF2k>?G;N`gOp;4oBG!YMfu(CRYYe@# z#^kL=E@LPUVIG!5jiVWKFez<=Hjos<~mu zA`LZ?nk&-YOsWT>5 zdgrKI>PYHjGV#>T(U;)Vc~pC2gqYgn^p+))7M-#fHc!R9Jk+%dd3w+-<{88b>fBmz zNJjO1a9VfU7NeN!hb@D>ODi%YWwGM!2~v-x_XbU4dZsNgX3BK4!bb)di$yyr!)fwKUvl5;A&) z_QZ*ovJ_O+=>tioLoxeEvr$&Qn3*|F2gsy zrKbO(LDB~y4I5IBq$9i{Ni#jor|!;)RNY%_g6#^*Pb1z~?lcS&nDe}wS6=<$Js283 z&8dP+?F6k-Msq7!?P7A$z@eweVZlk*fFCVh6_BE)nv^g6> zN_@8H{BKr@=3&}K)U!nUzSd|H4;<2^wCs=py>s9lZ}P`=3d|Vy&n(^N5=Ld=k)OB!InwGX-8M4Qi;TBbMpf;)abKJF z|3zmUJ6dh)d8#Inpsf=zwP-Vz*5e!D=-7|sK3!-sH0-DywXdbCr6X;q(k@9k*J3+- zbkBwi*g>c@*^*HUN6$rRhMTICgUlgWi<6-umOCw?dtrLfQ)FFb6s^k3Fjy z74MBGjX0P1Y~2Ad9a&X>UWp{W-UawD^J`RdPD*y|oqeq7md~!~XytuwJx86RrJk8n z);`vDRH@&WQa`uOyUwGoCYxQB!kMQ&a$Um+674<8394N7BE3ROW|w6Fbf%$tg~&^n zZI4WLm=x;h(YhydS|g=2e0Y531lKxR*&~-_>hzgqc8yl1bkM%l`hPaKGN0k){O6S1 z@$c0>z1L^~^Eu^z&S1-|xhqMZQ|{K!u6GM+W;R4Yx&##?VDnFA`qbgFj zewk(MLk;(#)R86OGwV9)_;Kqxda3(R>rp2^xBT=m`8f;8QK#w8E^i-dTF~QcXqc%@ zCE74M9O#)JXk7MSugEnONRl!Q05jjv^i?B~|F@qi zEh8yQfBwHEZT-aS|5;o2c_6owJk$0*zO0I8%6aMl!e*z2R(7uPlW|K&0(4p;M;mko zNPCBJ3dlF_=TfQOD|Owh3h_6uxLs+Aa&9x5MEGL{$Rp!LjzDRQ&@)UP+%73;gb~$) zXW_fp!(+E((nzh5nsraBWF1Vk=|Qc@Tq4y!t3rzyF1He~9XRk&$$h@EfV)Ir9DSI58dj+T-pJzA^F4y|kKs!>%=t^>rRDa zxJW;!qV%@lWkuv;ZCW7r7DyHyT9LWRR)w2Dbn0g=(x|TVdZou!aS0;R#J5)F97(Ah zS7nxFo=2B?WYp1uPR*txCE0VJJ(D327%9@N*wH7o02NW@y_0xsq2LJn0x+Jd+y+Jtzh^3t^}D86HGS=0jJo(u38M!eSn ze~_`0v4;_I4LYuD0r4IQa2sO^xP#t28t)pj|EIn00j=x0)4cEMN$*O2dVcT9PL=k_ zeQlkoJaxQsqIBF@rDIRk)Y)-5YNK=5D5)GOqI>}*N?@Zhy;t>G~+8s0C zpyt!{C;78;*3aIuvD}^xH)kn!xBo<2zj4Lq8r2;eV0cb_P?CT4S>32{QmI?}ZV;_3 zXRXV9AyL#+9US=kYtPiZu}@FTjmK2jbZ*_iF8bA4Jcen|x$_XK58m*^Cr?l`F6I(x*JZ_4UVZ#7kuHyVCGT+HTc8N37j|2J zgcu_{9E`VuoH}zaF4z1q)h3GO)BPoxHmDSn6cXdOmO@;pC&5`dIj-TlM7i0?N z(+Rn1zKXW{eHNK+v^7?z-evK&)hjEfJ=Z6gXMKUOa{nYbV#ukLnzu2e_eU#Jc&{mA9#<-=^DO9SvR`8t!_%%g?`VJM=dRGXt-(0O)#8%>}BXeH{!EXxupYY zxb-hIZk*;x&gxL7t$dfSVXFF0V~lU3hTKT`bd0(jli9$@Iw_mi(SsSzO^}B%O|q1D zqv4eX(NaaO7U&zOEYHiXwn#W5^Px&&-@Jul9mcgglE#yjPF1s5RI6O8C5OQ&Fbaj; zz#WanGy1nQ> z{A&n+uS)QQ|G!WK(fgb^o-`@CZc3TBoi9#`RKJIyHhI3*jpn2X^$zgqm)RO$K}r;H zVz97du@A5!$qS>*Gg+gswO|XD2at2U!cT6iy1uFR6dFXoPy2Y$^uN4SSP8d0cW+lD zrzQUxUGs}OC4_ybHvdPE9S}m}d$-=&mG_+)`j>idL!GRZdWplx%dkTp08_8Ppf0$s zUEc=so_z)7=8};jX0uPi*7^bbcvzJ>=T&tOKGndcVrz1^*ZWG(sw2Qm^9O1$DtVUN zEx?`q$SIG_NsI5ZwUF1E%<^6{Cp9la-S;+5f_|3G>Up=k?*Di@ckAC>{MJvO|7zcV z!BVBBVP)QYwr;2;=p*f%suGPsayzxDdZfXHwx0n>|05D^;|a7To?)W zb%h7QLqWzv{)Ot7T1!pgz8w6^Ik>8u3e{%|acfstRw~u4$Q=<-y0F|{N{5GX2oRQW zA7#z8G0F=;q56$nGjfYg`Ww6up`w$8=>4$zB7$`l;@_wcIpmbeDXMDpZn~uqz1Ldu zQy|1pe$ji(sR9|SOKre^yMa2sAC_5@3hPkuTiTGthE!nDDyVc;KDC6-pqgE|AQwhw z!e|=aaO6-Nxu)qtoV?=r8&ZW#5Jo=>qj!DOG6>7QpYhT;{`g%*quqb@z;<>+DoO1&u4g9Ph1y4EsNwbVcgl^t$$*SoO~T3n9G)qj334W5E^~{ z8jX6U83ieJHn-8OyMqvHa(7z()AHXPMl*^3jQnTvEAmDHt39TlS`k7QXp)&VauwoT zXt1VCDnK9TYR-Disj&Kbz`p>#t$vH2$){4Ot_}EKBvQ?gZMLg90JVeElEor=zn{wG zD7O#Mh1$S3{jd)M<@0%;P4hu&L&_0{+)Z_jOq&Z*K9$|+w2Q9Twy+=5R-J%TkQ`h1AW z1VQJgZd>3rkV_Kr3z@bsy2J5N3@cDo#iXcV^h*u(I~==ZXg;VC3>Ub;&0eYqB0`tB4-&L%;0fcrRP=4?q*4EkLXz8m{c8(Gz(UzXCDLx4RZ%4Z`Rn$$u20iQ5sV zoqc&+wLwVpt+b(~6aY0$?tLNCw7w91>|;>l7tUXq6Ia$1bq6omDI5ppnuZw8|5q5o z&OeFX3eDltq=HYeQkQ*nf%yQ%W{KZbJNyH*DY|VFbak;g1ER1B)gzcOa_r8zTvsvO z5?23`@~p;foE)m}psN)q2UImKFrB#2o=Kx2q8W^|LiAh6IMDf2T{72dQ8vp*qltZO)~nofEgwfV`bE&Kfrxw3-QxojHFvU^s}5VNmOSj_@BFwKSx>! zmkX)MXP1E8$Zk(Xk#qwN^DZ1mVyA7gj$~|9d)){!M6@yx*fO)sYOLc~UQ;4@hakRD~9C~m3q6+aMQs~4;Sexq1WSZK-ct;zGMNWIEw$4@* zQJq|A$FH}z3k=t;W^|H5{L3{NzYu?0-t-BpzeB{%HW#qDz0`!eot^GJ`Ug7H@8F}( z6Mq|7b}}zcQ=E)du$P(4R7%Br8G?Xv&t$qVs{C+g7;{W=rV5;nUPkqxsn%j-$FIYv z;D8KJHb^4g#7O{Q`_#Lr5aZXR2wro|fy?@x?JKI%2JU;G*}4$u+gkUIKg zU52&$4H6Ffx&muDnxypX@G^GO?+Q8)QU+-Jy)gb>5TLn#$b4Xl;xf6Vn*Q+lOnVrg zfFDX6&DSzOw?Gc^M)!d;0HL=4)Q`G4ykst$*KC(eJcy#%E-TsMiki%6cQ~cpsT+|9 zdjl*;v}Wa*%+u_LL01r@gLHF{ZEu4#(fLqz^czT_1%Hsa42h;x;7o^`eseC}20)$S z5o)NGrV8lWEw6K8JI(Inej!+?Z1dQeNEXJ9-EpF?gcUg;X!o1k zc2|h*rYo4zpHOAd6tpCi25s;tdAjnKzY#=fK@FE=R-_j^2}j;VIE8D zFsv_p8aGWXGy*Uh*4Gr|RH8Jex>(8TH#C%{o?$vP0CLUftmT}uk#d^j&qD(0{F)R@ z4p3DrNh{`w3JY}dFEU@EncqdD7hUjx>HyQ?0ICMt-~ID_KY-r*gbN4=T(!nZNwa?# zElOq(?eos3KDv1rUl3k=`bn0XMvLxWDrB(8JAh%p zWe=G1X^XprJHX1Pg7!2ktQbafFdIn8NSt%ZX^sP>sGOrY)+?u#z*UAs2?AIc8@xZv zWSmI};YsqVNZ!sY+9Y*?##t4_;x&Z)TBdbXq764891ej9ztE@XD!ev555Lw74Zv&Y zx9CKBKzVj1nG5D`%d=?4t~taQ9=3o@wo_X*+49D9+)#JCi4rN8A0R>bLmefawBzd` zTHBH`tYzl48y)zM9FU$R#ID+St1U?kMQKB@&tb7!bhX9wy=bn&QFb^;0L=KsB2-t= zRs?9pMc0^%Qud2aQReZ!RLO6~vL?IN3sWy*5F#?> zi+DDSZ`;HYE#s0CiyWGkW}Pmee-hq0R6Of{307Dhl9T;PWTza#r1H61&yCJ=b z%poumMA4O?MbtEfxYYlaMl#dQq9~f_=+HR0oAl>pKM&Q52uCc_95yU4-{IONB!A52 z7%K5B#fl|aF@SD~7cf*XPP@ViHy1Q=;An%T_`@*%xG=TD@~DJET*`oK#16@F8bTX7 z$xl+N%*c(|_+(PFlWwS;q?(L(P=$1MVyJZnF!ogrj8`91M&gF8gCUGL9{#Sqpn%dv z=3L1p79gUaRk`Rvt3~tpZLuba=8<>{ZZdI<#4-{q97gj&+oR8I@YZHppc&k@3GPBy zh4DkqysjT>t;T#GLUK^u2zsD}OTXw$5+XW78xI}hOj5@)K3&uS@%yz&M*ha1!Lkhl~x^ua=WBfbgEi03BA_ z*sZ>6*RaJiT%(|Upea#S?SRF9m(oHCn+7{LhsA$`b>n>mgv-@oYJ?@6s$i$X4|0X6 z1Jqj37Mv-x#&jDTQ-{zvxI^nm0icspS!j}4xHZs4$wgsh8t53ghM>4?qw_#@D8VY#DujYLp&1R4D@5J{W4EYO)ZCEaI}Qi?aR_0a>7XCeM2O{MsktUfj+ z4}3LKYY7O3mW1Sku~^15sV+Md4uusa$=yWsJPSb8hEb0_EEno??EO2OCzp!Og@s@~ z)Lwv8koQdq$=STC6SuR1ucx-vJKcxk9V+{(N|L3-FN;) z_$H|?bGHYbQ*&7ejiN=7>a>huQ=|puN|m8uqguOZqB(KLX-s+$hBNZ2J6(^VdSz>J zc{P)GLEXv=lK7R+5Q$!)yB(>N*}}}dY=fXrL1A1MDV6$_2H}8kccMu-M@dF>u%B1Q zp+qvU=*waJYp42ZqpB?X0GpCn>eB=@ft0zwjddF01e&s8*uT&5b9|37o3!RDW4e*Y zVQiDtMk3}2s;r~+K-2PwsAMX9(h-)>2z`5f@gEv2!I8JEXBelk5jh z`%(;ARn&yipwD^JPvpr9?!dC~snuI+F<_FRy-(uTbqth-tEEaV=@DmKk2q8Fn06l1 zIf}H51@f9q8mj`VUh${pW}FB~q6(`g>h(XV8Q>J{9@ntZNw-4f1E9nU4`H}yoo?`Q z!abd|3au3g_N?%n@VxLsQtWAMal;%%P%cDVgo{?ma#2e{*evMK)giVx6zdKYE-wdC zF<-O>p0RXzA;uUs4>5Rgqlk@I7*Xo2*%)lGN}Gqt@qO*T!xVsYx-coCAJ}?eQX2@cWH44WDZ%N%aF|Bcm;y&F-_BPCD*ZW0Ar%a*Rt(_7GEC)UxyV z0NNZS75gnYO)@vJn&KMk_#kf5XorJOep5N8uxSnODM=33C>1xL;PG_cZ>l}IqJu^W z64;PJU&eh{{djOdRVUqBZIyUQP>Oiy9=sRCzZ=* zT8sYL=3G}Z_8zO)unxR*suNp|T!w^GA8>@=7S||KA3-yBmvWxer)%EzC z4x|&Eae2Vpld0UF_)l}cE$)}fnn^roGy;Z&dxfLIaiP`##}8-K^xV!v>WvrEs8X?n z={u-BN5pTS2XtUOOOwgzUXCF=d~T{=p-3@wfE)OH zdQJIoqreSQs#9iC&2n_%5DcEC#2?}pU7v~!Y6w~vXJBB##ha-AX_%^>wgihmb}7;1 zbiv@^)cD74+4M2SqL#LpGkQ>Y9c(NXWiiQ$E|E8>w|M-w0N zc3|iyP5qwsw$LZ3)DwkWQ|R(wABg*T1ja=UuKvl7{;KfHwm%LA{`S8W|MqX@|9$#7 zyx_2L<(aA{e;y@W@jP!G&k!fa33<>^FQw6wCjuMa(`r0amSq5U>hhqMPmUoc|07_I z9z5d4lfxD7DPHp66qByM$@6&^jW3P-`KqkaywyVZ5ss_iC!Her1Z=T*X?muilf3jW z;WlA2!lX|VmI%8MI(>ss;*%6F{g3z|{8cKI=J787%U<+fx1^%~wCY1xTN=I#z9soa zS>3Pp9pKF+iYezSi z1`fS}Z@1x-XoF)vuIzvDjh^SQ$TF_E7kS}*=8h*~&GpGCxy{yAEUHLE1|QIjbC zvJ&QQtKZV8P_!=t+q4dvS_+f+JB&Z1h|PV%F~Z#>ZQSpoo#hl=WZKt9n7CPX2PO$t z?+#0J&jf~vsghJ_C~=fkw!eeOm+Qe4_g5+zAArASNd~oj%z58#qNOYv4Nv5Dyk7~* zgtBNk$Y&!8Tx3($=5dtuII9=$Qp4H2+2EdL1@Va;NV?6m+wNvMxg{7cKDSDM^eEO%*~9OzTzu z1W^YV>@?vVgV4}EP%CJq<|-!!a)<|ODC8%$r-;V1$6-w3ij@)5@SHi}LJNoS4J%2} z)twJR>Vet)m~|1SY~;u>PPEL3vxGSfC`yqJGm;BTe9m>VlEP5>z)5HYdf3 zmgU)3yT0Q~!>~TJ?MKGgo^+fD`&G5jGk)D!6A)XW+F!e|oRd@P>rTxXU$&0nWLe#n zgpYB=%38a=+k+INdC5y_s9IExkm{(O%{7#`Y#tH{ZLV5hskpK*?3ucAI_W`JrE(bU zp+1U#lawf$_Bj?B&gmf#o^)Vv_7Qg0nu*d+3XkIpRJ|@>T%w|isRxelCaCe&sv+si0SQ79fhT3lY78LvMJ1jw_z7r5`We(GE8*VE_S>U z)301JuedzZ-f-EKb2r_o*IL-*@=SoR++rwaRR%2Ntq9;48wy{O$xk0oRd`5>;TQ5Y%kf|a;xEAB!eoiH zXtadX+dQ}QhSRm&3=6W9;e9w24%+BT%~*HhiJ<4F^00Z)19(DfQy2bEY!>Qa9*4FN zr`0$hGj7>DiexN+nJW4XCOa;-*e!T=%=IMtu;guRNE8Q1XY$->L!ID6y=gc+RpL=M zv?3H=uUr^!dB5P*y&*}+Y_0Zs*xVE?a*!;T3`Q6C9zW?S+0)EgQnnSX6xDO%a-Iin zkw^j%0^HQ#n2lfKIeu);K``*rsfsS?6&?lc-@Nm3d_%6S*}t_xgyLbejcEl>eHV(U4^ZW8?}(_B`v|+ zH3p4NF}o`?6tbMd&K_DUnnR_Whnk;vG=OtFCxe1w97c0EL>j<}eQa#-c})Qw;elE^ zurDLnvd^C4I`$HxQZi`or7Wro-$h*F!6q`rb0^f>b=0Fc*^0#%i?qygnj9$&JRznp znK|GH5R&;}ZjEQ*hpdmIB^EJA3i18UPVr)ACwErD(az3JwkOiXs)Ay- zn9;n?>~&C!a!1-+r8%7fWn-?c!eI_RY>-c42`Gkwg3eBy2*3e}c!f!Zr2b+^GVU>H zKL12rc&G=%coH`-4>*THu%}|jow8t*3}YjXD;{iGhUQ76W4O=Lv56aDt;o2iyup+j zh{?GaICEw$%M7M6i_@j;tUz0?=$q==^VaR7`C^V5Z{CRm`LZqXM3^N+_1S?i5yn45 z1HFr*4g$3TP+ToO@q7tLBi+9(3M zLl`}#%&;4jWnaQE7Iuy3P6wa5I3IO0^t46mbPR>3oRl1n0321i3@KF?L&?qP*qK&Q zGi%kII7;Hn&_inoK)iL%CCCNqC_pv7Om`#7K*y>S!td4|6#~MnA4*YT~MT^C3G78{{xmPjR zHc4`*LK2;)d9mnG=ZKpFD;oA$q#w8pf{WQ|*JV9MCJOQqP9q~(9+Gr3#S)&QLkr-Y z1vV2k#9~&uDyO>nYdxM;Yj=Y?LOT zDTLx|Vpdq4afCRb=^0208gXWQGKre{;8ylVF`6xh$-MK`m?qz9@4pN2Vb2L0^aaD@-U;B+M=u;S_B z%u`Bgr60mlJ5ahBRuVtd1N&DctiYgAG8)Nlzd_+mviidRQ7p^`0ldequG_E$?&A}K$_+;jZ;FDRu=~-ZSlEmiomsG34+yX|V_QoYK& zs@H_qg*Sw=!dt@I!a3o*@UCz{cu#m=xF~!ed?ZY1dEo`&Md2mk72#FkHQ{yP4dJZtmhiT4PB<^TD_juX6W$jt z3Lgj`3LgoVgv-Lm#ABW?EewQNVP3dS*eNUuOTvx9ZsBHOkFZy`P1q;gF6F>=E_~w_)LdEwCaixu)84F&xN|)iw`bEx4R* zfOq?}I;9B)g9b0GdwHxl(x4)?JGfBhM*44SZtPXmm;=EqTj+Ig{QK|6>5YlHy`OHo}@Dc1$FUZ z8_qXGYRplmGZ0;3F9iX&7?idXu`9AGF_*YWdNBmdd|0i(?@$pW07Xs&ffXK%RlL1{ zP<(#tOkeHQaSf-#4YaAS3Wp7a$(1`i&XmI=p*&0jnbGbz#|E!+@bIkKyR@q?elSwNj>nPlVuU22Yjo*4abkCLX;E}|Glfhvax$Z0Dg~wn%54qO8 zv^I7|GtQvNctc;8#7VG6;fLe4dY=Z?oVjti3DQPkYe#3ywC75D)rpx6t3uYIXkE_t z*7#;p3X-O!#${>EI`*+smud@O+;T(8t@rr~yj`gWvD~oM+@(ItjMk zWf#&KWFL`1%5t(jXhPD#@HlZoGaMtlAJKqCN7|Xe@m5&CTsUG^x~tgSL(4GJL))>7 zYefc^cIbB(xiz?l0&YhD^lGJPs=iWh&(cq{=mjJ+%Cj_avH9L#Xw>?R&}=+(>^2Q~Q!1ba>HL?>=21bh*~3Q>p>7I$RJq0XtNNV55Lsu)vKC3u{<%p zZhXUd*7%n3ZR0s(Y~3lHyT%K~_l)lwFB(5EerWv2c*%I#__5b3L7@smeZG6`Fobh?%3&t0XFBxAkzG{5U_`2~8<5}Zd#U-fY}s+-tnexX*aI zali3S<6XuB#)HPYjrSN284nxpH6Aq{H=Z;;Y<$!>GCpQ}!uXW&Y2&lT=Zw!AUogIC ze98EV@m1q%#@CH+7|$BtGQMp*XFP9w*LcDBp7DL-MdJs?4~-ugFBvZzKX&|%XPh<; zjI+jh<8{WJ#zo_j@kZlrge9rj1@de|H#+Qt*7+*EMW_;cFhViWNE#uq9bH?+= zca0Z}?-}1WUNnAS{LuK3@sjbf@ngrwJ>#@-V4OA18?Q6&G%gyKj5iv08*eu5G43_q zrvD6~7{GYJ`F%{FaS4-gVi7p=90mjJKm0azdpZ0C`16~)`OG8ecBWquUd41b_8#TK zef*{XfdXs=UKL&wUKidF&I)e{Zwu#y^TNBr1>rs6ec__;f$*X5k#I@4EPPDFh9D9D z8HF|kiCMYx!gaz@-WJXY=Y@BL z3&MNC`@%)x1K~sABjJ*8S@@WU4MAdB7zne%yl|bcQ&<$1gd2t3!p*`SVXttTuur&M z*e~2E+$9_k4hnY*_XvlC!@|A7QQ^37Qg~Q+R2T`52~P-52~P{p3eO483oi&S3NHz- z2(JpS39k!p2xo=2gtvur!g=9c;ezm<@V;=YJ-CE-S4w{WwtN7yUeChQY#7xoKx3U>(ygoDD}!ac$v;jnP8a8x)hoD?1w9u-Ey zW5N@{Q^M22v%+)2^TG?li^5C7E5fV7Yr^Zo8^T%PE#Yn9oN!)vSGXX&C%i9Q6h06> z6h0Cz373VBiP#V%riFnpE6fYm2|I;FVM(}A*e%>F>=E_~x3zS;T|H274M1B8(9<5)0=jxmyE9%Up2mFeBJnl@vQMJkbxv&OfKZyV1U&l}%0UNF9AeBXG{_<`|5<449z#>>W!9c$5p_J1dB*MV`?IB&ep zxYM|3Tr%Eh+-e9`!l@fG8%#@CFm8{aUVHNIth+j!1+-uSNZg7H1$ z`^JmL4~!ogKQdl2UN(O0Sc@LC|M9e42gX_Byzx5YPUE6+$#|o2xAA7<9^+o)ZFn&u zg#$4B*^t89P=o$ce=$)W|L)lSZy$cMGR~Ka2KP>k{q$f8;qcNFewy(XmFE>wUUp}B zV&B;QBZHNLllbbyS5w}XUm4p!_R84sme(dnMh|ZJ*2w6Vmv?=qvcED>o*bJPm>4^J z1fO(Dc^kj}jdG>3_3Pzl-}rjvkDl%Qdhc`3ZtK~$_1WjY@!YmIo`0_Qg%^hLshk&5 zUgwtYetYm+V-uAfuZxPRo}_-OgacX8>hPkH$r z`-dkg<%6Yx^2BguZ||Kf{rAhI=X$n&-Sb{b zc{_HL-d1@>OZ&(6KRb-7O&of2a^xqK(s+6D&Ap}3kvAsF6GuvKjZKtTwo-ZjaEa9} z?He0DG+Kcmm=)jd+PVcF0OEzo319Altb1x#11ay%UOQMR?RfLe%E5z!V`C86E0xK; zW5fL@=-cJVO5aO|_P^OTh&Rf<`OR*KZ@e-wdF18&Z;fsFPGu6mE_64FSlP76^LkU> zzgeB&OCy!h;qO=8a+$cay5FKF(?O)@Kc~Ft{O=WQKg7F9HCF-$RrlYs^o_xinxF=g zm5J`jBjc5^x4H*6Z7wMXm&LfN?g%CN{gn60>JqM!)DAS#*#6Iw)vKS9)r%=_o3+HQ zkvFz{YizhOI8okzaC~I6GV!&(Z;kE$34}d3_UDHtUfyq=U~K=vZw~+HN83E_)s*+$ z!M!5~OZ(JbrLp~^M@p5$BNzyh&l}}8fBaMQy@RDU$M&J)jJz>2Ix=~rbZ}(8t$&@#?T9{AjCI7sJRxE3~BN$6;VYJ{+Qr>}o*mGa1bZ{NW;p(ZO^ zhDS#gMT3XQv6@?11*k{c0xg|EU@dwJx}mwxSjahJ=^4+s*4kD zd{WK(f%huj8vX&kJobIKUdFq{-^KOIz?b0W&&~Az_@8V-UlObHpXjcWpPK^B1H^8S zLaKb{`6av-&5z{0j5n9{IYr#tD5($PuKQREKhu|dlFp@aHT@8Yrcfq+(uHqazx{-i zZYxX=zA)qd_2MHfkb!sJ`%}c_cggnYD?R+?7KR(XrcpvV{H7K^F5@9yyTNJESvk}-zZA*hGI_WGd0OavFe{uWPSLZi{$f=$=5gzNd_##pL&H_ z+4#W|e&r+ijv4E|Ln-jv9q!v>1K8g9`)TvXaPMxYcRk!Ym=iF>LmSrXIw|lo`eGVc2t&-p8{@I4)-r^T}txspZyWi)! zPWbMAXS{4x%ST^w;pLb6n@2ZXJQ{4de01aGqeq;4`smC3i!QG9^kfe( zq)$20vQFr7tlhu6bwgO%*4KhY@Dc!pH`X21v%Y{Z_HPxMs}@{*nGZzrDoCVt z?;d!ENpkGxLCO{X$eV2?t=nF2Tc;fFSQ&w~e}46Yq_wU6xX;)eySc=&vL%E|8J=Z( z69{dmMX;S{3DmOs7dX~xQ0cO)K4%LWXe4X9wS(Nsd~X>Mg^DY`{&D~+1#gAGnco%K z{=tZr@})xQq0x_3X8&$<-kE3KaZW6|e+UrP7;rnczmnh*c}Q6szyn0pHxPi6D+X`s z2TNmlEj~NL8hZr*BRBG=niwA_=XT{8_YVb$onr;9WNt>+*eeEz$Da_7amDjikx@tl z6_yaK5SI+Wkc>b>xu8!%E;B)6id=~73Z+iDFt^*}Z(J^)eebC^b;yO;_aYa`w|BW9 zM@%lv|3c(Kf-g)ii@d{uYx`Z6RW5}OM*>v3vhkjb<&N2{TO2E~%e@^BjiZp@*+jji z@m@{66{%xR>o!4p-&CZXiu4vmdaEG4zZ2=(jSnc&+Z5^T3=*w(Fi5s`NR~UMX8E9I zd8cN1mu7jlW_gcfxpQik4{4U$HOqT7%lkCTPRa7&safvQEbrGWAJ8m!XqFF3mXA!$ z@=?ulr)K$(X8EvYxl6KqY-*NC&9YImY|<>3YL?3+%d0!H+}+rwSzfMLF4ru+;J${q zN3)%JVTnrLZd{|dS19f)6gOayY&9gywVmW|X}ngmY}PDW1nIg?q`Mo}E7FyU^hySa z)>VT0x~aIYSKO-=cdOzy1@{e|xVJQJP~2B3ZcC8f*oky^<3>fARHRokNVK*I?oFMz zw=}jZ?rRkH8bNv!kzjSR9=bZU`DR7BR*_ySNN=7>=@v!0PLZw`q+2_Y?rz+sNUu|* z*E2}8-XOSd>BPOV@m9sXK~mq=nR-j(?V9>VrF5epy<;lU4n?|2k+uucJ3Eo?ZoEs8 z-lRx3Gf1@FEV%FP#GPw9-Y=;**V~Tc{|+(l7x=Ml|5!@v_ly_3KN5qezhY$Eb`su` zjP)-_S~r`~$@WWKmOE7QG%{Q(25*8fD}+W~lD*FZXJ-S$fm?Da3!N~JMyBRbXgp6y zyW6P@m^}4d@D$2v%p=X<*e*mLM|3NByJFwGy%UHpmbZAGJ$VbBh{~P}R@l!)p1f`B z;7uwSqv;(7@mGq$bp4g>lu{IX0Lu zNIFix#PAkiZgR!QK63cSgTRM}VnaS1>;~dKir%xtp#)1cBtIBjn%H+mr5J;GP4EaX zLjgK%k=PN!3TRP?V7C#R2;tk$jW8L^b)QQWYQcF(SM99@ld<@FWAQJ<;*Uph%&BY( zW54{f3u}*rag%9n55htGmG=wWR7gEIr5$~JJNnfqx+(ZG;_XhsMl;pi5qRl#(ll#V z9^K8B373CT8|}-%ES z12q(W5ukRm1vN|TGZE8~MCoZ~tlBefdoU>Ofd3R~I=#zw{OyQ%*OEw+XsEb^CA&$I zj(-NEQQF>#$dpP?h;b${1}FfmW>f$Y0SQtZ*yWTK*-f(Rqo!F5IP5EP(AJPA=K4Ug zvu@SSmt4V8T(P=>>sSTgLZ*GTGsE&v2aSsDmS%MAnmL4JLaGFMj;v83*EUXdTEagA zxLAU@{+WnK8}x-NN(9n;9jjjOE@j}fW+2%f@LG5(-D48-VImel3A-u3{?DLV5X!R% z6dR;AAy=W63r0?cjY86O?x%T7x6gYndsrm%Ith zYid%TX*VekCz1C@p(VC=ImI&K{S?90C3gO%B3jPdOyF|cF^^mLi1uQCJ{)0M`ZGmS z$;Fs*wr`Z|*?g`%(@ojltAOF8c6ggn(#}+2O)lkb-?->W+tmVu-a+dm#vn{NAtt{~> zeU85g!pPDXxzcE*YixfTC0WqWsr5RL>KA&TZ?q0uA$ZcDPlNxQ$`K~RekOxtuk|}h zCKnvu8)j96YCGxSD2bKLv$7WiusT;b`Xo~p?_4V8CVLWWCkkuEwlQoGS?v_FqW7wt zBmw}2Lj_0#0CZyoNCbf45Fil%MnZr@V1hAJ(4!#TmxaJZr6eQ*!0Zqp5ddIt6hk6k z_x_$h*Znx8Hfz9xaVd|F40tdg<#9YiY0KmJ0S^sU9xr&G02T{@zAFidob({d8u88p zC$=--{Td@mse~z%iR#pxz~y%1d>~`!(z<^2IU%1Q5!2>kVsYOZ`l%D)hi?iseX0pXS}!M62eDpkqTpK$#NPwlT#+Cb2sEhdL+`wX-*qApX6Wb>F>glNY(}lRVF{?)wU9 z{$E)4JqK_5T=z+y7q;#b)c*_XJ}vSEuKNV}`PO}c#5PS?&@=h>|LwX@3fyzu_f0nB z|I2lskbFVwKEZshb)P}l_qxv@?0elO2+y(ZlXSE06YVwYJ`nb)Uh1!RtQ3-|Mt}m51e`A zSo%@}Pk1jwOdl~nI3Tpm5&>W=1V{vc@em*p01gZR60uYs7>MMb?fnkCcpFhh<#gNo z10JyKiunUeiAvE9-hxS4!pZv|NMVw$&O%Ph#A8^pgFBI&l-b%Ii88CfsIRjciGZhh zAwVLB*@>jM7*X&4rd?QxwZpFv37=20lW-M1DUifW?`h&3WhV_z2-(r;)O7qm15XHc zROm|EV?t%Xb~KjvTUcKjvQt{>8DQ$1*Rgg&Vpx~LcfhG3mU5U2W3|R#e-j}2g;(iQ z3|3g{Uy7uxEAG14^)VZ(e~jhZHx|~$(AKodq*Z8D5R@kryMX0U45zSGkjlexpn_6% zUI`?;U6c(ZOnMBf4Cz^r$Z@t)@USuHj#b@pZ!IF=o@+PE(xh&fc{ArCOuFnQnW;<% zD5_#%?fhx_=xDvlHGS;BktC^RXWq3tA~_5jF;sFI$?wRPUGH2llucBoh4nWOFi9Q{ z0gq{}TMUmWT@Qj$vh1wY)t-|pPj^$!T072mWqPzq94Y^G{aJN+M1a$4kYZD#NUXoaplI7;sY zJ6m0U1M3-5rT>0T2X`w0@!tv5Hg^$d-`wp5Wv|@jQX-IjahK}kPFTI$_Wnh7_EYV` zmT*iF^Nq{c=NkV2M#-kC*Fsg72mtd#fJ6Ym>Rvm8M80RdFOHW(%^{Zzg5>tfG+LX+ zb(m~>J%E(#$py%#@g=sGC9?lhroh1_PmcdHK8hYVawG!2DA);2M7v1x5_^|B1iZ$X zWSLLh(6GkKQ4P4Syly16X-1a<&Z9v$CjB6ZESV9Qw!${0X4t4z!Fn=3-6`+q7V5J@bJ+*C&!yl|6l2b=u5uSp zY;QJlIf-V>}Iz zl4w-0{K7+nrJRT5cxh8ILUNp>rYAHBbzL0RRU*=!wuL2PPZH%uCdz5l34=O<}|DocNBSv8FL<(}Y@CFzylzAI`LFr2>ktj|y%QG$g1r z);GQZDSgQSy8f~|%Ugg8e^{K!ZoyqnvgNaoj2;p%Rg-D9cb1p-CH)rZAJCT!E`;*= zlXx2Nq|E80gOc)I5gl!3IV*J8!**s0YkD&2?HdpM{;u7-+X78{uSWJepd}+w>!QKz z=z*;8j1MbZQvi7_)WY zyR1d+8XIhh=7nhr91`Hz%oe6ViPC8%UyHgqHmn&gF+kZb>PXpSQXHAnb|jtEL8+Kg zQl0ciIvH{rMewdvidjvYLdHNl$;?vJMFWkek>5Lx0$60n9$+9Fe~OacqntHvfwse| z^+pD^55)?L&h52m1HH!N{-R`S&4ru{$K9H(1EJvmdz-F7*lBOCy51^sX=Bz{PuTe# zr4DJ$R!ZoE#rjUW-V&u%HmF7{x;mJeIqlCkgtIVT>w1U3@y`Uq-g)>@XdwnY2hf7I8o!El z0b=>s^LjkP)XI4II70NsJ~i!?^Nopz`L!#j=)-0{WNoj6Y;tHIXOP&7EBhNHK6I!3 zw?lW*{#)_uyBPN1TC_7I_z15D1bY&s(Vyr`%wEvlm%ua(v09;gZeOC9s~wBq?%J{C z6K#*SuoU>PZ#o7{e!EkVendqANhlIiJQ$aRRiS`W+_qJFI+Eyka=TpzcuZArH4J#*{rO_LNktr>|WxX~(4XG7N<1P8c&O;;efl zrge03bMuiCA`44%3yLX+VKG`*ufbmenX>rXgg83AHG%yvD=3qCuvp6QICSEipNHWs zOM{a|BXTgH1NjC#day+h_#wd7W|OEKwlXiBdOvxVHiQI%Soa z`{fKs3z~<%LrXBThCO9QjG7lmHkeM6OG#_6CBoK#55ii7zccZNA=tVNe;+n~Yz;n7 zd;mXKRFme!`x&>TIb^|DP%va?+1RoSo8%@4_d-c3Zzh}_Nw%X6a21*`qjeV@at-0<|O z$f}B^f`SyIhW&DlO_5>>HHX5BUN1#RNpg~mvBPTM?jWFd{!~qgXDp2VtA_b7){XGWj>lx!g$%GovH(~YaFdb134jC%k27y zl@N)Sc9~H<7yJd?RJV(5N0urwY$MKjq73I)lhIhp$Z4C|B=<|GO8lo~&Z@)INk{0% zBJwitFfEihhnXZ@3=^}gNeX7qX!|{s#EtVn%y>XfQ&1(VVFB$|C|zKHJrR&I>^$~a z#heof3vd>RD3k6o8Ac7XR?x^$y<1h=yOk}*!}Dz4WHg5`;+Z)RdS-&N!S2q+6EJdH z4Go;5#0ineccV~K2gP$lma^YEh@}SCd$Sp0U)ZcZRIhA{;RyArz3A|+L_K89k#%q6 z(xaN?As5C{B~DDpFBFbv3CwtwsH5F`6NPp8T@DTkKz3Lo%G9klF-TzLsKg+vz7b%V z>68*f_1U(EVNsMH_B31Rfu6nv5pBZ6a2?cN3WV--C6)FLM-1}50zoB{#4h%?C=x`p z{;u~8K9jezz#CSEID<0>KI5yAA)6G|;71g`vNIg552ilS>!nYlm}%)PtR{-nHCMuE zizS`%zRnV^wK!tq|s z)RpkQG85iI5Uk_ zO+UOvXoH0JMr7jXGEl;i&XY)CWtb=F|DBoOxgbS3#}qyMlqsYIyUBA}q}BKyCau|| z%Lap|A?4%snP{&31K?pK!+{m{#F2Wh?QKVd=bTumf{WSuI!DF{+Z-q|#<0O4|S~g-YLz%sq9H;XOKIc71#B&^&({l+6Wj_zQ zERa3)=Qe=7TbLF#OpS?R+xH9GLA`yq0u3EBqg9+$W1Gmifzp?qN)a0s(y$5Hy|KOP zf$jej&EWqYOq3D|*9Vvs3O8(TXp&84Q??g90A$6(6IFq6JBJ(4Gf1T*E6`G z@o7{I-Wnpop~E~_BohTE{v^lcisC3rUQza%R_I6ruapHS%0K02sO|=TK+k|Cv#7!P z+#oxE)rmaacp#)@H(9vdWZB;PA@5R9nuYUXbuDTlXMd{ZwS*R0+9+G$T4YMhx+Uf-Nr@^fNn5%CU)}2 zssrP8lXdg&MlI=Y_&7pQAoRQ_!EW3S{<8{k{eJ@v7O7H$L=~PY7K5|d*h}>u1^YQ1 z>U)6o_P)*E6nq3JltV1o{0qRwjhHHFW&f$`EReK#v|o{SRo@-wBpO0RAUIt1_Vv9@iKCr|^RT z-TO4c#%J);V`ne{{Vv$h<8V{%OIA;-RiISaVRntqs`1sp+Jr@Cax9=5g9 z_!YD=>v|7C*{TP+jeEco=0l%FfRn?h-FHyCo}WL2%C*lL#wxdUO6Afs(o-tepFr)L z@xDZGeJ5_)9Tk&~s#AYbLD+2(g=(@JK&v9HE3A!Gc!ul$2<-Q#?7n17D1AKM(YL3D z0XCqzmMK|!UUIgTud9sMA6|~i!op!bDkrDI8C))S1{KiV#)SVlNT`|%Ch{T35nqL0 zfV{ZVnG4oONePiM8k5z)h+hVdc;vX8^nU=}$Eo*hpPwVd7#X&Y?aB6O9WCy}YV3au zt%mn1_L2HgY11uGmM)0E-OLDE!5(*{JxlHuENw(GMP$;|-AOb?R6T{zD|!eteLRJQImN zV0rB}NL0_$mYde){|0T9OH~%9nm>RIs#bB7<|d%pV|kf>=e@f@WM_ExA=Ug(VC`)5 z0+=nYzMTp8wWlSd*JG(>L3E8yT*<(BDjFNJ_qoqR8 zI})-gLR~1YyAaN6>oiO~bkPk}`|NOWQae$rs*4+0@nLRT#hVa?HFDe}m&!Xku`$%0 z!oE);y^)z@_#Ixr@ubSKYTC>qL}6BP5yi)Z_Y?f;y6G6mga11F zIQ&V{M@<)fn0!Qaz`B!s^T>(o9YspP|3o$A+r~Qs!vYbkD=!;_ zWX@vONK`TjUB==~o-apWha1}N2w?V5h>Br>b|ax?!$-QV3#EpSkPj@ciQzv*ez8d8 z177Xr(+K9DXM-<@vk%*B{{?i+qBs~TE;8G?E~4zqvHsPb0>H7PBh7c$ridzJFov&H`kbE5NePR$2UAl?T^JID^h55dXAbc+tx>{!qXh#eeExV&pvM$!JkkftoR{Cj~kJ{FvS z^f3P3D6Z?#tgZdz^su~{EzE&1x5a!DbJ_bqp>-nxc9TKls|3J%5&`Zf;7u{We-LmJ z0Z!{?1f-v!oz*i;j`uWbozIr@wp&=EzFaJ@yng};K-Q&=`4y!inIF<}{jY(l-DJW3 z*Com#{clK=h56rPRGCK~K$*6@Cp#VO<7~`2`)mFFeCX1Y<6c(B3x6}wgB;JH#CUY0 zbebFyMJypk#M?z6QS5!9{EH|o%dsfGDs>^7aJE;SfwJS|cbrvKPjtTJP7vN9P;8u4I{+yQBw+lwA|va;L@cKvUoXbh=iDgOb)xXzF=XLdeoQPWUY zs$pS`Xk#fXE)3s8H`;%Sbuhh4b;EJQy`0F;^yW|slVRsmJ{-sBTVbY3iEWp%u7sRK zPkiAV*AHCI8kiD1h|%4uqr)(Kk&f(MCA?>WV+M_(`Y^!U!14YEKd?0mGdA?-A4CaT zFJhGH>fkNNnO>1Rs{S0S`e{WKb97pV@^o5f%G@ zek@?$5D&F9Kd!!!=Epn9(y1N)J5UNQfxHA!&mGYi1!;diE@~&;6vCWHcM{2Z)D%Ng zn<9-FC96`hn<9GuyHQap`@4%#Aj5etAKb^t}}hFo0g9z|o?lK#N9%;}Yn zL?GS>#ag_6zkSVI93M8RVW<2Hhe9BZRy+ zmqh{L1uEl0*S5|;c#z1SwlzQh2#8Uv zY_<${jYK&DtJ=9f>|VAq8a{m+PkWYA`Gy*58E;Mm{fx+;{d4xNuEe^^Tt|+Q@hxRc z=lPa$!5e_AqSusvSE4)Y&0vTxX}1``%K1_@x|mN)1a1Qi!?Q+o%8hHP2)tXHc=D&A zkLV}j5&R>4L_Yw-W80YGVel_UxIgQ}eVVvG6kMiwa1ga|AL+#XQyZ5liaW=4);ZWJ zRvWdx3qruZ_I%9om&qa?FJla&;^^?+EQI6EVDvnOGRK|bst5qD1OUU)Gdu-&o)bM| zHpL9nBfvZbj79*kLyR_{EP&B%VD+pwyE*)bR<5g<0om#U(?FmM6`!3YK; zFRfdYR)K-RsEoyg+0eJ7wIBHIp+5T@{k#+KgFV0r z1a`0n!4cp$E!PLB zV>}GU_k^V%+p9`+_^|iESD#Hy+V1Lr>OY%&%cT6bWaz#;s_fky9 zflOsMx~~OSwYWR`-ShyCMLmL~aedQz`2o9k?~cE0Z6tIGCG=RQg#JPaJ&L?z+nB=Z z63afkdfEO|`SiY&{}`j`zUL4_f2J>9rA(ixQ_6t*vwi6S*Xhf8MN|fycW0 zQk4`Q3wV4q&f$M`a`=DR95Q9^9On9R52DL=G@>*Vf&JZm-ToJ-OdC;FCDWIarYffl zxWA__M-F?uIs4|&E_K0N*?6Ptm!n--Xa?fzyVl>^)va;cUcbSXILGj-0EDukPyZK+ zoO*(P76#VG0hhUPHqo1isX0!dN|rZZUL1RuAk1=ETzDnfvt;19OLgG92R~g=Izi zAdWX1734zyqa4b)=_e5YJ{AHb5}uOrUPfD2K_K?Zc(a4J#=O!cK%NSp-@k1D2sqsS@U#0QY zj31`)!x`spBl6PC?{O0n@go`M-XtaQ$&eq30Pv|0AQ1pQ9Ref*z-K~$L;$!a1V{vc z&xQbr0PwjGAQ1pCan=Tq2mtWvD?lOuz+`0uVo_hzmc0 z|AGpIEoI<%vg^n%78$gZkYiBV;_a)Z!q(UB7WiIo6 z4fr|z$;S%gdsl_Nr23UHr?8&w>MEQJE6usxiD(=Cvx^HBW$og!1xdu#mh9r{MLl-$ zs0HaFpsUxe0pvnLE>XyZfCN3owvO00z8)0Kxy!>yEdy%1Y(4j>b9Sz&`Oo%Fge)`$!=%Q=YFtiIZg;! zeVO8g^%?GL|2IG$2jPBq;zZ(Siajfi&`7pE-7RKAjER~;H{h7_oCr9iz)OhQ9YGz1 zUfxOIkLa18k8Liu*c0LYSfQH<%|y`q6nZ70xd{3Zg`NkshHzr6-=Ya~nJ_n=aJ42J z$bTZmu0`TfZWY;0O~aB56%`!YsxrR!x1^&wDe*1GNB z9l&b*m3D8iXW}+w%na5yAhA_n*q3bEK<_E)Le|Cf@lJD{e z&LYtLMnrcH_G&;)S6iLx%Ucdu8X=VC8co?c-R;~TGiRD1sHeo6|x*G%*7VO~7C^a0DP@oP< zZeRCQCPPXKCqDeo-Mb;<^XfeX^hDDE%R@MNz~X)n*;cz<%-dBl``tclmvZ)xX*!MV zp%);3s!~o@y|6Y5=X9{w<6;&aYbbciN9^E{7^ALi*o4gL?hOua{sH9WfcOm%ODW42 z4LFa1Ey0W%=Pv(<^ei@jpGH{5MQ@+PHlU9#;Nob`8Y%`84EO>(H@}0SzBpd{k?yx) zE*huvoO$X!G4kaf@Sd=Ox0Y;?C&yX|?SM0@Zy;VC4xu!GvJ5jGTsReRhqwBeGn?v; z@s@LzZ39|Ld92m1l-l*+6s*+lfLQ*FAuzRiIj52h^VhzQiSVCLeO%n}XNpOm2G{l{ z%6Y!eOd!PcD>BlTC;*PR!hBpJm~P}S7sb1gws#H_J9w;|oAO@;XytV8aRhu_+V!45 ztc(tt@_vC>j8VfUR3_P{XmMgnsrrq+r0x9@8FA~R%JO}X!=aF63>nwfv+Z)JVqw8p zs+d`OB*qkOz{-WhCm>vpzf`fWHV%~9e&zF1MQ=fOsyMec8?oAG`Giz)@!H`fsZ{ac z+Klo_+ij##%@XF@1%|d!j)Qk~HD(JS-d@<@2ZJR$Uoa8xTt1^nu6z2>_N&ORcOI(| zV|yScYBHsko&{n$Wtvxb{2<9@@eX$VQjl{)k3x|JLVh69F zOeXHaPh7l%-c2a52aW;z`^L|eXmOstTPIwkq9${L+2Ad-b*{)jO~{xwCgwr@UpSbceD=}v z5%M4Q`o02XLI9bEM;#NbFY&=?-Or%O2Q!z%o4I__iTmj8U2tH}fWwwBo#$xIWa~cw z;UT>*BCMisiD=|W_n{9!*@uaqY^0Ek#vN44Xsl3A+fCA_KE};i+gk^$7KxdDaU}8| z&iu`L0faA`X*iL^THs6|)GC3Of2358EepRl5$%)^vC~@JOpA!ByNTP*WP4-;z(;Py1xK#`cx)x zig}JBBUx#(j8e61sW9pzOn5H^KhJ3kZ~`uWPkZbVJe%YJIJ2k*D`$T@g?8~#oP0B0gBoOrP=z~3A2Oc=HltpWTU z1K2SBunxEIii;egJKf+B_xb)-0q1m-!_Kw|e^?q=6R!m@T|8H<p;Ftl9Zcj9oyO|?{dXn1ney_?1d3_aX~veza6c$qfJ!) zU_7cML>H?E#x%LGa7drbl5;;T`Q=)2H6kQ@aae|=T>PGkm(d`tfT!iXO~8hUE-rCX zFx#qwMO>sATpi=O9MLM`{drT};=Fd$Yex@kN5|XI<;4SvV|diMAmS^Rdv7-F1+lin z(R4AuY2IFzttQjOb@hJRYmj-@?sGx#6(pF}i7y)rS4h98B4FIP#yl0edldTYC^vE% z|HB!N3|+b8@^?d}!~R)Gn-1(j^?3iRs9p;)3&)XOoOR^7Bx1dINWC`|e1&2?6=ZS! zDS8w^GBPUYm^iijff{<_L2axM*XEF{Ds}h7CXA%X;<;FpK~OlZ7Akd5ZEGfCc43_S zw7FBr$0yGr`AqTr`a+z2yBM;pczI0vFfvHb#awRuOi7k6?=y;nRagi+2Y9vm^mMhSfK^Kf zJp!2Ye5KY^dNb7+dKu^!;|_^5yd2X~mHCKVA!VIX-#^M;=qdCk3V1b?66`lBwERLy zr?|8~nX2T{ne`R7IMJUhr&5(faT)e(WD!Fp$J?Kn{7MfsvMeGzM#dKo8aOYgCKX4S zaB@VJ&WcUBuLbV0F}bU7a^(dn8KX_sf`eOkF=ftbIjLe#EsYlsWyPlC!-4CY_7q&h z(}xfq7b_=kRI$5!HPwA_s=CPa(J$azn-{qjaPCiw4Nivwh)sgK>L4ePQ9j_vst*?o zB@877Gik(`3M`djc*3i&Xl8!;^8ocjfo6Pf_Ei(DGv4|5sn8qb_i z!6Ka#s8b&e`NCnk-lRKCvBchKOWN4-L4rO|FDsx6KW3lfT?M*4@&va{w$f)K_BX_E zEuqOd7)ARRq~(kcvRXJ0d_U$>3A`k6pG*(GD!*TrU(U`OSAy;sHkv3BGYM@;vd+Y0 z3a|5O`UCdXM`oowY)N6*NnzM2?7ctW&S~}=Zcuy|rPABbk_c$h*MK>x{0sL?THhgu ziPqN`BwOEP(3@y|T|&3@4GEL2Z%LSLeOtm*>j8wATP7_IMkld7UPVH?@`qq-5)OiQ zvkP%J$rq>l&mhbOx&5sxhQsgJG+CCsZ-ODkZn8x4G-x+jo_WILf?Vj6Tu_xu#ffKy zcrGY|IKL*qr6bSAiQ`9KWdx6`I}V)iP6y~t3l3ZCfeid))`r9GK&m@>!@E8)Z}GA2 z=pUYWf6w9(cXZ7=+}w&g(IFMIVm_vj6Zm!$p1Ett#Aa31Xj!; z)}?~=WZx%GTs-cMx|dHZTk%OjdC%5~)hoUzQKqhd)5MxMM6h3X!kK5R=#`|`&O7M* z6~B`x`CIWl@Hc@qJ&L>e+P{31OnznQ)7O#7yS8lKv;s~eBoVaYG31)~gK(C9>EZ8M zaj{T2_OOTFv%-}q39Nvd-FK0}`7-u8n~I zMj!?)056V!I|*DSz)K?_J*5)|2yjaTyq>^m0=zZ?UPT~IcY@{{Bj6?ipA_KRBH;N1 zJ}5xGn5lf8Lg2p(@b(B3heE804+`+Z5pW5CctIQmeLMorBM@ia0Q^h@oJHVE1<3n? zw4iAOo+iNiB21e=PDJeBTM-au$C4}j?BM$m5H`-5=oieNN5Dr2#OfW{e;)xwTb>f& zUnAgs#QdQEcSpca69~(M?70I%KJO&(F8mBN{+DKCSc4G_W@#{6gE<-$GbYTMFlWLZ z6ZV>LnhB?yu+M}uOxSP20Ta$NVcvuV6Aqftu*iEdj==_As`RX_&c>y6n1N$p2C%@U zRVpqc;Mf>of`H=*2nwis<5|>otJsJk)cq9N2+1k$kk48>xFqQo@!1ew1bMxoxd?ko zN___Qlr&bAJtd)Y?i4yhwG-T8|Joy`0iMENW*>iH8pFN)xy9Prk;THvTkWP;Z84o{ zByBOOYCfID?R=fG1IrOps#uN~*~DJ4#LNpVr%6+9u(o5A>*<0c=|oT-$2WcucZwr3 z^ZwBQ`2V2BWb-%_-1sM=Ehg(YQ>q0m%K33vn623YMZMUfL@7eP9^uF>< zSKn4*Uj$h0isvYID9mpNCHFyybY?#X9mD^42{@cPzG9!aiQ36A0R&$|JA`8}_HXbR zEDo61$Z_HW^3Zd+A7j2vBQwW362=LGeO%T~{xs&mhn;zy!fAHn z2TJubQ;1;=a4tYSnJqZbV9QCJ4E)lKI|MJ2ZhVXpREGZDr3g=>Op96ZM`t6ybelV6 zV+3rZ+{WjH3L>9n1bqsgeC=cs0boW5kO%-6R1`xZ01Sixi2yJ&1V|)2b0z&_W(#lD z!pH30g7R#q*duIuZ^si(%+iIPg*)PU|6lehKqk;ReRSu$5>H z0?qM1%FVR7eTsg&@%17Ta=j|pVUz%CEkP0 zuo#9Rp7Gxl(ai`UXR3OYR><6=fyqX;RV4BEqFOODDza+g0k!@xx_>R*hvRxEc-)}U zm2X&ruMgj40x4|WkvD9$ic*}&BD6Mr$r!UC(F1Ve#o)GPv(kkYCR~Z~Jba>w)Rc_B z8)RbqDQ~4!$8yctWWMSVn!(3;W%vMiZ4~pzfY@m4LYkJcmqg6ovUm-x$>McCtu=~h zhoZ3|LLEz3KY`HBgbpk=q0W$7od%QgPk1*Ym#vH!kT>;+S3KC@S>*w>Cs z9~igp{I2!^o~e2{>I_qdeGp!yK$iV;!7$%kpuBxtw+R>I2-fjQ#JU~5bF7f}*GX0J ziUX3cc*#~D#jCD(^AieUUUhZyd99EfpGKDT%u;aqne*rl0q;nC|Me16x)B)f>k(`s z^S+5Rddu1DpN$j>YvH(hXxBvRvx%Hh+;Det?!#vpe!v86;_adrVU$O2tt z^-ahMi}9GO+FcsARKN!Elpx@V8WcwEG`~T-WSXM#=>5(7GFR zg_SbyWR3*k$fyUER=K9yXnP1~y!MT~3Mht{wUZc6p05TvFIobg;d8{DP_Xd4Zxb)) z2wN9`(cpNP(a^UQgBu4I2jUUDbI9Wk=YR%ONCaet1vZ)$b2P&93(+Ze=WOpbOQvwvIF2w{}h47?t>63G>%NHQfNiZ z=nng+)nH@d<2t7@Go~smC$(1R8JwSA0DjJs{(vP*w&j6RB*sX`{H-8T9phVew<3l< zK)jTX85!me50`UBHZnH0RZ~Z-<+Ue72YDjj;(kBYrOaNzo-#up(>T1B*8}1hp+ohv z`0WW*Kimy~S-DZawp|>shHI)1UpE}vc!P-(wt{?frv=YOr)3~OEra%nkAWZ5pM64- zq)#}_?_kQ)+>Rd~v&C$60q(N{QN;=V5o>vq?JW?gdiEOdvf|a#kAKUwwi;Fl!odq?HNTZ`IJ6`*;CKRWc!9X8nM9^r1$5vWZ2;Pw3RM5W>KbY(9 za-ZdRjCpX@!+Nvq!D(D>m4S?z3P0vZKo^p6)<-akvi!Qkj|*sde+kjEAm z+|3k7Uvh_&#{f%1i>c=to(0nsyxRY0Xc|C~(#6%foW`J>vn43bHck+%Mkuq8nH5My z7O5wz6Ebvy<{C-qLT5ZyDIe3mtf@dxC%#mwKN0fciUY>S1T@?4mV0No{6Y&3zlnvy z6Q+LI>Qfkm++BIhJB<}cXCxgLr+^1DXItDga!OY(x-{%;!ouJs|3YIHo3HQ`V(TDQ zus9w~Vg$HYY8{z9qviFXn0W&|L4_|hQN~;N`)e_OK2+ZFX{4y`Ow+lJYP!Zv&yz8z z4h%c#bm;9;!psP?Na7vZIw7E#qAmoWW8y;sqKF+(<}*f*bHo*p%Y~|+f*;pUQrx$G zs&HgD8S+nK+uKd8slWz9m`GOU5NKn;3l+IICsZ(~1Pe1dZ8s^8Z~(KLto;<_lUf+% zlbou21^@IHsDyDn!^S%%nbb2?BRq(jajt_I=bmadq|*|kZpQxp%Nj0(`sP?~RXuff9SnuP~PtzQ#pG!)mn z@x5uuE_U-t0Ah-lY7$#Y$J;mR3!-~W%AX}v_g1=2!1hW!6^`p9yw8&O-sw%y@t1Hc zqz5;PH`zq~KIZM&?umDS3B1LE$!WI7$taf``0=TsN;hgmn=8g8)1(lL9u{XA)y*Lf ztE#G-X_8gN!F!VU{5)I%;!J^ti};mvm`Sv@6A?o0a6W@KOwhB-*I=0seYAJwv7|F* zFU}r5S%T)R1Sp7+ctt``n@@xD0+}_~l6`L+tF=y+XW+s?_M({30FkcrBv?CKXRh-m52dCb_sJ844 zO9X&JLV!d7SQ!E&0>GgmKq3IF3IP%UV08$Pi0UourTnuSm{Mn3hXExTR>r~+L}yJN z_9W4wA=LK1hG(6RdjEkaT#1CJpXgrVN*A*Iw_@%gUczq@E8%?`0ghxgS(wB7wef-% z@7&^X!i&^?AC4sdBr(J~ulK&nypn|%ZevnC1Cwej9&oe(Z@IYsPW%9ZS$DR|7Q)OL z>&j?mt?T76t9XRA1NYroi1y!v?30VnG;)>>81v8q;nvZ@YGhEtvFu2Ki=ivk*q0*! zv{CuU=9#tdF@(p>%27fzHWOnhdyaC<7n(Zph5Qt9x|lY=%)Lm*jBU8ioY*p&w5RH3 z9u|&OxCEU@GU>v-=7>)jk)%Y@z}?sFRFln2jb=U1)Tlc)D)nGca-=(@b?)P|2PG%o zlfqFb^bg%-4gZI>*L#ds!V!BcSi(_=@NI_WaAFQh#H2==drFP@RBD{Z{sH`Q9vk*=64}?>72~0hlpe2(D%{sD zrOMPb8r-{qNkU<_C-+mjy>MYBqJnC=`tz=WVqKjlKVRGgG``Nc#~OZ6`#bl1u+MK@ z0y&_aeg-3)MPK{`h7trH=OWwxI$qn)`=7_Mq}!F2%srM(S8$q5W+~Wa`)}mxd8`+o zo%>%nFZg&zS5^11b$Dj(LBzNMG#v#!a;voE}|x9+VTDJUlE@8bnnw>uGhes4%G zwzA+ktZ%j$)_1kF^3`E|C#*AXYS;I+cWHgOxp^AugK_5!^ccSE!7%JO=df{j%bEhu z0H%J^XSlHoYYx4yaTsM|>%7n2jMbmr_!koJF@a|=yT%*ePa+F+h~rjqZ+PBQKxqAM z40(ytalBtM>vG!}wYK35NaJ^WBppmewY;(BE>7W_%)P$FxZV6Geye?0YmH@F|3cht z5mK_7d~jO-;IBTko<$tfBqj)5oHS9oLRLv-$E5Ui_!N@z;r$kD5PAiib_zwz`;CFR z5frfqibajVMJ%L%6^?;2W^f1B;X@tR>JfScc_`?E+a&MRwA&=#xH$F^sEwXzahnu2 z?u4UBN3m?sPCgq^!IAa&Cz#pLc_~ONCE-NYbQBQ2lQ=C#W&u{>dc46kp*SD zIDNqkd$@LmJ*Vd5p|)va83aFSAGazR7}Ngy6n*G(zxSJ51rVL(0b? zHVTXx2vHl{xFFB9dF2I&tpa2&LXfY`F3&0MUnBHz||QVn#j)m>i5t>6%EGX04}u!iRFA$s)y!k43tj{h=1vk3`uSQOs| zUr{B4ZBIr(^H?vWiNv=6){QiVJ<1M`i>RUvFhULU45YSy`2toR7}P6f z6LQfkUe^q-qnx7%!}XcwYFfRPvV z#0V!G9%R)VN}x8bVbxrJjxSIupioC6gEw4m!lSR~W82Y)SfwU#ogsCMOyFra{ve`> z03Dq?85jLI@YHbTgO`qFy6v>qL3Qc3Vd(hOa-2WLZcBD+1U!>KxSB2^Dt#;xK{IeK z`;%yi%h?BYN`<3U-KbgQCc&`*UiR$j5uw%wkgyZ}>7dwpiNsqMO2JVE*~7pKnWY6Y zGDUC2f1wyW)Mic0K|vXLo&9wJJ9x$Y)P$61LX>?UJG7Q>Xum-WHw+iFQHT$8{rne}vdjZ3r$!lnuH00hW|)CimM_ z=fPufA&oL7lLDpL3Xcsr`GUWJiF}h9x0%A}!P=+fk_a6r4pg>T4Nc3GQ)nYj5_*J>6Yz`9<)ps-E|*V~^D%}*i0-PjSP<+1t;dZS zH>EgkVok$4`1%@Es5hF!LLeVg0W#x-wcX{TT_-K)DE%5B)uNL&&%px_3^r^&j%vwQ zrhV-BXH)5fsArgoJHGgw$y!{@JpVIVt|RAwFhAhk3iItIyOrx>_L!Bg(k1lUbjN{g zrAHrJ`2vi&_f7Eqg+O0)zh+MOST@7BYyu(rM;U60kG<7w>-#v)qJ#{TnG0)`Xf61D zWDF=19LV5Zd40H`b?$huu!IX6o7zv8z;+b;58yYz;zz%%%@bhKal|VYZD!>hx1gt+ zx^;Jg=K$E_%;~1K=e=RFs4VP>?RFB zIHxwJ7^E``r{+x59;X)Kn{4>P3J*+W^uSc;d8hvoGcRikWE583_@~*BB5@phHiZ0> zL7~M4N%#jr>X@MOVXVos-h=5iYsB5(;o)_X9^E>evxffbLyl9w%Li>|63ZLCJ9jtM zkh@%5a;gVq$F?zLz)K+SIA<6>%8^YawQvnO@pOQDH>ta@{v>a_f7W8Cs>GEiN5;NI) z1(LAlyn?dOJ`df7pt#N*6PPP3oPpL85`6TJL0=hf>wUL1 zJRa_CwXq6|`ZZ79Oon8dtzQj42cIz(ZFgD?FpP%ZO#b%-Zy{Lq0d2v1fT0h}66ymH zDl@?yBjNFEInj-kE{SfzIbypqOCL7#A&U!eTy#Fzr#PZ9K?AVMk`L!_ZG5`W;E;!u z^{RdIfXe}Jh%3&891`!~0-c?t4HpSqbzu02qZ&RE$vrqxEY5A%H+o{Sh;&m6sABaS zaZ-lFSgNjb#HX8We`n^W?9Z|&clA3bxuCBcg5zXii>wc82KXJMSOZ%z3B23VyOd@3qmLDhnxWH(uW zzC@y=Wa`98Qt5`!o+MgKa+FvuNL9khhy`Mro~J7v2Bi@yNqw=Qv;+Vt;8u3e=;<~- zp{}g=_YodSx$6q(YEZKqOVE8{pS6qE|dv4)BbZTT`bwxzu0{7phfiUO7Cn zd?Bi#vg12ej#q?=mhssVZ>oXub;F@eUV#+OmnEa@MP*?-6)`$4uM=rFg;;G|UA7b=ptA6OThcl%q!lY# zPg8L5Bi6^r2KtCOf`PO3RV_IkTjn`9OHXdh z)q%J`?E+FU2kE1fMPH*X>3m_xT5@u@N0_ia2RQ5tdMjCnZ|l-Xb-m*uHS-YzTr_D4 zqBZX+&%${GT63I?LRn)t-efxZKadA$V%#4=9dJA$Zrs^&A`_fToreQK6iUCF!eLGo z3rz|hR4ig&{z-z#woq_#s4Wzn@0n!{gGN^h)wQ}YkDp|_%MJ0|?P0rrg(k6>Nis=v!1du{#nnf*>*5;q7^)N)gGa9qEp# zfX^WE){roMaV*X|LP9vN1_|TW#^SsPB!u(+k1)<_Kf?I!F*q;z2;scjBaHJ(k1$T< z3FBN}gmK>65yp94M;PZV9buf8bA)l~M;PB7i}MB!g>PebDg8nfY)-vu`!Kk!|8`g& zeBi15;5$jneQ-TAqWu94_~?hd)v!FJ2XAUydKyq+I#Tl-MLLhiHpL>4)ow;*n;>TLj{jg>Dtsv{%gKTn5vF%Hops z57}*+4leSPr;a=disw-~X3M-9;~@OfPoqLvTdwSu!<?C)^&T(J>4!Y_6`E!7(+Qjym){~;rtzwIF^t5PSf(8m>0;v zf%W%*0q&Y*s*Yif&cK{oaZ(rKOD32Gy8fB02+HL72d+KnYa7VQm&Yory$#^K zvVU+gR2d%=!Gr$>%!lM2SX^*~XYW|d39{vie3h({4$cQ=wmds%N;oqF-!6}C@L`7X zT{0XS3OE0V8V;5!~AgYN2Hd9gMTr= zSC38r0!Aq2Ee%g%M8G}9_BIKJCtqzIB^;jIW*#LRo_vjYlyG?R8uKXO@Z`1TQ9>E# z)%w5b@hn6Y5L$s%=mT8!~!mxziB<655uVGpNQx2eB&ep+nDdM-tj{0 z$q+jk$@1Qug`<7-L#=ypC0 z31i!s;vJ6CC!J25Byr9V9Hw|j0LNQL5%K?Hmb)fwbwBGx^tw%Qt_9@UoZa3Gw31B zmb`Q1_gwx49|gzyAu{AVkDy^1Cn0o!31OGyc|F6qxE}#K+u71R!9BucIe$Q+J zz+-v;B?vqmX@Dybdofubn=rUh%Rv|VA#mYNo7s8;S`@(z43HV@+lia5fd;l-&$fOj zOtYKB#HXj5gm2$?zUGuCO>=gFz-`i!I^JMUL;l%KQlE+MCo7^9RvyI@;f1v!$rx>6 zN0Rf4dNp0K`HlCdLxTLgt|9V5^Ky;JLzA?FwnyCz9)&2RrEt0xeHmZ#z(<2Wh!pP* zO2hjAeyhdc*%yX3uonVWHWxV$@a)~WpcWK=SiH295wcdya?mgYakf&QD0>JhVAd!{$k}3dto&PSI zIrxmT!#-}PydTzf=u?$99o4G9I^h&Q@aR! zi5kie-W5rfn{C8_ZnuK#@81Nfm72l$3T}+nPb$r_#WkXE{klLqt6MqP_HJTETxY=Y zgM%qYT(^;}%#CM(Y6tP2xx*5zS5;%hk% zKpIjh9#cOW?zCzKj-~$rmFhq&6)>!haDsmTXrr+N1IAaeB1*f4dA{9acRAJigxz%@ zJY6sth|lYqF&M^IbeXZi<^3V@&-Olo3OfTHoOIbr%3FtEdKa4dO{@z(;ERCg^Rk7T zUXM;amM>P`%`K@cF0#R!`)`h6Z3MKj2|uly0WIC-6z3Ofa_u%gGh93`isOsDNSA@x z(SaEkZFz468z{KRypB)D4Da8Hz~norO<3y|GJ(A= zhKdLr`l}q%Xnmi95K~e!k%_LB|fR(BC1A?#Q5lFAn?fr9Rl{QuNW`NBNl? z8EaM}WPh{04+E)Meo_5S?Sl3~bQtU={6Flyd7NBTng3t8x9V1P^^#P&Q{Cw-9hPn` zRn-mY80e(g70`g}LV~g>DnaBzRf3wP+lqJl=Fjyfv1qKJSB3Mc__8HI=o zqBuG_j?3s6zwgg;?yb7j3Ck?s-|P3sH?MTvbDr~@=RD^*`*WUij%#A(5dB={Tsrs!TIUw6CVNZ>t#ixgt=9o<-xQj4ID1CxY;{$MHN*P*PrnI8*;h={_8gp&n6?q`ri^~r>SV| zCtpAMX*A3==xR-2+m#MFu`Q_Ef`a-wpaWMewc5W!Bb!auxTNOoh5Z$L*N7xhL>`3AC#dM4}uSv#GL__xb%o|Fk2UAzGnAZb72LW~{x;etdeqk}X zm7fvoXa>h0j@4u*q_YvyRySu<(sB*ZW|u- z!aMMVU*~6=-JD&y%j4Gf{;@)}t9}9TEAl|8a)-?0Wf2b(`6*y`qN=cDpo!I<|E z9Dek2D(u>6xdDBc50CKZ1C>URzSONZSSi^*$4!5v#F#i6Gv4JQxvR`Ryvv2-$We2Q zy2LCC>f>HPn$!QJB*O{vN>%-YQoMurX!(vVgp-fqfD?Uq?3UkX6OjlI1_AnbmJI`} za{w2gtyqhJN;lbqedZJLuhjRzzYNcZV{uUSL!Nc*4)KPFf=3ghY&=i*%pPr#qd7}Ji%vC-o$4`p2F7};*G+O zM28pIro@?<10F!HCJ&|y79|zLR!R7e;3F_$nShS*XOmY(y8Vx|-D#M|T~R8g#2PWnVpIG4&`oSB%j!6&rMX z#Sk5pUAkcjgwJyG+-yeu$6G|Bhc3JrWT%VFg;^Ny3g&8l+=>cblIB~5PgiJZ(jUx9 zla^3yP$`Qc^J;ZeYNHq*#O+E9M2NeX68zQ(DCfJ^szXvynBWy}ig<4$J+CkP3@Ej{ zQ~yx0fgCRgYOgAa+r9F&r9srkPF$^AAECHBvpR4(9UDFKO^Q;N z8meg&E8I@A;5;f#p>n3pdv~BsaFbgvI+e+-#@}09=jdOq+=(*W)?qAnR3DS|PESv!$XwNQ-e@IclgoH7bDPF}&`&Hj?#J!yH97)btckvb zKepKTcW}oate;4+#;*mPth^mIw5zcIHiy0G`LO8n>F({(n#!{kvDuF_?L_8zX)6*; zOW2bcieHDvNMv5@bbJu(`T%D#@;%#M^ks|g^5E#R0#!?h2}5L2TN>RK?AZ(%>x*WV zd1WqAmWiOZd7doy_cgcWtt`)lhj*DA83l^1H_57pp%eKLAfEim7i!u5&T5#iEeK+0 z_y8%oP4;R}0(Aazr?YZ%dA%u4PW7frS_}!XJV*CRvL{-4`RL~)7e9G!xvY4V%i;<0 zj$XPft7(2?8I5uJ+r-hpsjnaHr4E^K-h5XIHyiCM7%Kt8WWr!!*=Shcvz>PbMrtut z2j|j(?G`)yj_AX$?uF={lhHj&*m-^z==A64$Gyh)W?z5*sX@>^wt5DyJh*=qECBBEb>Tib)&oM>|Eb^2ZO{wS@OK^!ShM+k> z*y#JXiy~zqXMA>SS%3ITF&C9=oL$z{ne%?T(%oI$Q%BEG`giPZC}TQ&l`n|LdQiAqEF;(ZB@u19{@N!FKx z)%Xed+LWp&e)M#*H9As1kx*wU8L5C%Wt}Q%Zo1Q6_)!okR-Y4?m5m-VyIi@f@H03z zT)!d3O*$;3S_d(Ao}YxQ28f$w9Y!r=El4U)+iga!uAG`V?^B5lpRae%?4RX^Q#;DX zez9k@JcF_4aCw}X&QF@vFC~mi$tXupR8_cH{io!r!Zk+O(As`$#s7!`=q&X0qo2vQ zhL+x^*8eCz>oF>0I}Cu;M-g|k!%-hXYjcVha{!>tIehS32BwAZYU1TaEUHh2RwGVGu-@k4v`nlwxutP4Hf7MZ6LrB50 z|9t4^V^^LQESt*Qzg+KoEj{{0xydRUhQYGaKJ=}wNlFl>LMNFS@wQb?{!gCOanK|y z4xD}WeEiVK7khGw*5o)&URsp@2d_T+Owla-_KnY)e6^umdyjwIs_om5JmeADM*Ihly{F_(`o!n2cF11*1c-dvwPVTaxU#>fP`{es9S<#xD zuY4S5SRd@K?GR1xMW=o?X8!O$zU<4BSjIwAp_8`|abk&K58wZ~hm?zR1G4W&Ie*L(~ct*F{QvZelU(f`yBbS*#=?CUi z6Zj|PhCMidS2cmZ6mW|HuWthB3NsV0FyLpKK>Eqd#Q6sNRuf3yo|!n&fWK-2sgjuq zbo3;YKP2Ikikq1jHsDYbNN1m!m}kI)n?MG(%*0>FfnR@46Ue}qnc!X?fG=(WnVV)N z?la(Pn?R<>nF%gU1@nd`ka;-eZ@{~oK&I-MiK`9xXcKscfL!E8^uINMR-rF2;P9bI zj_8;(6P%(4^T;NU1w&@yFaw^^1hUS^OzdyKs0m~#l9}i;;5AJk>y*sI-}r&~CXl5} zX5uje-qr-N7|cvCr2z9MO(1K=%!Fuo3#}ul@NkYg1XgwNw;X?YS;u;c`d<~xp5qy-V=s#<0#5-nWyKGQn0=lwpR+aAcpBQYC_Kmz^m_Z2)`6P#CHo`N}{di9kl*kGK- zsuav+$a9DPoXi{=d0ZUhZ%oN^NepY1gIxMOJ~z7#V<1#%RNmUi~o4;$g?Z?h+ewrY!PhDq&4gNn>1q@Mvy7#zkpl$PIFN} zU(U5j!=vR9KN=%Ro1NEhgIinTghv3Q_~_kj+amcgdv)^*bD(_`9V1X#<7?;2LHuZx z_*%{=qR+(RDE#O-qb#~1b{b~GdvZ}uA*_`Yg9(cwM1f$Z&%%!Kf>Hx#nDM&9%S>Ha z(GO`wuevzASs~@Cn51GBC{DOmuFcPe{R@GUL+0=#SI(8gGli(SH-a1wkQ}EJ$juS< z&5HeBL7kJC;Q*DO;zZA^i%Gweia7u<^>+Ys0AP0P0OkO|oX`Qx0pO7YU=9FG2OWkv z05BtT0CT{j=5crN)juS5qGc$2wm!M=R6g8bfg;_iHy89jLr0XJqfmovsXfOzVy~un z(Fx$x4vpia11$0xioAOyg+;2J;W{DI?^6S`G-ehiB{X0qw6{v=*H%Ib3DHeE8-Vb2 zwD0g_qF1&y_HinYLB;i_7wMCoUTg&R6@h<+0Gl1mr=M%NQ3s%_((Q7-Z1_C9viqde z{UwbWm4%a;XNfd&()rl^7+trke2vFquw}iiy*er1ztY@B{c?3qe^*bg@8oj##P1k3 zgAOM>olPm$%00Q0MhiW;#&7YRjHe^|H(XwL8fiEa#M)dg&*~WM$aiG7O`s)iQrJPg zN*z$Bf#GkPctaXvuiY?m+a}hhG0+aAY0S-czV_l!K9iCF*eEE)dwigVj95=2#rx0rA|n>gr~O>=@rVamj}4o~3s#P-lD;n>p9~{pj~3-@o>%A*5Q&ZPcgMpWC3X z%q)T$_6>F|!L}-{INCObh5=rtTkB>0eDnt>Y&CM*-1uNL$A1`vr$C@mT!j5Xe>PkN zf2!|Ri$}Zx2hj?U6`Si6%mIM@&jHK4n_r|Au|*g^UmRuVDd2!CYw| zji0XSwByF*h+)6DWjzD=mi47zZNgJtJmu>TiKl#uC+Tl2>uH`SRkf5-Fn)I_pf;Ba zr2@w`OToIWjnj;4YFsZs%_e^0q+MHF*iswtchGQSf=-M595}4bDRbb!M(cp9-ZW;l z%%Gx4cK>UBd7hVDvLYXTg64>G(TdLcuVB_`B?>b`?ZOBpjQ=!YC?sT?h7(56VANg@ zc`v+x-+>jK-i9xzX0L{8wFxhyBR}kdm%OsoAPxUnw5MSQ_|luMfvENYq^Gau0sIv} zje`u0i@?ATmiW@YzJ>3HL?_y%81sCzlf8#04C?@;D$or2)pQ~5FTVd~d@Ce8LwsA@ z>2%zUs+&^eeO-x<1y)UUe5&Y>?@b|F&lO`#(ovy=Q`^4-{@9H#d4sOq^|80C*#Mi> z=a7-kK3*}kD(vb@cZY2faO!l+`i^xrw5TTws)teywxTcF>j?73+$7i}z(!edsgBe- z)Zv!$+p0ZsD9rlIZ6%cSy`25+7{5gwVu=n>b*N+Q)@{wsFGuxxG*x%$N4XH8D2uNj zAUtl4;;N)`C3ZIkR{!4^=!WY5HwMz4A7>0?TA@|R;hAI{%x1RHDbdIOch+TZr`o&0 zqqQ#c>+dzFH6M=(n-0368Du+}tu*Ea%C1$dzyaz_ZEIU4KAG{|450THBW4X{qHYK8Ty$h zyP@A40GPizfH?p#g>?XP#QpI%`Rbo$9A{HIVr};!(sp;&`^iJB;O=1onIG}(3WTsh zURfN}`Rv%1j9fMg7(F@$v*1ll4o1gfFl*K{7|T8*GSdcT>Je^)w%uasg=dN{p9(XN zYXY^5jX_z}p|uPJ;lcdOV~xw9mRAtZ`n_z1X>#Vn)L$7}`|NmOTg6n3Nevx54+1l! zo(p9+7o$skFrLgRxgEm^L;cZ_$f6jmx)~`2TULEq!s-uSCJEG!QyK3HUXRCCDt#e} z+92FyfoHkE*X=Rzr7rLv_89m|7kI@U1K;cd->}EP>s{cLdkC!TWSaL<^2RVtVJqui z{hz>hCm&qU{4~fRx$*xjRTBPTF9&URI(Xp{@qaxnR%~r)1xo8hE+^g;6MI7Qz+A;p zh|=|@SeFn|XoZk}t;4@Sz0yCIiyDS|qOtmefaB`CJ*hp`CThodGi>F#2@TuC%t~vT z&~!j~c(X#LB@SyQ)Qi)SwPM>N=N`d{RbmXyC3JQr+7;Z;8pp}9` z+ES2it2Mh<3M1{24{W8NkT?Z4Khtb1Z>6A+wiKp!OCfBJJl;w{A#n=(w2g#JQ8}u_)8*LSw@C(-_Qap2#M!r@92GJxLf%ZZ9ciVI zx(rDn(@G^N$M$YgDb(5{AJj@gA#n<*+|p7$xRruJ+EQrmag_o(z;xt^RtgG`eR<}}6NSwm*cBIKx3JP&4Jf(7c25s>p^z%8J!3>Nos9%{X zeLvYP&XrBW8cxx;xevL&U+ogqT5k9BPIl?B zACpe+uvU5sNz#jvSGFS`-bzm)Pn=%i>6X8P+DRPIN>3q4dNK0B?Z`*A(o@J2r>E;# zkmp1@iK9e<9c4tMknr!FJY(cl?Z{6Ta@mj-62A6{8)-j@eGkrogCh*5LYw3 zdW_7zQCj#%3t1I^c(X#{>QDc?r}ESEhV%|;Cvi+GJ%uFc#mI-YBOlvJPa!V77iBqi zWb;hlM(m1aNPou+sq*1meIA8qrju9=hnFD^6z#i$S<^acIHcc@o}`;L>o#8+w=fyX zrjBmPeqB3D{dTF#G%7*uf4l{v%qia{QP<;b8|USzv=mO|^KoD{4RzPl4;8sKVv(bf z&|=b$3eHXX%>e*)oCBBxfas%LjNY!)FRbrPZWH??FT4g|(-uhkB^#OKpqhIsYeL0s zEzMj4rbUi#mB}Hh*?&I))*r*MG0RNsaS7hH5Z6U%;v;%^g`bbv)A9>m&Wt!|JF*8H{6Nf-AnQjjjrEnT8#XsHsrxw(g{(@jCAJB&<0 z7bX_9i=9PntuIlQ#WuIq?sBzQMuF$G6nO6@4O8H$eos>VH;{Imh-rAu$k4QL-y;)l zy;x4O)E8p$RUGY2appiyEywZR*|7$|uU`f{I@wlVY4q2m(7FC*j7=ZiwdQnrJKo!z zR8FB9a0&b@9m8p;(bo;lTfW$@_16zNCUeQqJUd0^jMvSgt6a%zn|KY|#J$d>5;faK zNIK&$%kH~~nPfK!r8>r`{%0z*xrzLV*zOiBr~UI96VRNdR%6e3ANMmhFcd0Iqg_n= zgYu)Zjg34};j7S5;fL4r^-QL|mHadsQm}M+pdYFDr%`?CM48~G+YjHXvZT@f6^NbU zGY5cO3BVk$YHmb??;%Fb_oO0j{1#D%tf-lv$S&|ga)9ogy;HHzKDcb~7Qt?=TtE9* zX+N$bvME+#r_4gl5^zyD@@ThFl^-lom0hkqUrgMZt`pr(Jlk>YwtKRIa0wv~cCfB@ zHhdkK=bY_q_@0c9EB-rtEKT_Mz*FW!ed7w`@k!*-coSoc=6UREXE{3ZbUwW3MIM7G zxtZ?0S)HM${2+O6A+M zCJo#v;1TAqe&(gLQ&bhD#?>(|&v3jP&BrxES2*nr&DzG6^_}^nOYox3c|Lz^a@iRZ zjcv#9cPfA9^7m5yFcH<=3Z~bm>1n>2Ve23xgTZg}H}McZWcV6o_;`LQc-|t%GApo(-|*I(c3m zd#;z~^4Rkpc{azM_sa8{*t1QZ*Tn*zK3?8(aMyC^ZS~TfT(v;X``ZOP`JeXd6On@m-$VK}=Y&;}l2LH>xp9Iy|SOqG8dbe6;?*lQ!~MwYTu2@G?4IZ-7iGk&spx z?3!acpsi^AoS2*vD@KHCMA)AT&f)C0p_HQ$NZVdfG^v@RHzipl!HwXOjuthr|;{;>uoEHyqURJH9;@)1}$k#k#@H zgk)ucCiq4bX80+7Lwdl#N=(~eD_z2p8ukTZsNW^b9+(o&lW-~!Qv|pDl@j#$&4t>+ zSU837CXy(mEMP!_F6xivB_5o9n zr7QN$Pbq&*X!{&Bsu82g)EO}y=F<%}OY^iZ^mzI5`*&M(DWy*t;GRcGGG z&}7B*%Gx$!)LVe1q4Rmh>#xYiS^9KpQit>=rfK__Oyx`2RTtsRMTgQ(=yxMo)2zmV zBn6VqlUm9wgPzqZzyi9LG#by-%tmGqtCu)vyW!4i?M0O6>6DDw=g`HF#9?^jy-75I z8JbLAZywu$rhyJdEVYA5`O$2tqgU5y$1SAEZ!S6<5~YrEc(dAbF(?(o9;Tqh>dmyJ z6-P7;cOcSIx7R|O1Gx0l0V%^BHlB;sQ%l@78VrSwCijsZm2lRwwCQXz!)A5M;|y(# ztFEBb-a^!Z9wXSIM>ym&2=LSRPb8L0l&;3m1%wqN64zA_vU)KU) zX77Rm9-q_hWh?HwGuIXSgZSnVs}sJxZ>h6%S>8!mhSWHRq1pNsi&mI7 z{_tjnglCbk-mlGuUm~hAk<(pR{|cYX&z{QqU1_!!pUF0;`uJH(;AB7Z3cBK^p{*M? zSHMwM^~KD7o`$QtW8)WTESh!H_IJj&oRX40giSb%Z*#HnEe-*rBPoq43%e#p{W_B9 zaFx_{VXsuPv(-IIh=62PlFaCvxLn@bRemEy5}{2fKA6b%qRfVe-Tm*(8c^H2S%WzM zprUsGa{xdi>;UEffJ)Z^%mDz!l>?Xq0BSD>Fo#>OOOI=|GrL*Ke3|OkZY^^^;!Si( zTZpHBPU*8J(;wzlcMySj)z|cKwnE`Oa(m%d`9udHkMJ%5od00_=&}04Z?=&7qK6UE zH0s|h$Ms{x2}Rst5%1)8fcsj*2XMq~NxIboRL>KOweTAR+Dl5JyPPo74HOM}bVED?t1R4Q#J)ROh>yv*(QyFNhciLWQJj%@+G@LqBy zJ3W*6XnhylCHFz4*fYy9yFKS_@MASzE=KqvMIIYh7K4rdK*~&2T^&(Suw9^Vk-A9- zc9I_TvphAxfm-CW^PmxTRx%YY{4U+_rpJRfX#T+_&A;9KaqBXxuFeclmlz#!e>gMn zg2a7$CO9AWDYG)mZJG;p6-T;0iJsj&pS~yzQvCZQ8D{}x~E5!lK0f3DI2QbGU;?Bq^nWXtcKk_+idlN^?!ko)2TldeCa%%ZKeMt zN> zZHdbtaoU7a@za7?8G2d^*gl7E7n;}h_rfB|!RlV#l+l^8Xp59{9iQM#%EgS&?rCQW z8X#!&ezTay;DWilcnLe9F>PF#-_puvMoEi~gM-hF4iJh9%?f+C>~xWIaL(|0c))#~ zIrZ7@^%D0aNwk+Z=_a!4D?HhtX0p(9n1?iN}yR65EE{SGgiv-`N}E z8%o!1D96OyNwn6;QF>UZI4M&L&}+K7W)5n}NuW|o7DzCs$9qfuGOyRRpN`9|*>xnSGhfT*yLK)j;28vr z?sU%?y`?!@>#G!EeNxqwPJ#jR}GrU}&<1_E}SeB+Emt5q0<0m9$kz&*nNe`Rr?umO4 z8*iU)?G5yHlC~Nw+0|^7WlG4Tm8LAPS6yr1vh5$OVjTgFMZ!Qoh){+VYfpBeH~zTZVjdu!2rTw?RD0)lggH5Rn5hWLIpiBq0BH25 zRGN-mWd^W#!D0d_Vd#B$1x425=Cm^?bPF>s5TE&OPU}$1C-K@?X!XJ$yA++i#`06zIItyyzHp6$j%u z7DvdGu{yecfVWxBqk~@TEzNU<9UjtFze;n_(tSLZeFC23$wB~qAQnjbw=DL`|6 zJ5A>;v9?qN3hzs*pDUsgG`|?rJYF<=jb<-2`$~P$vtpWk?KGXY#QMZ&zO$WXZ;Iw| zqPYN?jnV>WmV;6`Mao^D1xT}l(c_H|e22>CF0Lq*ckJ8V+_%*BTC&Je&lAUlMZ zI(_5WH3#<7Us~9}R>t)bp51g{KjqRu3tn-#*ufE)w3%ZqO{amm%Xr7T9A)Z?i zVvAF(oy82Boe4(AR>1$(8M$gTKXYm_!k=Oz{0$o6*|=wfS4iq$-2W#h(@tyW=0aG-Lw==&a);7pok?>>AW!*BnhpxL&ZKRpPO~0}&!jn%kt3*-iD^fC zCha-x9Dhx=BSnItO~9 zV~6@WFntcfgXryo@z8n7yJ#LHEDrDF6PrFuTFk)6ks z@~sS7nzQ4+P;ooG%T=Vz?M_$R7g^k5p~lVM{S?=Ol_OE8c+?-QtJ$*1oW)-exBf|w z_CG%NrE1VpGbQJ~tXI=?Gacl{GzIN;?#n7u^W0anO6^I9);jlPXe%jL*UoH{pgD?o zQ|ez^=e~?1)3!aqxvv&dJF)+tocm$}+l^=DV1Q$r*a6G|pqc>80e~%EhhYwOyV<(U zx#s&m+ReKM4Mcqo+w!`u|H#AxyHX%(EEV|!{bsZHe4`jpOG>_ELH zbNt5PN@+%$XHLzj*LGv4?(YjN+^yHIwpOD#u4dry*cl4wzGBA ze4Tpo3pBVqBvnd^cyqI?H%LL7NuqtB>NIm>iI%v0e-;~se7}v*!{t(8n~jeQTyS zYgcX-TysjTV>6jEGM9cW*^k5hu-lK*TstK6PGrQBkEsEomJJ5Z1=1Qofb^fs+dBvC z))!?n|E?tX_XNw;WRWWl!)|6!=|!&FpHYvLhPwSZbu#IBg|@@IaP=&x+Y_6d1^tQb z&zVqcGBA6bPwmgOkhJxcPSEWuH6=T1bE$HaH-+^FHhAOw`fIPzTQzAw_T9PU&!JMHk#s^Y=|#V&!QuRJl4){@354 zozGcV}xo{8RSr^pWTu7%n3mW6v9F!@XyVE|t*_i42>#3!d zC>CZk3lR$Z;LYzgvoN1Y_b;)gv)m+%lv{CL6 zuXFykNd0@F)E`sADlR&fbo^a&Dr3HLd$?D(;B3uSR=0xWb@PkoB8RWh?(F3oT{Sl- z<&3E5P{+?HCo3$wz&Q>WeF$~oV8FxCyQ4{~7YB?!6u26~mC4GWE6H`*TyJ@IWmUIQ zeow5mqjh zridMcF6Moj3kcx$gb#x@E5P`TBNtou@HwjaYur8HcDCPvwyZyZj!Km#f6eIknir5u zM|b#tgBFjTxJ1{Ee>Wz^_G+|43~qw;Iu0+ohhGfz*X2h2f$E%v(JQoPNd5LQ7m()1 zB^!sDoQvuPoaIl;vw*(>e;TT~_>)et%pW^RW|rR<`vt4^EkI^vY$FDFXtFP60hjSC0th^VpcKBPpXU=ldW*-tlg>i-)fX%#u$NkyYkc4)wifw$kx4b0FI3t|vOT|5=o*QHMv0wM<;JDjy zj}p0BMX^G+gI=yqEN$l1g2`!x{0)v_IVi_MS=T!IRxhK%ry?*OS9t(iQXX;r;P`Lt z{Ks;_P48@BZ1Nh(VI#D7jAIpa?wd><%acT0$IZE4$DEb$wB=OgI~O|dNtbW<>}FVT zc39;o>Uv!3LspEzChlTP7mdrODzGb4mru?_np{*zwGV%nr58OwLSp0Nm8 zLG)(i(Uq;$d8xHruuLr(^ZToXLNrRqmi6;^BS11+GS<}}TMHDnRndSbSO&nlZmjHa zd)!t_JHAiMS*GSV5^=!%QZ_rj6XDwKxkCc-8`lKhAt6XUXKcLEp2e9{Qdgf6s}( z=jwa5H?)k`C5Af2JG`OpT{a}BW`ciSjQ2;;SyTTG{f`$uA7FWx4GX6@(xWK(ehG>? z_cAT?K9{f@ma-g{v>cZ93Eq7q+KG1?th;y@8f-ql9pgoQQ>-YS@gj#Yb0!Ya#u)0p zRNCs;HMI=#=}VCKf;KSx9Qdv9R(o2fr@y?G%~59%+6ay z{=0ZAT12?@W!BAQ!&~7>9hzEt_(^_79G&NZr+?nYZA`^RPw_Tx*LRJ#@dNrEuvx_ftAe~VVP;l| z5G=^W2{*HZ9!orYF|u_ieI`Fl=$}x2#>p(98ElrM3^q$X2K&D(l_7t$!wF_RcH$HKlQG|FZMa@C6dGHF>X*)W z5*ZR-ozjI6pCw5`1X0K=c@iRsLS{)d?`jife3gZtyW(raIrc|)&quWvIf-xP1In{lJYX27wesLFGRX=cVLnZ4N$(2 z2e#|k6El}^#r!RdJ&l)u7hWV}mBH&MgS%~PcqyQP=wg1Oh@XWkhP@~Sy9C(M@KS!m zIzP$VPFw&n3yH_3r_tqf6_!0R+V$wlm4#*3?$`B`l>>!kAOBF-%CWPYfAY0-eG`A- zr?9NHf7egP&T#l+=S}>~fbToB>$fWx6_zc3TGuZPxv)&2Nu-gP__CpV{fMsLjh$Fn zw&2*VKaQO~@iBn;dWEbyThmJ?-er)nCgg<^R~Y0S*nT^lb=9eSeY}ZqQuU&I{h+4z z*y>sN`pKgS@?q5z^7Ylt(3RE0^Yz1;-dgoQQ}$Hq-z7$+{!9D)k^TPOe($v3XAdbf znx|*MX3NfY9M5r({^OeLeY8;Wu%p7ku#VxKjn)_AaBcaz^&YyXUnzR+fnW< zmU}zPy&PO+M&48IojbIwcOFyy$3P#MIb8Z3w<6pC?aqlm1zc{;O{>O4zmUu0)~h7 ziCVMY-u&>;XxbkP4^`9t!tl_tw7+9$zqGego6hHOUvF=RB()4yHurY9L>D@FoRIdV z<*{oQq zQ~#m0V;umZF@JGyx39CJ(lQpP#uL9l_CCi&ITqS8bis;COF5hK*7{00>(4K^QvE%M zw}MEZ`gRMHqZvqNtw8n67AS|Cq7d`%U7$MW+B9V4NCK-Dk?ch)UPNT2Q{x1;8(?g3 zV`+PQ!jMANgW`rgLdFTvAYpOja5D))1`<~0630dhn?UsDT>C{5N3ZPXcX;x3Vh!&k zD&ovUzi+-{C*O(>@Y+fADRR7E-O#OV`A^ah5uHE%PY)Hy#LAug6W?UCY4R?5s6P^Gdn!%XC$C z=lcZ1N~V6yEHt6q)m*VSN3X}W$5BX{8!K$P9I3Da2z!;nMoPI2a>V^of9#Fxe3O`z!4ogA z^sJxlDs`8~i-mHj+qy(dhfREAi+LcRyHM)Fy!K^q(^c(so|nsGM3#z(#IK<#C4Lhc zoz^Vb|0|_ZKP4@=-EkP?GxgJ-80Uk+IrE8eUM-xnpAe_|d<1uc1h=gId|~Fc%hx-9 z{(LL`@D*g#)a;hq>I$o6yH&4zMC~m)TOq4w)uL-T5)tBj@|MgFtl(w=;^aI}^dnX`#OQ~4D0QR%XfNfrpH&Au2`o+L{ zvfPZXVVJ_%xEBxJd(Qs$*?{WLW}G)QWoJ)d6>k-lC&N+e@Mdb^((sieU4%l=lN(yL zV#Kd44BWJ3GXb?Oa|iJ@L||7a6{}BoT#OB&|E_Tu#-=&KcjxfeaZ*For}#v~q->S$ zQZJFn4#u@^s_M0Gy zWL0@)3*yjN5U6N;%lkko?@vl5);9;4)wHnxTQac1sqRSz1y>!I$7RAR$Z8?kZ06>f z@Q9{R@>Gk1dJ6sFP3pDw?%}2Go*=1kUG?IiNk>mx&6dsufmKKM%2k1D@jfY{vB(zp zi-oH{~Z z9g8yufFl!tIbIZRXxcATlaP({xV{QNq$0`e9;e(q%av7mB=Kgy%8=-?}UKlc?n@)%u(Quc?ZD!|EY zIF=jILDjljt}S_!kw5-0QOq{|mwdmee&T5Im%5Iu+!b4{oya@^Fu zt{J8x>`iReCw(It_ZqN4=4Fn}%zV|Ew4goBeD7iB9(21r$i}mMhI{uQ8)L(ZUPtnc z*YnerCSOSIO(vEcXiYeY^9@WaCBu z0QK-f#MWt2Zi)<(3Qlmy`)r#-)1^5ro>=fh+{mc7+k9ozY=n|?oN^Ucx@%~ zl-$%A&~ZgnbVU@iVkKL2<&-WCc)p4>S0BMSElzP|ca@n`$EUbb!sXVpn^e=-nT{3PYqpIJ1Xl;1NlcXqn7B+_@N9o$FOJzb4abay*uqf@BJom^qaP8Y2xZ_j@? zdv=wz^z`>zoGqj_dXg8tk?*{WANFEV6OACjVs#{lpj~wD66Yhmbz7UCH%x9hCBzPC zmg9pL_opqYTIVwtXU0!(e80`^G2E=8Z@h`rHmiC!-Yl1zM%k(^4OMSh>u$PliN8MG zy}*rsx^O}Kt-2o0y|4tAGiOl_cE8Ep7>+{1 zi-m)^dz#KI!g-6~C?t%)iCDNG5zg_w4M+5LewKtcvQUXL|7be%F?;8>ll@YQJ%u>- z&TGfHwZ)!79DC1i$GNS=o>?eExQDkui?(L4AJFM<8=Qsv=Y^quZJ7bR<_tM5&- ztv=HA6{m0#JQvQeC&$1icT{^x+_ z&6zELzAne>d@p(@+>Yc{|A?q;VS-)D_jNz?7~Xh{IZ9*93))Ndc9rUBwBO;)3JIS< zf$2`O#`P2$*MgL|RLLaJ{m^?_lcRZ8Y|V+3E91-e0()x0K%%d_`QnOSOpAb8*Bg<);+fj6UEc36A7; zyN5HesVu>7H}ca8u!vj#H1DCi%%~i~Uz03G5`CPlVD>gP5sWU03!ddWqO5U)kyS7A zRtH>Q5M58G3vq5rOLSL2mqEEi9oXB1;P*#IAO+VF>Ijw#Rj>_h)VB+G~wEg&DY11;qpPfO+mUUVufaK1VG3Ay#m_Rm#3 zB40ve5B17%MzfTgA6O--@L*u9fjz9#>lO`9gc^N`9;U( zNU7+Om|H5Q(lFiN$sHU}5!LFkj=v64*i|^1_vBq7XQD0BjY}*xfQNQG18@NNPnD# zRDY$qch;T3rm_0Bn{I3_joT^Zf?nl=@Gt94@#M7u#~OcviFeRb1k6`NpD?Wk~ZkLQT)oI9&tZne~l(0>eX) zSq|+li-+Cbv2zCiA81WUskq?&oEU&<1MRGUi#D7yu5opv)03N zN;2B;WbVDrj8F5@jsLU6W-m>$i|5zB0ekU@mXl%FF0>HBJY~w=xWU}a8}Tbk`i94fLCa1)&QAWxvg7LsLd&AQ?p{EDqeimY z-u?!CyE`I=(PBB=59+n*!k&?wi75 zT`;FM9|CfO`>JTI82yc`23&|7;czR&Y7^6Hwq}a8-hBPMd0cidgU5}r-11vHJ0HCQ z0Plmc$TuyAd*yK=U;hPl+-|Kc%r7{ds;YYQVT#<39>&T1i=m#`m9?+t1COQb`0c3o zP33_g7s5|mMZyQv?a>C*$CB&JgQ|BMYvRAp-=MFo_NW2zl}7$Xjajw1{sw*Zf=jnL zu%_+VKS4wPhC1!B%HeWr|DJ<1tu1P?i72}R)ILpoaJwer|R z`M6ND;Z}U;_weVna@0imgiz1`fWi_Sl=Zk%mO=1xgHQx6KJ;-zdH-%GHws1I;zL&& z3UxD0rHA{pg(7fq=S5yi>W&e@Kc#SLG2zF1tzvD4e_G)mvGAJLssqjN&nWz(7T&ef zAK964BCaqG*W6$Ky7{Xks#o&oMI6RF*u1B(8-`x9tM;d zaJmEx_*w%>xUJ~5Mq0!|tDWby>Yckg_(ZQ&l^x{Jw8$)0H7z8AEKG~VAbH=6Tj=XP zi*&F@@iV`Ir-x{tt^VIL!{gS$x5#rNK{pG7&k>Caqr5z(6s$UF@kg^rLV@T0vV7<9 zC;9HfpBjM%;tBp<&flf{RWIX^YP5>TS8CY9Et_|%{BtbGKS@3Vcg3RH)cr~xNjf%EY{h>Rk z&jW;yF%Zm~Gh66<4ua# z8}1F1+Y@~2j<9ISqDQp45%{?0&2eIq&eBSjF)x$j?^*nPhQD<*mFxIJ-j}u1eb7 zp>B5KyV=P;ekw#NSQ~lWi%YBSLIS0ZN}ii1tVZ`1=NE0yDSU&msQ%pwEoQN^)ETq* zCdKSbvDj=7F^hT1&ss*;j#tg1!X9@_WsRtGK}9ITWfx%^S7deDB^L;l?k!zyplQv_ z-%)G}Sf#0=3ybEf<@h^ns?{nEVp@~(cW7$Ws)?)apoX}xh+D+l+N>?xQ_Z%wC)HGY z=a%OBrJkt3uumQDso*{ysl}q=`m9*l-G?hF%2H2LJSUgt5+fjXSM_LrhfRK)?2h)k z>evFCwnH+F`xkw<9fy|DTPl@cphPjwFVyDd3zWEZ8~dm+Ikz8vC+XHj4!9 zx}$se{*Axwl{A5XNJZT8lFuUUYhgG)_JhZSaY(cU&A-1xsP*e%S z@-?g33W0g?qRQ;{8CTi!6!rXsxV1ok=*+ez$9iBdiJoc!i>+yss~It!g^mtqkS)al zJ};;Q3;8_1GP}jZQGSLzjOAI4!bCm0LEMxe&uRw_c~%>^(jlj+dhPO*2W@Fip;e9IXl;S9T}9!xvFuo?4Ao1Xrp3a0IC#volftd^kOixr}CoN&M^ylcFNDKYJ~C zxq$@rk+%|qnaUVS^GY=+?#nEn# ziv9@4m7-F~WnGifeGghMjMARG22WfETDlMEa&?<;dK~=u0@dHHWQ}R58$7M4H<#AJTn5DM z9XtE-8#SQYAk2EmZEPlEyqD?89Co}NX=Ba9nJDU`*8g$8!}7&-lCIxDqH(~-9xs3? z<(^|8%{>Ra^YH@c4E7kqDLP-~n~Jj-=xK;lagH7o>|o-ShU^>cUiZ4l`i zgB_$e3yX{fSL9o%7Q>qrGIMcz@LjE7g{ZwWY0oOb z5pO1Hk9>EFF@=OLOGTbl%0#}TJ@P%Rlob*(w{E69t0q*vnM>Ovf1{PMLM-LzHgcKB zWmc7eLcN{BH(SXo#FCF#+$0pNZms$%t?+-hdM)tdiKVuW%Uijw%Z7$0uW@^~M@&)a zhe~KvW~)tVjQ!!Lg5t`G=Eqgm9^Xycy>Nd8_i;~c&dz<(qp<*aBce>PWTW)nEdZDQ|zD}wmcB;Ox z{9*OqTJNK)%luR_xQkE-cqW6c>JlsyL_jwV+)a>^0m~kzFfJ_rq3$U5xpgFyc|`HQ zq4-|9@J8dp^W)MpI^DWkN@W*)(^1uM^Be>h7u?;m3eN2#&(EwnE-AOb&hjvcVcx0o zvbWaI*Db^yQlvUl%}0k8CPGG|JXfz~b)PYd$kt@cjqrUo+O2{eTaa&?_#N2+t$kj@ zOPk_S^M0G#Z;P)hp1rD=YiL?nY@7J75sFX+7Hc&qwx$eLs0$6Xx!BR3;N_rht8N7M zRqbO&m*Gk~u&sJJ!R-^GnhVxEep~fm>f5xE+_#k5tx?Yt9a1WXL$uv(Y_=*_JPI3Z zla*@+1}TT%%X2=Q9#u`oo-AU~jwR^|HTBpx(B>O-z8f3&(k=4LPD7ogKfOVp?DR%H zy8>pEZF@*(dwTTL(dGgzXxCW9OW~Q^d4=d$O6l>l(IU35UWk$=*yOv!YI}2mj)Dg3 zzdJeg+--CK!1%T+-*G9_l@9grQyvf1WlyM2bEt29JXGx})urClUX(ciG!lS00BlGA z<^ZrU0hj~8WeLC>0A7{=%mDy{NtcQ_uiX@kz81N^Ci`sy{ zV^PwE`lI_5i7uwfZh*`5?e#|~WG6l|`{UXs2_-y+-;_ZrNFgdqXDjJvpke>mT@v0r z?upcww_Hx(Tf?s9R5m_OC&DJnk4wOJ1_qt=^O~Wa3z=3yvVWlRLR`p;{YQE%{Yzdn z5nrtvlVX;CqfGAY9zTvj(xjQ~LJPUNs14&p_-X}uVe7_BD18#%V$kDIN@289^6Ou< z$dX<9Ql)PjRFXp+Tnla25$8de>De(4ljcXfkrjfJFcMzM}-+?3zB^!Mg$Hr?2K)F4}^&Fpp z{feJOCc3RUXnY)h^gS^d$6{xe$M6gy`a$L*>g+F|&oP4$$FW`6Z5$1T6HtM4mzO76 zUv!xymWy7(urtqhPY?}`#g{jlqA!ZT;OX3boLcw*hV}k)`neH9scIAqH-NAQtbua| z(pZfYR@kC7c+TL8g)LgbN)ufDUWDaMak#_1Bznl3<#+F83fPKy(NkeF+#PEVY$jMw zfz24z_AgF?wZ~MhuBhzwPD=MMtV1cT^755&K;vT+AxC9D#V{H~H{fxll*?NwQZCUK z0XFxElS*iHq*=k7Pi=|5HShA~(8!TrZ|3 z=~7jDB!>s~U<-pP|T{-rMC%LEaRPZglN{gN< z$f6d#W(8>?TYEV zfD9BlP!v6gqq+}BJ;_BqymX8)?m&EPkKR4*=iTFCP;UH%%Gb#&rN5vBcd4{gQ?Lb) zig~Cw7S=mYYhBgoyV)F9S6cX$`_l&Fxm`zV9?k`^muPfW|BL;5=GRRb=aTYh)ybTl z6z|M03pc!T^~%%o%kmHZX780F`DL$|dhOuk2whly*|$DjT0D6tQgES@AH+GqA_@ON zKe~SZ$=fXG4bOXLb+Tv?L~F7S=S0!4KJt|-|4uY-JN3R}CqHD^*S^Jj$)s+s%r8@@ zqxqouZ?8Rg^~y#0W#h%qy?&AfASeP&dLnp}p-ff2__axRS+% z8ELJaBh)Ph+}Z^G8T^S?81Ms4pjI{$=Ns_GCXm*dnczxaXx`EUvYg9I=*&{CepeI7 zzFlTw*nr<@0>3NZJOe)51l}zmrA_q5n!qm!D8+NG{_joTCk4FEfXuDp9GT1+r@$;X zf$tQiLUMIxaxvx`1-zOc8f*P}bu2SfDfJfhYdEgd@}1G<%O8>sJF+e8wzMz2JSG=z zN*u3998-zom5F1s9Lxx03_=H}we=Xh{VR3S-*BW>=UDst*%gH#7d=F~srb1S9YNOI zFRauLBU_D!-R6qo9z@*V5Vvt3%O|}@e;JFZAUpjseWvugQABIK98S1@)(Xq5r5ugl zipIBM8kiZ2#w(15ep@si*dq-;a~^#B8#J=EXtU`^{cIJ1I&o!*%azG-jmyz(Ri*|P z9&AE!$QLEZ%mEhWW8{2{%+TYI`zmj1d>a;6LvDgE7LQYWb_L&=#nv}{%~@6ym^o}w z(?^KRMw~laE6}YNI9Ds=Vknoc?P1JH*l+a@HfCk5%xEelGii|ueldn~_B>D*6{}#C zs=K4+vD?XQTzS^a<2#+a-CJh|6E~%0m`~rzJ!sj$BF5$eRL~om6%F~Sy!WRS>7RG> z1i!=apLP-S*UcZ=_{9N+#G-Q!dFoxJbz|dHAv6vUT$^hpH+u;(&UCWD@1#T z)etFF!`DzxSuej@HR*&ndf7ISEr`p~mSv8>4*Q2hp2nCUUMFx#H-*+S zn{C_yvFJx?*(}iol18T*-Knmq4$F1DC-S1)t3CJTOa7s0euEd5e62iA4!wREB7qer3Q@GxXs?0JQJ z8)kSl6r71$zJFb3zI?zPtXtn?sk-`GBi-MVu3uk)37)ec?yTR_?A1I6f1@~IQS?zp zeP)&|;E3r90$Znv4sYJ;Dm~@5htqS>&z1dbbcK?nJrUf$&bUJU?MQ(`TGJ+hyA`h; zbdNx71@o2Nk#_73x=*0Xyv8r!Xj;acN>1;J6q0oCr+o1Aog35*yb4b!uNs_EvIED^|vOW=2#Oeb{gm#i{Um9 zu}VJOl6zm1@Re!=M15Nl)f@oco&d}N;2jCT900CO0OkO&H366dz&jIwIe5H~PL7ru z{-byy{9Q@7IRJ2~#fi@x061ad0OoM(+GkMb{$O>T11Ge`Us4QI2j}@0JR{Ci-$Y*h z@K->1>!^MuzVO$0!so(85dAC8$}L{wH+-iRxyjyVV9#s(R;Zsd=X>}KKQE@$XRG)9 zqtb8O42WwHKg%bG{tbLpZpTQ7KDO${Agvj!jVuT`l|lrE;W(fh6_ z^tltTv5N$z#gqg2(U(X#`Ww*N(|zp#1@$*kdy*UFT{vr;HgxPMixGQKLG;#q+?cmD z=#vA64bgV7{Adn%obgtTi#*)klqx{P9!pT!=}o-{k9fjDx;QP51NjlN@IA$YG?hnp z;%iaxAJ#66#+-_a8L`MI7AG_n=e}Po#EaA{D}f8#H?IR!Jj5RPa8oM2JWoJ?I(d`g`eRg(=t5>>nyO~|(b*P8UsNOm{n|ld z_?IoJ!%@}^$Ayh{)^8;LjfYh;BDJDQGp>>t>#YBw8SYIPM^g$g+(@HCv|Z#aNO@JC zv^QVYe~ka)rdaLcOv5c;xv6Imi3yZzzvxp3B@}8SpvrZLPsk|i;;=8^OWcO{BG2() z88fG6&3&ZudFry5jG80S^b4pEDWD(mB1X}l5?CW|J9dkq+r&_lJr%e+|MztA_7~+X zx*9r5FC1U#g*nQ(F^!jwJWwMV`qPE|(c1uw?6u_CC!To1KHY=6=5+4t3OHcqwuAMA zY+NtOnW2?VAEG|@N3@&&gud^nKCCzD8+nL!dfd74D13AkD7AyuQfiknbZiQmNkj$E zf@LQ`A=<7{B^PxFQkY`*A!{HmF0MY;oBA03DYk>NoJLq16+xP>V}&oJtKh1h4o}Za zJwQ@eyzW=Z*$KaDIX>P;V22B2BU*-@C63yS|G4C?KF+$tmcKI}bwV~ggn`G!-`B-MD)@c?q~P?2)spV}4gS7Z;y-e6|r#;%hGf zckU|FyTro{J%^YQXJh<~i{|u#MoN6$PJ98= z*B48A-N*!!&sYi%xNnxLGybcE%O!2UM)Cf>qaM1){Z| z(5zR52;EW9Y7_8HO?VH62@4>4ibLpjA3N8g;&SI&xHXJ(#NiD{FnwU<7hH~4<>IMQ zek)flM4yy^S;FdsF~?aR)k|u_d6W~JYq=0!dplCH#@d&5S4@hmWBGT2-r~;Gvt*@I zU%P+i%m3oq)E2MtLu&Hy)W@mrzMXiA%CJAucqx5ecnmC59`h_T;vzBzz)z^L4B+&= z#>361**!<4EB`e3nm>M2U#$eqp|&h_*DjSu_y~0L@`-T!$Kpr>V?2^4$igxDIyu6B zb|bW*@f_2_K0o|1QHxc2Dgg)33dcD)1N+PTKJmGpxW7}P&WFuJW@1NF)9?*T{b z5YdlojM^mj_{W=>pOHisOp6?!oAhy`?lLvQ#zLyXv}z9varp=3S0(Wpi{!k)oZn}G zKm9p5bJNNN@1NhOk3TIwcs<1A-l}vXWa@v54}VGtyVQM?p<}_H5(_aOCJ)DqKm953 z)Nee+eAFcj{ymXAvR3t+EV9;4oE-kA>8q_=tHkGM&LG~{&z_Q8v7<4+c^F+6Bk=O_ z{TM~%LL;$+k2h9s2T&J&L}^!CyK^-mx`X;X5Zy$#*GXwunJ+@~r{>k=6|6mU?;MD; z&q?suZY!?Q9jb%w{)?An`d*nSiCr&qO6IUPxjL=-`4wPj!q8LlDyC{oIa&&T)KRW@ zmp>af*j#u3-P=5$%ZYkjNG{mw!t72W-t8WLG!EFZ{v!9JhV@#mdum)v^kIh(4Uxn& zJu(VQJ5Uf6$mui#Wp;SMG?AL{F2U*8vcuuKi9gNIDMK0RcQ#rEk&#q<)|P&&jd)X^ zhx`6@)A}TB#0mzRl~w_zoJslYrSh@ZT<31pv{rH7sTuTOwI#9VmGU4pRpcpoqJMzJ z%2>EJ1fqTTv3lxfreYa;;ZN1hy(zWV^7tXn5sa;fi~DwUXGhcP)b^?mtV63|h_*6t zx(!=+J3|RCaIHSh=319cm%q3|S_Sl?eZ|N@Ot{rqSWlyRAz4Wez?uj2Rv)&;Iz-FIk#Hlc&ccbpvjX$c~BN3TL2gXeIi`TrsAP2l7zs{Qes zd%N%LZ6@i=O!s86bQWf~Oiy<*(=5Z9urDIWV#u%sLRb<)F5ZR!879PltRbSfKorEt zq9};CqNuo_f`|w{;i2S-xbl4NyBPl8?>Tk5Z_fjNcg`+<2RhMk>K$qR%mcFiVd^6IzCzNU@9N>#{com=D2_E2UF&9 zr;K}IpT$4d?Ctpf5SFy%4=Q;j7As~=NeV|XVtkIVButSNVsW%;Oh7RffJKAUnhBZ} z3!0ElLffegVVKFui{fiP*m@H@; zgG?z8znh+j-qr9(f9HJNk^au{-3cy2kq213K_p?dI|g694=u$1FS3XZ$aAV^Ax;8u z>N_I@s_w5ajt_UXyaygI-vusI-unQp?3}_hoBb*12Xu@rKPBxS76(i~83BEWHh^#C zAt?l0kT=^i)=-lc-OLbYnarV8a-{hox7w}9_gQMK=MlcIC9+|=pxiEEpTJz}y zmw+=T7$tw(@H4Qqn8oV!2o!Q9pH2&-5Cg(?xh;(#?C)wWG!TY8w6kA<29pl3XvvhP zw}6minA(wR|U{X2Ge0gufIqy*_@4Mu|Fe|Br`40l1 ztB-|P$6^r!+c+lC%7d!AY^I3S@X1jwxrj-&=CeJYjAzsY!%$T-r&#kyR^zz`O!CU3 z8rUb)ImTS;#!p82!so2RdLw;df_Bj@m-S~Rx^^edHC&E%hnZ4Sd|roq;WZbT^tRbv z%KrnJA+A38zvnlJ8~%T!15d&EzoP>?EseEK#Kk3kAJqB{-*lfdIaJt*pNSTpl5 z&&M(!z_O*0fN`?wgUtw)xyHfB7yn*m{(%jhW%7;+Q41EnN z2OaCV&xA=+xIn9+>R$E%8QkX*FJ!m&Q`UIphj@*wzF9hwrDX}iC_pac>%tdOHv*X}yx}D%t z3L<_mlat3rX_fag(M@vFc2c99a!(|LuEL0hpfsq|=ro{Co01xZ;wCY$bAuYa&9rn@ zE_^iGVQg?+{1=QprVZL+9}Ojg8$MjZ&#J*f4UgXkU6}IX#teaT2*iDANI+De0s5Z- z00vHEIjDHSPXW^NatZ5h(8NN4wpsoH$$%Z(F6a@Hpo*p=?;V2`og33Qz9=AQ^bLGfdqPF8wC@^~l$pS~=0YqN#BVAQmUOjUSzR z*b98)gaPhjXx9B#OE)3un%!tD0{@PjpmTFQ&bi_h)Y(M&=b00z5m*AqEOrsrN(ppA zfeSqj2Y=|^QWp9J?bziDt26fKgMb!R4?@gyoe71C{|r*V+#R#mELwA;0jrt=tHXwH z97x$|$Nx{DyT#SZV3KSWijDL@%@G}ie+(Fab+*Mw(V>lAZTTF=* zEU>H!{{ILVHvBA}DdYb|NRPXRP_J3t_&*K)JMjM?{-47Ci}?RK;HTh!3*4M}K7s!e zL4(K3w&RNpZR=Y6F9&aj;{Q1O?*{&c55bR0=d^0JYmlJBcY;NQwuEs3t}E-|D~OTy zv0itFFK%F`N56;CH7KVGisN?!2gZ*}w=6jtkm27VX;#aE26&j@XAF2w13ZslnwTK= zj0QME@ZAR7(*XbRUjW}|z_S|Q9}#@10ncoJA13%r1D@Rg-%IdO23%@@-%Icw20X6; z-bgSO`@-Pd26z?0*enq6{08_~g8zUzu(hO}&Q2p{XY4?;qm1qJr#H|S5FMM2rgqE- zx+`mdN3Bg6|LY~Gh8&^||6bi$x_>5an1pK3dkT=@dV^ljpu}wl>E{E`2a5y7pp`|f zwVN4pPhq^^_=N_=w-P=N@Hp}s0&{}wC&QGtnT(NKy!@{ep5_6>|GVg4JM(Dh0@MdEAK?b5wM-5%XbB_@};Iz^FL zNk=k0`Zz$cea6RfnJRVtTZ1<@$z3-lcGyyI;@fZ5ChqI|5^TG!y=Ewp99zV^o2OyWmjmW1m`bZ=*DA0{;-%Q zpT2;Y!Bt>+U?EiF<%z2CZm68b4H`N~;6*4t_dd-((c~ONl+N01R%clz*&c?U(~-0INFi5VN$2sW>l1xf~tJHXO)=kXBKYtQ~!oFrh`~gO z(rQ=9@!VJOdIcg-VOd;05HDG_vVa?oPTJr47=+6 z+=Jua0{lOVA@g7Oe+R0!2SpHvZ^3BW0RZQzTt{C6Hn%y?_rSWnnbDb?`!`g}1-Ni1ZyWW$xk1_R1!XWGR z?(Lm2tq%F7RzjZK02Bs))Lv;cpFaP5tCLNN^J&hDrgA9Zh~*kdX>&N zz+VC`db8WSM6d=fnHM>7q7$dAIWL-mh5@x5 zPy`{4D-dK&=6s}!J=nZbMAnF6D7ZonY8h7O>{^c(y%0R)3a9<*_qM`hO$1T8u5~Es zOWA+2!LKv=U2*f*-r??wvaDm6&iHTTQgIW0>S$I}Y?FUCw(F?-R>y)S|D511puuEt ztZ?JCiU~LoiZy81$H`!=cYVAe9A#5G8%wqIEQ^H2I>coHQ^v}LY%yrzjOeFOUxO%o zSuNMS2F?q+tn>CzKTo9{{T`x5i#$9`4kvWZwsCVcBafTe-RJ=Yn*H0PE0f2q%=pf zYBl6$dj}3wN^ZC^#6t|aPBRth$>P{=$7<8Gqt`>9?dv3z1y(RE?Mv*7wKg&ieoC@i zTO9#e4l44uDD8SX_{weBgTsNN;%VFK7hA}Vbq4U7vaO{Z#~LY`0qmB#1HPsu&8yGA zaBXrK>qCP~KCv-MPCWTt7S!R^bD-4!H~USqADZY*fIj)&jWZx0rKtEZJ6^ykE_Hg^ zN$5kCVsqX`-xxO+*0iZVdzG|ZAwQ1bSL0%=SEc9}6=qsG(y_*<`oaLkiF+h(a3+O1 zCDfnH5r#QhxT3o$44Fz*Z?GqV^h_-#4HM!xiiKi$x={?SOox~!=+zOY2kAH!uOQ5r zBGBO|5&^Xn8y#iv=sKi*CR>1^45(jOl1>V5-rzu%tXVPPQd1vzXlH0U)PB=Z!IS~e z?J-QvB^aqN^nz#7GYx~A!p=xy#8FADJVN(uLA5opEw6$kFZ$Cc$_u$+ZV5rpu^NdC zb=z9hsjV{F$yh&pClM45Sv68yAK{3aF}$NUI(&b+Qb~=prKL>xI%cvwmlhx}j-HDI zbYaMLyG>1#(7H)JnI8I~Nw}HJRy=bc!;Odl;iOz@us4C8Ac(D$h{WQZF*Ve7krHji ztVUTlIt1Vrp;IX1b$X0wjVYxnc>>u`J`h@E)Q#!nedAty;~V+M9KySF9Zw?+8?#=~R3-IH*so48Xw|du0I}TnAKo za3{LZ?p3BID^qQpf$Bg#PqV$!w9_C?bWB*n?LXToNE^WZ053N191WLv%&Q(aaAn#$ z#;HFpM{z08n>WV@y7UA>a*Rp$wkO8OOK-b3Mh<$t*dYTY0{g?90^>y<*}TA2i=@u^ zqpo0<4_bKGMvy_)6g)d|pWT`8yV%h+4>Omr2P(Sr`cF(~F5K}4CmIEyha^mR4jk(N zir1CTGSKBPM!)Sg$0y9?>qd=wF4TA0FZft2PFUge!Y?AE%j1pbfpC0VypA z2g^Qeqf=-gQpQYBu$mj;YOb*Gr#9m7f=Z!N6*Q)l@D$yTD8M<_bHp6%Z1e%_!U`K1 zN*T9>1(7m|!54KoDDuwC_H=~c;Iay9V{(GdCX+wt-OB|9VcHP!h9kuPLbh*$R4WFVl~xLz|MO~Z#C#Ef?3bCgiAhQk(QNtkSV4XW4|(~{&JAFf%| zn>KCE@uZz`T{h*UXasasmyc@$Oh$dSestqn+jX=$jB&F~mw8Iu!@4PD-GXu`HDr+) z`%Tx()^K#Ckrj;f`yjWbWW3;NRHjnGFw&UoTnlK*l)Fek?7{)WNh>TWsS^iQp?Jl_ zU^9LTonCMQzM{)BO{J8>8M}mNoJ+T0mU3l`4VBs)+Y_~D9+Tu4V_SP7FIdK6Ek&(L zsk8&O#)W^)Rx_sCh#4c?XU?j)*4sd5UZYH!(-A|h34jda(gDvH^V0?sva*7}cCtE* zjskElB>?4TS!_vx0}Gxj*O{Tr9E3!XW;(f z6L{if?$_dp^Mm>SgStC1s=EVQYt7#U`>?E?#=LshT%NYPq)MCJcmmVBR~|8qrR+a_rMCHm)>(j*c*auMvp2SLZ8XEdyT* zCaYiVin!mVUC%5K;AI7!*_xIs7yA?bBJ{XisxX8-MOgok?#B8^W0AVkWNE~tg$R?Z z>(#K)MiJ>_JIRPc^j(?S%&9_$#5v9!*xf)kr`-UPy(m!$R_y9w`mLBq#VTeVj9skF zR5F!`NtMlf+Fw2)^;xxrIAOtk(t$qXfXama3EZ@$Q3Po-iHBH*aL&XQR1LX!FUlU5 zuS}XvF*s2!D(5q5MxM{8&C^oSwVq$|F-~ zjW~xzOX~~GqFtDR@ccT3XNpT)k)MD4Z}YytGBTq!Y-+e!#t~rB~jE*`z&*gw(YR7HCBa=i9EQ*_RgBfX9kJ3W4iH98!ttpJMeF z@mEs*Okuwh%^*ITEgy)vB&}Cy$H6V=Av^}4ac9*UafjIyJFr-3iHjQf8fzq~kGFuA zzSq^3+~90b#2CvDUIUw*9>9JE`&>FD4+`l)51FLvcslLCMr&vpPruq2Pm{0&6W4i2 zS+q9H})voYNh^5HhrMIpAJgKNs$JHqxHrInrWEf2iZ7nHp3+&vQw|EyZGbo*u z813%(bW$drlqip|`j$=1mrw%Io>SH@AftSwQCbO|u}pWS;hL9mPTw&l=g8i2ZzX30 zuw)UZ=}0IVu9{5%>n>HUcetG$-tb0H=i^9FWl;pps;x>@a2rdQfu@c(Dn!Rsnce{6 z1Qyvt4Z!r`Jt#6c*}#(l135@CD);uh2L+VE-H@+wE-QNs6Qi3X=hT+N#nT!csfiTR zhT_1f*n_3ng0wca56p{RB=Jb{|>Q?2%cHZ z8W*-uPGb&ACr3HZrYI$8N*btU#mGjAT*Q6RF??yjC^HS4k?8DfESGQ?5YNMTZeia` zuy^l&myDoYrR07drXjI9&o}G5bImS)kd{QRkIScx^Zbp^5n(# zPPHU&M1g3H^BP|cIg=VkpC4D_N(hc|N*WkWEso4%x%CQ}&3+>>L$=X4qMzx$Kulw$n+Qykk)B|dJ9c%k> zscp)Ugsf-K{U+=UQ7aPL7|%fv<0R(LG|wH%rCiS)DK7K(!dfztM>v9IK_LJ-r$hCV zam7$O6`_mq&y7W!2*+z53ckfYas+<*ZD_bwAcnsS=1Gny*!+H~JzyLepO!5j1L6i- z%O1~%&@LL+#U=gCP;uJO<10ZU=f0skl(VWc!`~x@`o!#&*%!xHv|f*yC4!g9+;9FH zF_OI%w@;hgKNy$UhMax@Uku@``2ItWFB{T){y@9~a7+8JuSub@dSGuzsnX z&6RgnPiiZ2yAn;wQOhVfZ9J*1G$fP)B4#rG=1O_spxYp?3eWX|%VqCptBI4ws@T7m z`H$}VDh5;w-#1oQHquZ*!B|zPqIx=#SZ%4X75}4B^Q`xm!8T5aw%p-*c&DJDTsDu#8=6Bufuf`rhrpRcYFTk) z5@M;X+f;8HC8)$S9UKGZ2HnV38I)(^^=#b(#kCzp7fo6lORosi#wf2CD^Z~-=ForO zRvE_>&IuPuo6!C=r?%kroL;QpAVKpGrXEpjIi!gOVK$1QF-|0+V+OopGB)6Kp+l0F z6Tpk?&A`5m|5sE#h#c0Q#C2_n3pH(QB%PG3;6+1hcY9pUA`#X-_h1KE_o{VF49r4b znC=?YuMFd+)`ngz?}7@O=i)vf2R;3j{pjLIY9!d3YErGNp&vG6%E);7b3wo0Vk5;H zF%(3wzXd9m5NdH;tZ2<~T5uzh^#-096$To|peg;ahw=hPQ zarN;DeEcOW*ehEQN_$MiXeH4dAw1b2ac3)u77T2cG}7wpABQ<3POBBOHPM+7B2TO; z-?z3T>L@~Dj9m35$9Rrq()=xqC^gwf9kXwq%*KPiN$3hCr!>FT(-K&!3NZ=ob4oi^ zlTK;Dc$``cr~k>8_(j7@p)}VCxZahE4#D5pFJguEXiKD0r->EZ%$_3ob;SHmEYq(i z3Qd^AqO`KsOoeE;t2TK$t(7p#a5nZ`R)W^Wq?Hh5To5P4=!r-*t7%+A9ePcS!HF1K zTZ(eFLEzTsl%ix&nOWU}R@zPQCgpS2iD`_Byn8J2ms;s|MUh$AC@(fzwB2SKN4rc| z2WqoyZI`8}C#$`e8pMf~x)XdT)+}yst>ba5EsC+Gec+QwOXliwgbH(Y2LMPU3lnjf zlMZi$Bbu0>1o_d|nx&~GOFCUY5Hpx&cKYymQ(PF8vQhqdpiYVx7KmF>+9pE@zW|{c z=|mhehnGJ5Q*bwd|KkDVT#<9?;ZNY_Rrqg@r)Qe55hpl$!8xSpdtN0|nOCMTc@}f8 z9bwW8pVd-QbA*bv(bisBN}B{GG;`K@!m6P}hA<~d3?xhWOgWpx7n`h3X=Ys(?Eh;_}h8tGxo*2zsF26Ml z@0lJ9{FgF6;h)hc{R2TtPN!zdvvJN~`9Lv_qpuu8`IpIfrMa;{8LmFg5kLcbmcz!I z5|_IM20Ul^K(_(gqG0pSB##;g2ZJMq^$gjlU)+<4hf0~(7x zF(M|8Fo|Yq=L}S>K(UAkVd*La<=O>+$XhJRh4BEJb{Q8%+h!w38uuEMD$L#wv;jcb zl=ROb1jV-q5Ej;!RYDN6>kb3)wknoc!z$9yPnb8`Dj;vPkKqiYF>}2e36`gH;pmYN zk@bfyhBb&|i}@iK?OurcSB|?C!O~B^kMFK*zcQ7}U?q1K=t!qjBMPSNwv-aEJ`Ajc z4&XRRC8cDbGU7=?gyD3-lOf1MDG~4yl4ntQEF9NK`sbn)MD%$moOaj2^CqhZVi|cT zo!ym6&P|B{D;be+vI2*!SsdvI-#mC@3UcUqJ$ptGjV^%v<9;FbS*Gx5R$DTOfyhHB z6-6ohx8uJD|9$x9uKN)F%X{ddWH}RoXrv)KB5)r>H&4>S%}j|1$Af^PW(xm_e(T1R zJ^uiP?t9zvehPVlSr)t-h-*Tyd0dn)EUj>TuB}wvhD)?zwqxOTD!#Muzl(vDcSgKD z5zo*{$mzrY!z>7^f>@ekzi_2w$FtUdZ>&=!I*)NWkLg8qq_9L}Jsg92zyo4ZxX1J_ zZJ5z}3n~hy8gRl+Y&PMToUX>ifz<^AzyfdWbG3$>QW`D}0OmyiaR31OB#I#p0AQ;` z0pb7vHbWF34ghdZK>^}8!tmxF!unYU{`wCQx9wjDkB_TwR3dV2&idGmZQrMXhuPH* zb$?rY;2K5v% zM33oJU9psaS|wp6Q9BZP33_V_@kj@g7|OyydW-Brzkw}GYglrG5C)F)n|LGsVQ8Fl zm$$+6*5%xYr8{3?^ocaF)o91mT8^K*_#h7yDs*#D72hw(7NT z^+#-i7(R?S5dV5$47lMvXatxZ$T?D6O>`5d4a^E30MHFSOB&587t=&73@^DK*;9_R z4r<4Sk09zy)2NgL6#6kL(u`sa2hAwfaL|mHlX$}kfA+slF}?hMnMX(h7;{ibAPxX9 zYf^wX02qt_;s5~m;uJ$10N|9L0>l9T^nnTx2LQN!qyTXMuzLg$2l|+6VFoCF;cm!g zvwz>wC|B0UT}>al^+%dM)W=8RBA9(Az23;s5{|I0c9U0O-^dAPxX9?N@*}0Knv30pb7v69)x|136~iDckmtv1k~h zh3YQ-uY#lc4)|$T`*ZL>hJGF&H>@K=j%*_ZFC!eS{Q*I$k6biIXA<`$mb%LIo* zo?t$B1c$;086WcXOZ+DNhww4uppAZp6ArlRPubuEB3j;n2hF}9UIMooVgE4P+3_K4 z_&!5&Em6FbHzI1MepPfT`L^LJkX2Z|A=z5h&`QS>faPl?l->$;KePuK-nNA1^Ke8H z<_2TY8mYr(O%$!b!|#%|i8-|qqyRaGtHky@AII3W;`@(MjafoeW(6^KjfVjW*LUKe z|JWN?m^yaVkA!>djri%`FiEge#|3f|F7@lkJJ6V-VXuj?9K5e|G`@IWDG|I0jzWL^ z7<@9uYp~qlSO8(ghHV7z$l*H)4S529%+X4jP!Eoy51L;3g5%?!H`9ZOnc^*luTSj7 zlqaV>0}P!&BwU8Ur;weIoMLTi1Is^+um+0bpHBa}W;kgyeS+Ow?5`j)%YP2|!Es^K;TZtw<0*8p zHdo@y#~9RphQl|%Etml1{mwHH1oO<(VN=sT3vT9^4T|TC&W1mSGCl`ivEjmWs*QA* zbRt-VU#wXrQ5X+j4euB|3W9e3TzI4}+p>BQ9;0-5_m5<7^|KizIFB(3cnrg21>?E6 zoM0_OupPn#U};0XK|tU;qlw$t`S4=a{}z6dV8XVoTgSJT|rpp;D!%aC&XFSzgMBY z4lX=6a}hrDUaa0r=tUn&T9B?5PT-&pnE;3TncO*^&D}C#(rYq)yR=I_31irKmTSs{ zwFhV{ZqTT+)$6M=6G;SY_Ga-eIpVGw=aXDrnm(`qTb}EIhI5|IW+u6#z+XAC$R;Iy z0t*VD41K?=zZ!mmWwI27xPC7*FIcwV`9aRBK$HB<$Cqn<^si3GO)N9?B|C9qFYtjB z8EzaYh_Nw{q7dQN`4E|)s1RR5Vw3lTyjUYT1qp&WZv*f%)aTRTI)du3PfCcRa0muN z3YFG4%)$0fJ%;pi4R3UIP4YyIrnD$jlrc12GS&ne4k-yPBmX1*MtmaK`6i?Rtxz6v zVHd*}>~E6aIGu4xD{+i>nh%4oKF@zE$+JB6iujM1nLt-qewBnm4Z|%D<_Wf`_99~i zq0_&*5lD7hg3xIQ6XCmw4oF(OePdky6>#*SXZdd<1YZ`6_JjSwGykA~zyu;X3RH@F z@q*h_rx5--THDM*fJp+>^1&8-BBUR}t>v4PcU0TZ=AtmFF=kdLqxXwAJ=~s3VO89@ z!|lSi{##61^vS|rxzFVCeFMJC5?qb%1tm<{1I&`h0dC#8!3AKQHn);ucX5B#Zz23W z5-6pWV~9P$FE;VBa!)Xq($meDjNqASITTTQBeY6*6M+X4_`Y;nopL2_1|}kkG&Kb$ zQG&}6wVa}tL}W%0#Ta^^jMc(x-Z9+e)vv;EY zv6;d$dpTZaN(&c3SacFUmlDIDRF%z({nO7gSLNLl*TZ$rw$rGvCWGP3rnB61E^ayx zhcnu7No|JM!h?X%gWdJ_JHbo%78m9U;l~z%;A4cw+m`+un}v2(5JCk zXXNT@rDRUgWy7nGqMp)p#S(7aHyQ@uEPtMQ9 z0Y%Hd78$}Nn0Mea04tafJZ`^YMFmeGIit!Z&s0p4Z!6D@!r3xATs$8{Qa-Nls^EiV z1!=Q#qdA}GuIBTJIaS<7!XSi0&ZTL!LRzm&T!lV{LvvT-gS*dbh_Ub_#28!}L(r!w z_TbAC0R&2>v}lA!YzsrMBZQX`vf;j9PWyg7xi+6zed2lf^spB3f&2CDjL~3~ZZ`f62#lqgZLvvTQ3w9FQJ|cO3ALPugy^FtZrE(yH}9 z@`(EZ%cspC$`1}bG5LYQVC$PxZ8TgSwGZAU?3lB%n6J4KPh?&pyc{Jq$H-;%%`$RX z&gsK$Pk_5tkXVP9`gsu~b&E%3*g4?gu%;Y>IOhxrMNL+WaYzVc`ou1z##O+q8peLP zT0$$6@sK$T5$@5xOo;X~%09rX!((ZpOW%Tg7|ucIBHjpa4#Axm3LS0oEpGnCl$OO{FF=KXOUhsavQRrMYws-7lSDIybOFEK|YJ2 zbH+H4iHjJKNMPCE?768mNDd;K8K+((&jBNg)8MfB5v*muG`P`hM-R=qTXhbFx2y1! zIMAZm4tZJgf6a?YGr(mAb4v(qP4}2g8O-?almU@nR~eXeoohYvk}yQ1Bl{ zGg*&Yh%#G_;V8uz4uk}QQ%cE`dXtmky5)*?l(0^~&~qesr)YznZ(Xru%@i-!m>RX| zR0^y~cf;=8d{3sZ9SMV*HKi2Af%rg*NPYkpw%H6Z*-*!fDz1U)^{kF$O0Gh8c+)+( zhgosbWxB6}2wVk=*ogw3cWUFxMW$~wm&0fG9s;v{(A2U(#oZxR2`-~mf~_0@^4_V>-~^s$yH_o1oCS_;z^>y1tMA|*4Y zc2Lw`DF`5Ic`NBc40JZ$3l`4>^o-t)Qyq42Is&uXmlVS1z(V*}{AiOv?Bx@jUv3oBt$&d%$m@@6uw8m7){__mxYpYj{2irjdDF@UF(Z1-ML!bD)_;%FCa{)4 zp8UH(Ft^GQb?q zrI~q}#zmIFn8@i|ZE{8;Q7o%!tUJ@&d`?Xf8J*U`odw(z=M!!dm%mAwFsSH`;vf@^ zvSP9`s{@S$mcx$(LYUD$-wvu>u_*_CT?%B8)* zWvaP$TBgVEp-r6TVrpmRg3)~mHk+YexCL9zk8E>wB|g81ljv-0^I=z294sxgarQwh z^YgN@$lyGESE^XWahX?GS^T6kl7~`FGpo~#$TO|4mWf1Qd3HPm4_g((yHJLahDSU$ z%*gx`{NhP(^2YTw#(lBlJUjd}OC0Q#`<&o+sEhh<@IwvGzEML8zYG*|^FJMi@QxRvQ zQONG0&Zn_lqACG(>4?>lP9KPS2^9_RNASYWEM3peaxr&HeqnM2k94sE0GRY5I7F7gbl(8 zrUR@0ov(xt%tmmZh#qP{3;to`46dTOq#hD@HP*85%$r#s541-Enkfk!7DhBF+J4kV zOflL+MX1iND2-;+%8~bMcww~DKO8X=175fd$|@W?Ii`X_I|i^!x!Wl)-~nU|0pG-@ zAo;@v3alCf#iTf=z$KgLt2xGf4ZK2tav}W`;+O$b21=uCXf`3O5Vup3Am4T{3&T1s zBV`oxR&i1_=4JiGyf?La_iI5G6+vMT^Imcl{t7=j$+1#~6`j~%1tu@5_`9N&rb{b= z-SOqTF?7z3WKXWx;nqFEljR{C48)YOl9pBlz`Q7=8`qGr%110yxC#I5tlgxI31?aw zoHXAKZ~HDCsm6$T63T;$=9E^987a{d@KDKiD=+q=bbR}wb;bTuc41wtwJ&@SpMhL> z8Re$iNYM?hJ@$~^HOXaU#*ZLG{^BCE)Xj3ufp-p;ZR z=YDn}>h{xshJB=l7|7EvYl5ATegg8Y*$V=kz~E~^Qfkr>n*{0}MVmw(zVLOoiv|@GG;?#5wFlvb{$~NikoZ z(Qr5T3lMO0R;C5Bpjs_}+4_$$*00}i1Da9}+Pz<*j>BsKmyryr3}zy{6#!2~2*+_s z*G~T=^m7adIH0v0F7@gfqKbaJlzd6KN76JaeG6Tc&1GioY$IKQAHWOko8Fo4sg>Yj zCpJB78Xq^MUl_`jAAwi;`eEanc=$20&7#(|e~0_rohTr)xT|4Dhh%bP?rZ%8sL`wk zl7-$Ll`L&o_|j_>Do}E9lSf7gCsBqZl@duS#0#xe5bY47!trCE-{Eerb$H`>gHG{9 zl(T5Yya`)AE;?eoZT;&)kBHS;JYl8?F-w+v#eImdn;aF?D-ij+1&QlH)jqy$XZB#m)gO*L9G|s$oWWG zAT5!>#sL!}fd&Szn3*nU$X$CKV z4vsIhY?>@FdV?iP{GX%2i!H44UJueLo(STBPS~{^N^f`*PIHw?@9r0eT^zxt1LyDi zAg{|rKH6rhz&``Em?`3{f#bEna%f+{JJ9*`pn&cU49Qso&pH=$MublIG2|>x3yn4g zLS2h5QeR@Z>8?WO4e_P@CGkUsrP9JnTG&W|qZPCoaMp4Rk;^mgX)(TB>s0W? zKa}|*FOghUDh+1C$H1YsG}uF&6HZ^$^hub3};BekJP>g&%`y4q-iLVaD~&WDYQgm%=?G6KMl=U3VyGVmv0zzr<)t z+n^L`!d{u?%8VN;lcd;1H#8IHHu;Qb#$Xd@u8jCx(WLokI2}}+4IhrCe0Rx|@2{Nl zy%Wu}cOygmvrr5X8;^&xB+5rxqx`KIg&`6JhJ|z(do=VV&fTqX{@#qkkd|aZl-i`(iMIQd5!dL zxEJ~mCreQB>|%Qnk2$ZBPDx6cayzBD{4&H}Z}?q7-1}Q`?IvA@H0Z7&?k8JuohB|r8n_n__fxI7i6$;X z8n_#X`{`EPWD}Pm4cse<`^fR4JJ%o{=NDRB`q4fj^|yw0Ub_ zex)ZJAul`a2KpPq4T=Tmy=62e7-eOrWuJL{-|k&UuObeXvNJoaT(%YYH%MT?t`tk zxh5_{6jx-$VJKj;Zu~wXM)EBxWR*Go`%!CA?iCaZP~)^_gA%2&oeX@jm4UV<0}N^O zub&h5ORc!=#8uhB5N(5OSDeS4s_p7G*F)TgT5+c}aT(IU-HEtgX~oSqaT(IU ztrGXEt+>;hxD09F?oQmVwc>U(aT%hxtg9nXR~*YA17ts6bqJh-#JBfx7;6cr2G^ky zph8&&x6XZap{~3BqGVScfo+`)y4W=VUDYdewjNK2%A9L*Iip~L@d#$@cmkd^GS%Q| z`h3B0AZI+H?)LNugY*#`=8O8sZQ#)8L(JAgMi$Vn7~(T?ZO#QupSpJJ|Ho0E{ zrxMSR7a294qszY(_tm=Y7_#hGwAH`2R#OK+Dg8wa! z{1F_AO7b8&OVB+{``C+!X|xED8pbnf8>^|RGJBMVK#h})o@9|G&V8+afP|_rdb>S6 zZxk)L(|v-~y7n$v2d=Qv)Y-8Oaot%^UPvhF?|h6}HoBoqk{+9Fa^uldfg2{)q9HBC zQW)<5CFQ=#=}KoG1VkEzW-OD3y_kn)(-6j4mwdzXQ~0h>8gF@1gNEoyGI2r&g2q61 z9D3CSIh~0pBtn4?Qneg@JCAaK&TTYIqZ}rq#H#nV=|FqspbOa(|S?N|c4|&^+~R5Y!cK!|_;R z;Aq4Y|AQ3VhvJ9&#BKOynuk?)HDbg}hCzq)aM{2*ll1%#5nV{R{_Svcys#U(TDi)- z;W#8zzXO5(hcP-tJuNyVYo<3aA*bt$gF)NK)!UG{XuL+1iPx+Zba;b}0-bbmw$iU| z9q@F>X4_`}I1t~nz_0L=QJqa;a3_fF`;kPXaZ|-31gUv2=^o*By1k?8S#*(^pCmT@8c5)wAj72Dn7H3Dwp?$5V? z+h!gHfk9#lJ^v#tewY$Whn4*>g5D2dhc}jsa>cN%JQEln1t0#$@WZFm?!g!D0Xlu# zt#tS}=*d3uo=*RHdC86~6#--!Li&Czwh{?~M_i zj)V7xYT7E~pkNI?hpHNw=}nzN&jaxIpU00LP#%U=N#dVb+jZ)c@blnM0%fO$=cBt9 zw#yM>*dslLTqQm1%)BT`{uP}BG1u1Gy;8=o@q)rGAj}+#9{#Sv4y+Zxf^?zM%9DaG zfQ7!{Piole+B}A_(NZLa50MXyrxBAlU(BQ~tjz=#8$E41SDe`$9&J1esq(o%xTw|{ zA*6UPTtkjQ$eY1Y_$^j|Fkjvmu%+W*y8w2=I9ONsI3h)Jk?2g89NM>K?`rXpuz&2S zt&dGIRBYtXHTQlj;fqBW-6+-{1j8FSaB>`QtTHllF6CwHY>l3uQOh#Yku|W#m9x8g z|LGjWTZ4EQfR21iV`D2*|02kngDu#idK?`*U07YgEc_>biaWCJTwW$9Aje>V1SDRB& zu@jj36lhOvz#OwoU^Ky*?Mx-_c;Qmy#f;Z|<+%vK84j#P3*&-RzPGvQ?`czupRB*$ zSij`1ppLcNyEgNI(?H+MZ84zG7tNTlqY?AsRg4V}N6Wac!?DA~%kww_fzs;T$PoVl zh{_4{SGUloFGfG+CH-%-(l0jYGeqehVi$%o_FNn`v=-TRe`#?v9~l7zJCu1RG2PKo zH0>v_({1avWD-tHlh|pqo}5$5(WLGiEZCFm_|GFC_zE&(795dfU2o{Ql&Wk%pYt%z zrLd4dy~=*5m(ZcxkpZ^ge-$KFVWU6O9LsSlCXLg?il^PeHNFjKt?Qd5q^y!hbIsx` z+X*)z9EZz(i_)!s4b+1hSdR?Zq9K2n81jA%`OCzRJ2m7ttsz*{h4&#Oegc5!CiQMg zKZ73}AN+OjVn#;J2fvDPIbZwVx0`tMz>nt-`#w^}d z_sTU1oySyQUKYw_#%bP{G3zjvT0U{2bhM6Sp~EfjM4R<#KHVwXxS75@wBfQxEkZ7j z`?@%Ug32HI0i^cgMs8WOPk?6>`afEWDVq11b^7*LK4Uq7PJ*XxlyQ^dNjxd!Ka3)) zeGRxTxK(vfAn5YGZUB`Jadtzv=jb8}5avuC9w?iy}E|#dTIRm(9SZV1jYUeR#zL^G+()GpuWj*_$| zrIVB6)NN2)muH|fSx^}KSPXL5F&gz!gFms-?hbbbhu}xio&rd;%=R9c_&x^bg**a2 zz+*}Dt>11@;LbJ#Az1m^MgWN#uv{WZivvRd4gGuUyxq-K=RH=>rLvattly$cHexQD z9LVYcr#9 zY-uFs2(Z)Cf=S2$jh=A!exEd#&2KP8*Ajo++%;L4(Hf{uU7DJMsp*_Cff0OE9 zvl+scff$SMDK5;0D;f!;(J*?jD}qMBUu|oqRgB2^n_T!~?wX{et+4BfoVd4{r^i6d z$Uw>odLucZ>R`=SH!}9~M!MrOHevH7ZCGB22|{t<)N}#Shc1Z9C7%S}{g4*P8m1M7 zZ(PUae1Ums$%o7%(u3dQr5eh(DprjbYkj;Q6l1*8+fd+ihxD4Pe%&6{)J5|zes{P^J;4^ygC7;%lLO4&W2x5uA918#l3U3z|v>*Yh8C>-;>tVRE zy0~oI!tw^Mh6rxXyI6Lv+!eTl{lJlD$z+!Yb_e#-7%D)fG~Ytj4LG1D=F&&$zgu^~;-l-pp6v$z4C>#(R zr;6V#^;R=(>7?rZ!w)hW8eAzGqMrlNa1csB{~Dk-FFC{#>cj1UWeDsyaZ9Jx@U)6j zA5ZR2BsXK;tCO3^VU1gIWSft+v`{XcQhQ$`eU%Be1!a!4p8d+d7jd#)>GTn?V9Pz9 zTky}d*-^$#e~5ptw5EEjS2};>;4FN7_&>w^FEro%&G!g=xiV(>$@njyZu{>;I+^lm zw*P){oM8L6hy!+mZWYJDw*LWf>|^`4iKAxww~GS?7e6SDdDsFF2kiBINE}mX9TK)v z9J`7ZSA@}AQmc6GfOsM52}L_}sq=-pP^y!tFUxzoEa077Z!ImSYB-^ZZ7ubDuqTv({$V# zpPtS&vJ30_=%G?zm9MFNGkvoZGi=(JN?<(5IIfgy3>S8rUEv#su783YR-P&EWE-@k zb}j!`fM>l<|2$R(A6|q=K-Yh!6XSY9))iwS3*-11JZq}>D71KbJyG^Gy@6&U`I6G3 zj!f-VQIJ78%pHnK^4M`D8A&B)><2)Ne91A2JRA8PBL!YxVh(yc{WZ)$yWJ}y5{9K1 zEO|IIO4HvCOcp%p-;dXETJ+K zX|TQ=A?oe{6Hz9RDWUw|i?m>P+VxL_!`Q1!S~obBh4oQGo+ebT+Q`x)h(}>PMu*#I zeCx&)nQ3ZSZ_kr)XVxq2G$J;GaIMc;0*062%l{M-_>I9paS{tGOcDY&E{b;G{UBTaOgJ$wv~ zO&PRlw&5GS^l0r?Gq2yoqmza3R^$-V-0+e}uw$D!yib5L!iVsc!2@&U3jmib5rltN7c~+0(3)?aDRB~1pA&YBg zV41RjH|$tNSUAa7yF*9RqaEBWih{t+v;P7&4paOoYcQV_6N-jJ1FRT7YhpxlJ)Z!L z_ke~IReoi9lqc3PsX6^oc4@7=A-4va*-gc>o6e^+yJ$uxvwI_x>|hiYr{F&&c}>Ui znrg`_)FdK}oP4GqOX;YI{pKajYs%y`-I`YeW?oai=2b8nc}*d&cY_AUz!hMFauDTZ z_a~j*klwul1Yy3;Tb+Pyn!@{WHqimlh= zpz_&Bw%>{LC?`>Zlw(5+ol2*6jYLvqm46i?_M1^?I~sqy*#l0AV!E%)goev%eo)4l-6qeD;v|1{7=!|*5VKvyY zWI|p@S?^yBq8Pi{Y!?#uKhfAYh`HX-Z?QV(*n`Eabs5K=B6d7&gX zlcr2snhA6tUX&1*KlT_4CGXDt78@BQeBMosvH0=TF=C9}DDUEJpb40HT-}fnup@1{ z!Jf_lUdv65;}NeKvoeLfyxZC7{~9^#Ud9C`tYL@`oktk#C0E*WS0Tzk0*~!(vi)n} zE@ItM^#2P{;V7ts{o~7E9k2W5CEIv9_C zOs4SmF|Y-I+B?PMIUWmIHIrjgD+72~R&xiD3Gu1JjZykIdAy|sid;1(O*RvfO*WEP zHY@Kcu-zqP|L?8L%PgZ8dy3IV`Kvn~P)p6GTB^AhBJFhp$^hHHc_>$Zj{*_}uI!M= zJ^YMa>?!zll%Z&TvUYde>$5HA-1*SEKhRUc|8EoQ4GUrw>5KFeNl|(Iyu&6c-FEkqIHcIN< zb`&-^f+;LzgyXMAh-@yp2c3yY1nbPRJ_07MXyhT|&hRe)TGpGP!zboB&@k%aD~E4+ zSBU+j|0lL%;JzEUHvH@oc;lq#zlW($vkN9spu8OrOoK#x6d~8Q(BI&evKVi0cKO%C zQJLk2zeARzl{mng0p2ES=RVDVt|t>bm%}qQ<76BfhyPAQkyc@(p5~~{?x8%s0fAzt z&5WHCF4}i-i{s=Ksc6vs2db+3Do}N|Bq-R#$^z0o|AdqK-yIzd8av6{y zG5jm=&3i^3c`)xPo` zsnYyvGF3XcR&h!v3qFm=kUx#gGI7FhSIp?r?Mp*G&)}4%EqrsK%`NUR%`-E{?_$C_ zU>9NUZH^O$s0@QCx*M_c=3ffp<+2lA@yelXEliklg3-B=%w9t%n<#5KuFgT5<6Oq! z#WN{VEche~19QSHY!4h~4amPAZZnWSiQdOCWUXRxzq7b(_0V)X+=^K79v(%es$yVV zB06~<2P{=(+J1b9bOWpB&3Fu9YH5};jWjMQu99vV4sa55|AmPW!R#O$aHj z9w|tO$}v2lYF#bqy%pHFs%m@KM{7)e+AxYUS)3o#o}I+xL%Qwo7-GWWE?XkRbPsgl zv1Vx|9>ZMy;jo<|)n<-E(ltxl@DQ=2VYF>H6~|((zZ#1%|0t&G&ZQiU0X&G}-mE*A zgKfpbI#CKY0?kIZ^#6t~-e#ESfN?SQ-8OWiLwAE21e|~R1B#a3y?+JAFb?(7w`}kG z6nw_W!5wf>V@566+hYF-pc2OK`2Q0PoZ|t*IJWUiILKdIJ%0G_bV3&$iD#G&c^6n) zWwfQAfqQr^q96-@r<9tAx_a{%3S^rvsN6++Wz@BsF)N5rADukqHJh(#;&9@RI`#a& zBJJT1)8|c3H(LPI<4|dtUMb7!LOxwuxyLkQgTok_0|&_RTjG4@VTX^*mfv89!)U6a zcAcIQGXrvLOlJ$r7{qf2;^m|w6BSZgSInE)5_BNL+7U^3(l12F!K$YysPkk$87mR>~~f$aY7A^vn5$u6#@!0Gt*9#1TD% zRq$U%d0|F6g{CDUsH4z?Lb=Cv01EUP_Aj${s~6I_c>z@w_nok8(G81oSAtvEaxT|# zaiZ6j6TLVkRCkz$u6bztLDoF}4y0cgO4;HlPt~s@$97RXyGP}v9r~g#njBF)e&SqxB_w2K49gS zj%;tb8~%;+-%7D>!}sBUiDOR19}+t_3~AcIiOS(Ybmq;VuSVf2RXZqvddw6`x5zVj zv!%CKYP%(P3S4GfJHn*S+hn&)`hhW`!a1f;bQ(m>VrJCJ}JQs?f| zz;qlt=a!S)zi1o29T8-EvTgWgjoU~7iqfFF!CZL^bT4SoU7#g2X|ZT-%10u?`bLDx zt3(LjSb{*|*;ErXpOQidHzK+k>wyhR*0UU5&`Mf-qa50V)X(T?2%cA1jyzxkp6$7m zaKoF%l_ZABpa)r!R%;9ZK4Z(3>D+d|Z%0q#Mx>Mpw2JX78j_6O$WWWNc$aQB%i$pd zBx~(9Z1v8D?4$kPlim;4~_QhqXe|>^$bc@c*u=MiQk109c!5Rz|3r}uV67=ludh7Uh6bS%K?8Fl zm<~+DR{5+ujJ>d`dIkw)5T=m+vSlQ|fM4UvxPP_hZ$cq9Y7%8v%PsAW{ItCBC znzDkQBp+}|@ST;F_~J}MZhf?K+aKEg&WIz=wj>5}`W1^^VDpH0pxE{r2g0O-7x<~e z>P4ABBx3$ZdX5Mh><*9tx8MXn0GF_;>(!rzgHM4pQw^R0=zC2VnRS9XN?+xgZ7pGi z;4g|r4D(z}BKUX2Hta*|#`J<`1;io<9sq}Saf})7>|Z-6fgb|9h`LW&81v=NWfihNt1jXKKZh8o>G>Fmkoz(n0VZWKiiA~Qawk7XKjFY`>Uu$4##&j2wNf-Em3%Uq_ z{f1s9_}j?wyH5LlI*4(vSl;ag_Cm51j$F~~fROuMJgMO&P5Vq{!`VsPjM7j}t8SCP=@YWY1^e%IhP z_zotQsJPMd1hiIu0|q?K0K_|AKyQ)XP=4zS{w{-$utzTt(1is3z>r!eAiT4L^e$$= z(6LPdVL5Pe3pk zfM8s=$M3BS{+~HU>Xwf<0 zX|Q<#YV1CS`18>=g9Z2*yB~huEwLMOd3%&N*f;-I6?^`*jJ4gf{ZF9L{z%`d;cL@& zhviXVGalNdh12@b?J^!lf=}X;^9E90v3gV?v;88wOH)nx=c8nUd1%LDp8}3u`j-Th z7uloq2Xhh9hPzL=8RG^xHtLRjf@np-DrUAOW}QU+rz2tJ7Hf0|u>ee?>~*k3?DUI%}Le|#MfXIvfp4WQSo1I9zP z{*KSMI*?T3b?^`1utXWaI-tL`4z7d1ANwcKih>o@fsj)Cs1C>+>VPz~4qk%m73-i$ zfOQ}VM|HsD*pvB{chVwR$^GefN?kCv)&)y$TwMrhsf&LhkafYb7~_xB#SS=Pb@6$& zuqKJI2iXLq?pGr{2w=2p;$-a6FXDrjNb`C$7!_-bL)LP+k00dea$bqYceZsPck6+ zeaN&u@;SyI$?rGV_L|Qeen}E&?eBz*Yir*|tXF7joU6h3AIgRCP<;>KW7^t5sQdGf zRfgE{w)PbQ(AF4G#(au?X={CGG+$<5QNp4YCIl2qTG)4NpDY2HF|~y~LLObQ+08zF z*=5Z>-i*%nC5c7ti-}=6_5>nt{3_kM0@;s{YW>&5e}?$SA``p{fe-IL)Ui>DiI@S3gJ4AYJ)``{u)mly z#P>MC?p$!qbe`CiGku&2%oKEPw+FJa1*Y6EPmN(!UKtDTLShbz0qA-BcKaX2k$cPn zW3ZyaFgQ2y{JV(;&`%I4xEqhiJHfm7lsw)jB*>|EYj$x5(cOr$X8k#Iql{S%em(*0 zL!97fupYh>^K#K}0Lvp*La_o&8ZxL;e-$%`@;<8}kW;|0ec)xrJR+~NNBIqKED!x& zLKW~789|1UKy?y#Kz%$ja9PJ-HG>$50P*-ZDkEhaMMx7=t#^=@9Lb~M9EHvwlS1tP z$EDCTqM=Q5UHlV#vG75EBUZ*e7-zUFY797ed>{Tr76}D=6eW=u>%dQW2ir%>hJVNQ9zZ4(AOB>mSwe44h|q{C!vM0E_}*0dsMum^lgQH zM3d_tmz*u(I8aA4^=QN`tA@Uagh)f=ADNBGzxOust@4lMGEV+UcCgJPk7px|;{J|(2_DQuGdyL^DlZdz9;cie&L$Z9yw-b@!EP{a%5oXvC* zf~F}D-6=pCcW@g3U)c^I#y0rZ!tvy0x(NA@fqZW>U4-0jAm?qSi&VHnKq~T`?j%lw z?j^CDKd1M%J7J3VTDS2S$uG z3A}B=4}sMu=Xa)yU05W82T2YBl0AxpY>CcsfBuJsCGa#kzVqM3AD6;w(oZB?Qi$U( zAoKW(^DnyYcnlMP{O_db6Y<;QX=nuEQ#ZuF3G~~O?Kq$l{Y5NE8TDzD$>X$pQJl^w z64TlF)#J3MqFAxK_^%bGJ#At&^Rvrq$7#=KtcX3y6J;s?CM}0{uRYbCZqKx5C1wN+OriG%bVLZ8ph^)5iTXzAo=3^=(fEbd3ziWn zQSTx8n+!Vi#f05Q*s+9dvg>US2xH3_Q`fMGu@mTV#+DMHTK^1@N9exQ3HCvHI)Dun zW#CefJ$e!lg5xlJVJ=gxe}QpMW}J17qG5@S4QS4NioyOst7BZr^e=S+j7WGPTfb&5 zglrS)1Q?Dob&NE4WjrE-9;#EQIGID7CvJ-1TpP#&XC*n>i{(?Te}nw2CO=Sa)bWKf zMt(0O(jbw3V2`d7&_(!#F7#y-mCQeJK)B)dF9vF12QOZzEPoVg8;NzvYsXT{F#TV$ zN7qY~(N~KyhJ-YUYW*2M3ZaK9n= z7*IG#yPEVzufeb5|A|Goy3vBJl_37&k|fcoI)X3Qo1SX@1#)v80I`)5rWHmnp0J>Y zvTl~KjZh3|(2zmPE^EVKnU4{UNfKsInex82H+nu0@W43z{snN$o9UeQaBWRwnVU&0 zLCV?KtCHv%$KeJZz{3nn->Zfi%kW4Lc)X;^L@XlQMzpL8%FOFMi~vo`9~J#9W2gj{V#N58bJ3;bbpL) zOyawVi|-8c#UxDpn1)dwoA5Cc69A@P@||P8C4NtIbS&L?){+w!bXN{K{-5*GOHLxa zxd!e1$iC3og>vc$gKt?&5@H(uLIC?AbgL6KXEqv2doKJ5E*}1vP?PQ1KY~!Iq>{rN{tV~Gxe|GA9=H$ zPT<+;tY!VMUGE^Ez_3KItX=UX9$1hw^2_nXOHmfn`33$N#(xL@7vcXD{9_F+e#`n2 zuHo}9W9@$GL$|rsl;!;Wko?X*W$FhytiBH-9c%q5J8g2Ux1Gx0v*h>M<#f-J-@WAb zLBTolln;W!Cr{?@;exqMC_jD5x6XF0n@%SEFMj%wi(Ko2Cr$l;YYie5*P3%Ge>?Mk zzRR`loyO2B&!@Y66W!~U^Y@N*FVAtUv-Zsq%@XNuDwdHC*j z>)F*012RkU__wdJOpf>te;>YyzdOWzPWh3GK46`?$+EsZg}MHK5{Om3D*tv|Q{mai2{xeBs)*3=SfB4ID+O02NJ@o_a)+g>`y=;?MKlmui;bxSB zYpu9~m`6#zPDAqtv`2TItr@&YLciFV* zXLjRY1r>V%RKPBZ(tB?TYOpKzuGq1IC}Iai?7eqHvBr+rON>$Tvm1MhCMGe)BzLVf z!{V=VqsP;&A7b{t3Cbbg8o3x zjDvQv`_To{fue{Wc0qz(BRbX%$%@4j=|mRR4hY^&*7@jynal#;Mo0SDMo>p0&rL@c zECsDAmDd}}ON6q4V4lA|3F}fCo|Z|0(r>OtvW2fwq4qBB=z+w2}Ae4>6C352C@TZP(ac9`fH z)N?5tM`Xjkp%F3}2#*+Z!aS$2qnM1HIr)`rSadkHeGYp^xolpCxh!H;6-@V$j`R*7 zY@bgyrrW`)DcHV~)V>|ektnMxj=G(!u7YhiwH-ZoF&Cn;JCO32tFi`4j}M;%7xHge zIF+z{%bbu{pa>-e(siE#8vmBHfvdd@8?{JiU5TnpKsv)lDR2PUz;EV+EM-?%3TZjB zP%F&^*&`5gtN6Ky(-CD`%>lrHcv&(nYw5v?Vv%-0kBdkD44d?S$^X;t_Z zqPav4d;^E)>*A72|r)R@6MzwJ@@V z<_y}(h*F_u4?7AioYLhj71T+^@tFdinS2@_2qnO?@ofifT?*gy8B5v{(lR9dv>?Am zF3;fUr1hAeU*jO3&1cEd^J~PyQ>JrB`yM>M(xmy}J5TWWd=Y6;L<{&*YI{qdSomaf zg^Kk&))8qTUqfv$K*LYlc2K(Ilx`8lMp2a(qEV=@<{E&L+YW%!_Ky9D9682|+H3cp5L0%_a$b)qpuJNZp2;q^T0S~d6{ z{y!q;h1RvI@I3y2$YqRmt>J7he?l~Q>grjsY#(gw;*nmv6KOxU*6@fuU$%M{1F8&< z)7!8ZTcK4Unw*7nfZLPjBp?Q}nKNnMR6y-8cOh-|Wz>%FMns{YAvM)F9NYbESI=4s zRC@|ri=3r)43|) zq&ciQT8xHwL~5>#AWzS&NdC$}@@$rg6rh|Tn%@~INVy>ML<&`IQLV>Z`D3jhEH8*C zQh7+)u%KDHV_7TZ1GVTz&tRZ<<&%y_Z25AeHp*wB5-Wmtd%$nlSsAFcTo$~WsR>F+ z1KaL$VeQ%i&`J??1Bzt{N*SVLNwx-#)@SF}!oLKi0(=f>!-j2(hchQZsR+cFqok^& zbs*}X)F28Y8mc%FwUAVY@|?N60_+3cKo=a0@Qk_(wwv&Qu89J4;p%U6U9m` z(BM$|jNA&lmx2}qWW)ZUTt)z4Yqu@KJjW{?;8vKiHzEDfVXjlAQeDn&?FVz~Tp%1F z%g^*nZw!=X;9M|$R=;$39$1+{T7@k1oTtnr?SJ!8o3G3wtz|rF^A)(MfLuBaL&{Q? z5toppC7^a2*=FTB)Y^vGEVil{ z%eE20-QgzGjw&szP`ln4wE`q4{cVs{%@}r0X-RZ64YkWkFYtu@j$lmpgEEw~wPR5$ zqYkyg9_+EBO~0iK-ZcYc!;%8q^owP%n{9<7RzG{@=Q=K5ap4Upk5^^wFTRdp#EqP{V52(st>K*TYGlxOeH}T^d@vRcy?Fe+ccng z^}Am)lJ`)ptdZ_I-mm#y?V*;jP60~ncppYj549|5{y?ZXlGX|61(1`qDDOUuo<6FZ zHJ-P9ld4>m1i38fShzWs^;O~bfProtE!=F;`l{_ImrbC3QTwW0h^_)@pba3;wjqll zU4M0=HICLqxKl)$WUWF8uq6jHK%GnkZ`1=CqNWhxS~eE8$EOMnuGX<^lsb**8tkRT zvN38p(G!?AW7#-0gXle+cd&X=XAv!gF%-)tt8<99L0`qPRCONFZ&1HjmZoMB?V!3$ zR~Ha%rn<~fvx!bpU1q5{MDP|xNH<$uLgYfF&sCQZ*;48A)fGhE*eYuBKUo9C~vj8g(#0|yR`io9i&t0Rm$^S0_Itu zUN5Gd`wv}FevP>Tg7%UmmM}TB|T3vxi}=e^S4I25&80inV^O{!LmRxHpSspCsW{Z7lnu{!^@k zf7O4B5!aLwUwcB+;7x5%LhXD^XQh=Wfnzt6T4b%2A`(ZXgjR;A3uz@an_{_?(#nzM zMB}WCW>-wBsMRP&l{I_vEJ??j2yK3y6A?jaAP$JPRc$f{-^(MMnMNogDtg3{&1thTL~7N_kfMs2j+luH+X z^i0t971KIu`IL(ot(~=l#k4NkVba80*IPSX0v3zR=zycOk9MY*=RhQAk(eh&YFCL~ zQTxVd-w{=y^C(&So(Mjwfx3*>ekhj9MC}&U^YaD8w<8e0-~BpR2RHOQ(*kYNMi`&m6zwZd13v8=-trcNM}^&ma{sDT z%Q_)NbA-CF)Qy;Se8UZnoE>Y4c`k8%1Tn`OVND)2!uTi6UPH{U%`rTB(|ZVaI*y(Z zQ5Z9!8QK}mF-Fudx5J;{Gtv7`sO{t6e;_RSWP+>Ol(T?_M!o;G;@==l`^QDhr^-BA z@cFb4>pV4nux`t4Har6P^lgUrI<&}^RVEu5QUN^wxqxl#+zYMfB_Has;cl4Q-sV-B z7WIG9gqlqgQrzpKz2J;?2ib%DFuca8Cgirh5!Uu(EVg}Wd~Gvr_|$)(-(J;6`-$vL zvXkp!OvRmOPf<+gga0%sD$yA33^~{9h4yw5+6HHxo7ytV*@p1B=7e}>u;&Xg9CR+v zL1l^2Uq?&+h{1Y7N@4@FQRC1ao`?4F0JQ#TXrsf?j*UWVUmNXxC$vpTeQ^rIcfhty zIk_HfrwxPkqPi_P;R^M6>4^4Xx~tjJO7SfrW@$EB8}=q;*$rT9#!+sZm<+wDbrYZuzV z>(D+PfcC{|w9(;c*Y!hdI~eV|IJA??p$%+}ws0)kTD8zF9*A~4`{ZDFZ)>y_Mxh<#gw`!@gQF80 z0%0fC65^d$7+I@047-qRLoqEW{7?HsP-0*2ucPjG-$T&4FJ!B?!f=u=+S)_Vrp!R= z+zG8+AGAA2ji8v5i!jDN4Q+Erv<+vXtwNq3k}+%yN1Hza?Zp#lzsyG4VISJTl>dow z7@p7z?FsU{6^Y?^N?ULk!%5_mN-;yowxO6`DcqEDtFjqAS5qn1(=nV+Hi+tW>=MRY zA$!{o!!O7-?SSDxvU@1cldCZ14^np(V)(*nwD)VH?dFVjAJyvd01SH!M0Li$4Z-=#JBHEcVmPzry=6gzLCg0b8*-j)B(Aa13O7 z;Zbf!*Ozj$u7mm^7h`Hoz6IeKqZ)#p+y_%VNW$?GHVy4^m{p4Aw8C9?K^@xX3D~fW zFTuub{1t36rK&jWZwUX{N^#}vD$Glqt=)vyInN4WgwLO?jzP?gb+!;QYeyxpaT{xZ z6=}a+gr(2&MJqhNr99uoU_Nh7qUR`Iw3lb1E$*MY(Gg6xI3v%;C++uj;@lHC*FU zPS)}U+p{{_HPz5wv`2fF;yY0MSqO9Xi!GLzTLJB}B^c9WHikRoVV@tEis62AbXKm! zV^=Fl6g62HOGmQ`ti_FMzBlYG#= zbw#U_b$}VKXl8zpJlGYkJEzg+tQ-tA&%Znz?D|u99Q7e+ZBOD1|I`;tFHdvbeQ&Ho z*NdaU^YyweV5VY}brlh1lmD^vVoir;qveGGrJKaNJbkL;V;7=F8x1jM_TWh9Igv&?)kKsvmTI`wZ36g+7X7Cm=qw0_^9$C%J+UV3 zvQxlQq;1#|dq(tG^5U7`FV3&5LYxg|1!8VijnV$TYaXb$vMnn8 z|9{{9<+kMiv(C}k*eBb_irVhYUfIlGw{~m-TMAZj?NTzKuiB;TsDZYAO|(<0p-roW z_6@1i$xbA70jW>O{$`Ip?y%-8&k9#;yOpMe)tA+=G}Gm(l(X_a?Tiw$DpQ+jGb}uoKDR-A79}>;HzB z{nWR`k5MNWEzQ}L^-A;RtYf{B&D*A&ftt5XiG$X)O&Lmd9ki=$N=LFoNnJ)Z)&b)u zlkH2k9QnJD{Xq5;d4@tiv`sliJ^>Uz1^S_FN*}TwWaG$|guBb48RMiAj^s97&_3@T z4)rfSLiRMpF|e>Hj;m@k()?le=Ij!U&)?hOs5mkaN9?B^HqBwx4Ypm%J^NbL?NYb{ z+U8`dfcMaw?FJjS!ydvN z*VhNzXQvz3ea)~Yi`>w@Xo}X;1FwTuT`??HN_Cs~f#>K(!C$*U=HLV#|Zs`Z%nRNaAcmh2)I-$*@_0MzYlcHX|d29sK_8Hjt zlJA6NJV4g#KiX)61l^tEU(7b+CieCYB7;y}3SF%He9f>gs zVBrZwC>@^igVN#YuvIWB*c~X#VDo}cLy4lt(ub~vvL3*CPh}Odwl`PdGoW0sf5Pff zWsOeTx~i;tt6H%0X@Prcl`TlZn#`(zwpQP-che!Wa-rPC1JMqmaH-1}-Z>L({?V1{B>x@8akk5d#7_Qd~ZQ_24KaX}Z`ClS?i&8xzYZ-@X|JrD8C8KrT zj5cp1+KpuEQ$Ee9R@M7pd_OAjz#@cdQ1LfmSY611qHv#C=ut6>yub5+a zyb8A1!nv>sjt)GHn0CI2!Q4{Ow#h=f#_tH&jpR-vnbo z?lolxYP5Qk!U(wpYi5J(7?TfU<9pl*VJe)bMez}7I5vbTJO`3$RfwJ){|`^$BT}{4jHBqk zF|W)LBhrj|UsVjZ*!)$U6oPg5yx<-9=N4f2@uIgJeK1cEPA@zKD}mxM#p8?oG0)=h zqLhfVUm;%D;xWbREb1UyTD(t0Z~UL}#e1f>zsN`Q?JuwgSk&s`@#HVhvUhO>u5^BmaR=bjUcS6 z%_zJG5E9mK*Pe^`8#5L>+G!$=hWyt~Kt5u%NHknrchE;r`;w zY_v(fXe;NUU9|x1^WkW3ll`6SpJYS#Vf<)kv>$7ueKZ>FnL@N>{L#iO@`iHfti;k? zXJUAD;Vh_I{WEA|*P=aj3T;x7w=1@KKD1&E&!*Nbhwur@8n8pl?xC0?5Kgh)(X=AV zFMGnR=-x2u$a%=2S@uyIv{xu>vBlhOgMW3)J@70#zPJX%;|_O)g|Pq3YVX6C8RgNg ztxyu?gqL=C4w&{ir1e*RqguUz@CnNY$a(JOFJQ&FY&Qhgv7f8*78Tif!vNc?Yt$ZX5_f&H!=hOO-|+^-^r4_3l(ttuFPR0YG&wAx@Z$}|M)QXa#J zb{IZZ5yNFGV|Yvz40}|?@KN3vO4(J~1MDpY! z#{;7G=0MIjt+F6y?&cL>|J!40LG55{qF%~{@c+B_oFM1E&74?+kbmG)n>%nXfc=?I zsfI!SKWbCEDaIUzm_>HS!TwtDI@plPPrwe*G3G%TjOkh)V_NV%kZO|izb%Tc;FaRF zmbj0NwrnQaZgaJkn5T71JRh6E5$|0FtyaDa#Q4~uT~-leURT0!n<^NtP_;};_~wmM z6|i61h^?*(F*rj0Z(%H}HsmuzcLC2-*e5aA1Lr6hlmDh3hBS*RJ*uIgI+TE%Z6Vx(cK|y`K^v!{J+5~kwI_rfO7sKUo}q2Q(GF73#;HkQ zw`v$ZuA@C~pq*%iwyOp0S!=Y}3_Z6fXy;I>XBvh->y(2P+7sk|l=8nt`9D@Mtsmv= z3^`-3j)#~b`V_Doj49Cbqv*EW_L-9jg1XWjQubQfJUAvXzpa099qzB&7n? zWNICe%T`F|$XZJJz3yKvomfvvvEW&cEt0glu9bTucGx64_a^L_Nlxy~Sh>0)&((EX zy0>8NCI!2@vtE+U)@|eN#WEy~tlQn)hwYTqukK)XKX%=u@$LcaACqRg2eaz<_zASV zU)>z{5Ef|C2KO-5*Q7n}5o`fbIvk^u?$K__E9riZvK|9irljvet9T4%dnDE0Y40(VU6=G*J$sKN_N%0%_V?UJ zu=4dq35(A;dyHhYB;6-!AZhGK)Z8V_7=aXAl&&UHoTOzg4S`lk%5`bxF^U~B$-`qb zduURC#~5ZqFKEbNl?DZPB(nfXt9_z8#g^_hT_DXCuF zjzBjg-C5PsV+#A(q=E2b6y@o~4ux#YELnQCh_zwqEVY>DjQ^CEfrkXX|B7R9 zA}^vs-Z5-z#aU7-3#X8bSk8vHLrsg?!tpMZ4y>NUe!|Ld`pIhv$wzDCUN<^0O z+|JU7(%IJVik>^!LPv30m- zE^B4d4v$=xXi|*l9+qoTThBc9R*+>>=dPanm`ziWOZUKDK>kFcclWb!BGJ41S)A~+ zY*_fP>;cwQ(xHV-Z4O`-@K;zGkIF$dn6w=BeCANkgDl&mv7U!nzDZL&zh$>g%J4kK z8oFXG=@y&nuhJXEnklJP>Uz)9EK5?;)PFrrvt^Qev(|eSuyvAJW&P_>z_v@8;ur3D26j$GJy-Z; z0(~opYF5Y!h*Bk8lAgXj!#xYx4N1{GGlA|&;`uGvS@u+3NCuko2Mb zW6!H>kV!v#UT5P?Pu1%OHqWHeUN>P!49mkjD|_8$TP1D3=-~AuJ0)pIy?S1E;K>i+ zIiy|_ue)k?> zUVXp?uTSiuq|$A__xjA>3#?SL&uuPv{l!L0Ds$w2UVpO{lB{b#_F}xGyYMV?R>3$F0yDZO;^j&CK?{a*rr20E6dRO2VB>h&eqIX69T2fN`@4YJXk{+Uj z#ph~xSK*Z;-6yIoY3xYUnn;>40?E55T}`A2Ny}UufpR3}y0~~(<$FwO>|KrDHmRj| zb#Cz#rB@o%(z_=2khIz-z}ud;k#xj4+}n{4k@Urty)%$0W}O0&F#Uq*7}J*Bp3=m_s-e4y0!4V?ruQfh9Yjd^oE zL28lkfOc~}O=`nJotPV+BPk=)4rqz+WLcpzy<2eIo7%^5P2|C?h|(-)#$|hZ@Uldy zmaF5I0aYeSx1^^yF;8wUX>*DlP(6_j+P>D?i+h>0-P@N(nY7p2pLa9qgm*AcBEnht zqIVcyEHtRuX^(K8Pb7Lif?p!Swas_l5qvzpP8O~}bB@1aQGBYTW+R<|W)Y=Yf=Ai` zEtHz?*n8g5e2XAvz5gfg7#`zGxv-1pUwOCUizMAl_~6}|?~?S7;}`F?{EVa*2X&tW zeoIo+*4Bhm^h;^kC5cl>z;Q%-a*pqgjPQN`D`L_ zjqA@B6Q!~Y%hrnh`9?|KS-0~U!1s!DwAvoXj|h!c+XH!l)UFKa;xmX}lG^6U(RJ%Qk6m}nrZN}xrO%8YY@Z(etj)VhCTK9Tp66h34< zc#e{^$FU#ek}B!eslh<=BwckJ4zyg7atZTXPn5&fj2HvjHc8>*CIA&mIxsKQcM^Xr z>A`>*zLWVMCe8Jo!fivb);a9KfJMG3yrHE3#jW(6%7Y|r?6AQ%jklLnZRmF2X?&zf zM}5=zOi2;p1->)*DoNYJ#`tFN?WT4K=%7hAd}s1PNtb6o@SV-yne@_kF4w|D%}&mK z?>mpzC0fXK&mQADpNC0pRMdN*BuTOL-}`2AJzS)#QTvl`7OzT_&dOd2_gugmNva-? z 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 diff --git a/Libraries/moonsharp b/Libraries/moonsharp new file mode 160000 index 000000000..6a2b661f5 --- /dev/null +++ b/Libraries/moonsharp @@ -0,0 +1 @@ +Subproject commit 6a2b661f505b6edf44b4c7d4e27f0932e5cccb15 diff --git a/LinuxSolution.sln b/LinuxSolution.sln index 6c841ff6d..16e6b7939 100644 --- a/LinuxSolution.sln +++ b/LinuxSolution.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29201.188 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32319.34 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D32A29D8-AC7B-4189-B734-8ED9EB4120D0}" ProjectSection(SolutionItems) = preProject @@ -40,7 +40,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LinuxServer", "Barotrauma\B EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoGame.Framework.Linux.NetStandard", "Libraries\MonoGame.Framework\Src\MonoGame.Framework\MonoGame.Framework.Linux.NetStandard.csproj", "{33E95A21-E071-4432-819F-AA64CF3EF3F1}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "LinuxTest", "Barotrauma\BarotraumaTest\LinuxTest.csproj", "{F1B80D94-8BD6-48CE-8D17-BB2A5C98BCA3}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "LinuxTest", "Barotrauma\BarotraumaTest\LinuxTest.csproj", "{F1B80D94-8BD6-48CE-8D17-BB2A5C98BCA3}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MoonSharp.Interpreter.netcore", "Libraries\moonsharp\src\MoonSharp.Interpreter\_Projects\MoonSharp.Interpreter.netcore\MoonSharp.Interpreter.netcore.csproj", "{382DFA63-78FC-41AC-BA85-630960A56E5C}" EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution @@ -199,6 +201,18 @@ Global {F1B80D94-8BD6-48CE-8D17-BB2A5C98BCA3}.Unstable|Any CPU.Build.0 = Debug|Any CPU {F1B80D94-8BD6-48CE-8D17-BB2A5C98BCA3}.Unstable|x64.ActiveCfg = Debug|Any CPU {F1B80D94-8BD6-48CE-8D17-BB2A5C98BCA3}.Unstable|x64.Build.0 = Debug|Any CPU + {382DFA63-78FC-41AC-BA85-630960A56E5C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {382DFA63-78FC-41AC-BA85-630960A56E5C}.Debug|Any CPU.Build.0 = Debug|Any CPU + {382DFA63-78FC-41AC-BA85-630960A56E5C}.Debug|x64.ActiveCfg = Debug|Any CPU + {382DFA63-78FC-41AC-BA85-630960A56E5C}.Debug|x64.Build.0 = Debug|Any CPU + {382DFA63-78FC-41AC-BA85-630960A56E5C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {382DFA63-78FC-41AC-BA85-630960A56E5C}.Release|Any CPU.Build.0 = Release|Any CPU + {382DFA63-78FC-41AC-BA85-630960A56E5C}.Release|x64.ActiveCfg = Release|Any CPU + {382DFA63-78FC-41AC-BA85-630960A56E5C}.Release|x64.Build.0 = Release|Any CPU + {382DFA63-78FC-41AC-BA85-630960A56E5C}.Unstable|Any CPU.ActiveCfg = Debug|Any CPU + {382DFA63-78FC-41AC-BA85-630960A56E5C}.Unstable|Any CPU.Build.0 = Debug|Any CPU + {382DFA63-78FC-41AC-BA85-630960A56E5C}.Unstable|x64.ActiveCfg = Debug|Any CPU + {382DFA63-78FC-41AC-BA85-630960A56E5C}.Unstable|x64.Build.0 = Debug|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -217,6 +231,7 @@ Global {2B0881F6-9C67-4446-A1F2-FC042763A462} = {68B18BE6-9EE0-49DA-AE3A-4C7326F768F9} {33E95A21-E071-4432-819F-AA64CF3EF3F1} = {DE36F45F-F09E-4719-B953-00D148F7722A} {F1B80D94-8BD6-48CE-8D17-BB2A5C98BCA3} = {68B18BE6-9EE0-49DA-AE3A-4C7326F768F9} + {382DFA63-78FC-41AC-BA85-630960A56E5C} = {DE36F45F-F09E-4719-B953-00D148F7722A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {17032EAB-554B-4B44-A4F6-EFB177ACAB7A} diff --git a/MacSolution.sln b/MacSolution.sln index beaa4a347..a801ae405 100644 --- a/MacSolution.sln +++ b/MacSolution.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29201.188 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32319.34 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D32A29D8-AC7B-4189-B734-8ED9EB4120D0}" ProjectSection(SolutionItems) = preProject @@ -37,7 +37,9 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MonoGame.Framework.MacOS.Ne EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Facepunch.Steamworks.Posix", "Libraries\Facepunch.Steamworks\Facepunch.Steamworks.Posix.csproj", "{F10CE3BB-26B8-446E-84D2-86D25E850F61}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "MacTest", "Barotrauma\BarotraumaTest\MacTest.csproj", "{20BC9336-B439-4BF1-8B65-D587DBF421D1}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MacTest", "Barotrauma\BarotraumaTest\MacTest.csproj", "{20BC9336-B439-4BF1-8B65-D587DBF421D1}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MoonSharp.Interpreter.netcore", "Libraries\moonsharp\src\MoonSharp.Interpreter\_Projects\MoonSharp.Interpreter.netcore\MoonSharp.Interpreter.netcore.csproj", "{40BDE83D-61D5-481C-A53E-E0F5B23881E2}" EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution @@ -196,6 +198,18 @@ Global {20BC9336-B439-4BF1-8B65-D587DBF421D1}.Unstable|Any CPU.Build.0 = Debug|Any CPU {20BC9336-B439-4BF1-8B65-D587DBF421D1}.Unstable|x64.ActiveCfg = Debug|Any CPU {20BC9336-B439-4BF1-8B65-D587DBF421D1}.Unstable|x64.Build.0 = Debug|Any CPU + {40BDE83D-61D5-481C-A53E-E0F5B23881E2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {40BDE83D-61D5-481C-A53E-E0F5B23881E2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {40BDE83D-61D5-481C-A53E-E0F5B23881E2}.Debug|x64.ActiveCfg = Debug|Any CPU + {40BDE83D-61D5-481C-A53E-E0F5B23881E2}.Debug|x64.Build.0 = Debug|Any CPU + {40BDE83D-61D5-481C-A53E-E0F5B23881E2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {40BDE83D-61D5-481C-A53E-E0F5B23881E2}.Release|Any CPU.Build.0 = Release|Any CPU + {40BDE83D-61D5-481C-A53E-E0F5B23881E2}.Release|x64.ActiveCfg = Release|Any CPU + {40BDE83D-61D5-481C-A53E-E0F5B23881E2}.Release|x64.Build.0 = Release|Any CPU + {40BDE83D-61D5-481C-A53E-E0F5B23881E2}.Unstable|Any CPU.ActiveCfg = Debug|Any CPU + {40BDE83D-61D5-481C-A53E-E0F5B23881E2}.Unstable|Any CPU.Build.0 = Debug|Any CPU + {40BDE83D-61D5-481C-A53E-E0F5B23881E2}.Unstable|x64.ActiveCfg = Debug|Any CPU + {40BDE83D-61D5-481C-A53E-E0F5B23881E2}.Unstable|x64.Build.0 = Debug|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -214,6 +228,7 @@ Global {35DDDA7D-328D-4A5D-BCBB-2E60C830A899} = {DE36F45F-F09E-4719-B953-00D148F7722A} {F10CE3BB-26B8-446E-84D2-86D25E850F61} = {DE36F45F-F09E-4719-B953-00D148F7722A} {20BC9336-B439-4BF1-8B65-D587DBF421D1} = {DFD82BBD-8D05-403D-BEBC-F4C1CF783E18} + {40BDE83D-61D5-481C-A53E-E0F5B23881E2} = {DE36F45F-F09E-4719-B953-00D148F7722A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {17032EAB-554B-4B44-A4F6-EFB177ACAB7A} diff --git a/WindowsSolution.sln b/WindowsSolution.sln index accf6ea19..b0d437056 100644 --- a/WindowsSolution.sln +++ b/WindowsSolution.sln @@ -1,7 +1,7 @@  Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio Version 16 -VisualStudioVersion = 16.0.29201.188 +# Visual Studio Version 17 +VisualStudioVersion = 17.1.32319.34 MinimumVisualStudioVersion = 10.0.40219.1 Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{D32A29D8-AC7B-4189-B734-8ED9EB4120D0}" ProjectSection(SolutionItems) = preProject @@ -40,90 +40,179 @@ Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "XNATypes", "Libraries\XNATy EndProject Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "SharpFont.NetStandard", "Libraries\SharpFont\Source\SharpFont\SharpFont.NetStandard.csproj", "{6911872D-40EF-400C-B0A1-9985A19ED488}" EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "WindowsTest", "Barotrauma\BarotraumaTest\WindowsTest.csproj", "{C7212AE2-A925-4225-A639-AE0653EF65B0}" +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "WindowsTest", "Barotrauma\BarotraumaTest\WindowsTest.csproj", "{C7212AE2-A925-4225-A639-AE0653EF65B0}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "MoonSharp.Interpreter.netcore", "Libraries\moonsharp\src\MoonSharp.Interpreter\_Projects\MoonSharp.Interpreter.netcore\MoonSharp.Interpreter.netcore.csproj", "{2EEF2610-64A3-4E5D-95ED-0E181C1A34ED}" EndProject Global GlobalSection(SharedMSBuildProjectFiles) = preSolution Libraries\GameAnalytics\GA-SDK-MONO-SHARED\GA-SDK-MONO-SHARED.projitems*{95c4d59d-9be4-4278-b4f8-46c0ba1a3916}*SharedItemsImports = 5 EndGlobalSection GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU Debug|x64 = Debug|x64 + Release|Any CPU = Release|Any CPU Release|x64 = Release|x64 + Unstable|Any CPU = Unstable|Any CPU Unstable|x64 = Unstable|x64 EndGlobalSection GlobalSection(ProjectConfigurationPlatforms) = postSolution + {E1BBC67C-DC2A-40E8-89F3-B57299D7B16C}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E1BBC67C-DC2A-40E8-89F3-B57299D7B16C}.Debug|Any CPU.Build.0 = Debug|Any CPU {E1BBC67C-DC2A-40E8-89F3-B57299D7B16C}.Debug|x64.ActiveCfg = Debug|x64 {E1BBC67C-DC2A-40E8-89F3-B57299D7B16C}.Debug|x64.Build.0 = Debug|x64 + {E1BBC67C-DC2A-40E8-89F3-B57299D7B16C}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E1BBC67C-DC2A-40E8-89F3-B57299D7B16C}.Release|Any CPU.Build.0 = Release|Any CPU {E1BBC67C-DC2A-40E8-89F3-B57299D7B16C}.Release|x64.ActiveCfg = Release|x64 {E1BBC67C-DC2A-40E8-89F3-B57299D7B16C}.Release|x64.Build.0 = Release|x64 + {E1BBC67C-DC2A-40E8-89F3-B57299D7B16C}.Unstable|Any CPU.ActiveCfg = Debug|Any CPU + {E1BBC67C-DC2A-40E8-89F3-B57299D7B16C}.Unstable|Any CPU.Build.0 = Debug|Any CPU {E1BBC67C-DC2A-40E8-89F3-B57299D7B16C}.Unstable|x64.ActiveCfg = Release|x64 {E1BBC67C-DC2A-40E8-89F3-B57299D7B16C}.Unstable|x64.Build.0 = Release|x64 + {95C4D59D-9BE4-4278-B4F8-46C0BA1A3916}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {95C4D59D-9BE4-4278-B4F8-46C0BA1A3916}.Debug|Any CPU.Build.0 = Debug|Any CPU {95C4D59D-9BE4-4278-B4F8-46C0BA1A3916}.Debug|x64.ActiveCfg = Debug|x64 {95C4D59D-9BE4-4278-B4F8-46C0BA1A3916}.Debug|x64.Build.0 = Debug|x64 + {95C4D59D-9BE4-4278-B4F8-46C0BA1A3916}.Release|Any CPU.ActiveCfg = Release|Any CPU + {95C4D59D-9BE4-4278-B4F8-46C0BA1A3916}.Release|Any CPU.Build.0 = Release|Any CPU {95C4D59D-9BE4-4278-B4F8-46C0BA1A3916}.Release|x64.ActiveCfg = Release|x64 {95C4D59D-9BE4-4278-B4F8-46C0BA1A3916}.Release|x64.Build.0 = Release|x64 + {95C4D59D-9BE4-4278-B4F8-46C0BA1A3916}.Unstable|Any CPU.ActiveCfg = Debug|Any CPU + {95C4D59D-9BE4-4278-B4F8-46C0BA1A3916}.Unstable|Any CPU.Build.0 = Debug|Any CPU {95C4D59D-9BE4-4278-B4F8-46C0BA1A3916}.Unstable|x64.ActiveCfg = Release|x64 {95C4D59D-9BE4-4278-B4F8-46C0BA1A3916}.Unstable|x64.Build.0 = Release|x64 + {AD30AE95-7BF6-4CE5-AEED-B6C30A88F139}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AD30AE95-7BF6-4CE5-AEED-B6C30A88F139}.Debug|Any CPU.Build.0 = Debug|Any CPU {AD30AE95-7BF6-4CE5-AEED-B6C30A88F139}.Debug|x64.ActiveCfg = Debug|x64 {AD30AE95-7BF6-4CE5-AEED-B6C30A88F139}.Debug|x64.Build.0 = Debug|x64 + {AD30AE95-7BF6-4CE5-AEED-B6C30A88F139}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AD30AE95-7BF6-4CE5-AEED-B6C30A88F139}.Release|Any CPU.Build.0 = Release|Any CPU {AD30AE95-7BF6-4CE5-AEED-B6C30A88F139}.Release|x64.ActiveCfg = Release|x64 {AD30AE95-7BF6-4CE5-AEED-B6C30A88F139}.Release|x64.Build.0 = Release|x64 + {AD30AE95-7BF6-4CE5-AEED-B6C30A88F139}.Unstable|Any CPU.ActiveCfg = Debug|Any CPU + {AD30AE95-7BF6-4CE5-AEED-B6C30A88F139}.Unstable|Any CPU.Build.0 = Debug|Any CPU {AD30AE95-7BF6-4CE5-AEED-B6C30A88F139}.Unstable|x64.ActiveCfg = Release|x64 {AD30AE95-7BF6-4CE5-AEED-B6C30A88F139}.Unstable|x64.Build.0 = Release|x64 + {894D3518-A0E3-4B88-B9BF-9E1AFC3F9523}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {894D3518-A0E3-4B88-B9BF-9E1AFC3F9523}.Debug|Any CPU.Build.0 = Debug|Any CPU {894D3518-A0E3-4B88-B9BF-9E1AFC3F9523}.Debug|x64.ActiveCfg = Debug|x64 {894D3518-A0E3-4B88-B9BF-9E1AFC3F9523}.Debug|x64.Build.0 = Debug|x64 + {894D3518-A0E3-4B88-B9BF-9E1AFC3F9523}.Release|Any CPU.ActiveCfg = Release|Any CPU + {894D3518-A0E3-4B88-B9BF-9E1AFC3F9523}.Release|Any CPU.Build.0 = Release|Any CPU {894D3518-A0E3-4B88-B9BF-9E1AFC3F9523}.Release|x64.ActiveCfg = Release|x64 {894D3518-A0E3-4B88-B9BF-9E1AFC3F9523}.Release|x64.Build.0 = Release|x64 + {894D3518-A0E3-4B88-B9BF-9E1AFC3F9523}.Unstable|Any CPU.ActiveCfg = Debug|Any CPU + {894D3518-A0E3-4B88-B9BF-9E1AFC3F9523}.Unstable|Any CPU.Build.0 = Debug|Any CPU {894D3518-A0E3-4B88-B9BF-9E1AFC3F9523}.Unstable|x64.ActiveCfg = Release|x64 {894D3518-A0E3-4B88-B9BF-9E1AFC3F9523}.Unstable|x64.Build.0 = Release|x64 + {ED2873CA-C209-4CBC-ADD4-DAA753DFEEAF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {ED2873CA-C209-4CBC-ADD4-DAA753DFEEAF}.Debug|Any CPU.Build.0 = Debug|Any CPU {ED2873CA-C209-4CBC-ADD4-DAA753DFEEAF}.Debug|x64.ActiveCfg = Debug|x64 {ED2873CA-C209-4CBC-ADD4-DAA753DFEEAF}.Debug|x64.Build.0 = Debug|x64 + {ED2873CA-C209-4CBC-ADD4-DAA753DFEEAF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {ED2873CA-C209-4CBC-ADD4-DAA753DFEEAF}.Release|Any CPU.Build.0 = Release|Any CPU {ED2873CA-C209-4CBC-ADD4-DAA753DFEEAF}.Release|x64.ActiveCfg = Release|x64 {ED2873CA-C209-4CBC-ADD4-DAA753DFEEAF}.Release|x64.Build.0 = Release|x64 + {ED2873CA-C209-4CBC-ADD4-DAA753DFEEAF}.Unstable|Any CPU.ActiveCfg = Debug|Any CPU + {ED2873CA-C209-4CBC-ADD4-DAA753DFEEAF}.Unstable|Any CPU.Build.0 = Debug|Any CPU {ED2873CA-C209-4CBC-ADD4-DAA753DFEEAF}.Unstable|x64.ActiveCfg = Release|x64 {ED2873CA-C209-4CBC-ADD4-DAA753DFEEAF}.Unstable|x64.Build.0 = Release|x64 + {978633A8-094A-4623-9B82-8533FC8BA1CC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {978633A8-094A-4623-9B82-8533FC8BA1CC}.Debug|Any CPU.Build.0 = Debug|Any CPU {978633A8-094A-4623-9B82-8533FC8BA1CC}.Debug|x64.ActiveCfg = Debug|x64 {978633A8-094A-4623-9B82-8533FC8BA1CC}.Debug|x64.Build.0 = Debug|x64 + {978633A8-094A-4623-9B82-8533FC8BA1CC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {978633A8-094A-4623-9B82-8533FC8BA1CC}.Release|Any CPU.Build.0 = Release|Any CPU {978633A8-094A-4623-9B82-8533FC8BA1CC}.Release|x64.ActiveCfg = Release|x64 {978633A8-094A-4623-9B82-8533FC8BA1CC}.Release|x64.Build.0 = Release|x64 + {978633A8-094A-4623-9B82-8533FC8BA1CC}.Unstable|Any CPU.ActiveCfg = Unstable|Any CPU + {978633A8-094A-4623-9B82-8533FC8BA1CC}.Unstable|Any CPU.Build.0 = Unstable|Any CPU {978633A8-094A-4623-9B82-8533FC8BA1CC}.Unstable|x64.ActiveCfg = Unstable|x64 {978633A8-094A-4623-9B82-8533FC8BA1CC}.Unstable|x64.Build.0 = Unstable|x64 + {39E52316-D6C1-4D1F-95FF-37F41C9AB5A7}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {39E52316-D6C1-4D1F-95FF-37F41C9AB5A7}.Debug|Any CPU.Build.0 = Debug|Any CPU {39E52316-D6C1-4D1F-95FF-37F41C9AB5A7}.Debug|x64.ActiveCfg = Debug|x64 {39E52316-D6C1-4D1F-95FF-37F41C9AB5A7}.Debug|x64.Build.0 = Debug|x64 + {39E52316-D6C1-4D1F-95FF-37F41C9AB5A7}.Release|Any CPU.ActiveCfg = Release|Any CPU + {39E52316-D6C1-4D1F-95FF-37F41C9AB5A7}.Release|Any CPU.Build.0 = Release|Any CPU {39E52316-D6C1-4D1F-95FF-37F41C9AB5A7}.Release|x64.ActiveCfg = Release|x64 {39E52316-D6C1-4D1F-95FF-37F41C9AB5A7}.Release|x64.Build.0 = Release|x64 + {39E52316-D6C1-4D1F-95FF-37F41C9AB5A7}.Unstable|Any CPU.ActiveCfg = Debug|Any CPU + {39E52316-D6C1-4D1F-95FF-37F41C9AB5A7}.Unstable|Any CPU.Build.0 = Debug|Any CPU {39E52316-D6C1-4D1F-95FF-37F41C9AB5A7}.Unstable|x64.ActiveCfg = Release|x64 {39E52316-D6C1-4D1F-95FF-37F41C9AB5A7}.Unstable|x64.Build.0 = Release|x64 + {D379BF8E-D696-4AB9-A27F-4D0C493BF484}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D379BF8E-D696-4AB9-A27F-4D0C493BF484}.Debug|Any CPU.Build.0 = Debug|Any CPU {D379BF8E-D696-4AB9-A27F-4D0C493BF484}.Debug|x64.ActiveCfg = Debug|x64 {D379BF8E-D696-4AB9-A27F-4D0C493BF484}.Debug|x64.Build.0 = Debug|x64 + {D379BF8E-D696-4AB9-A27F-4D0C493BF484}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D379BF8E-D696-4AB9-A27F-4D0C493BF484}.Release|Any CPU.Build.0 = Release|Any CPU {D379BF8E-D696-4AB9-A27F-4D0C493BF484}.Release|x64.ActiveCfg = Release|x64 {D379BF8E-D696-4AB9-A27F-4D0C493BF484}.Release|x64.Build.0 = Release|x64 + {D379BF8E-D696-4AB9-A27F-4D0C493BF484}.Unstable|Any CPU.ActiveCfg = Debug|Any CPU + {D379BF8E-D696-4AB9-A27F-4D0C493BF484}.Unstable|Any CPU.Build.0 = Debug|Any CPU {D379BF8E-D696-4AB9-A27F-4D0C493BF484}.Unstable|x64.ActiveCfg = Release|x64 {D379BF8E-D696-4AB9-A27F-4D0C493BF484}.Unstable|x64.Build.0 = Release|x64 + {47848C6E-C7A8-4EC3-96C2-3BC8A4234AFA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {47848C6E-C7A8-4EC3-96C2-3BC8A4234AFA}.Debug|Any CPU.Build.0 = Debug|Any CPU {47848C6E-C7A8-4EC3-96C2-3BC8A4234AFA}.Debug|x64.ActiveCfg = Debug|x64 {47848C6E-C7A8-4EC3-96C2-3BC8A4234AFA}.Debug|x64.Build.0 = Debug|x64 + {47848C6E-C7A8-4EC3-96C2-3BC8A4234AFA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {47848C6E-C7A8-4EC3-96C2-3BC8A4234AFA}.Release|Any CPU.Build.0 = Release|Any CPU {47848C6E-C7A8-4EC3-96C2-3BC8A4234AFA}.Release|x64.ActiveCfg = Release|x64 {47848C6E-C7A8-4EC3-96C2-3BC8A4234AFA}.Release|x64.Build.0 = Release|x64 + {47848C6E-C7A8-4EC3-96C2-3BC8A4234AFA}.Unstable|Any CPU.ActiveCfg = Unstable|Any CPU + {47848C6E-C7A8-4EC3-96C2-3BC8A4234AFA}.Unstable|Any CPU.Build.0 = Unstable|Any CPU {47848C6E-C7A8-4EC3-96C2-3BC8A4234AFA}.Unstable|x64.ActiveCfg = Unstable|x64 {47848C6E-C7A8-4EC3-96C2-3BC8A4234AFA}.Unstable|x64.Build.0 = Unstable|x64 + {1F318AC4-F808-4130-867F-B98DF9AA8F95}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {1F318AC4-F808-4130-867F-B98DF9AA8F95}.Debug|Any CPU.Build.0 = Debug|Any CPU {1F318AC4-F808-4130-867F-B98DF9AA8F95}.Debug|x64.ActiveCfg = Debug|x64 {1F318AC4-F808-4130-867F-B98DF9AA8F95}.Debug|x64.Build.0 = Debug|x64 + {1F318AC4-F808-4130-867F-B98DF9AA8F95}.Release|Any CPU.ActiveCfg = Release|Any CPU + {1F318AC4-F808-4130-867F-B98DF9AA8F95}.Release|Any CPU.Build.0 = Release|Any CPU {1F318AC4-F808-4130-867F-B98DF9AA8F95}.Release|x64.ActiveCfg = Release|x64 {1F318AC4-F808-4130-867F-B98DF9AA8F95}.Release|x64.Build.0 = Release|x64 + {1F318AC4-F808-4130-867F-B98DF9AA8F95}.Unstable|Any CPU.ActiveCfg = Debug|Any CPU + {1F318AC4-F808-4130-867F-B98DF9AA8F95}.Unstable|Any CPU.Build.0 = Debug|Any CPU {1F318AC4-F808-4130-867F-B98DF9AA8F95}.Unstable|x64.ActiveCfg = Release|x64 {1F318AC4-F808-4130-867F-B98DF9AA8F95}.Unstable|x64.Build.0 = Release|x64 + {6911872D-40EF-400C-B0A1-9985A19ED488}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {6911872D-40EF-400C-B0A1-9985A19ED488}.Debug|Any CPU.Build.0 = Debug|Any CPU {6911872D-40EF-400C-B0A1-9985A19ED488}.Debug|x64.ActiveCfg = Debug|x64 {6911872D-40EF-400C-B0A1-9985A19ED488}.Debug|x64.Build.0 = Debug|x64 + {6911872D-40EF-400C-B0A1-9985A19ED488}.Release|Any CPU.ActiveCfg = Release|Any CPU + {6911872D-40EF-400C-B0A1-9985A19ED488}.Release|Any CPU.Build.0 = Release|Any CPU {6911872D-40EF-400C-B0A1-9985A19ED488}.Release|x64.ActiveCfg = Release|x64 {6911872D-40EF-400C-B0A1-9985A19ED488}.Release|x64.Build.0 = Release|x64 + {6911872D-40EF-400C-B0A1-9985A19ED488}.Unstable|Any CPU.ActiveCfg = Debug|Any CPU + {6911872D-40EF-400C-B0A1-9985A19ED488}.Unstable|Any CPU.Build.0 = Debug|Any CPU {6911872D-40EF-400C-B0A1-9985A19ED488}.Unstable|x64.ActiveCfg = Release|x64 {6911872D-40EF-400C-B0A1-9985A19ED488}.Unstable|x64.Build.0 = Release|x64 + {C7212AE2-A925-4225-A639-AE0653EF65B0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {C7212AE2-A925-4225-A639-AE0653EF65B0}.Debug|Any CPU.Build.0 = Debug|Any CPU {C7212AE2-A925-4225-A639-AE0653EF65B0}.Debug|x64.ActiveCfg = Debug|Any CPU {C7212AE2-A925-4225-A639-AE0653EF65B0}.Debug|x64.Build.0 = Debug|Any CPU + {C7212AE2-A925-4225-A639-AE0653EF65B0}.Release|Any CPU.ActiveCfg = Release|Any CPU + {C7212AE2-A925-4225-A639-AE0653EF65B0}.Release|Any CPU.Build.0 = Release|Any CPU {C7212AE2-A925-4225-A639-AE0653EF65B0}.Release|x64.ActiveCfg = Release|Any CPU {C7212AE2-A925-4225-A639-AE0653EF65B0}.Release|x64.Build.0 = Release|Any CPU + {C7212AE2-A925-4225-A639-AE0653EF65B0}.Unstable|Any CPU.ActiveCfg = Release|Any CPU + {C7212AE2-A925-4225-A639-AE0653EF65B0}.Unstable|Any CPU.Build.0 = Release|Any CPU {C7212AE2-A925-4225-A639-AE0653EF65B0}.Unstable|x64.ActiveCfg = Debug|Any CPU {C7212AE2-A925-4225-A639-AE0653EF65B0}.Unstable|x64.Build.0 = Debug|Any CPU + {2EEF2610-64A3-4E5D-95ED-0E181C1A34ED}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2EEF2610-64A3-4E5D-95ED-0E181C1A34ED}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2EEF2610-64A3-4E5D-95ED-0E181C1A34ED}.Debug|x64.ActiveCfg = Debug|Any CPU + {2EEF2610-64A3-4E5D-95ED-0E181C1A34ED}.Debug|x64.Build.0 = Debug|Any CPU + {2EEF2610-64A3-4E5D-95ED-0E181C1A34ED}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2EEF2610-64A3-4E5D-95ED-0E181C1A34ED}.Release|Any CPU.Build.0 = Release|Any CPU + {2EEF2610-64A3-4E5D-95ED-0E181C1A34ED}.Release|x64.ActiveCfg = Release|Any CPU + {2EEF2610-64A3-4E5D-95ED-0E181C1A34ED}.Release|x64.Build.0 = Release|Any CPU + {2EEF2610-64A3-4E5D-95ED-0E181C1A34ED}.Unstable|Any CPU.ActiveCfg = Debug|Any CPU + {2EEF2610-64A3-4E5D-95ED-0E181C1A34ED}.Unstable|Any CPU.Build.0 = Debug|Any CPU + {2EEF2610-64A3-4E5D-95ED-0E181C1A34ED}.Unstable|x64.ActiveCfg = Debug|Any CPU + {2EEF2610-64A3-4E5D-95ED-0E181C1A34ED}.Unstable|x64.Build.0 = Debug|Any CPU EndGlobalSection GlobalSection(SolutionProperties) = preSolution HideSolutionNode = FALSE @@ -142,6 +231,7 @@ Global {1F318AC4-F808-4130-867F-B98DF9AA8F95} = {DE36F45F-F09E-4719-B953-00D148F7722A} {6911872D-40EF-400C-B0A1-9985A19ED488} = {DE36F45F-F09E-4719-B953-00D148F7722A} {C7212AE2-A925-4225-A639-AE0653EF65B0} = {78A9F0AA-5519-407A-9B72-2A09F5DF7068} + {2EEF2610-64A3-4E5D-95ED-0E181C1A34ED} = {DE36F45F-F09E-4719-B953-00D148F7722A} EndGlobalSection GlobalSection(ExtensibilityGlobals) = postSolution SolutionGuid = {17032EAB-554B-4B44-A4F6-EFB177ACAB7A} From d98daf008e5da0dec76848f91125aecd047b1379 Mon Sep 17 00:00:00 2001 From: peelz Date: Wed, 3 Aug 2022 21:34:41 -0400 Subject: [PATCH 06/35] Add lua types for C# primitives --- .../Lua/DefaultLib/LibShared.lua | 16 +- .../Lua/DefaultRegister/RegisterShared.lua | 11 +- .../LuaCs/Lua/LuaClasses/LuaTypes.cs | 199 +++++++++++++++--- .../LuaCs/Lua/LuaCustomConverters.cs | 76 +++++-- 4 files changed, 254 insertions(+), 48 deletions(-) diff --git a/Barotrauma/BarotraumaShared/Lua/DefaultLib/LibShared.lua b/Barotrauma/BarotraumaShared/Lua/DefaultLib/LibShared.lua index 72d3809a2..66eed4c4f 100644 --- a/Barotrauma/BarotraumaShared/Lua/DefaultLib/LibShared.lua +++ b/Barotrauma/BarotraumaShared/Lua/DefaultLib/LibShared.lua @@ -6,9 +6,21 @@ local CreateEnum = LuaSetup.LuaUserData.CreateEnumTable require("DefaultLib/Utils/SteamApi") +defaultLib["SByte"] = CreateStatic("Barotrauma.LuaSByte", true) defaultLib["Byte"] = CreateStatic("Barotrauma.LuaByte", true) -defaultLib["UShort"] = CreateStatic("Barotrauma.LuaUShort", true) -defaultLib["Float"] = CreateStatic("Barotrauma.LuaFloat", true) +defaultLib["Int16"] = CreateStatic("Barotrauma.LuaInt16", true) +defaultLib["UInt16"] = CreateStatic("Barotrauma.LuaUInt16", true) +defaultLib["Int32"] = CreateStatic("Barotrauma.LuaInt32", true) +defaultLib["UInt32"] = CreateStatic("Barotrauma.LuaUInt32", true) +defaultLib["Int64"] = CreateStatic("Barotrauma.LuaInt64", true) +defaultLib["UInt64"] = CreateStatic("Barotrauma.LuaUInt64", true) +defaultLib["Single"] = CreateStatic("Barotrauma.LuaSingle", true) +defaultLib["Double"] = CreateStatic("Barotrauma.LuaDouble", true) + +-- Backward compatibility +defaultLib["Float"] = CreateStatic("Barotrauma.LuaSingle", true) +defaultLib["Short"] = CreateStatic("Barotrauma.LuaInt16", true) +defaultLib["UShort"] = CreateStatic("Barotrauma.LuaUInt16", true) defaultLib["SpawnType"] = CreateEnum("Barotrauma.SpawnType") defaultLib["ChatMessageType"] = CreateEnum("Barotrauma.Networking.ChatMessageType") diff --git a/Barotrauma/BarotraumaShared/Lua/DefaultRegister/RegisterShared.lua b/Barotrauma/BarotraumaShared/Lua/DefaultRegister/RegisterShared.lua index 95b2910c4..b10842ef6 100644 --- a/Barotrauma/BarotraumaShared/Lua/DefaultRegister/RegisterShared.lua +++ b/Barotrauma/BarotraumaShared/Lua/DefaultRegister/RegisterShared.lua @@ -4,9 +4,16 @@ local RegisterBarotrauma = LuaSetup.LuaUserData.RegisterTypeBarotrauma Register("System.TimeSpan") Register("System.Exception") +RegisterBarotrauma("LuaSByte") RegisterBarotrauma("LuaByte") -RegisterBarotrauma("LuaUShort") -RegisterBarotrauma("LuaFloat") +RegisterBarotrauma("LuaInt16") +RegisterBarotrauma("LuaUInt16") +RegisterBarotrauma("LuaInt32") +RegisterBarotrauma("LuaUInt32") +RegisterBarotrauma("LuaInt64") +RegisterBarotrauma("LuaUInt64") +RegisterBarotrauma("LuaSingle") +RegisterBarotrauma("LuaDouble") RegisterBarotrauma("Range`1[System.Single]") RegisterBarotrauma("Range`1[System.Int32]") diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaClasses/LuaTypes.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaClasses/LuaTypes.cs index 26fc72d66..7ba5e796f 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaClasses/LuaTypes.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaClasses/LuaTypes.cs @@ -1,41 +1,184 @@ using System; -using System.ComponentModel; namespace Barotrauma { - public class LuaByte - { - public byte Value; + public struct LuaSByte + { + private readonly sbyte value; - public LuaByte(byte v) - { - Value = v; - } + public LuaSByte(double v) + { + value = (sbyte)v; + } - public static implicit operator byte(LuaByte lb) => lb.Value; - } + public LuaSByte(string v, int radix = 10) + { + value = Convert.ToSByte(v, radix); + } - public class LuaUShort - { - public ushort Value; + public static implicit operator sbyte(LuaSByte luaValue) => luaValue.value; + } - public LuaUShort(ushort v) - { - Value = v; - } + public struct LuaByte + { + private readonly byte value; - public static implicit operator ushort(LuaUShort lb) => lb.Value; - } + public LuaByte(double v) + { + value = (byte)v; + } - public class LuaFloat - { - public float Value; + public LuaByte(string v, int radix = 10) + { + value = Convert.ToByte(v, radix); + } - public LuaFloat(float v) - { - Value = v; - } + public static implicit operator byte(LuaByte luaValue) => luaValue.value; + } - public static implicit operator float(LuaFloat lb) => lb.Value; - } + public struct LuaInt16 + { + private readonly short value; + + public LuaInt16(double v) + { + value = (short)v; + } + + public LuaInt16(string v, int radix = 10) + { + value = Convert.ToInt16(v, radix); + } + + public static implicit operator short(LuaInt16 luaValue) => luaValue.value; + } + + public struct LuaUInt16 + { + private readonly ushort value; + + public LuaUInt16(double v) + { + value = (ushort)v; + } + + public LuaUInt16(string v, int radix = 10) + { + value = Convert.ToUInt16(v, radix); + } + + public static implicit operator ushort(LuaUInt16 luaValue) => luaValue.value; + } + + public struct LuaInt32 + { + private readonly int value; + + public LuaInt32(double v) + { + value = (int)v; + } + + public LuaInt32(string v, int radix = 10) + { + value = Convert.ToInt32(v, radix); + } + + public static implicit operator int(LuaInt32 luaValue) => luaValue.value; + } + + public struct LuaUInt32 + { + private readonly uint value; + + public LuaUInt32(double v) + { + value = (uint)v; + } + + public LuaUInt32(string v, int radix = 10) + { + value = Convert.ToUInt32(v, radix); + } + + public static implicit operator uint(LuaUInt32 luaValue) => luaValue.value; + } + + public struct LuaInt64 + { + private readonly long value; + + public LuaInt64(double v) + { + value = (long)v; + } + + public LuaInt64(double lo, double hi) + { + value = Convert.ToUInt32(lo) | (long)Convert.ToInt32(hi) << 32; + } + + public LuaInt64(string v, int radix = 10) + { + value = Convert.ToInt64(v, radix); + } + + public static implicit operator long(LuaInt64 luaValue) => luaValue.value; + } + + public struct LuaUInt64 + { + private readonly ulong value; + + public LuaUInt64(double v) + { + value = (ulong)v; + } + + public LuaUInt64(double lo, double hi) + { + value = Convert.ToUInt32(lo) | (ulong)Convert.ToUInt32(hi) << 32; + } + + public LuaUInt64(string v, int radix = 10) + { + value = Convert.ToUInt64(v, radix); + } + + public static implicit operator ulong(LuaUInt64 luaValue) => luaValue.value; + } + + public struct LuaSingle + { + private readonly float value; + + public LuaSingle(double v) + { + value = (float)v; + } + + public LuaSingle(string v) + { + value = float.Parse(v); + } + + public static implicit operator float(LuaSingle luaValue) => luaValue.value; + } + + public struct LuaDouble + { + private readonly double value; + + public LuaDouble(double v) + { + value = v; + } + + public LuaDouble(string v) + { + value = double.Parse(v); + } + + public static implicit operator double(LuaDouble luaValue) => luaValue.value; + } } \ No newline at end of file diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaCustomConverters.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaCustomConverters.cs index a0a4fd308..d3366e7d7 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaCustomConverters.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaCustomConverters.cs @@ -118,22 +118,66 @@ namespace Barotrauma return UInt64.Parse(v.String); }); - Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion(DataType.UserData, typeof(object), v => - { - if (v.UserData.Object is LuaByte lbyte) - { - return lbyte.Value; - } - else if (v.UserData.Object is LuaUShort lushort) - { - return lushort.Value; - } - else if (v.UserData.Object is LuaFloat lfloat) - { - return lfloat.Value; - } - return v.UserData.Object; - }); + Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion( + scriptDataType: DataType.UserData, + clrDataType: typeof(sbyte), + converter: luaValue => luaValue.UserData.Object is LuaSByte v + ? (sbyte)v + : throw new ScriptRuntimeException("use SByte(value) to pass primitive type 'sbyte' to C#")); + Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion( + scriptDataType: DataType.UserData, + clrDataType: typeof(byte), + converter: luaValue => luaValue.UserData.Object is LuaByte v + ? (byte)v + : throw new ScriptRuntimeException("use Byte(value) to pass primitive type 'byte' to C#")); + Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion( + scriptDataType: DataType.UserData, + clrDataType: typeof(short), + converter: luaValue => luaValue.UserData.Object is LuaInt16 v + ? (short)v + : throw new ScriptRuntimeException("use Int16(value) to pass primitive type 'short' to C#")); + Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion( + scriptDataType: DataType.UserData, + clrDataType: typeof(ushort), + converter: luaValue => luaValue.UserData.Object is LuaUInt16 v + ? (ushort)v + : throw new ScriptRuntimeException("use UInt16(value) to pass primitive type 'ushort' to C#")); + Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion( + scriptDataType: DataType.UserData, + clrDataType: typeof(int), + converter: luaValue => luaValue.UserData.Object is LuaInt32 v + ? (int)v + : throw new ScriptRuntimeException("use Int32(value) to pass primitive type 'int' to C#")); + Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion( + scriptDataType: DataType.UserData, + clrDataType: typeof(uint), + converter: luaValue => luaValue.UserData.Object is LuaUInt32 v + ? (uint)v + : throw new ScriptRuntimeException("use UInt32(value) to pass primitive type 'uint' to C#")); + Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion( + scriptDataType: DataType.UserData, + clrDataType: typeof(long), + converter: luaValue => luaValue.UserData.Object is LuaInt64 v + ? (long)v + : throw new ScriptRuntimeException("use Int64(value) to pass primitive type 'long' to C#")); + Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion( + scriptDataType: DataType.UserData, + clrDataType: typeof(ulong), + converter: luaValue => luaValue.UserData.Object is LuaUInt64 v + ? (ulong)v + : throw new ScriptRuntimeException("use UInt64(value) to pass primitive type 'ulong' to C#")); + Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion( + scriptDataType: DataType.UserData, + clrDataType: typeof(float), + converter: luaValue => luaValue.UserData.Object is LuaSingle v + ? (float)v + : throw new ScriptRuntimeException("use Single(value) to pass primitive type 'float' to C#")); + Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion( + scriptDataType: DataType.UserData, + clrDataType: typeof(double), + converter: luaValue => luaValue.UserData.Object is LuaDouble v + ? (double)v + : throw new ScriptRuntimeException("use Double(value) to pass primitive type 'double' to C#")); } public static void RegisterAction() From d9dc84425d809af1e020672078af4331a9a120a3 Mon Sep 17 00:00:00 2001 From: peelz Date: Wed, 3 Aug 2022 21:34:41 -0400 Subject: [PATCH 07/35] Make LuaCsSetup easier to integrate into unit tests --- .../LuaCs/Lua/LuaCustomConverters.cs | 84 +++++++++---------- .../SharedSource/LuaCs/LuaCsHook.cs | 52 ++++++++---- .../SharedSource/LuaCs/LuaCsSetup.cs | 8 +- 3 files changed, 82 insertions(+), 62 deletions(-) diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaCustomConverters.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaCustomConverters.cs index d3366e7d7..55e6e514d 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaCustomConverters.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaCustomConverters.cs @@ -1,6 +1,4 @@ using System; -using System.Collections.Generic; -using System.Text; using MoonSharp.Interpreter; using Microsoft.Xna.Framework; using FarseerPhysics.Dynamics; @@ -8,42 +6,47 @@ using LuaCsCompatPatchFunc = Barotrauma.LuaCsPatch; namespace Barotrauma { - public static class LuaCustomConverters - { - public static void RegisterAll() - { + public delegate DynValue CallLuaFunctionFunc(object function, params object[] args); + + internal static class LuaCustomConverters + { + private static CallLuaFunctionFunc CallLuaFunction; + + public static void Initialize(CallLuaFunctionFunc callLuaFunction) + { + CallLuaFunction = callLuaFunction; + RegisterAction(); RegisterAction(); RegisterAction(); RegisterAction(); - RegisterFunc(); RegisterFunc(); Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion( DataType.Function, typeof(LuaCsAction), - v => (LuaCsAction)(args => GameMain.LuaCs.CallLuaFunction(v.Function, args))); + v => (LuaCsAction)(args => CallLuaFunction(v.Function, args))); Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion( DataType.Function, typeof(LuaCsFunc), - v => (LuaCsFunc)(args => GameMain.LuaCs.CallLuaFunction(v.Function, args))); + v => (LuaCsFunc)(args => CallLuaFunction(v.Function, args))); Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion( DataType.Function, typeof(LuaCsCompatPatchFunc), - v => (LuaCsCompatPatchFunc)((self, args) => GameMain.LuaCs.CallLuaFunction(v.Function, self, args))); + v => (LuaCsCompatPatchFunc)((self, args) => CallLuaFunction(v.Function, self, args))); Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion( DataType.Function, typeof(LuaCsPatchFunc), - v => (LuaCsPatchFunc)((self, args) => GameMain.LuaCs.CallLuaFunction(v.Function, self, args))); + v => (LuaCsPatchFunc)((self, args) => CallLuaFunction(v.Function, self, args))); #if CLIENT RegisterAction(); RegisterAction(); { - DynValue Call(object function, params object[] arguments) => GameMain.LuaCs.CallLuaFunction(function, arguments); + DynValue Call(object function, params object[] arguments) => CallLuaFunction(function, arguments); void RegisterHandler(Func converter) => Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion(DataType.Function, typeof(T), v => converter(v.Function)); @@ -51,71 +54,70 @@ namespace Barotrauma (a1, a2) => Call(f, a1, a2).CastToBool())); RegisterHandler(f => (GUIButton.OnClickedHandler)( - (a1, a2) => Call(f, a1, a2).CastToBool())); + (a1, a2) => Call(f, a1, a2).CastToBool())); RegisterHandler(f => (GUIButton.OnButtonDownHandler)( - () => Call(f).CastToBool())); + () => Call(f).CastToBool())); RegisterHandler(f => (GUIButton.OnPressedHandler)( - () => Call(f).CastToBool())); + () => Call(f).CastToBool())); RegisterHandler(f => (GUIColorPicker.OnColorSelectedHandler)( - (a1, a2) => Call(f, a1, a2).CastToBool())); + (a1, a2) => Call(f, a1, a2).CastToBool())); RegisterHandler(f => (GUIDropDown.OnSelectedHandler)( - (a1, a2) => Call(f, a1, a2).CastToBool())); + (a1, a2) => Call(f, a1, a2).CastToBool())); RegisterHandler(f => (GUIListBox.OnSelectedHandler)( - (a1, a2) => Call(f, a1, a2).CastToBool())); + (a1, a2) => Call(f, a1, a2).CastToBool())); RegisterHandler(f => (GUIListBox.OnRearrangedHandler)( - (a1, a2) => Call(f, a1, a2))); + (a1, a2) => Call(f, a1, a2))); RegisterHandler(f => (GUIListBox.CheckSelectedHandler)( - () => Call(f).ToObject())); + () => Call(f).ToObject())); RegisterHandler(f => (GUINumberInput.OnValueEnteredHandler)( (a1) => Call(f, a1))); RegisterHandler(f => (GUINumberInput.OnValueChangedHandler)( - (a1) => Call(f, a1))); + (a1) => Call(f, a1))); RegisterHandler(f => (GUIProgressBar.ProgressGetterHandler)( - () => (float)(Call(f).CastToNumber() ?? 0))); + () => (float)(Call(f).CastToNumber() ?? 0))); RegisterHandler(f => (GUIRadioButtonGroup.RadioButtonGroupDelegate)( - (a1, a2) => Call(f, a1, a2))); + (a1, a2) => Call(f, a1, a2))); RegisterHandler(f => (GUIScrollBar.OnMovedHandler)( - (a1, a2) => Call(f, a1, a2).CastToBool())); + (a1, a2) => Call(f, a1, a2).CastToBool())); RegisterHandler(f => (GUIScrollBar.ScrollConversion)( - (a1, a2) => (float)(Call(f, a1, a2).CastToNumber() ?? 0))); + (a1, a2) => (float)(Call(f, a1, a2).CastToNumber() ?? 0))); RegisterHandler(f => (GUITextBlock.TextGetterHandler)( - () => Call(f, new object[0]).CastToString())); + () => Call(f, new object[0]).CastToString())); RegisterHandler(f => (GUITextBox.OnEnterHandler)( - (a1, a2) => Call(f, a1, a2).CastToBool())); + (a1, a2) => Call(f, a1, a2).CastToBool())); RegisterHandler(f => (GUITextBox.OnTextChangedHandler)( - (a1, a2) => Call(f, a1, a2).CastToBool())); + (a1, a2) => Call(f, a1, a2).CastToBool())); RegisterHandler(f => (TextBoxEvent)( - (a1, a2) => Call(f, a1, a2))); + (a1, a2) => Call(f, a1, a2))); RegisterHandler(f => (GUITickBox.OnSelectedHandler)( - (a1) => Call(f, a1).CastToBool())); + (a1) => Call(f, a1).CastToBool())); } #endif - Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion(DataType.Table, typeof(Pair), v => { return new Pair((JobPrefab)v.Table.Get(1).ToObject(), (int)v.Table.Get(2).CastToNumber()); }); - Script.GlobalOptions.CustomConverters.SetClrToScriptCustomConversion((Script script, UInt64 v) => + Script.GlobalOptions.CustomConverters.SetClrToScriptCustomConversion((Script script, ulong v) => { return DynValue.NewString(v.ToString()); }); - Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion(DataType.String, typeof(UInt64), v => + Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion(DataType.String, typeof(ulong), v => { - return UInt64.Parse(v.String); + return ulong.Parse(v.String); }); Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion( @@ -185,13 +187,13 @@ namespace Barotrauma Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion(DataType.Function, typeof(Action), v => { var function = v.Function; - return (Action)(p => GameMain.LuaCs.CallLuaFunction(function, p)); + return (Action)(p => CallLuaFunction(function, p)); }); Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion(DataType.ClrFunction, typeof(Action), v => { var function = v.Function; - return (Action)(p => GameMain.LuaCs.CallLuaFunction(function, p)); + return (Action)(p => CallLuaFunction(function, p)); }); } @@ -200,13 +202,13 @@ namespace Barotrauma Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion(DataType.Function, typeof(Action), v => { var function = v.Function; - return (Action)((a1, a2) => GameMain.LuaCs.CallLuaFunction(function, a1, a2)); + return (Action)((a1, a2) => CallLuaFunction(function, a1, a2)); }); Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion(DataType.ClrFunction, typeof(Action), v => { var function = v.Function; - return (Action)((a1, a2) => GameMain.LuaCs.CallLuaFunction(function, a1, a2)); + return (Action)((a1, a2) => CallLuaFunction(function, a1, a2)); }); } @@ -215,13 +217,13 @@ namespace Barotrauma Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion(DataType.Function, typeof(Action), v => { var function = v.Function; - return (Action)(() => GameMain.LuaCs.CallLuaFunction(function)); + return (Action)(() => CallLuaFunction(function)); }); Script.GlobalOptions.CustomConverters.SetScriptToClrCustomConversion(DataType.ClrFunction, typeof(Action), v => { var function = v.Function; - return (Action)(() => GameMain.LuaCs.CallLuaFunction(function)); + return (Action)(() => CallLuaFunction(function)); }); } @@ -269,7 +271,5 @@ namespace Barotrauma return (Func)((T1 a, T2 b, T3 c, T4 d) => function.Call(a, b, c, d).ToObject()); }); } - } - } diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsHook.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsHook.cs index 1d6f53405..31bf20425 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsHook.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsHook.cs @@ -1,4 +1,4 @@ -using System; +using System; using System.Collections; using System.Collections.Generic; using System.Diagnostics; @@ -541,6 +541,8 @@ namespace Barotrauma private readonly Dictionary registeredPatches = new Dictionary(); + private LuaCsSetup luaCs; + private static LuaCsHook instance; private struct MethodKey : IEquatable @@ -581,9 +583,10 @@ namespace Barotrauma }; } - public LuaCsHook() + internal LuaCsHook(LuaCsSetup luaCs) { instance = this; + this.luaCs = luaCs; } public void Initialize() @@ -722,6 +725,18 @@ namespace Barotrauma { harmony?.UnpatchAll(); + foreach (var (_, patch) in registeredPatches) + { + // Remove references stored in our dynamic types so the generated + // assembly can be garbage-collected. + patch.HarmonyPrefixMethod.DeclaringType + .GetField(FIELD_LUACS, BindingFlags.Public | BindingFlags.Static) + .SetValue(null, null); + patch.HarmonyPostfixMethod.DeclaringType + .GetField(FIELD_LUACS, BindingFlags.Public | BindingFlags.Static) + .SetValue(null, null); + } + hookFunctions.Clear(); registeredPatches.Clear(); patchModuleBuilder = null; @@ -737,7 +752,6 @@ namespace Barotrauma [MoonSharpHidden] public T Call(string name, params object[] args) { - if (GameMain.LuaCs == null) throw new InvalidOperationException("Can't call hooks before LuaCsHook is initialized."); if (name == null) throw new ArgumentNullException(name); if (args == null) args = new object[0]; @@ -757,7 +771,7 @@ namespace Barotrauma try { - if (GameMain.LuaCs.PerformanceCounter.EnablePerformanceCounter) + if (luaCs.PerformanceCounter.EnablePerformanceCounter) { performanceMeasurement.Start(); } @@ -769,10 +783,10 @@ namespace Barotrauma lastResult = result.ToObject(); } - if (GameMain.LuaCs.PerformanceCounter.EnablePerformanceCounter) + if (luaCs.PerformanceCounter.EnablePerformanceCounter) { performanceMeasurement.Stop(); - GameMain.LuaCs.PerformanceCounter.SetHookElapsedTicks(name, key, performanceMeasurement.ElapsedTicks); + luaCs.PerformanceCounter.SetHookElapsedTicks(name, key, performanceMeasurement.ElapsedTicks); performanceMeasurement.Reset(); } } @@ -840,6 +854,8 @@ namespace Barotrauma private static readonly Regex InvalidIdentifierCharsRegex = new Regex(@"[^\w\d]", RegexOptions.Compiled); + private const string FIELD_LUACS = "LuaCs"; + // If you need to debug this: // - use https://sharplab.io ; it's a very useful for resource for writing IL by hand. // - use il.NewMessage("") or il.WriteLine("") to see where the IL crashes at runtime. @@ -876,6 +892,8 @@ namespace Barotrauma : MangleName(original); var typeBuilder = moduleBuilder.DefineType($"Patch_{identifier}_{Guid.NewGuid():N}_{mangledName}", TypeAttributes.Public); + var luaCsField = typeBuilder.DefineField(FIELD_LUACS, typeof(LuaCsSetup), FieldAttributes.Public | FieldAttributes.Static); + var methodName = hookType == HookMethodType.Before ? "HarmonyPrefix" : "HarmonyPostfix"; var il = Emit.BuildMethod( returnType: hookType == HookMethodType.Before ? typeof(bool) : typeof(void), @@ -1094,17 +1112,18 @@ namespace Barotrauma var exception = il.DeclareLocal("exception"); il.StoreLocal(exception); - // IL: var luaCsSetup = GameMain.LuaCs; - var luaCsSetup = il.DeclareLocal("luaCsSetup"); - il.LoadField(typeof(GameMain).GetField(nameof(GameMain.LuaCs), BindingFlags.Public | BindingFlags.Static)); - il.StoreLocal(luaCsSetup); + // IL: if (LuaCs != null) + il.LoadField(luaCsField); + il.If((il) => + { + // IL: LuaCs.HandleException(exception, "", ExceptionType.Lua); + il.LoadField(luaCsField); + il.LoadLocal(exception); + il.LoadConstant(""); + il.LoadConstant((int)ExceptionType.Lua); // underlying enum type is int + il.Call(typeof(LuaCsSetup).GetMethod(nameof(LuaCsSetup.HandleException))); + }); - // IL: luaCsSetup.HandleException(exception, "", ExceptionType.Lua); - il.LoadLocal(luaCsSetup); - il.LoadLocal(exception); - il.LoadConstant(""); - il.LoadConstant((int)ExceptionType.Lua); // underlying enum type is int - il.Call(typeof(LuaCsSetup).GetMethod(nameof(LuaCsSetup.HandleException))); il.EndCatchBlock(catchBlock); il.EndExceptionBlock(exceptionBlock); @@ -1123,6 +1142,7 @@ namespace Barotrauma } var type = typeBuilder.CreateType(); + type.GetField(FIELD_LUACS, BindingFlags.Public | BindingFlags.Static).SetValue(null, luaCs); return type.GetMethod(methodName, BindingFlags.Public | BindingFlags.Static); } diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsSetup.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsSetup.cs index e5ca284cd..daf0e669b 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsSetup.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsSetup.cs @@ -77,7 +77,7 @@ namespace Barotrauma public LuaCsSetup() { - Hook = new LuaCsHook(); + Hook = new LuaCsHook(this); ModStore = new LuaCsModStore(); Game = new LuaGame(); @@ -285,13 +285,13 @@ namespace Barotrauma return lua.LoadFile(file, globalContext, codeStringFriendly); } - public DynValue CallLuaFunction(object function, params object[] arguments) + public DynValue CallLuaFunction(object function, params object[] args) { lock (lua) { try { - return lua.Call(function, arguments); + return lua.Call(function, args); } catch (Exception e) { @@ -376,7 +376,7 @@ namespace Barotrauma LuaScriptLoader = new LuaScriptLoader(); LuaScriptLoader.ModulePaths = new string[] { }; - LuaCustomConverters.RegisterAll(); + LuaCustomConverters.Initialize(CallLuaFunction); lua = new Script(CoreModules.Preset_SoftSandbox | CoreModules.Debug); lua.Options.DebugPrint = PrintMessage; From 9a621237f0a0f3e0231cba58f4b9081ee9e6c5fc Mon Sep 17 00:00:00 2001 From: peelz Date: Wed, 3 Aug 2022 21:34:41 -0400 Subject: [PATCH 08/35] Add unit tests for LuaCsHook Patch API --- .../BarotraumaTest/LuaCs/HookPatchTests.cs | 524 ++++++++++++++++++ .../LuaCs/TestOutputTextWriterAdapter.cs | 25 + .../LuaCs/TestOutputTraceListenerAdapter.cs | 20 + 3 files changed, 569 insertions(+) create mode 100644 Barotrauma/BarotraumaTest/LuaCs/HookPatchTests.cs create mode 100644 Barotrauma/BarotraumaTest/LuaCs/TestOutputTextWriterAdapter.cs create mode 100644 Barotrauma/BarotraumaTest/LuaCs/TestOutputTraceListenerAdapter.cs diff --git a/Barotrauma/BarotraumaTest/LuaCs/HookPatchTests.cs b/Barotrauma/BarotraumaTest/LuaCs/HookPatchTests.cs new file mode 100644 index 000000000..209c751bd --- /dev/null +++ b/Barotrauma/BarotraumaTest/LuaCs/HookPatchTests.cs @@ -0,0 +1,524 @@ +using Barotrauma; +using Microsoft.Xna.Framework; +using MoonSharp.Interpreter; +using System; +using System.Diagnostics; +using Xunit; +using Xunit.Abstractions; + +namespace TestProject.LuaCs +{ + public class HookPatchTests + { + private readonly LuaCsSetup luaCs = new(); + + public HookPatchTests(ITestOutputHelper output) + { + Console.SetOut(new TestOutputTextWriterAdapter(output)); + Trace.Listeners.Add(new TestOutputTraceListenerAdapter(output)); + + UserData.RegisterType(); + UserData.RegisterType(); + UserData.RegisterType(); + UserData.RegisterType(); + UserData.RegisterType(); + UserData.RegisterType(); + UserData.RegisterType(); + UserData.RegisterType(); + UserData.RegisterType(); + + luaCs.Initialize(); + luaCs.Lua.Globals["TestValueType"] = UserData.CreateStatic(); + luaCs.Lua.Globals["InterfaceImplementingType"] = UserData.CreateStatic(); + } + + private DynValue AddPrefix(string body, string testMethod = "Run", string? patchId = null) + { + var className = typeof(T).FullName; + if (patchId != null) + { + return luaCs.Lua.DoString(@$" + return Hook.Patch('{patchId}', '{className}', '{testMethod}', function(instance, ptable) + {body} + end, Hook.HookMethodType.Before) + "); + } + else + { + return luaCs.Lua.DoString(@$" + return Hook.Patch('{className}', '{testMethod}', function(instance, ptable) + {body} + end, Hook.HookMethodType.Before) + "); + } + } + + private DynValue AddPostfix(string body, string testMethod = "Run", string? patchId = null) + { + var className = typeof(T).FullName; + if (patchId != null) + { + return luaCs.Lua.DoString(@$" + return Hook.Patch('{patchId}', '{className}', '{testMethod}', function(instance, ptable) + {body} + end, Hook.HookMethodType.After) + "); + } + else + { + return luaCs.Lua.DoString(@$" + return Hook.Patch('{className}', '{testMethod}', function(instance, ptable) + {body} + end, Hook.HookMethodType.After) + "); + } + } + + private DynValue RemovePrefix(string patchName, string testMethod = "Run") + { + var className = typeof(T).FullName; + return luaCs.Lua.DoString($@" + return Hook.RemovePatch('{patchName}', '{className}', '{testMethod}', Hook.HookMethodType.Before) + "); + } + + private DynValue RemovePostfix(string patchName, string testMethod = "Run") + { + var className = typeof(T).FullName; + return luaCs.Lua.DoString($@" + return Hook.RemovePatch('{patchName}', '{className}', '{testMethod}', Hook.HookMethodType.After) + "); + } + + public class PatchTarget1 + { + public bool ran; + + public void Run() + { + ran = true; + } + } + + [Fact] + public void TestFullMethodReplacement() + { + var target = new PatchTarget1(); + AddPrefix("ptable.PreventExecution = true"); + target.Run(); + Assert.False(target.ran); + } + + [Fact] + public void TestOverrideExistingPatch() + { + var target = new PatchTarget1(); + AddPrefix(@" + ptable.PreventExecution = true + originalPatchRan = true + ", patchId: "test"); + target.Run(); + Assert.False(target.ran); + Assert.True(luaCs.Lua.Globals["originalPatchRan"] as bool?); + + // Reset this global so we can test if the original patch ran + // after replacing it. + luaCs.Lua.Globals["originalPatchRan"] = false; + + // Replace the existing prefix, but don't prevent execution this time + AddPrefix("replacementPatchRan = true", patchId: "test"); + target.Run(); + Assert.True(target.ran); + + // Make sure the original patch didn't run + Assert.False(luaCs.Lua.Globals["originalPatchRan"] as bool?); + + // Test if the replacement patch ran + Assert.True(luaCs.Lua.Globals["replacementPatchRan"] as bool?); + } + + [Fact] + public void TestRemovePrefix() + { + var target = new PatchTarget1(); + var patchId = AddPrefix(@" + ptable.PreventExecution = true + patchRan = true + "); + target.Run(); + Assert.False(target.ran); + Assert.True(luaCs.Lua.Globals["patchRan"] as bool?); + + luaCs.Lua.Globals["patchRan"] = false; + + Assert.Equal(DataType.String, patchId.Type); + RemovePrefix(patchId.String); + target.Run(); + Assert.True(target.ran); + Assert.False(luaCs.Lua.Globals["patchRan"] as bool?); + } + + [Fact] + public void TestRemovePostfix() + { + var target = new PatchTarget1(); + var patchId = AddPostfix(@" + patchRan = true + "); + target.Run(); + Assert.True(target.ran); + Assert.True(luaCs.Lua.Globals["patchRan"] as bool?); + + target.ran = false; + luaCs.Lua.Globals["patchRan"] = false; + + Assert.Equal(DataType.String, patchId.Type); + RemovePostfix(patchId.String); + target.Run(); + Assert.True(target.ran); + Assert.False(luaCs.Lua.Globals["patchRan"] as bool?); + } + + public struct TestValueType + { + public int foo; + + public TestValueType(int foo) + { + this.foo = foo; + } + } + + public class PatchTarget2 + { + public bool ran; + + public object Run() + { + ran = true; + return 5; + } + } + + public interface IBogusInterface + { + int GetFoo(); + } + + public class InterfaceImplementingType : IBogusInterface + { + private readonly int foo; + + public InterfaceImplementingType(int foo) + { + this.foo = foo; + } + + public int GetFoo() => foo; + } + + [Fact] + public void TestReturnBoxed() + { + var target = new PatchTarget2(); + AddPrefix(@" + ptable.PreventExecution = true + return 123 + "); + var returnValue = target.Run(); + Assert.False(target.ran); + Assert.Equal(123, (int)(double)returnValue); + } + + [Fact] + public void TestReturnVoid() + { + var target = new PatchTarget2(); + // This should have no effect + AddPrefix("return"); + var returnValue = target.Run(); + Assert.True(target.ran); + Assert.Equal(5, returnValue); + } + + [Fact] + public void TestReturnNil() + { + var target = new PatchTarget2(); + // This should modify the return value to "null" + AddPostfix("return nil"); + var returnValue = target.Run(); + Assert.True(target.ran); + Assert.Null(returnValue); + } + + [Fact] + public void TestReturnValueType() + { + var target = new PatchTarget2(); + AddPostfix(@" + return TestValueType.__new(100) + "); + var returnValue = target.Run(); + Assert.True(target.ran); + Assert.IsType(returnValue); + Assert.Equal(100, ((TestValueType)returnValue).foo); + } + + public class PatchTarget3 + { + public bool ran; + + public IBogusInterface Run() + { + ran = true; + return new InterfaceImplementingType(5); + } + } + + [Fact] + public void TestReturnInterfaceImplementingType() + { + var target = new PatchTarget3(); + AddPostfix(@" + return InterfaceImplementingType.__new(100); + "); + var returnValue = target.Run()!; + Assert.True(target.ran); + Assert.Equal(100, returnValue.GetFoo()); + } + + public class PatchTarget4 + { + public bool ran; + + public void Run(int a, out string outString, ref byte refByte, string b) + { + ran = true; + outString = a + b + refByte; + } + } + + [Fact] + public void TestModifyParameters() + { + var target = new PatchTarget4(); + AddPrefix(@" + ptable['a'] = Int32(100) + ptable['b'] = 'abc' + ptable['refByte'] = Byte(4) + "); + byte refByte = 123; + target.Run(5, out var outString, ref refByte, "foo"); + Assert.True(target.ran); + Assert.Equal("100abc4", outString); + } + + public class PatchTarget5 + { + public bool ran; + + public string Run(Vector2 vec) + { + ran = true; + return vec.ToString(); + } + } + + [Fact] + public void TestParameterValueType() + { + var target = new PatchTarget5(); + AddPrefix("patchRan = true"); + var returnValue = target.Run(new Vector2(1, 2)); + Assert.True(target.ran); + Assert.True(luaCs.Lua.Globals["patchRan"] as bool?); + Assert.Equal("{X:1 Y:2}", returnValue); + } + + public class PatchTarget6 + { + public bool ran; + + public sbyte RunSByte(sbyte v) + { + ran = true; + return v; + } + + public byte RunByte(byte v) + { + ran = true; + return v; + } + + public short RunInt16(short v) + { + ran = true; + return v; + } + + public ushort RunUInt16(ushort v) + { + ran = true; + return v; + } + + public int RunInt32(int v) + { + ran = true; + return v; + } + + public uint RunUInt32(uint v) + { + ran = true; + return v; + } + + public long RunInt64(long v) + { + ran = true; + return v; + } + + public ulong RunUInt64(ulong v) + { + ran = true; + return v; + } + + public float RunSingle(float v) + { + ran = true; + return v; + } + + public double RunDouble(double v) + { + ran = true; + return v; + } + } + + [Fact] + public void TestCastPrimitiveWrapperSByte() + { + var target = new PatchTarget6(); + AddPrefix(@" + ptable['v'] = SByte(-6) + ", testMethod: nameof(PatchTarget6.RunSByte)); + var returnValue = target.RunSByte(-5); + Assert.True(target.ran); + Assert.Equal(-6, returnValue); + } + + [Fact] + public void TestCastPrimitiveWrapperByte() + { + var target = new PatchTarget6(); + AddPrefix(@" + ptable['v'] = Byte(6) + ", testMethod: nameof(PatchTarget6.RunByte)); + var returnValue = target.RunByte(5); + Assert.True(target.ran); + Assert.Equal(6, returnValue); + } + + [Fact] + public void TestCastPrimitiveWrapperInt16() + { + var target = new PatchTarget6(); + AddPrefix(@" + ptable['v'] = Int16(-25000) + ", testMethod: nameof(PatchTarget6.RunInt16)); + var returnValue = target.RunInt16(30000); + Assert.True(target.ran); + Assert.Equal(-25000, returnValue); + } + + [Fact] + public void TestCastPrimitiveWrapperUInt16() + { + var target = new PatchTarget6(); + AddPrefix(@" + ptable['v'] = UInt16(60000) + ", testMethod: nameof(PatchTarget6.RunUInt16)); + var returnValue = target.RunUInt16(50000); + Assert.True(target.ran); + Assert.Equal(60000, returnValue); + } + + [Fact] + public void TestCastPrimitiveWrapperInt32() + { + var target = new PatchTarget6(); + AddPrefix(@" + ptable['v'] = Int32('7FFFFF00', 16) + ", testMethod: nameof(PatchTarget6.RunInt32)); + var returnValue = target.RunInt32(900000); + Assert.True(target.ran); + Assert.Equal(0x7FFFFF00, returnValue); + } + + [Fact] + public void TestCastPrimitiveWrapperUInt32() + { + var target = new PatchTarget6(); + AddPrefix(@" + ptable['v'] = UInt32('AFFFFFFF', 16) + ", testMethod: nameof(PatchTarget6.RunUInt32)); + var returnValue = target.RunUInt32(300500); + Assert.True(target.ran); + Assert.Equal(0xAFFFFFFF, returnValue); + } + + [Fact] + public void TestCastPrimitiveWrapperInt64() + { + var target = new PatchTarget6(); + AddPrefix(@" + ptable['v'] = Int64('7555555555555555', 16) + ", testMethod: nameof(PatchTarget6.RunInt64)); + var returnValue = target.RunInt64(0x7FFFFFFF00000000); + Assert.True(target.ran); + Assert.Equal(0x7555555555555555, returnValue); + } + + [Fact] + public void TestCastPrimitiveWrapperUInt64() + { + var target = new PatchTarget6(); + AddPrefix(@" + ptable['v'] = UInt64('F555555555555555', 16) + ", testMethod: nameof(PatchTarget6.RunUInt64)); + var returnValue = target.RunUInt64(0xFFFFFFFF00000000); + Assert.True(target.ran); + Assert.Equal(0xF555555555555555, returnValue); + } + + [Fact] + public void TestCastPrimitiveWrapperSingle() + { + var target = new PatchTarget6(); + AddPrefix(@" + ptable['v'] = Single(123.456) + ", testMethod: nameof(PatchTarget6.RunSingle)); + var returnValue = target.RunSingle(111.111f); + Assert.True(target.ran); + Assert.Equal(123.456f, returnValue); + } + + [Fact] + public void TestCastPrimitiveWrapperDouble() + { + var target = new PatchTarget6(); + AddPrefix(@" + ptable['v'] = Double(123.456) + ", testMethod: nameof(PatchTarget6.RunDouble)); + var returnValue = target.RunDouble(111.111d); + Assert.True(target.ran); + Assert.Equal(123.456d, returnValue); + } + } +} diff --git a/Barotrauma/BarotraumaTest/LuaCs/TestOutputTextWriterAdapter.cs b/Barotrauma/BarotraumaTest/LuaCs/TestOutputTextWriterAdapter.cs new file mode 100644 index 000000000..1623b352f --- /dev/null +++ b/Barotrauma/BarotraumaTest/LuaCs/TestOutputTextWriterAdapter.cs @@ -0,0 +1,25 @@ +using System; +using System.IO; +using System.Text; +using Xunit.Abstractions; + +namespace TestProject.LuaCs +{ + internal class TestOutputTextWriterAdapter : TextWriter + { + private readonly ITestOutputHelper output; + + public TestOutputTextWriterAdapter(ITestOutputHelper output) + { + this.output = output; + } + + public override Encoding Encoding => Encoding.UTF8; + + public override void WriteLine(string? message) => output.WriteLine(message); + + public override void WriteLine(string? format, params object?[] args) => output.WriteLine(format, args); + + public override void Write(char value) => throw new NotImplementedException(); + } +} diff --git a/Barotrauma/BarotraumaTest/LuaCs/TestOutputTraceListenerAdapter.cs b/Barotrauma/BarotraumaTest/LuaCs/TestOutputTraceListenerAdapter.cs new file mode 100644 index 000000000..f64190228 --- /dev/null +++ b/Barotrauma/BarotraumaTest/LuaCs/TestOutputTraceListenerAdapter.cs @@ -0,0 +1,20 @@ +using System; +using System.Diagnostics; +using Xunit.Abstractions; + +namespace TestProject.LuaCs +{ + internal class TestOutputTraceListenerAdapter : TraceListener + { + private readonly ITestOutputHelper output; + + public TestOutputTraceListenerAdapter(ITestOutputHelper output) + { + this.output = output; + } + + public override void Write(string? message) => throw new NotImplementedException(); + + public override void WriteLine(string? message) => output.WriteLine(message); + } +} From 5d11c28db6a8b14677e678e5864d502987749453 Mon Sep 17 00:00:00 2001 From: peelz Date: Wed, 3 Aug 2022 21:34:41 -0400 Subject: [PATCH 09/35] Clean up lua docs scripts - ldoc is now a git submodule - ldoc can be installed using scripts/install.{sh,ps1} - lua docs can be build using scripts/build.{sh,ps1} - all lua docs-related files now reside inside of the docs folder --- .github/workflows/generate-lua-docs.yml | 26 ++++++---------- .gitignore | 1 - .gitmodules | 3 ++ docs/.gitignore | 2 ++ docs/build_docs.bat | 5 --- config.ld => docs/config.ld | 40 ++++++++++++------------ docs/http_server.bat | 1 - docs/libs/ldoc | 1 + docs/scripts/build.ps1 | 17 ++++++++++ docs/scripts/build.sh | 19 ++++++++++++ docs/scripts/install.ps1 | 41 +++++++++++++++++++++++++ docs/scripts/install.sh | 39 +++++++++++++++++++++++ docs/scripts/serve.ps1 | 12 ++++++++ docs/scripts/serve.sh | 11 +++++++ docs/templates/ldoc.ltp | 6 ++-- 15 files changed, 178 insertions(+), 46 deletions(-) create mode 100644 docs/.gitignore delete mode 100644 docs/build_docs.bat rename config.ld => docs/config.ld (74%) delete mode 100644 docs/http_server.bat create mode 160000 docs/libs/ldoc create mode 100644 docs/scripts/build.ps1 create mode 100755 docs/scripts/build.sh create mode 100644 docs/scripts/install.ps1 create mode 100755 docs/scripts/install.sh create mode 100644 docs/scripts/serve.ps1 create mode 100755 docs/scripts/serve.sh diff --git a/.github/workflows/generate-lua-docs.yml b/.github/workflows/generate-lua-docs.yml index 42b930917..365c823f7 100644 --- a/.github/workflows/generate-lua-docs.yml +++ b/.github/workflows/generate-lua-docs.yml @@ -13,30 +13,22 @@ jobs: steps: - name: Checkout branch uses: actions/checkout@v2 - + with: + submodules: recursive + - uses: leafo/gh-actions-lua@v8.0.0 with: luaVersion: "5.2" - uses: leafo/gh-actions-luarocks@v4.0.0 - - name: Pull LDoc - uses: actions/checkout@v2 - with: - repository: impulsh/LDoc - path: ldoc + - name: Run install script + working-directory: docs + run: ./scripts/install.sh - - name: Build LDoc - working-directory: ldoc - run: luarocks make - - - name: Build docs - run: ldoc . - - - name: Copy assets - run: | - cp -v docs/css/* docs/html - cp -v docs/js/* docs/html + - name: Run build script + working-directory: docs + run: ./scripts/build.sh - name: Deploy uses: peaceiris/actions-gh-pages@v3 diff --git a/.gitignore b/.gitignore index 83b4a80fa..7d4b67772 100644 --- a/.gitignore +++ b/.gitignore @@ -51,7 +51,6 @@ desktop.ini # Merge script temp.txt -docs/html # Private assets Barotrauma/BarotraumaShared/Content/* Barotrauma/**/GameAnalyticsKeys.cs diff --git a/.gitmodules b/.gitmodules index dd5e75d54..399c0ff11 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,3 +1,6 @@ [submodule "Libraries/moonsharp"] path = Libraries/moonsharp url = https://github.com/evilfactory/moonsharp.git +[submodule "docs/libs/ldoc"] + path = docs/libs/ldoc + url = https://github.com/impulsh/LDoc.git diff --git a/docs/.gitignore b/docs/.gitignore new file mode 100644 index 000000000..97249b860 --- /dev/null +++ b/docs/.gitignore @@ -0,0 +1,2 @@ +html +lua_modules diff --git a/docs/build_docs.bat b/docs/build_docs.bat deleted file mode 100644 index 2cc1e0904..000000000 --- a/docs/build_docs.bat +++ /dev/null @@ -1,5 +0,0 @@ -xcopy css html /Y -xcopy js html /Y -cd .. -lua B:\programming\lua\LDoc\ldoc.lua . -cd docs \ No newline at end of file diff --git a/config.ld b/docs/config.ld similarity index 74% rename from config.ld rename to docs/config.ld index 79cc5cf19..b0b9ed32d 100644 --- a/config.ld +++ b/docs/config.ld @@ -1,24 +1,26 @@ +-- vim:ft=lua file = { - "docs/lua" + "lua" } -module_file = { +module_file = {} -} - -dir = "docs/html" +dir = "html" project = "LuaForBarotrauma" title = "LuaForBarotrauma Documentation" no_space_before_args = true -style = "docs/css" -template = "docs/templates" +style = "css" +template = "templates" format = "markdown" ignore = true -topics = "docs/manual" +topics = "manual" use_markdown_titles = true -kind_names = {topic = "Manual", module = "Classes"} +kind_names = { + topic = "Manual", + module = "Classes" +} merge = true sort = true sort_modules = true @@ -31,20 +33,20 @@ pretty_urls = true -- avoid showing .html in urls pretty_topic_names = true -- strips extension from manual filenames, this does not check filename collisions custom_tags = { - {"realm", hidden = true}, - {"internal", hidden = true} + {"realm", hidden = true}, + {"internal", hidden = true} } custom_display_name_handler = function(item, default_handler) - if (item.type == "function" and item.module) then - if (item.module.type == "classmod" or item.module.type == "panel") then - return item.module.mod_name .. ":" .. default_handler(item) - elseif (item.module.type == "hooks") then - return item.module.mod_name:upper() .. ":" .. default_handler(item) - end - end + if (item.type == "function" and item.module) then + if (item.module.type == "classmod" or item.module.type == "panel") then + return item.module.mod_name .. ":" .. default_handler(item) + elseif (item.module.type == "hooks") then + return item.module.mod_name:upper() .. ":" .. default_handler(item) + end + end - return default_handler(item) + return default_handler(item) end new_type("code", "Code", true) diff --git a/docs/http_server.bat b/docs/http_server.bat deleted file mode 100644 index e57abb414..000000000 --- a/docs/http_server.bat +++ /dev/null @@ -1 +0,0 @@ -py -m http.server -d html \ No newline at end of file diff --git a/docs/libs/ldoc b/docs/libs/ldoc new file mode 160000 index 000000000..69ef8c976 --- /dev/null +++ b/docs/libs/ldoc @@ -0,0 +1 @@ +Subproject commit 69ef8c976e3ac94146f87e99c8885211128a1c13 diff --git a/docs/scripts/build.ps1 b/docs/scripts/build.ps1 new file mode 100644 index 000000000..850f8f7a3 --- /dev/null +++ b/docs/scripts/build.ps1 @@ -0,0 +1,17 @@ +try { + cd $PSScriptRoot/.. + + Remove-Item -Force -Recurse ./html | Out-Null + New-Item -ItemType Directory ./html | Out-Null + Copy-Item -Path ./css/. -Destination ./html -Recurse -Force | Out-Null + Copy-Item -Path ./js/. -Destination ./html -Recurse -Force | Out-Null + + if ((Get-Command "lua_modules/bin/ldoc" -ErrorAction SilentlyContinue) -eq $null) { + echo "ldoc not found; please run docs/scripts/install.ps1" + exit 1 + } + + lua_modules/bin/ldoc . +} finally { + popd +} diff --git a/docs/scripts/build.sh b/docs/scripts/build.sh new file mode 100755 index 000000000..df79e31cd --- /dev/null +++ b/docs/scripts/build.sh @@ -0,0 +1,19 @@ +#!/usr/bin/env bash + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" +cd "$DIR/.." + +ldoc_path=./lua_modules/bin/ldoc + +if [[ ! -x "$ldoc_path" ]]; then + echo "ldoc not found; please run docs/scripts/install.sh" + exit 1 +fi + +rm -rf ./html +mkdir ./html + +cp -r ./js/. ./html +cp -r ./css/. ./html + +"$ldoc_path" . diff --git a/docs/scripts/install.ps1 b/docs/scripts/install.ps1 new file mode 100644 index 000000000..262e7f93c --- /dev/null +++ b/docs/scripts/install.ps1 @@ -0,0 +1,41 @@ +try { + cd $PSScriptRoot/.. + + $lua_binary = $env:LUA_BINARY + if ($lua_binary -eq $null) { + $lua_binary = "lua" + } + + if ((Get-Command "$lua_binary" -ErrorAction SilentlyContinue) -eq $null) { + if ($env:LUA_BINARY -eq $null) { + echo "lua binary not found; please set `$LUA_BINARY manually." + } else { + echo "lua binary not found: $lua_binary" + } + exit 1 + } + + if ((Get-Command "luarocks" -ErrorAction SilentlyContinue) -eq $null) { + echo "luarocks not found" + exit 1 + } + + $lua_version = (Invoke-Expression -Command "& $lua_binary -v 2>&1") -Replace '^Lua (\d+)\.(\d+).*$','$1.$2' + echo "Detected lua version $lua_version" + + $luarocks_args=@( + "--tree", + "$(Get-Location)/lua_modules", + "--lua-version", + "$lua_version" + ) + + try { + cd ./libs/ldoc + luarocks @luarocks_args make + } finally { + popd + } +} finally { + popd +} diff --git a/docs/scripts/install.sh b/docs/scripts/install.sh new file mode 100755 index 000000000..a1b868bb5 --- /dev/null +++ b/docs/scripts/install.sh @@ -0,0 +1,39 @@ +#!/usr/bin/env bash + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" +cd "$DIR/.." + +lua_binary="${LUA_BINARY:-lua}" + +if ! command -v "$lua_binary" &> /dev/null; then + if [[ -z "${LUA_BINARY+x}" ]]; then + echo "lua binary not found; please set \$LUA_BINARY manually." + else + echo "lua binary not found: $lua_binary" + fi + exit 1 +fi + +if ! command -v "$lua_binary" &> /dev/null; then + echo "luarocks not found" + exit 1 +fi + +lua_version="$("$lua_binary" -v | grep -Po '^Lua \K(\d+)\.(\d+)')" +echo "Detected lua version $lua_version" + +# Install dependencies (npm style) +# NOTE: you need to have lua header files installed. +# On debian-based distros: apt install libluaX.X-dev + +luarocks_args=( + "--tree" + "$PWD/lua_modules" + "--lua-version" + "$lua_version" +) + +( + cd libs/ldoc + luarocks ${luarocks_args[@]} make +) diff --git a/docs/scripts/serve.ps1 b/docs/scripts/serve.ps1 new file mode 100644 index 000000000..9a9bffed2 --- /dev/null +++ b/docs/scripts/serve.ps1 @@ -0,0 +1,12 @@ +try { + cd $PSScriptRoot/.. + + if ((Get-Command "python3" -ErrorAction SilentlyContinue) -eq $null) { + echo "python3 not found" + exit 1 + } + + python3 -m http.server -d html +} finally { + popd +} diff --git a/docs/scripts/serve.sh b/docs/scripts/serve.sh new file mode 100755 index 000000000..198bca784 --- /dev/null +++ b/docs/scripts/serve.sh @@ -0,0 +1,11 @@ +#!/usr/bin/env bash + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" +cd "$DIR/.." + +if ! command -v "python3" &> /dev/null; then + echo "python3 not found" + exit 1 +fi + +python3 -m http.server -d html diff --git a/docs/templates/ldoc.ltp b/docs/templates/ldoc.ltp index d0b6f6e6f..95ad3f592 100644 --- a/docs/templates/ldoc.ltp +++ b/docs/templates/ldoc.ltp @@ -55,18 +55,18 @@ end - - + +
- {(docs/templates/sidebar.ltp)} + {(templates/sidebar.ltp)}
{% if (ldoc.root) then -- we're rendering the landing page (index.html) %} - {(docs/templates/landing.ltp)} + {(templates/landing.ltp)} {% elseif (ldoc.body) then -- we're rendering non-code elements %}
{* ldoc.body *}
{% elseif (module) then -- we're rendering libary contents %}
- {(docs/templates/module.ltp)} + {(templates/module.ltp)}
{% end %}
From ca51130517f13092606b23d81679b9e7a57f16e5 Mon Sep 17 00:00:00 2001 From: peelz Date: Wed, 3 Aug 2022 21:34:41 -0400 Subject: [PATCH 10/35] Add `@deprecated` tag to lua docs --- docs/config.ld | 1 + docs/css/ldoc.css | 4 +++ docs/templates/ldoc.ltp | 50 +++++++++++++++++++++++++++++++++++++- docs/templates/module.ltp | 11 ++++++--- docs/templates/sidebar.ltp | 18 ++------------ 5 files changed, 64 insertions(+), 20 deletions(-) diff --git a/docs/config.ld b/docs/config.ld index b0b9ed32d..bd278528e 100644 --- a/docs/config.ld +++ b/docs/config.ld @@ -34,6 +34,7 @@ pretty_topic_names = true -- strips extension from manual filenames, this does n custom_tags = { {"realm", hidden = true}, + {"deprecated", hidden = true}, {"internal", hidden = true} } diff --git a/docs/css/ldoc.css b/docs/css/ldoc.css index 7f4c90734..4f590feb1 100644 --- a/docs/css/ldoc.css +++ b/docs/css/ldoc.css @@ -537,3 +537,7 @@ a.type { background-color: inherit; padding: 0 8px 0 8px; } + +.strikethrough { + text-decoration: line-through; +} diff --git a/docs/templates/ldoc.ltp b/docs/templates/ldoc.ltp index 95ad3f592..b42ac394c 100644 --- a/docs/templates/ldoc.ltp +++ b/docs/templates/ldoc.ltp @@ -14,7 +14,55 @@ function ldoc.url(path) end function ldoc.realm_icon(realm) - return ""; + return "" +end + +function ldoc.sidebar_item(item, module) + local text = "" + + local deprecated = item.tags.deprecated + if deprecated then + text = text .. "" + else + text = text .. "" + end + + text = text .. ldoc.realm_icon(item.tags.realm[1]) + text = text .. "
" + + if ldoc.is_kind_classmethod(module.kind) then + text = text .. item.name:gsub(".+:", "") + else + text = text .. item.name:gsub(module.name .. ".", "") + end + + text = text + .. "" + .. "" + return text +end + +function ldoc.item_header(item) + local text = "" + + text = text .. "" + + local deprecated = item.tags.deprecated + if deprecated then + text = text .. "

" + else + text = text .. "

" + end + + text = text + .. ldoc.realm_icon(item.tags.realm[1]) + .. ldoc.display_name(item) + .. "

" + .. "
" + + return text end function ldoc.is_kind_classmethod(kind) diff --git a/docs/templates/module.ltp b/docs/templates/module.ltp index 6e9e76931..b3903674c 100644 --- a/docs/templates/module.ltp +++ b/docs/templates/module.ltp @@ -40,9 +40,14 @@ {% for item, _ in pairs(items) do %}
- -

{* ldoc.realm_icon(item.tags.realm[1]) *}{{ldoc.display_name(item)}}

-
+ {* ldoc.item_header(item) *} + + {% if item.tags.deprecated then %} +
+
Deprecated
+

This API is deprecated and shouldn't be used anymore.

+
+ {% end %} {% if (item.tags.internal) then %}
diff --git a/docs/templates/sidebar.ltp b/docs/templates/sidebar.ltp index c3f6dc3dc..322b3fd1f 100644 --- a/docs/templates/sidebar.ltp +++ b/docs/templates/sidebar.ltp @@ -59,14 +59,7 @@ end {% for k, v in pairs(currentMod.items) do %} {% if (v.kind == "functions") then %}
  • - {* ldoc.realm_icon(v.tags.realm[1]) *} - - {% if (ldoc.is_kind_classmethod(currentMod.kind)) then - echo((v.name:gsub(".+:", ""))) - else - echo((v.name:gsub(currentMod.name .. ".", ""))) - end %} - + {* ldoc.sidebar_item(v, currentMod) *}
  • {% end %} {% end %} @@ -90,14 +83,7 @@ end {% for k, v in pairs(currentMod.items) do %} {% if (v.kind == "fields" or v.kind == "tables") then %}
  • - {* ldoc.realm_icon(v.tags.realm[1]) *} - - {% if (ldoc.is_kind_classmethod(currentMod.kind)) then - echo((v.name:gsub(".+:", ""))) - else - echo((v.name:gsub(currentMod.name .. ".", ""))) - end %} - + {* ldoc.sidebar_item(v, currentMod) *}
  • {% end %} {% end %} From 69c627b65109d3fed6e45cb0790a9bc0ba89f259 Mon Sep 17 00:00:00 2001 From: peelz Date: Wed, 3 Aug 2022 21:34:41 -0400 Subject: [PATCH 11/35] Deprecate Hook.HookMethod --- .../BarotraumaShared/SharedSource/LuaCs/LuaCsHookCompat.cs | 1 + docs/lua/Hooks.lua | 1 + 2 files changed, 2 insertions(+) diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsHookCompat.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsHookCompat.cs index 0967b6165..4cba3a26b 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsHookCompat.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/LuaCsHookCompat.cs @@ -120,6 +120,7 @@ namespace Barotrauma private static MethodInfo _miHookLuaCsPatchRetPrefix = typeof(LuaCsHook).GetMethod("HookLuaCsPatchRetPrefix", BindingFlags.NonPublic | BindingFlags.Static); private static MethodInfo _miHookLuaCsPatchRetPostfix = typeof(LuaCsHook).GetMethod("HookLuaCsPatchRetPostfix", BindingFlags.NonPublic | BindingFlags.Static); + // TODO: deprecate this public void HookMethod(string identifier, MethodInfo method, LuaCsCompatPatchFunc patch, HookMethodType hookType = HookMethodType.Before, ACsMod owner = null) { if (identifier == null || method == null || patch == null) diff --git a/docs/lua/Hooks.lua b/docs/lua/Hooks.lua index 27d369d8c..7b916d761 100644 --- a/docs/lua/Hooks.lua +++ b/docs/lua/Hooks.lua @@ -43,6 +43,7 @@ function Hook.Call(eventName, parameters) end -- @tparam string methodName -- @tparam function callback -- @realm shared +-- @deprecated -- @usage -- Hook.HookMethod("Barotrauma.CharacterInfo", "IncreaseSkillLevel", function (instance, ptable) -- print(string.format("%s gained % xp", instance.Character.Name, ptable.increase)) From 53ea2b897342e537f188ddab18ddc4113e1270a4 Mon Sep 17 00:00:00 2001 From: peelz Date: Wed, 3 Aug 2022 21:34:41 -0400 Subject: [PATCH 12/35] Simplify github workflow for docs generation This also fixes not being able to generate docs from other branches. --- .github/workflows/clean-docs.yml | 17 ---- .github/workflows/generate-cs-docs.yml | 43 --------- .github/workflows/generate-lua-docs.yml | 39 -------- .github/workflows/update-docs.yml | 114 ++++++++++++++++++++++++ 4 files changed, 114 insertions(+), 99 deletions(-) delete mode 100644 .github/workflows/clean-docs.yml delete mode 100644 .github/workflows/generate-cs-docs.yml delete mode 100644 .github/workflows/generate-lua-docs.yml create mode 100644 .github/workflows/update-docs.yml diff --git a/.github/workflows/clean-docs.yml b/.github/workflows/clean-docs.yml deleted file mode 100644 index cc9e934f4..000000000 --- a/.github/workflows/clean-docs.yml +++ /dev/null @@ -1,17 +0,0 @@ -name: Clean Docs - -on: - workflow_dispatch: - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - name: Checkout branch - uses: actions/checkout@v2 - - - name: Deploy - uses: peaceiris/actions-gh-pages@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: docs-landing-page diff --git a/.github/workflows/generate-cs-docs.yml b/.github/workflows/generate-cs-docs.yml deleted file mode 100644 index 31fe41870..000000000 --- a/.github/workflows/generate-cs-docs.yml +++ /dev/null @@ -1,43 +0,0 @@ -name: Generate Docs - Cs - -on: - workflow_run: - workflows: ["Clean Docs"] - types: - - completed - -jobs: - deploy: - runs-on: ubuntu-latest - steps: - - name: Checkout branch - uses: actions/checkout@v2 - - - name: Create directories - run: mkdir -p doxygen/build ; mkdir -p doxygen/build/baro-server ; mkdir -p doxygen/build/baro-client - - - name: Build server documentation - uses: mattnotmitt/doxygen-action@v1.9.1 - with: - working-directory: 'doxygen/baro-server' - doxyfile-path: './Doxyfile' - - - name: Build client documentation - uses: mattnotmitt/doxygen-action@v1.9.1 - with: - working-directory: 'doxygen/baro-client' - doxyfile-path: './Doxyfile' - - - name: Build containing documentation - uses: mattnotmitt/doxygen-action@v1.9.1 - with: - working-directory: 'doxygen/' - doxyfile-path: './Doxyfile' - - - name: Deploy - uses: peaceiris/actions-gh-pages@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: doxygen/build - destination_dir: cs-docs - keep_files: true diff --git a/.github/workflows/generate-lua-docs.yml b/.github/workflows/generate-lua-docs.yml deleted file mode 100644 index 365c823f7..000000000 --- a/.github/workflows/generate-lua-docs.yml +++ /dev/null @@ -1,39 +0,0 @@ -name: Generate Docs - Lua - -on: - workflow_run: - workflows: ["Clean Docs"] - types: - - completed - -jobs: - docs: - runs-on: ubuntu-latest - - steps: - - name: Checkout branch - uses: actions/checkout@v2 - with: - submodules: recursive - - - uses: leafo/gh-actions-lua@v8.0.0 - with: - luaVersion: "5.2" - - - uses: leafo/gh-actions-luarocks@v4.0.0 - - - name: Run install script - working-directory: docs - run: ./scripts/install.sh - - - name: Run build script - working-directory: docs - run: ./scripts/build.sh - - - name: Deploy - uses: peaceiris/actions-gh-pages@v3 - with: - github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: docs/html - destination_dir: lua-docs - keep_files: true diff --git a/.github/workflows/update-docs.yml b/.github/workflows/update-docs.yml new file mode 100644 index 000000000..dddc9d133 --- /dev/null +++ b/.github/workflows/update-docs.yml @@ -0,0 +1,114 @@ +name: Update documentation + +on: + workflow_dispatch: + +jobs: + update-docs-lua: + runs-on: ubuntu-latest + steps: + - name: Checkout branch + uses: actions/checkout@v3 + with: + submodules: recursive + + - uses: leafo/gh-actions-lua@v8 + with: + luaVersion: "5.2" + + - uses: leafo/gh-actions-luarocks@v4 + + - name: Run install script + working-directory: docs + run: ./scripts/install.sh + + - name: Run build script + working-directory: docs + run: ./scripts/build.sh + + - name: Create tarball + working-directory: docs/html + run: tar -czf ../../docs-lua.tar.gz . + + - name: Upload tarball + uses: actions/upload-artifact@v3 + with: + name: docs-lua + path: docs-lua.tar.gz + + update-docs-cs: + runs-on: ubuntu-latest + steps: + - name: Checkout branch + uses: actions/checkout@v3 + + - name: Create directories + run: | + mkdir -p doxygen/build/baro-server + mkdir -p doxygen/build/baro-client + + - name: Build server documentation + uses: mattnotmitt/doxygen-action@v1 + with: + working-directory: 'doxygen/baro-server' + doxyfile-path: './Doxyfile' + + - name: Build client documentation + uses: mattnotmitt/doxygen-action@v1 + with: + working-directory: 'doxygen/baro-client' + doxyfile-path: './Doxyfile' + + - name: Build containing documentation + uses: mattnotmitt/doxygen-action@v1 + with: + working-directory: 'doxygen/' + doxyfile-path: './Doxyfile' + + - name: Create tarball + working-directory: doxygen/build + run: tar -czf ../../docs-cs.tar.gz . + + - name: Upload tarball + uses: actions/upload-artifact@v3 + with: + name: docs-cs + path: docs-cs.tar.gz + + deploy-docs: + runs-on: ubuntu-latest + needs: [update-docs-lua, update-docs-cs] + steps: + - name: Checkout branch + uses: actions/checkout@v3 + + - run: mkdir -p docs_deploy + + - name: "Download build artifacts: lua docs" + uses: actions/download-artifact@v3 + with: + name: docs-lua + path: docs_deploy + + - name: "Download build artifacts: cs docs" + uses: actions/download-artifact@v3 + with: + name: docs-cs + path: docs_deploy + + - name: Copy landing page files + run: cp -r docs-landing-page/. docs_deploy/public + + - name: Extract lua and cs tarballs + working-directory: docs_deploy + run: | + mkdir -p public/lua-docs public/cs-docs + tar -xzf docs-lua.tar.gz -C public/lua-docs + tar -xzf docs-cs.tar.gz -C public/cs-docs + + - name: Deploy + uses: peaceiris/actions-gh-pages@v3 + with: + github_token: ${{ secrets.GITHUB_TOKEN }} + publish_dir: docs_deploy/public + keep_files: true From 1bb784381190aab94759c2feba61f7393eb26931 Mon Sep 17 00:00:00 2001 From: peelz Date: Wed, 3 Aug 2022 21:34:42 -0400 Subject: [PATCH 13/35] Move docs to luacs-docs/{lua,cs,landing-page} luacs-docs/cs also has a proper http server for testing locally --- .github/workflows/dotnet-release.yml | 39 +- .github/workflows/update-docs.yml | 74 ++- .gitignore | 6 +- .gitmodules | 4 +- .../SharedSource/LuaCs/Lua/LuaDocs.cs | 75 ++- docs-landing-page/index.html | 25 - docs-landing-page/landing.css | 132 ----- docs/css/ldoc.css | 543 ------------------ docs/js/app.js | 168 ------ docs/scripts/build.ps1 | 17 - docs/scripts/serve.ps1 | 12 - docs/templates/landing.ltp | 24 - docs/templates/ldoc.ltp | 138 ----- docs/templates/module.ltp | 152 ----- docs/templates/sidebar.ltp | 102 ---- doxygen/DoxygenLayout.xml | 8 - doxygen/baro-client/DoxygenLayout.xml | 244 -------- doxygen/baro-server/DoxygenLayout.xml | 244 -------- doxygen/build-docs.bat | 16 - doxygen/build-shared.bat | 5 - luacs-docs/cs/.gitignore | 4 + {doxygen => luacs-docs/cs}/Doxyfile | 0 luacs-docs/cs/DoxygenLayout.xml | 8 + .../cs}/baro-client/Doxyfile | 4 +- luacs-docs/cs/baro-client/DoxygenLayout.xml | 243 ++++++++ .../cs}/baro-server/Doxyfile | 4 +- luacs-docs/cs/baro-server/DoxygenLayout.xml | 243 ++++++++ {doxygen => luacs-docs/cs}/custom.css | 300 ++++------ {doxygen => luacs-docs/cs}/intro.md | 0 luacs-docs/cs/scripts/build.ps1 | 36 ++ luacs-docs/cs/scripts/build.sh | 29 + luacs-docs/cs/scripts/serve.ps1 | 18 + luacs-docs/cs/scripts/serve.sh | 15 + {doxygen => luacs-docs/cs}/searchdata.xml | 0 .../landing-page}/bg.jpg | Bin .../landing-page}/cs_logo.png | Bin luacs-docs/landing-page/index.html | 25 + luacs-docs/landing-page/landing.css | 131 +++++ .../landing-page}/lua_logo.png | Bin {docs => luacs-docs/lua}/.gitignore | 2 +- .../lua}/baseluadocs/Affliction.lua | 0 .../lua}/baseluadocs/AfflictionPrefab.lua | 0 .../lua}/baseluadocs/AnimController.lua | 0 .../lua}/baseluadocs/Character.lua | 0 .../lua}/baseluadocs/CharacterHealth.lua | 0 .../lua}/baseluadocs/CharacterInfo.lua | 0 .../lua}/baseluadocs/CharacterInventory.lua | 0 .../lua}/baseluadocs/Client.lua | 0 .../lua}/baseluadocs/Entity.Spawner.lua | 0 .../lua}/baseluadocs/Entity.lua | 0 .../lua}/baseluadocs/GameScreen.lua | 0 .../lua}/baseluadocs/GameSession.lua | 0 .../lua}/baseluadocs/GameSettings.lua | 0 {docs => luacs-docs/lua}/baseluadocs/Hull.lua | 0 .../lua}/baseluadocs/Inventory.lua | 0 {docs => luacs-docs/lua}/baseluadocs/Item.lua | 0 .../lua}/baseluadocs/ItemInventory.lua | 0 .../lua}/baseluadocs/ItemPrefab.lua | 0 {docs => luacs-docs/lua}/baseluadocs/Job.lua | 0 .../lua}/baseluadocs/JobPrefab.lua | 0 .../lua}/baseluadocs/Level.lua | 0 .../lua}/baseluadocs/NetLobbyScreen.lua | 0 .../lua}/baseluadocs/ServerSettings.lua | 0 .../lua}/baseluadocs/Submarine.lua | 0 .../lua}/baseluadocs/SubmarineInfo.lua | 0 .../lua}/baseluadocs/WayPoint.lua | 0 .../lua}/baseluadocs/World.lua | 0 {docs => luacs-docs/lua}/config.ld | 2 +- {docs => luacs-docs/lua}/css/highlight.css | 2 +- luacs-docs/lua/css/ldoc.css | 543 ++++++++++++++++++ luacs-docs/lua/js/app.js | 138 +++++ {docs => luacs-docs/lua}/js/highlight.min.js | 0 {docs => luacs-docs/lua}/libs/ldoc | 0 {docs => luacs-docs/lua}/lua/File.lua | 0 {docs => luacs-docs/lua}/lua/Game.lua | 0 {docs => luacs-docs/lua}/lua/Hooks.lua | 0 {docs => luacs-docs/lua}/lua/Networking.lua | 0 {docs => luacs-docs/lua}/lua/Signal.lua | 0 {docs => luacs-docs/lua}/lua/Steam.lua | 0 {docs => luacs-docs/lua}/lua/Timer.lua | 0 {docs => luacs-docs/lua}/lua/Vectors.lua | 0 .../lua}/lua/enums/CauseOfDeathType.lua | 0 .../lua}/lua/enums/ChatMessageType.lua | 0 .../lua}/lua/enums/ClientPermissions.lua | 0 .../lua}/lua/enums/DisconnectReason.lua | 0 .../lua}/lua/enums/Hook.HookMethodType.lua | 0 .../lua}/lua/enums/InvSlotType.lua | 0 .../lua}/lua/enums/LimbType.lua | 0 .../lua}/lua/enums/NetEntityEvent.Type.lua | 0 .../lua}/lua/enums/RandSync.lua | 0 .../lua}/lua/enums/ServerLogMessageType.lua | 0 .../lua}/lua/enums/SpawnType.lua | 0 .../lua}/lua/generated/Affliction.lua | 0 .../lua}/lua/generated/AfflictionPrefab.lua | 0 .../lua}/lua/generated/AnimController.lua | 0 .../lua}/lua/generated/Character.lua | 0 .../lua}/lua/generated/CharacterHealth.lua | 0 .../lua}/lua/generated/CharacterInfo.lua | 0 .../lua}/lua/generated/CharacterInventory.lua | 0 .../lua}/lua/generated/Client.lua | 0 .../lua}/lua/generated/Entity.Spawner.lua | 0 .../lua}/lua/generated/Entity.lua | 0 .../lua}/lua/generated/GameScreen.lua | 0 .../lua}/lua/generated/GameSession.lua | 0 .../lua}/lua/generated/GameSettings.lua | 0 .../lua}/lua/generated/Hull.lua | 0 .../lua}/lua/generated/Inventory.lua | 0 .../lua}/lua/generated/Item.lua | 0 .../lua}/lua/generated/ItemInventory.lua | 0 .../lua}/lua/generated/ItemPrefab.lua | 0 .../lua}/lua/generated/Job.lua | 0 .../lua}/lua/generated/JobPrefab.lua | 0 .../lua}/lua/generated/Level.lua | 0 .../lua}/lua/generated/NetLobbyScreen.lua | 0 .../lua}/lua/generated/ServerSettings.lua | 0 .../lua}/lua/generated/Submarine.lua | 0 .../lua}/lua/generated/SubmarineInfo.lua | 0 .../lua}/lua/generated/WayPoint.lua | 0 .../lua}/lua/generated/World.lua | 0 .../lua}/manual/common-questions.md | 0 .../lua}/manual/getting-started.md | 0 .../lua}/manual/how-to-use-hooks.md | 0 .../installing-lua-for-barotrauma-manually.md | 0 .../lua}/manual/lua-examples.md | 0 luacs-docs/lua/scripts/build.ps1 | 19 + {docs => luacs-docs/lua}/scripts/build.sh | 8 +- {docs => luacs-docs/lua}/scripts/install.ps1 | 10 +- {docs => luacs-docs/lua}/scripts/install.sh | 0 luacs-docs/lua/scripts/serve.ps1 | 14 + {docs => luacs-docs/lua}/scripts/serve.sh | 2 +- luacs-docs/lua/templates/landing.ltp | 24 + luacs-docs/lua/templates/ldoc.ltp | 138 +++++ luacs-docs/lua/templates/module.ltp | 152 +++++ luacs-docs/lua/templates/sidebar.ltp | 102 ++++ luacs-docs/scripts/http_server.py | 63 ++ luacs-docs/scripts/location.ps1 | 14 + 136 files changed, 2208 insertions(+), 2113 deletions(-) delete mode 100644 docs-landing-page/index.html delete mode 100644 docs-landing-page/landing.css delete mode 100644 docs/css/ldoc.css delete mode 100644 docs/js/app.js delete mode 100644 docs/scripts/build.ps1 delete mode 100644 docs/scripts/serve.ps1 delete mode 100644 docs/templates/landing.ltp delete mode 100644 docs/templates/ldoc.ltp delete mode 100644 docs/templates/module.ltp delete mode 100644 docs/templates/sidebar.ltp delete mode 100644 doxygen/DoxygenLayout.xml delete mode 100644 doxygen/baro-client/DoxygenLayout.xml delete mode 100644 doxygen/baro-server/DoxygenLayout.xml delete mode 100644 doxygen/build-docs.bat delete mode 100644 doxygen/build-shared.bat create mode 100644 luacs-docs/cs/.gitignore rename {doxygen => luacs-docs/cs}/Doxyfile (100%) create mode 100644 luacs-docs/cs/DoxygenLayout.xml rename {doxygen => luacs-docs/cs}/baro-client/Doxyfile (99%) create mode 100644 luacs-docs/cs/baro-client/DoxygenLayout.xml rename {doxygen => luacs-docs/cs}/baro-server/Doxyfile (99%) create mode 100644 luacs-docs/cs/baro-server/DoxygenLayout.xml rename {doxygen => luacs-docs/cs}/custom.css (72%) rename {doxygen => luacs-docs/cs}/intro.md (100%) create mode 100644 luacs-docs/cs/scripts/build.ps1 create mode 100755 luacs-docs/cs/scripts/build.sh create mode 100644 luacs-docs/cs/scripts/serve.ps1 create mode 100755 luacs-docs/cs/scripts/serve.sh rename {doxygen => luacs-docs/cs}/searchdata.xml (100%) rename {docs-landing-page => luacs-docs/landing-page}/bg.jpg (100%) rename {docs-landing-page => luacs-docs/landing-page}/cs_logo.png (100%) create mode 100644 luacs-docs/landing-page/index.html create mode 100644 luacs-docs/landing-page/landing.css rename {docs-landing-page => luacs-docs/landing-page}/lua_logo.png (100%) rename {docs => luacs-docs/lua}/.gitignore (66%) rename {docs => luacs-docs/lua}/baseluadocs/Affliction.lua (100%) rename {docs => luacs-docs/lua}/baseluadocs/AfflictionPrefab.lua (100%) rename {docs => luacs-docs/lua}/baseluadocs/AnimController.lua (100%) rename {docs => luacs-docs/lua}/baseluadocs/Character.lua (100%) rename {docs => luacs-docs/lua}/baseluadocs/CharacterHealth.lua (100%) rename {docs => luacs-docs/lua}/baseluadocs/CharacterInfo.lua (100%) rename {docs => luacs-docs/lua}/baseluadocs/CharacterInventory.lua (100%) rename {docs => luacs-docs/lua}/baseluadocs/Client.lua (100%) rename {docs => luacs-docs/lua}/baseluadocs/Entity.Spawner.lua (100%) rename {docs => luacs-docs/lua}/baseluadocs/Entity.lua (100%) rename {docs => luacs-docs/lua}/baseluadocs/GameScreen.lua (100%) rename {docs => luacs-docs/lua}/baseluadocs/GameSession.lua (100%) rename {docs => luacs-docs/lua}/baseluadocs/GameSettings.lua (100%) rename {docs => luacs-docs/lua}/baseluadocs/Hull.lua (100%) rename {docs => luacs-docs/lua}/baseluadocs/Inventory.lua (100%) rename {docs => luacs-docs/lua}/baseluadocs/Item.lua (100%) rename {docs => luacs-docs/lua}/baseluadocs/ItemInventory.lua (100%) rename {docs => luacs-docs/lua}/baseluadocs/ItemPrefab.lua (100%) rename {docs => luacs-docs/lua}/baseluadocs/Job.lua (100%) rename {docs => luacs-docs/lua}/baseluadocs/JobPrefab.lua (100%) rename {docs => luacs-docs/lua}/baseluadocs/Level.lua (100%) rename {docs => luacs-docs/lua}/baseluadocs/NetLobbyScreen.lua (100%) rename {docs => luacs-docs/lua}/baseluadocs/ServerSettings.lua (100%) rename {docs => luacs-docs/lua}/baseluadocs/Submarine.lua (100%) rename {docs => luacs-docs/lua}/baseluadocs/SubmarineInfo.lua (100%) rename {docs => luacs-docs/lua}/baseluadocs/WayPoint.lua (100%) rename {docs => luacs-docs/lua}/baseluadocs/World.lua (100%) rename {docs => luacs-docs/lua}/config.ld (99%) rename {docs => luacs-docs/lua}/css/highlight.css (99%) create mode 100644 luacs-docs/lua/css/ldoc.css create mode 100644 luacs-docs/lua/js/app.js rename {docs => luacs-docs/lua}/js/highlight.min.js (100%) rename {docs => luacs-docs/lua}/libs/ldoc (100%) rename {docs => luacs-docs/lua}/lua/File.lua (100%) rename {docs => luacs-docs/lua}/lua/Game.lua (100%) rename {docs => luacs-docs/lua}/lua/Hooks.lua (100%) rename {docs => luacs-docs/lua}/lua/Networking.lua (100%) rename {docs => luacs-docs/lua}/lua/Signal.lua (100%) rename {docs => luacs-docs/lua}/lua/Steam.lua (100%) rename {docs => luacs-docs/lua}/lua/Timer.lua (100%) rename {docs => luacs-docs/lua}/lua/Vectors.lua (100%) rename {docs => luacs-docs/lua}/lua/enums/CauseOfDeathType.lua (100%) rename {docs => luacs-docs/lua}/lua/enums/ChatMessageType.lua (100%) rename {docs => luacs-docs/lua}/lua/enums/ClientPermissions.lua (100%) rename {docs => luacs-docs/lua}/lua/enums/DisconnectReason.lua (100%) rename {docs => luacs-docs/lua}/lua/enums/Hook.HookMethodType.lua (100%) rename {docs => luacs-docs/lua}/lua/enums/InvSlotType.lua (100%) rename {docs => luacs-docs/lua}/lua/enums/LimbType.lua (100%) rename {docs => luacs-docs/lua}/lua/enums/NetEntityEvent.Type.lua (100%) rename {docs => luacs-docs/lua}/lua/enums/RandSync.lua (100%) rename {docs => luacs-docs/lua}/lua/enums/ServerLogMessageType.lua (100%) rename {docs => luacs-docs/lua}/lua/enums/SpawnType.lua (100%) rename {docs => luacs-docs/lua}/lua/generated/Affliction.lua (100%) rename {docs => luacs-docs/lua}/lua/generated/AfflictionPrefab.lua (100%) rename {docs => luacs-docs/lua}/lua/generated/AnimController.lua (100%) rename {docs => luacs-docs/lua}/lua/generated/Character.lua (100%) rename {docs => luacs-docs/lua}/lua/generated/CharacterHealth.lua (100%) rename {docs => luacs-docs/lua}/lua/generated/CharacterInfo.lua (100%) rename {docs => luacs-docs/lua}/lua/generated/CharacterInventory.lua (100%) rename {docs => luacs-docs/lua}/lua/generated/Client.lua (100%) rename {docs => luacs-docs/lua}/lua/generated/Entity.Spawner.lua (100%) rename {docs => luacs-docs/lua}/lua/generated/Entity.lua (100%) rename {docs => luacs-docs/lua}/lua/generated/GameScreen.lua (100%) rename {docs => luacs-docs/lua}/lua/generated/GameSession.lua (100%) rename {docs => luacs-docs/lua}/lua/generated/GameSettings.lua (100%) rename {docs => luacs-docs/lua}/lua/generated/Hull.lua (100%) rename {docs => luacs-docs/lua}/lua/generated/Inventory.lua (100%) rename {docs => luacs-docs/lua}/lua/generated/Item.lua (100%) rename {docs => luacs-docs/lua}/lua/generated/ItemInventory.lua (100%) rename {docs => luacs-docs/lua}/lua/generated/ItemPrefab.lua (100%) rename {docs => luacs-docs/lua}/lua/generated/Job.lua (100%) rename {docs => luacs-docs/lua}/lua/generated/JobPrefab.lua (100%) rename {docs => luacs-docs/lua}/lua/generated/Level.lua (100%) rename {docs => luacs-docs/lua}/lua/generated/NetLobbyScreen.lua (100%) rename {docs => luacs-docs/lua}/lua/generated/ServerSettings.lua (100%) rename {docs => luacs-docs/lua}/lua/generated/Submarine.lua (100%) rename {docs => luacs-docs/lua}/lua/generated/SubmarineInfo.lua (100%) rename {docs => luacs-docs/lua}/lua/generated/WayPoint.lua (100%) rename {docs => luacs-docs/lua}/lua/generated/World.lua (100%) rename {docs => luacs-docs/lua}/manual/common-questions.md (100%) rename {docs => luacs-docs/lua}/manual/getting-started.md (100%) rename {docs => luacs-docs/lua}/manual/how-to-use-hooks.md (100%) rename {docs => luacs-docs/lua}/manual/installing-lua-for-barotrauma-manually.md (100%) rename {docs => luacs-docs/lua}/manual/lua-examples.md (100%) create mode 100644 luacs-docs/lua/scripts/build.ps1 rename {docs => luacs-docs/lua}/scripts/build.sh (78%) rename {docs => luacs-docs/lua}/scripts/install.ps1 (83%) rename {docs => luacs-docs/lua}/scripts/install.sh (100%) create mode 100644 luacs-docs/lua/scripts/serve.ps1 rename {docs => luacs-docs/lua}/scripts/serve.sh (77%) create mode 100644 luacs-docs/lua/templates/landing.ltp create mode 100644 luacs-docs/lua/templates/ldoc.ltp create mode 100644 luacs-docs/lua/templates/module.ltp create mode 100644 luacs-docs/lua/templates/sidebar.ltp create mode 100644 luacs-docs/scripts/http_server.py create mode 100644 luacs-docs/scripts/location.ps1 diff --git a/.github/workflows/dotnet-release.yml b/.github/workflows/dotnet-release.yml index a27daa0be..0e68d8e48 100644 --- a/.github/workflows/dotnet-release.yml +++ b/.github/workflows/dotnet-release.yml @@ -1,9 +1,10 @@ +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json + name: .NET Pre-Release on: push: branches: [ master ] - workflow_dispatch: jobs: @@ -26,7 +27,7 @@ jobs: run: dotnet build LinuxSolution.sln --no-restore - name: Test run: dotnet test LinuxSolution.sln --no-build --verbosity normal - + - name: Publish WindowsServer run: dotnet publish Barotrauma/BarotraumaServer/WindowsServer.csproj -c Release -clp:"ErrorsOnly;Summary" --self-contained -r win-x64 \/p:Platform="x64" - name: Publish WindowsClient @@ -42,38 +43,34 @@ jobs: - name: Publish MacClient run: dotnet publish Barotrauma/BarotraumaClient/MacClient.csproj -c Release -clp:"ErrorsOnly;Summary" --self-contained -r osx-x64 \/p:Platform="x64" - - name: Archive Windows Release uses: thedoctor0/zip-release@main with: - type: 'zip' - filename: 'barotrauma_lua_windows.zip' - #exclusions: '*.git* /*node_modules/* .editorconfig' - directory: 'Barotrauma/bin/ReleaseWindows/netcoreapp3.1/win-x64/publish' - + type: zip + filename: barotrauma_lua_windows.zip + directory: Barotrauma/bin/ReleaseWindows/netcoreapp3.1/win-x64/publish + - name: Archive Linux Release uses: thedoctor0/zip-release@main with: - type: 'zip' - filename: 'barotrauma_lua_linux.zip' - #exclusions: '*.git* /*node_modules/* .editorconfig' - directory: 'Barotrauma/bin/ReleaseLinux/netcoreapp3.1/linux-x64/publish' - + type: zip + filename: barotrauma_lua_linux.zip + directory: Barotrauma/bin/ReleaseLinux/netcoreapp3.1/linux-x64/publish + - name: Archive Mac Release uses: thedoctor0/zip-release@main with: - type: 'zip' - filename: 'barotrauma_lua_mac.zip' - #exclusions: '*.git* /*node_modules/* .editorconfig' - directory: 'Barotrauma/bin/ReleaseMac/netcoreapp3.1/osx-x64/publish' - + type: zip + filename: barotrauma_lua_mac.zip + directory: Barotrauma/bin/ReleaseMac/netcoreapp3.1/osx-x64/publish + - name: Automatic Release uses: marvinpinto/action-automatic-releases@v1.2.1 with: - repo_token: "${{ secrets.GITHUB_TOKEN }}" - automatic_release_tag: "latest" + repo_token: ${{ secrets.GITHUB_TOKEN }} + automatic_release_tag: latest prerelease: false - title: "Automatic Build" + title: Automatic Build files: | Barotrauma/bin/ReleaseWindows/netcoreapp3.1/win-x64/publish/barotrauma_lua_windows.zip Barotrauma/bin/ReleaseLinux/netcoreapp3.1/linux-x64/publish/barotrauma_lua_linux.zip diff --git a/.github/workflows/update-docs.yml b/.github/workflows/update-docs.yml index dddc9d133..f7c994d9d 100644 --- a/.github/workflows/update-docs.yml +++ b/.github/workflows/update-docs.yml @@ -1,8 +1,17 @@ +# yaml-language-server: $schema=https://json.schemastore.org/github-workflow.json + name: Update documentation on: workflow_dispatch: +env: + CI_DEPLOY_DIR: luacs-docs/ci-deploy + CI_ARTIFACTS_DIR: luacs-docs/ci-artifacts + DOCS_LUA_ROOT: luacs-docs/lua + DOCS_CS_ROOT: luacs-docs/cs + DOCS_LANDINGPAGE_ROOT: luacs-docs/landing-page + jobs: update-docs-lua: runs-on: ubuntu-latest @@ -19,22 +28,23 @@ jobs: - uses: leafo/gh-actions-luarocks@v4 - name: Run install script - working-directory: docs + working-directory: ${{ env.DOCS_LUA_ROOT }} run: ./scripts/install.sh - name: Run build script - working-directory: docs + working-directory: ${{ env.DOCS_LUA_ROOT }} run: ./scripts/build.sh - name: Create tarball - working-directory: docs/html - run: tar -czf ../../docs-lua.tar.gz . + run: | + mkdir -p "$CI_ARTIFACTS_DIR" + tar -czf "$CI_ARTIFACTS_DIR"/lua.tar.gz -C "$DOCS_LUA_ROOT"/build . - name: Upload tarball uses: actions/upload-artifact@v3 with: name: docs-lua - path: docs-lua.tar.gz + path: ${{ env.CI_ARTIFACTS_DIR }}/lua.tar.gz update-docs-cs: runs-on: ubuntu-latest @@ -42,38 +52,23 @@ jobs: - name: Checkout branch uses: actions/checkout@v3 - - name: Create directories - run: | - mkdir -p doxygen/build/baro-server - mkdir -p doxygen/build/baro-client + - name: Install doxygen + run: sudo apt-get update && sudo apt-get install -y doxygen - - name: Build server documentation - uses: mattnotmitt/doxygen-action@v1 - with: - working-directory: 'doxygen/baro-server' - doxyfile-path: './Doxyfile' - - - name: Build client documentation - uses: mattnotmitt/doxygen-action@v1 - with: - working-directory: 'doxygen/baro-client' - doxyfile-path: './Doxyfile' - - - name: Build containing documentation - uses: mattnotmitt/doxygen-action@v1 - with: - working-directory: 'doxygen/' - doxyfile-path: './Doxyfile' + - name: Run build script + working-directory: ${{ env.DOCS_CS_ROOT }} + run: ./scripts/build.sh - name: Create tarball - working-directory: doxygen/build - run: tar -czf ../../docs-cs.tar.gz . + run: | + mkdir -p "$CI_ARTIFACTS_DIR" + tar -czf "$CI_ARTIFACTS_DIR"/cs.tar.gz -C "$DOCS_CS_ROOT"/build . - name: Upload tarball uses: actions/upload-artifact@v3 with: name: docs-cs - path: docs-cs.tar.gz + path: ${{ env.CI_ARTIFACTS_DIR }}/cs.tar.gz deploy-docs: runs-on: ubuntu-latest @@ -82,33 +77,32 @@ jobs: - name: Checkout branch uses: actions/checkout@v3 - - run: mkdir -p docs_deploy + - run: mkdir -p "$CI_ARTIFACTS_DIR" "$CI_DEPLOY_DIR" - name: "Download build artifacts: lua docs" uses: actions/download-artifact@v3 with: name: docs-lua - path: docs_deploy + path: ${{ env.CI_ARTIFACTS_DIR }} - name: "Download build artifacts: cs docs" uses: actions/download-artifact@v3 with: name: docs-cs - path: docs_deploy - - - name: Copy landing page files - run: cp -r docs-landing-page/. docs_deploy/public + path: ${{ env.CI_ARTIFACTS_DIR }} - name: Extract lua and cs tarballs - working-directory: docs_deploy run: | - mkdir -p public/lua-docs public/cs-docs - tar -xzf docs-lua.tar.gz -C public/lua-docs - tar -xzf docs-cs.tar.gz -C public/cs-docs + mkdir -p "$CI_DEPLOY_DIR"/{lua,cs}-docs + tar -xzf "$CI_ARTIFACTS_DIR"/lua.tar.gz -C "$CI_DEPLOY_DIR"/lua-docs + tar -xzf "$CI_ARTIFACTS_DIR"/cs.tar.gz -C "$CI_DEPLOY_DIR"/cs-docs + + - name: Copy landing page files + run: cp -r "$DOCS_LANDINGPAGE_ROOT"/. "$CI_DEPLOY_DIR" - name: Deploy uses: peaceiris/actions-gh-pages@v3 with: github_token: ${{ secrets.GITHUB_TOKEN }} - publish_dir: docs_deploy/public + publish_dir: ${{ env.CI_DEPLOY_DIR }} keep_files: true diff --git a/.gitignore b/.gitignore index 7d4b67772..3009d17ba 100644 --- a/.gitignore +++ b/.gitignore @@ -13,12 +13,8 @@ bld/ [Rr]elease*/ *.o */Barotrauma*/doc/ -doxygen/html/ -doxygen/*/html/ -doxygen/*/*.tag -doxygen/build -# Misc vs crap +# Misc vs crap *.v12.suo *.suo *.csproj.user diff --git a/.gitmodules b/.gitmodules index 399c0ff11..568b2ac40 100644 --- a/.gitmodules +++ b/.gitmodules @@ -1,6 +1,6 @@ [submodule "Libraries/moonsharp"] path = Libraries/moonsharp url = https://github.com/evilfactory/moonsharp.git -[submodule "docs/libs/ldoc"] - path = docs/libs/ldoc +[submodule "luacs-docs/lua/libs/ldoc"] + path = luacs-docs/lua/libs/ldoc url = https://github.com/impulsh/LDoc.git diff --git a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaDocs.cs b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaDocs.cs index 8079c42cc..a889f5a65 100644 --- a/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaDocs.cs +++ b/Barotrauma/BarotraumaShared/SharedSource/LuaCs/Lua/LuaDocs.cs @@ -11,13 +11,14 @@ using System.Net; using System.Linq; using System.Xml.Linq; using System.Reflection; +using System.Diagnostics; +using System.Runtime.InteropServices; namespace Barotrauma { public static class LuaDocs { - public static string ConvertTypeName(string type) { switch (type) @@ -68,8 +69,68 @@ namespace Barotrauma return n; } - public static void GenerateDocsAll() + private static (bool Success, string Output, string Error) TryRunGitCommand(string args) + { + static string GetGitBinary() + { + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + return Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.Process) + ?.Split(';') + .Select(x => Path.Join(x, "git.exe")) + .FirstOrDefault(File.Exists); + } + else + { + return Environment.GetEnvironmentVariable("PATH", EnvironmentVariableTarget.Process) + ?.Split(':') + .Select(x => Path.Join(x, "git")) + .FirstOrDefault(File.Exists); + } + } + + var gitBinary = GetGitBinary(); + if (gitBinary == null) + { + throw new InvalidOperationException("Failed to find git binary in PATH"); + } + + using var process = Process.Start(new ProcessStartInfo(gitBinary, args) + { + WindowStyle = ProcessWindowStyle.Hidden, + CreateNoWindow = true, + UseShellExecute = false, + RedirectStandardInput = true, + RedirectStandardError = true, + RedirectStandardOutput = true, + }); + + process.Start(); + + var stdOut = process.StandardOutput.ReadToEndAsync(); + var stdErr = process.StandardError.ReadToEndAsync(); + Task.WhenAll(stdOut, stdErr).GetAwaiter().GetResult(); + process.WaitForExit(); + + return (process.ExitCode == 0, stdOut.Result.TrimEnd('\r', '\n'), stdErr.Result); + } + + + private static readonly Lazy GitDir = new Lazy(() => + { + var (success, gitDir, error) = TryRunGitCommand("rev-parse --show-toplevel"); + if (!success) + { + throw new InvalidDataException($"Failed to determine the root of the git repo: {error}"); + } + + return gitDir; + }); + + public static void GenerateDocs() { + var basePath = $"{GitDir.Value}/luacs-docs/lua"; + Directory.Delete($"{basePath}/lua/generated", true); GenerateDocs(typeof(Character), "Character.lua"); GenerateDocs(typeof(CharacterInfo), "CharacterInfo.lua"); GenerateDocs(typeof(CharacterHealth), "CharacterHealth.lua"); @@ -99,12 +160,13 @@ namespace Barotrauma GenerateDocs(typeof(GameSettings), "GameSettings.lua", "Game.Settings"); } - public static void GenerateDocs(Type type, string name, string categoryName = null) + private static void GenerateDocs(Type type, string file, string categoryName = null) { - GenerateDocs(type, "../../../../docs/baseluadocs/" + name, "../../../../docs/lua/generated/" + name, categoryName); + var basePath = $"{GitDir.Value}/luacs-docs/lua"; + GenerateDocs(type, $"{basePath}/baseluadocs/{file}", $"{basePath}/lua/generated/{file}", categoryName); } - public static void GenerateDocs(Type type, string baselua, string fileresult, string categoryName = null) + private static void GenerateDocs(Type type, string baselua, string fileresult, string categoryName = null) { var sb = new StringBuilder(); @@ -241,7 +303,7 @@ local {type.Name} = {EMPTY_TABLE}"; var returnName = ConvertTypeName(property.PropertyType.Name); - if (property.GetGetMethod().IsStatic) + if (property.GetGetMethod()?.IsStatic == true || property.GetSetMethod()?.IsStatic == true) name = type.Name + "." + property.Name; if (removed.Contains(name)) @@ -257,6 +319,7 @@ local {type.Name} = {EMPTY_TABLE}"; } } + new FileInfo(fileresult).Directory.Create(); File.WriteAllText(fileresult, sb.ToString()); } } diff --git a/docs-landing-page/index.html b/docs-landing-page/index.html deleted file mode 100644 index fcb6bb06b..000000000 --- a/docs-landing-page/index.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - - LuaCs For Barotrauma - - - - -
    -
    -
    - -
    -
    -
    -
    -
    -
    - -
    -
    -
    - - \ No newline at end of file diff --git a/docs-landing-page/landing.css b/docs-landing-page/landing.css deleted file mode 100644 index 7efcd8809..000000000 --- a/docs-landing-page/landing.css +++ /dev/null @@ -1,132 +0,0 @@ -body{ - overflow: hidden; - font-size: 100%; - background-color: #777; -} - -.common-docs{ - z-index: 1; - text-justify: auto; - position: fixed; - top: 0; - bottom: 0; - transition-property: transform; - transition-duration: 0.5s; - transition-timing-function: ease-in-out; - overflow: hidden; -} -#lua-docs{ - left: -10%; - right: 50%; -} -#cs-docs{ - right: -10%; - left: 50%; -} - -#cs-docs::before,#lua-docs::before{ - content: ""; - background: url(bg.jpg) repeat; - background-size: 80%; - background-blend-mode: luminosity; - filter: blur(2px); - position: absolute; - height: 200%; - width: 200%; -} -#lua-docs::before{ - background-color: rgba(49, 49, 135, 1); -} -#cs-docs::before{ - background-color: rgba(105, 44, 120, 1); -} - -.common-docs:hover{ - z-index: 2; - font-size: 300%; - transform: scale(1.05); -} - -.inner-bg{ - position: relative; - width: 100%; - height: 100%; - display: flex; - align-items: center; - justify-content: center; - transition-duration: 0.5s; - transition-timing-function: ease-in-out; -} -.inner-bg:hover{ - background-color: rgba(255, 255, 255, 0.35); -} - -.inner-docs{ - height: 100%; - width: 100%; - display: flex; - align-items: center; - justify-content: center; - position: relative; - background: radial-gradient(circle, #222, rgba(0, 0, 0, 0)); -} - - -@media (min-width: 80em) { - .inner-docs img{ - height: 30%; - width: auto; - } - .common-docs{ - transform: skewX(-11deg); - } - #cs-docs::before,#lua-docs::before{ - transform: skewX(11deg); - } - .common-docs:hover{ - transform: skewX(-11deg) scale(1.05); - } - .inner-docs{ - transform: skewX(11deg); - } - .inner-docs img{ - height: 30%; - width: auto; - } -} -@media (max-width: 80em) { - .common-docs{ - transform: skewX(-9deg); - } - #cs-docs::before,#lua-docs::before{ - transform: skewX(9deg); - } - .common-docs:hover{ - transform: skewX(-9deg) scale(1.05); - } - .inner-docs{ - transform: skewX(9deg); - } - .inner-docs img{ - height: 30%; - width: auto; - } -} -@media (max-width: 70em) { - .common-docs{ - transform: skewX(0deg); - } - #cs-docs::before,#lua-docs::before{ - transform: skewX(0deg); - } - .common-docs:hover{ - transform: skewX(0deg) scale(1.05); - } - .inner-docs{ - transform: skewX(0deg); - } - .inner-docs img{ - height: auto; - width: 50%; - } -} \ No newline at end of file diff --git a/docs/css/ldoc.css b/docs/css/ldoc.css deleted file mode 100644 index 4f590feb1..000000000 --- a/docs/css/ldoc.css +++ /dev/null @@ -1,543 +0,0 @@ - -:root { - --content-width: 100%; - --sidebar-width: 25%; - - --padding-big: 48px; - --padding-normal: 24px; - --padding-small: 16px; - --padding-tiny: 10px; - - --font-massive: 32px; - --font-huge: 24px; - --font-big: 18px; - --font-normal: 16px; - --font-tiny: 12px; - - --font-style-normal: Segoe UI, Helvetica, Arial, sans-serif; - --font-style-code: Consolas, monospace; - - --color-accent: rgb(47, 100, 74); - --color-accent-dark: rgb(33, 33, 33); - --color-white: rgb(255, 255, 255); - --color-offwhite: rgb(200, 200, 200); - --color-white-accent: rgb(203, 190, 209); - --color-black: rgb(0, 0, 0); - --color-lightgrey: rgb(160, 160, 160); - --color-background-light: rgb(245, 245, 245); - --color-background-dark: rgb(33, 33, 33); - --color-background-dark-ish: rgb(44, 44, 44); - --color-outline: rgb(149, 34, 160); - --color-good: #5190ff; -} - -* { - padding: 0; - margin: 0; - box-sizing: border-box; -} - -body { - background-color: var(--color-background-dark); - font-family: var(--font-style-normal); - - display: flex; - flex-direction: column; -} - -a { - color: inherit; - text-decoration: inherit; -} - -h1, h2, h3, h4 { - font-weight: 400; -} - -ul li { - margin-left: var(--padding-small); -} - -/* landing */ -.landing { - background-color: var(--color-accent); - color: var(--color-white); - - padding: 128px 0 128px 0; -} - -.landing h1 { - margin: 0; - padding: 0; - border: none; - - font-weight: 100; - font-size: var(--font-massive); - text-align: center; -} - -.wrapper { - padding: var(--padding-small); -} - -details { - user-select: none; -} - -details summary { - outline: none; -} - -code { - font-family: "Source Code Pro", monospace; - font-size: 85%; - white-space: pre; - tab-size: 4; - -moz-tab-size: 4; - padding: 1px 4px; - background-color: #282a36; - outline-style: solid; - outline-color: black; - outline-width: 2px; -} - -pre { - background-color: rgb(0, 0, 0, 1); - margin-top: var(--padding-small); - padding: 2px; - overflow: auto; -} - -pre code { - background-color: transparent; -} - -span.realm { - width: 14px; - height: 14px; - border-radius: 3px; - display: inline-block; - margin-right: 5px; -} - -span.realm.shared { - background: linear-gradient(45deg, #f80 0%, #f80 50%, #08f 51%, #08f 100%); -} - -span.realm.client { - background-color: #f80; -} - -span.realm.server { - background-color: #08f; -} - - -.colorful-label { - color: rgb(31, 141, 155); -} - -/* wrapper element for sidebar/content */ -main { - display: flex; - flex-direction: row; - justify-content: space-between; - align-items: flex-start; - - width: var(--content-width); - margin: auto; -} - -/* sidebar */ -nav { - color: var(--color-offwhite); - background-color: var(--color-background-dark); - - position: fixed; - display: flex; - flex-direction: column; - - width: var(--sidebar-width); - height: 100%; -} - -/* sidebar header */ -nav header { - color: var(--color-white); - background-color: var(--color-accent); - - padding: var(--padding-small); -} - -nav header h1 { - font-size: var(--font-huge); - font-weight: 100; - text-align: center; - - margin-bottom: var(--padding-small); -} - -#search { - background-color: var(--color-accent-dark); - color: var(--color-white); - - border: none; - font-size: var(--font-normal); - outline: none; - - width: 100%; - padding: 6px; -} - -#search::placeholder { - color: var(--color-white-accent); -} - -#search::-webkit-search-cancel-button { - display: none; -} - -/* sidebar contents */ -nav section { - padding: var(--padding-small); - overflow: auto; -} - -nav section ul { - list-style-type: none; -} - -nav section::-webkit-scrollbar, -pre::-webkit-scrollbar { - width: 8px; - height: 8px; -} - -nav section::-webkit-scrollbar-track, -pre::-webkit-scrollbar-track { - background: transparent; -} - -nav section::-webkit-scrollbar-thumb { - background-color: var(--color-lightgrey); -} - -pre::-webkit-scrollbar-thumb { - background-color: var(--color-lightgrey); -} - -/* sidebar contents category */ -nav section details.category { - padding-top: var(--padding-tiny); -} - -nav section details.category > ul > li { - margin: 0; - line-height: 1.5; -} - -nav section details.category > ul > li a { - display: inline-block; - width: 90%; -} - -nav section details.category:first-of-type { - padding-top: calc(var(--padding-tiny) * -1); -} - -nav section details.category summary::-webkit-details-marker { - opacity: 0.5; - cursor: pointer; -} - -nav section details.category summary h2 { - color: var(--color-accent); - - font-size: var(--font-big); - letter-spacing: 2px; - text-transform: uppercase; - cursor: pointer; - - padding-bottom: var(--padding-tiny); -} - -/* content */ -article { - background-color: var(--color-background-dark-ish); - color: white; - width: calc(100% - var(--sidebar-width)); - min-height: 100vh; - margin-left: var(--sidebar-width); -} - -article .wrapper > *:first-child { - margin-top: 0; -} - -/* header */ -article header { - color: rgb(255, 255, 255); - background-color: var(--color-accent); - padding: var(--padding-tiny); -} - -article header h1 { - border-bottom: 1px solid rgba(255, 255, 255, 0.25); - padding-bottom: 8px; - font-family: var(--font-style-code); - margin: 0; -} - -article header h2 { - padding-top: var(--padding-tiny); - margin: 0; - font-size: var(--font-normal); - font-weight: normal; -} - -article header.module a { - color: white !important; - text-decoration: underline; -} - -details.category > summary { - list-style: none; -} - -details.category > summary::-webkit-details-marker { - display: none; -} - -article h1 { - font-size: 28px; - font-weight: 600; - border-bottom: 1px solid rgba(0, 0, 0, 0.25); - margin-top: 24px; - margin-bottom: 16px; - padding-bottom: 8px; -} - -article h2 { - font-size: 20px; - font-weight: 600; - margin-top: 12px; -} - -article h3 { - color: var(--color-good); - margin-top: var(--padding-tiny); - text-transform: uppercase; -} - -article p { - margin-top: var(--padding-small); -} - -article p a, -article ul li a, -article h1 a, -article h2 a { - color: var(--color-good); - font-weight: 600; -} - -article h1.title { - color: rgb(255, 255, 255); - background-color: var(--color-accent); - margin-top: var(--padding-small); - margin-bottom: 0; - padding: var(--padding-tiny); - - font-size: var(--font-big); - font-weight: 100; - letter-spacing: 2px; - text-transform: uppercase; -} - -a.reference { - color: var(--color-good); - float: right; - margin-top: 8px; - padding-left: 8px; - font-size: 14px; - font-weight: 600; -} - -.notice { - --color-notice-background: var(--color-accent); - --color-notice-text: var(--color-notice-background); - - margin-top: var(--padding-tiny); - border: 2px solid var(--color-notice-background); -} - -.notice.error { - --color-notice-background: rgb(194, 52, 130); -} - -.notice.warning { - --color-notice-background: rgb(224, 169, 112); - --color-notice-text: rgb(167, 104, 37); -} - -.notice .title { - color: var(--color-white); - background-color: var(--color-notice-background); - - padding: var(--padding-tiny); - font-size: var(--font-normal); - - text-transform: uppercase; - letter-spacing: 2px; -} - -.notice p { - color: var(--color-notice-text); - - margin: 0 !important; - padding: var(--padding-tiny); -} - -/* function/table */ -.method { - display: flex; - flex-flow: column; - padding: var(--padding-tiny); - margin-top: var(--padding-small); -} - -.method header { - color: white; - background-color: inherit; - padding: 0; - order: -1; -} - -.method header .anchor { - color: inherit; - text-decoration: inherit; -} - -.method:target { - background-color: var(--color-background-dark); - - outline: solid; - outline-width: 1px; - outline-color: var(--color-accent); -} - -.method header:target { - background-color: var(--color-accent); -} - -.method header h1 { - font-family: "Source Code Pro", monospace; - padding-bottom: var(--padding-tiny); - border-bottom: 1px solid var(--color-accent); - font-size: 20px; -} - -.method header p:first-of-type { - margin-top: var(--padding-tiny); -} - -.method h3 { - color: var(--color-good); - font-size: var(--font-normal); - letter-spacing: 2px; - text-transform: uppercase; -} - -.method pre { - margin-top: var(--padding-tiny); -} - -@media only screen and (max-width: 1100px) { - main nav { - position: inherit; - } - - main article { - margin-left: 0; - } -} - -.method ul { - margin-top: var(--padding-tiny); - background-color: inherit; -} - -.method ul li { - list-style: none; - margin: 4px 0 0 var(--padding-normal); -} - -.method ul li:first-of-type { - margin-top: 0; -} - -.method ul li p { - margin: 4px 0 0 var(--padding-normal); -} - -.method ul li pre { - margin: 4px 0 0 var(--padding-normal); -} - -.method ul li a { - color: rgb(115, 53, 142); - font-weight: 600; -} - -/* we have to manually specify these instead of making a shared class since you cannot customize the parameter class in ldoc */ -.parameter, .type, .default { - display: inline-block; - color: rgb(255, 255, 255) !important; - - padding: 4px; - font-size: 14px; - font-family: "Source Code Pro", monospace; -} - -.parameter { - background-color: rgb(115, 53, 142); -} - -.type { - background-color: rgb(31, 141, 155); -} - -a.type { - font-weight: 300 !important; - text-decoration: underline; -} - -.default { - background-color: rgb(193, 114, 11); -} - -.type a { - padding: 0; -} - -.or { - color: rgba(115, 53, 142, 0.5); - background-color: inherit; - - width: calc(100% - 32px); - height: 8px; - margin: 0 0 8px 32px; - - text-align: center; - font-weight: 600; - border-bottom: 1px solid rgba(115, 53, 142, 0.5); -} - -.or span { - background-color: inherit; - padding: 0 8px 0 8px; -} - -.strikethrough { - text-decoration: line-through; -} diff --git a/docs/js/app.js b/docs/js/app.js deleted file mode 100644 index ce0cee1ba..000000000 --- a/docs/js/app.js +++ /dev/null @@ -1,168 +0,0 @@ - -const skippedCategories = ["manual"]; - -class Node -{ - constructor(name, element, expandable, noAutoCollapse, children = []) - { - this.name = name; - this.element = element; - this.expandable = expandable; - this.noAutoCollapse = noAutoCollapse; - this.children = children; - } - - AddChild(name, element, expandable, noAutoCollapse, children) - { - let newNode = new Node(name, element, expandable, noAutoCollapse, children); - this.children.push(newNode); - - return newNode; - } -} - -class SearchManager -{ - constructor(input, contents) - { - this.input = input; - this.input.addEventListener("input", event => - { - this.OnInputUpdated(this.input.value.toLowerCase().replace(/:/g, ".")); - }); - - // setup search tree - this.tree = new Node("", document.createElement("null"), true, true); - this.entries = {}; - - const categoryElements = contents.querySelectorAll(".category"); - - // iterate each kind (hooks/libraries/classes/etc) - for (const category of categoryElements) - { - const nameElement = category.querySelector(":scope > summary > h2"); - - if (!nameElement) - { - continue; - } - - const categoryName = nameElement.textContent.trim().toLowerCase(); - - if (skippedCategories.includes(categoryName)) - { - continue; - } - - let categoryNode = this.tree.AddChild(categoryName, category, true, true); - const sectionElements = category.querySelectorAll(":scope > ul > li"); - - for (const section of sectionElements) - { - const entryElements = section.querySelectorAll(":scope > details > ul > li > a"); - const sectionName = section.querySelector(":scope > details > summary > a") - .textContent - .trim() - .toLowerCase(); - - let sectionNode = categoryNode.AddChild(sectionName, section.querySelector(":scope > details"), true); - - for (let i = 0; i < entryElements.length; i++) - { - const entryElement = entryElements[i]; - const entryName = entryElement.textContent.trim().toLowerCase(); - - sectionNode.AddChild(sectionName + "." + entryName, entryElement.parentElement); - } - } - } - } - - ResetVisibility(current) - { - current.element.style.display = ""; - - if (current.noAutoCollapse) - { - current.element.open = true; - } - else if (current.expandable) - { - current.element.open = false; - } - - for (let node of current.children) - { - this.ResetVisibility(node); - } - } - - Search(input, current) - { - let matched = false; - - if (current.name.indexOf(input) != -1) - { - matched = true; - } - - for (let node of current.children) - { - let childMatched = this.Search(input, node); - matched = matched || childMatched; - } - - if (matched) - { - current.element.style.display = ""; - - if (current.expandable) - { - current.element.open = true; - } - } - else - { - current.element.style.display = "none"; - - if (current.expandable) - { - current.element.open = false; - } - } - - return matched; - } - - OnInputUpdated(input) - { - if (input.length <= 1) - { - this.ResetVisibility(this.tree); - return; - } - - this.Search(input, this.tree); - } -} - -window.onload = function() -{ - const openDetails = document.querySelector(".category > ul > li > details[open]"); - - if (openDetails) - { - openDetails.scrollIntoView(); - } -} - -document.addEventListener("DOMContentLoaded", function() -{ - const searchInput = document.getElementById("search"); - const contents = document.querySelector("body > main > nav > section"); - - if (searchInput && contents) - { - new SearchManager(searchInput, contents); - } -}); diff --git a/docs/scripts/build.ps1 b/docs/scripts/build.ps1 deleted file mode 100644 index 850f8f7a3..000000000 --- a/docs/scripts/build.ps1 +++ /dev/null @@ -1,17 +0,0 @@ -try { - cd $PSScriptRoot/.. - - Remove-Item -Force -Recurse ./html | Out-Null - New-Item -ItemType Directory ./html | Out-Null - Copy-Item -Path ./css/. -Destination ./html -Recurse -Force | Out-Null - Copy-Item -Path ./js/. -Destination ./html -Recurse -Force | Out-Null - - if ((Get-Command "lua_modules/bin/ldoc" -ErrorAction SilentlyContinue) -eq $null) { - echo "ldoc not found; please run docs/scripts/install.ps1" - exit 1 - } - - lua_modules/bin/ldoc . -} finally { - popd -} diff --git a/docs/scripts/serve.ps1 b/docs/scripts/serve.ps1 deleted file mode 100644 index 9a9bffed2..000000000 --- a/docs/scripts/serve.ps1 +++ /dev/null @@ -1,12 +0,0 @@ -try { - cd $PSScriptRoot/.. - - if ((Get-Command "python3" -ErrorAction SilentlyContinue) -eq $null) { - echo "python3 not found" - exit 1 - } - - python3 -m http.server -d html -} finally { - popd -} diff --git a/docs/templates/landing.ltp b/docs/templates/landing.ltp deleted file mode 100644 index 060380193..000000000 --- a/docs/templates/landing.ltp +++ /dev/null @@ -1,24 +0,0 @@ - -
    -

    Lua For Barotrauma Documentation

    -
    - -
    -

    -

    Welcome to the Lua For Barotrauma documentation!

    -

    This is a work in progress documentation, not everything is documented here, but because Barotrauma classes are exposed to Lua, you can check the Barotrauma source code for functions and fields that you can access.

    - -

    Installing Lua For Barotrauma

    -

    Downloading and using the Core Content Package from Workshop should be enough to have it installed, but if you want to install it manually, you can check out this guide. -

    - -

    Getting Started

    -

    If you want to get started with Lua For Barotrauma modding, check out this guide. -

    - -

    Links

    -

    - Github
    - Discord Server -

    -
    diff --git a/docs/templates/ldoc.ltp b/docs/templates/ldoc.ltp deleted file mode 100644 index b42ac394c..000000000 --- a/docs/templates/ldoc.ltp +++ /dev/null @@ -1,138 +0,0 @@ - -{% -local baseUrl = ldoc.css:gsub("ldoc.css", "") -local repo = "https://github.com/evilfactory/Barotrauma-lua-attempt/" -local pageTitle = mod and (ldoc.display_name(mod) .. " - " .. ldoc.title) or ldoc.title - -local oldmarkup = ldoc.markup -function ldoc.markup(text, item) - return oldmarkup(text, item, ldoc.plain) -end - -function ldoc.url(path) - return baseUrl .. path -end - -function ldoc.realm_icon(realm) - return "" -end - -function ldoc.sidebar_item(item, module) - local text = "" - - local deprecated = item.tags.deprecated - if deprecated then - text = text .. "" - else - text = text .. "" - end - - text = text .. ldoc.realm_icon(item.tags.realm[1]) - text = text .. "" - - if ldoc.is_kind_classmethod(module.kind) then - text = text .. item.name:gsub(".+:", "") - else - text = text .. item.name:gsub(module.name .. ".", "") - end - - text = text - .. "" - .. "" - return text -end - -function ldoc.item_header(item) - local text = "" - - text = text .. "" - - local deprecated = item.tags.deprecated - if deprecated then - text = text .. "

    " - else - text = text .. "

    " - end - - text = text - .. ldoc.realm_icon(item.tags.realm[1]) - .. ldoc.display_name(item) - .. "

    " - .. "
    " - - return text -end - -function ldoc.is_kind_classmethod(kind) - return kind ~= "libraries" -end - -function ldoc.repo_reference(item) - return repo .. "tree/master" .. item.file.filename:gsub(item.file.base, "/gamemode") .. "#L" .. item.lineno -end - -local function moduleDescription(mod) - if (mod.type == "topic") then - return mod.body:gsub(mod.display_name, ""):gsub("#", ""):sub(1, 256) .. "..." - end - - return mod.summary -end -%} - - - - {{pageTitle}} - - - - - - {% if (mod) then %} - - {% else %} - - {% end %} - - - - - - - -
    - {(templates/sidebar.ltp)} - -
    - {% if (ldoc.root) then -- we're rendering the landing page (index.html) %} - {(templates/landing.ltp)} - {% elseif (ldoc.body) then -- we're rendering non-code elements %} -
    - {* ldoc.body *} -
    - {% elseif (module) then -- we're rendering libary contents %} -
    - {(templates/module.ltp)} -
    - {% end %} -
    -
    - - - - - - diff --git a/docs/templates/module.ltp b/docs/templates/module.ltp deleted file mode 100644 index b3903674c..000000000 --- a/docs/templates/module.ltp +++ /dev/null @@ -1,152 +0,0 @@ - -
    -

    {{mod.name}}

    -

    {* ldoc.markup(mod.summary) *}

    -
    - -

    {* ldoc.markup(mod.description) *}

    - -{% - local kinds = {} - local kindsIpairs = {} - for kind, items in mod.kinds() do - local name = kind - if kind == "Tables" then - name = "Fields" - end - - for item in items() do - if kinds[name] == nil then - kinds[name] = {} - - local value = {} - value.kind = name - value.items = kinds[name] - table.insert(kindsIpairs, value) - end - kinds[name][item] = true - end - end - - -%} - -{% for i, value in ipairs(kindsIpairs) do - local kind = value.kind - local items = value.items -%} -

    {{kind}}

    - - {% for item, _ in pairs(items) do %} -
    -
    - {* ldoc.item_header(item) *} - - {% if item.tags.deprecated then %} -
    -
    Deprecated
    -

    This API is deprecated and shouldn't be used anymore.

    -
    - {% end %} - - {% if (item.tags.internal) then %} -
    -
    Internal
    -

    This is an internal function! You are able to use it, but you risk unintended side effects if used incorrectly.

    -
    - {% end %} - - {% if (ldoc.descript(item):len() == 0) then %} -
    -
    Incomplete
    -

    Documentation for this section is incomplete and needs expanding.

    -
    - {% else %} -

    {* ldoc.markup(ldoc.descript(item)) *}

    - {% end %} -
    - - {# function arguments #} - {% if (item.params and #item.params > 0) then %} - {% local subnames = mod.kinds:type_of(item).subnames %} - - {% if (subnames) then %} -

    {{subnames}}

    - {% end %} - - {% for argument in ldoc.modules.iter(item.params) do %} - {% local argument, sublist = item:subparam(argument) %} - -
      - {% for argumentName in ldoc.modules.iter(argument) do %} - {% local displayName = item:display_name_of(argumentName) %} - {% local type = ldoc.typename(item:type_of_param(argumentName)) %} - {% local default = item:default_of_param(argumentName) %} - -
    • - {{displayName}} - - {% if (type ~= "") then %} - {* type *} - {% end %} - - {% if (default and default ~= true) then %} - default: {{default}} - {% elseif (default) then %} - optional - {% end %} - -

      {* ldoc.markup(item.params.map[argumentName]) *}

      -
    • - {% end %} -
    - {% end %} - {% end %} - - {# function returns #} - {% if ((not ldoc.no_return_or_parms) and item.retgroups) then %} - {% local groups = item.retgroups %} - -

    Returns

    -
      - {% for i, group in ldoc.ipairs(groups) do %} - {% for returnValue in group:iter() do %} - {% local type, ctypes = item:return_type(returnValue) %} - {% type = ldoc.typename(type) %} - -
    • - {% if (type ~= "") then %} - {* type *} - {% else -- we'll assume that it will return a variable type if none is set %} - any - {% end %} - -

      {* ldoc.markup(returnValue.text) *}

      -
    • - {% end %} - - {% if (i ~= #groups) then %} -
      OR
      - {% end %} - {% end %} -
    - {% end %} - - {% if (item.usage) then -- function usage %} -

    Example Usage

    - {% for usage in ldoc.modules.iter(item.usage) do %} -
    {* usage *}
    - {% end %} - {% end %} - - {% if (item.see) then %} -

    See Also

    -
      - {% for see in ldoc.modules.iter(item.see) do %} -
    • {{see.label}}
    • - {% end %} -
    - {% end %} -
    - {% end %} -{% end %} diff --git a/docs/templates/sidebar.ltp b/docs/templates/sidebar.ltp deleted file mode 100644 index 322b3fd1f..000000000 --- a/docs/templates/sidebar.ltp +++ /dev/null @@ -1,102 +0,0 @@ - -{% -local function isKindExpandable(kind) - return kind ~= "Manual" -end -%} - - diff --git a/doxygen/DoxygenLayout.xml b/doxygen/DoxygenLayout.xml deleted file mode 100644 index 17afc9d01..000000000 --- a/doxygen/DoxygenLayout.xml +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - \ No newline at end of file diff --git a/doxygen/baro-client/DoxygenLayout.xml b/doxygen/baro-client/DoxygenLayout.xml deleted file mode 100644 index eff4d0fbb..000000000 --- a/doxygen/baro-client/DoxygenLayout.xml +++ /dev/null @@ -1,244 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/doxygen/baro-server/DoxygenLayout.xml b/doxygen/baro-server/DoxygenLayout.xml deleted file mode 100644 index 4eba30d35..000000000 --- a/doxygen/baro-server/DoxygenLayout.xml +++ /dev/null @@ -1,244 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/doxygen/build-docs.bat b/doxygen/build-docs.bat deleted file mode 100644 index 959a3bcf9..000000000 --- a/doxygen/build-docs.bat +++ /dev/null @@ -1,16 +0,0 @@ -@echo off -if not exist ".\build" mkdir ".\build" -if not exist ".\build\baro-server" mkdir ".\build\baro-server" -if not exist ".\build\baro-client" mkdir ".\build\baro-client" - -cd .\baro-server -echo Building server documentation -doxygen Doxyfile - -cd ..\baro-client -echo Building client documentation -doxygen Doxyfile - -cd .. -echo Building shared documentation -doxygen Doxyfile \ No newline at end of file diff --git a/doxygen/build-shared.bat b/doxygen/build-shared.bat deleted file mode 100644 index 7023da7f2..000000000 --- a/doxygen/build-shared.bat +++ /dev/null @@ -1,5 +0,0 @@ -@echo off -if not exist ".\build" mkdir ".\build" - -echo Building shared documentation -doxygen Doxyfile \ No newline at end of file diff --git a/luacs-docs/cs/.gitignore b/luacs-docs/cs/.gitignore new file mode 100644 index 000000000..297697bce --- /dev/null +++ b/luacs-docs/cs/.gitignore @@ -0,0 +1,4 @@ +html +*/html +*/*.tag +build diff --git a/doxygen/Doxyfile b/luacs-docs/cs/Doxyfile similarity index 100% rename from doxygen/Doxyfile rename to luacs-docs/cs/Doxyfile diff --git a/luacs-docs/cs/DoxygenLayout.xml b/luacs-docs/cs/DoxygenLayout.xml new file mode 100644 index 000000000..d43a0a564 --- /dev/null +++ b/luacs-docs/cs/DoxygenLayout.xml @@ -0,0 +1,8 @@ + + + + + + + + diff --git a/doxygen/baro-client/Doxyfile b/luacs-docs/cs/baro-client/Doxyfile similarity index 99% rename from doxygen/baro-client/Doxyfile rename to luacs-docs/cs/baro-client/Doxyfile index 16faf32d5..66f47267f 100644 --- a/doxygen/baro-client/Doxyfile +++ b/luacs-docs/cs/baro-client/Doxyfile @@ -906,8 +906,8 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = ../../Barotrauma/BarotraumaShared/SharedSource \ - ../../Barotrauma/BarotraumaClient/ClientSource +INPUT = ../../../Barotrauma/BarotraumaShared/SharedSource \ + ../../../Barotrauma/BarotraumaClient/ClientSource # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses diff --git a/luacs-docs/cs/baro-client/DoxygenLayout.xml b/luacs-docs/cs/baro-client/DoxygenLayout.xml new file mode 100644 index 000000000..8dc37b112 --- /dev/null +++ b/luacs-docs/cs/baro-client/DoxygenLayout.xml @@ -0,0 +1,243 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doxygen/baro-server/Doxyfile b/luacs-docs/cs/baro-server/Doxyfile similarity index 99% rename from doxygen/baro-server/Doxyfile rename to luacs-docs/cs/baro-server/Doxyfile index 9915adeeb..bf64a80e3 100644 --- a/doxygen/baro-server/Doxyfile +++ b/luacs-docs/cs/baro-server/Doxyfile @@ -906,8 +906,8 @@ WARN_LOGFILE = # spaces. See also FILE_PATTERNS and EXTENSION_MAPPING # Note: If this tag is empty the current directory is searched. -INPUT = ../../Barotrauma/BarotraumaShared/SharedSource \ - ../../Barotrauma/BarotraumaServer/ServerSource +INPUT = ../../../Barotrauma/BarotraumaShared/SharedSource \ + ../../../Barotrauma/BarotraumaServer/ServerSource # This tag can be used to specify the character encoding of the source files # that doxygen parses. Internally doxygen uses the UTF-8 encoding. Doxygen uses diff --git a/luacs-docs/cs/baro-server/DoxygenLayout.xml b/luacs-docs/cs/baro-server/DoxygenLayout.xml new file mode 100644 index 000000000..e3cf9460f --- /dev/null +++ b/luacs-docs/cs/baro-server/DoxygenLayout.xml @@ -0,0 +1,243 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/doxygen/custom.css b/luacs-docs/cs/custom.css similarity index 72% rename from doxygen/custom.css rename to luacs-docs/cs/custom.css index 33add5ebf..95930c3ac 100644 --- a/doxygen/custom.css +++ b/luacs-docs/cs/custom.css @@ -18,164 +18,137 @@ div.fragment { padding-left: 3px; padding-right: 3px; } - -body -{ + +body { background-color: #333333; /** for docs **/ color: #a1a1a1; } -#page-content -{ +#page-content { background-color: #333333; color: #bcbcbc; } -pre, pre.fragment -{ +pre, pre.fragment { background-color: #282828; } -.icon -{ +.icon { color: #333; background-color: #2f436b; /* from orig #728DC1; */; } -.arrow -{ +.arrow { color: #324872; /* from orig #9CAFD4; */; } /* to make keyword visible for above 'pre' */ -.highlight .nf -{ +.highlight .nf { color: #00adee; } -img -{ +img { background-color: #333333; } /** Note/Message boxes **/ -div.admonition, div.warning -{ +div.admonition, div.warning { background-color: #474747; } -div.admonition p.admonition-title, div.warning p.admonition-title -{ +div.admonition p.admonition-title, div.warning p.admonition-title { background-color: #707070; } /**** News ***/ -#content-outer -{ +#content-outer { background-color: #333333; } -.postmeta -{ +.postmeta { background-color: #474747; border-color: #595959; } /**** Blog ****/ -div.blogpost -{ +div.blogpost { background-color: #333; } -div.blogpost-header -{ +div.blogpost-header { background-color: #333; } -div.blogbody -{ +div.blogbody { background-color: #474747; } /**** Jobs ****/ -.dataTable tbody .even -{ +.dataTable tbody .even { background-color: #474747; } -.dataTable thead th -{ +.dataTable thead th { background-color: #6b6b6b; } /* Job Post Overlay */ -#fancybox-outer -{ +#fancybox-outer { color: #bcbcbc; background-color: #282828; } /***** Search box *****/ -#MSearchBox -{ +#MSearchBox { background-color: #333333; } -#MSearchBox .left -{ +#MSearchBox .left { filter: invert(80%); } -#MSearchSelect -{ +#MSearchSelect { background-color: transparent; } -.MSearchBoxActive #MSearchField -{ +.MSearchBoxActive #MSearchField { color: #333333; - /* this is inverted */; + /* this is inverted */ } -#MSearchBox .right -{ +#MSearchBox .right { filter: invert(80%); } -#MSearchCloseImg -{ +#MSearchCloseImg { background-color: transparent; } /** Magnifying glass dropdown list **/ -#MSearchSelectWindow -{ +#MSearchSelectWindow { background-color: #333333; border-color: #404040; } -a.SelectItem, a.SelectItem:focus, a.SelectItem:active -{ +a.SelectItem, a.SelectItem:focus, a.SelectItem:active { color: #999; } -a.SelectItem:hover -{ +a.SelectItem:hover { color: #bcbcbc; } /** Query Results dropdown list **/ -#MSearchResultsWindow -{ -/* left: !important; / * from orig 992px; * / dynamically generated value */ +#MSearchResultsWindow { + /* left: !important; / * from orig 992px; * / dynamically generated value */ border-color: #404040; } @@ -187,48 +160,47 @@ a.SelectItem:hover /* #MSearchCloseImg { } */ -.SRPage .SREntry -{ +.SRPage .SREntry { font-size: 80%; } -.SRResult:hover -{ +.SRResult:hover { background-color: #404040; } -.SRSymbol:hover -{ +.SRSymbol:hover { color: #4a69a9; /* from orig #425E97; */; } /***** For Docs *****/ -div.header -{ +div.header { background-color: #333333; border-bottom-color: #001; background-image: none; } -div.directory -{ +div.directory { border-top-color: #404040; border-bottom-color: #404040; } -td.memItemLeft, td.memItemRight, .mdescLeft, .mdescRight, .memdoc, .memTemplParams, .memTemplItemLeft, .memTemplItemRight -{ +td.memItemLeft, +td.memItemRight, +.mdescLeft, +.mdescRight, +.memdoc, +.memTemplParams, +.memTemplItemLeft, +.memTemplItemRight { background-color: #282828; } -.memSeparator -{ +.memSeparator { border-bottom-color: #333333; } -.memtitle -{ +.memtitle { background-color: #333333; background-image: none; border-top-color: #404040; @@ -236,8 +208,7 @@ td.memItemLeft, td.memItemRight, .mdescLeft, .mdescRight, .memdoc, .memTemplPara border-right-color: #404040; } -.memproto, dl.reflist dt -{ +.memproto, dl.reflist dt { /* 2nd for ToDo List */ color: #949b8c; /* #878f7e; */ @@ -249,57 +220,48 @@ td.memItemLeft, td.memItemRight, .mdescLeft, .mdescRight, .memdoc, .memTemplPara border-right-color: #404040; } -.memname -{ +.memname { background-color: #333333; } -a -{ +a { color: #adb8a1; } -a.el, a.el:visited -{ +a.el, a.el:visited { color: #6f8b4c; - /* #6d7c5d; */; + /* #6d7c5d; */; } -.memdoc -{ +.memdoc { background-image: none; } -.memdoc, dl.reflist dd -{ - /* 2nd for ToDo List */ +.memdoc, dl.reflist dd { + /* 2nd for ToDo List */ background-color: #282828; border-bottom-color: #404040; border-left-color: #404040; border-right-color: #404040; } -.paramname -{ +.paramname { color: #8f6262; } -img.formulaInl -{ +img.formulaInl { filter: invert(84%); background-color: #fff; } /* Template box headers */ -.memtemplate -{ +.memtemplate { color: #607fbb; /* from orignial #4665A2; */; } /* Labels - protected, static, virtual */ -span.mlabel -{ +span.mlabel { background-color: #282828; border-color: #333; color: #aaa; @@ -307,153 +269,127 @@ span.mlabel } /* Header */ -h2.groupheader -{ +h2.groupheader { color: #4c6db0; - /* from orig. #354C7B; */ + /* from orig. #354C7B; */ border-bottom-color: #333333; } -.inherit_header img -{ +.inherit_header img { background-color: #333333; } /* To make comments visible */ -span.comment -{ +span.comment { color: #7b7b7b; } /* Even rows of lists */ -.directory tr.even -{ +.directory tr.even { background-color: #303030; } /* Tables */ -table.fieldtable -{ +table.fieldtable { border-color: #404040; } -.fieldtable th -{ +.fieldtable th { color: #aaa; background-color: #333333; border-bottom-color: #404040; background-image: none; } -.fieldtable td.fieldtype, .fieldtable td.fieldname -{ +.fieldtable td.fieldtype, .fieldtable td.fieldname { border-right-color: #404040; border-bottom-color: #404040; } -.fieldtable td.fielddoc -{ +.fieldtable td.fielddoc { border-bottom-color: #404040; } -div.center > img -{ +div.center > img { background-color: #cccccc; filter: invert(100%); } /**** Source Code View ****/ -div.fragment -{ +div.fragment { background-color: #282828; border-color: #333333; } -.navpath li.navelem a -{ +.navpath li.navelem a { color: #d1d1d1; text-shadow: none; } -.navpath li.navelem a:hover -{ +.navpath li.navelem a:hover { color: #f1f1f1; text-shadow: 0px 1px 1px rgba(255, 255, 255, 0.9); } -span.lineno -{ +span.lineno { background-color: #333333; border-right-color: #333333; color: #666; } -span.line -{ +span.line { background-color: #3f3f3f; } -span.lineno a -{ +span.lineno a { background-color: #333333; } -span.lineno a:hover -{ +span.lineno a:hover { background-color: #444444; } -a.code, a.code:visited, a.line, a.line:visited -{ +a.code, a.code:visited, a.line, a.line:visited { color: #4fa2b3; } -span.keyword -{ +span.keyword { color: #00b300; } -span.keywordtype -{ +span.keywordtype { color: #ac7339; } -span.stringliteral -{ +span.stringliteral { color: #0140ff; } -#powerTip -{ +#powerTip { background-color: #282828; } -#powerTip div -{ +#powerTip div { background-color: #282828; } /**** Navigation ****/ -#nav-tree -{ +#nav-tree { background-color: #282828; background-image: none; } -#nav-tree img -{ +#nav-tree img { background-color: #282828; } -#nav-tree .selected a -{ +#nav-tree .selected a { color: #bcbcbc; } /** Split bar **/ -.ui-resizable-e -{ +.ui-resizable-e { filter: invert(70%); opacity: 0.2; } @@ -462,32 +398,27 @@ span.stringliteral /*** Type 1 e.g. https://dartsim.github.io/api/ ***/ -.tabs, .tabs2, .tabs3 -{ +.tabs, .tabs2, .tabs3 { background-image: none; } -.tablist li -{ +.tablist li { background-image: none; } -.tablist li.current a -{ +.tablist li.current a { color: #aaa; background-image: none; background-color: #444; } -.tablist a -{ +.tablist a { color: #bcbcbc; text-shadow: none; background-image: none; } -.tablist a:hover -{ +.tablist a:hover { color: #bcbcbc; background-color: #444444; background-image: none; @@ -495,20 +426,17 @@ span.stringliteral /*** Type 2 e.g. https://clang.llvm.org/doxygen/ ***/ -.sm-dox -{ +.sm-dox { background-image: none; } -.sm-dox a, .sm-dox a.highlighted -{ +.sm-dox a, .sm-dox a.highlighted { color: #bcbcbc; text-shadow: none; background-image: none; } -.sm-dox a:focus, .sm-dox a:active, .sm-dox a:hover -{ +.sm-dox a:focus, .sm-dox a:active, .sm-dox a:hover { color: #bcbcbc; background-color: #444444; background-image: none; @@ -516,83 +444,69 @@ span.stringliteral /** Pop-Pane **/ -.sm-dox ul -{ +.sm-dox ul { background-color: #333333; border-color: #666; } -.sm-dox ul a, .sm-dox ul a:focus, .sm-dox ul a:hover, .sm-dox ul a:active -{ +.sm-dox ul a, .sm-dox ul a:focus, .sm-dox ul a:hover, .sm-dox ul a:active { background-color: #333333; } -.sm-dox span.scroll-up, .sm-dox span.scroll-down -{ +.sm-dox span.scroll-up, .sm-dox span.scroll-down { background-color: #333333; } -.sm-dox span.scroll-up:hover, .sm-dox span.scroll-down:hover -{ +.sm-dox span.scroll-up:hover, .sm-dox span.scroll-down:hover { background-color: #444444; } -.sm-dox a span.sub-arrow -{ +.sm-dox a span.sub-arrow { border-top-color: #bcbcbc; } -.sm-dox a:hover span.sub-arrow -{ +.sm-dox a:hover span.sub-arrow { border-top-color: #cccccc; } -.sm-dox ul a, .sm-dox ul a:hover, .sm-dox ul a:focus, .sm-dox ul a:active, .sm-dox ul a.highlighted -{ +.sm-dox ul a, .sm-dox ul a:hover, .sm-dox ul a:focus, .sm-dox ul a:active, .sm-dox ul a.highlighted { color: #bcbcbc; } -.sm-dox > li > ul::before, .sm-dox > li > ul::after -{ +.sm-dox > li > ul::before, .sm-dox > li > ul::after { border-bottom-color: #333333; } /* Top menu-item highlighted only, for contrast with arrow */ -.sm-dox a.highlighted -{ +.sm-dox a.highlighted { background-color: #444444; } -.sm-dox ul a.highlighted -{ +.sm-dox ul a.highlighted { background-color: #333333; } /**** Bottom Tab ****/ -.navpath ul -{ +.navpath ul { background-image: none; border-color: #666; } -img.footer -{ +img.footer { background-color: #333333; opacity: 0.1; } /**** Navigation Tab ****/ /* e.g. nlohmann.github.io/json */ -div.navtab -{ +div.navtab { background-color: #333333; } /**** Classes ****/ /*** Class List, Member List ***/ -td.indexkey, td.indexvalue, tr.memlist -{ +td.indexkey, td.indexvalue, tr.memlist { background-color: #333333; } -/* } */ \ No newline at end of file +/* } */ diff --git a/doxygen/intro.md b/luacs-docs/cs/intro.md similarity index 100% rename from doxygen/intro.md rename to luacs-docs/cs/intro.md diff --git a/luacs-docs/cs/scripts/build.ps1 b/luacs-docs/cs/scripts/build.ps1 new file mode 100644 index 000000000..8115ee92b --- /dev/null +++ b/luacs-docs/cs/scripts/build.ps1 @@ -0,0 +1,36 @@ +Import-Module $PSScriptRoot/../../scripts/location.ps1 + +try { + Change-Location $PSScriptRoot/.. + + if ((Get-Command "doxgen" -ErrorAction SilentlyContinue) -eq $null) { + echo "doxygen not found" + exit 1 + } + + Remove-Item -Force -Recurse ./build | Out-Null + New-Item -ItemType Directory ./build | Out-Null + New-Item -ItemType Directory ./build/baro-server | Out-Null + New-Item -ItemType Directory ./build/baro-client | Out-Null + + echo "Building server docs" + try { + Change-Location ./baro-server + doxygen ./Doxyfile + } finally { + Restore-Location + } + + echo "Building client docs" + try { + Change-Location ./baro-client + doxygen ./Doxyfile + } finally { + Restore-Location + } + + echo "Building shared docs" + doxygen ./Doxyfile +} finally { + Restore-Location +} diff --git a/luacs-docs/cs/scripts/build.sh b/luacs-docs/cs/scripts/build.sh new file mode 100755 index 000000000..4e697f227 --- /dev/null +++ b/luacs-docs/cs/scripts/build.sh @@ -0,0 +1,29 @@ +#!/usr/bin/env bash + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" +cd "$DIR/.." + +if ! command -v "doxygen" &> /dev/null; then + echo "doxygen not found" + exit 1 +fi + +rm -rf ./build +mkdir -p ./build +mkdir -p ./build/baro-server +mkdir -p ./build/baro-client + +echo "Building server docs" +( + cd ./baro-server + doxygen ./Doxyfile +) + +echo "Building client docs" +( + cd ./baro-client + doxygen ./Doxyfile +) + +echo "Building shared docs" +doxygen ./Doxyfile diff --git a/luacs-docs/cs/scripts/serve.ps1 b/luacs-docs/cs/scripts/serve.ps1 new file mode 100644 index 000000000..4dcffcfa1 --- /dev/null +++ b/luacs-docs/cs/scripts/serve.ps1 @@ -0,0 +1,18 @@ +Import-Module $PSScriptRoot/../../scripts/location.ps1 + +try { + Change-Location $PSScriptRoot/.. + + if ((Get-Command "python3" -ErrorAction SilentlyContinue) -eq $null) { + echo "python3 not found" + exit 1 + } + + python3 ../scripts/http_server.py ./build ` + --port 8001 ` + --route /:html ` + --route /baro-client:baro-client ` + --route /baro-server:baro-server +} finally { + Restore-Location +} diff --git a/luacs-docs/cs/scripts/serve.sh b/luacs-docs/cs/scripts/serve.sh new file mode 100755 index 000000000..c320059e6 --- /dev/null +++ b/luacs-docs/cs/scripts/serve.sh @@ -0,0 +1,15 @@ +#!/usr/bin/env bash + +DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" >/dev/null 2>&1 && pwd)" +cd "$DIR/.." + +if ! command -v "python3" &> /dev/null; then + echo "python3 not found" + exit 1 +fi + +python3 ../scripts/http_server.py ./build \ + --port 8001 \ + --route /:html \ + --route /baro-client:baro-client \ + --route /baro-server:baro-server diff --git a/doxygen/searchdata.xml b/luacs-docs/cs/searchdata.xml similarity index 100% rename from doxygen/searchdata.xml rename to luacs-docs/cs/searchdata.xml diff --git a/docs-landing-page/bg.jpg b/luacs-docs/landing-page/bg.jpg similarity index 100% rename from docs-landing-page/bg.jpg rename to luacs-docs/landing-page/bg.jpg diff --git a/docs-landing-page/cs_logo.png b/luacs-docs/landing-page/cs_logo.png similarity index 100% rename from docs-landing-page/cs_logo.png rename to luacs-docs/landing-page/cs_logo.png diff --git a/luacs-docs/landing-page/index.html b/luacs-docs/landing-page/index.html new file mode 100644 index 000000000..ab682ca7e --- /dev/null +++ b/luacs-docs/landing-page/index.html @@ -0,0 +1,25 @@ + + + + + LuaCs For Barotrauma + + + + +
    +
    +
    + +
    +
    +
    +
    +
    +
    + +
    +
    +
    + + diff --git a/luacs-docs/landing-page/landing.css b/luacs-docs/landing-page/landing.css new file mode 100644 index 000000000..7ba88a55c --- /dev/null +++ b/luacs-docs/landing-page/landing.css @@ -0,0 +1,131 @@ +body { + overflow: hidden; + font-size: 100%; + background-color: #777; +} + +.common-docs { + z-index: 1; + text-justify: auto; + position: fixed; + top: 0; + bottom: 0; + transition-property: transform; + transition-duration: 0.5s; + transition-timing-function: ease-in-out; + overflow: hidden; +} +#lua-docs { + left: -10%; + right: 50%; +} +#cs-docs { + right: -10%; + left: 50%; +} + +#cs-docs::before, #lua-docs::before { + content: ""; + background: url(bg.jpg) repeat; + background-size: 80%; + background-blend-mode: luminosity; + filter: blur(2px); + position: absolute; + height: 200%; + width: 200%; +} +#lua-docs::before { + background-color: rgba(49, 49, 135, 1); +} +#cs-docs::before { + background-color: rgba(105, 44, 120, 1); +} + +.common-docs:hover { + z-index: 2; + font-size: 300%; + transform: scale(1.05); +} + +.inner-bg { + position: relative; + width: 100%; + height: 100%; + display: flex; + align-items: center; + justify-content: center; + transition-duration: 0.5s; + transition-timing-function: ease-in-out; +} +.inner-bg:hover { + background-color: rgba(255, 255, 255, 0.35); +} + +.inner-docs { + height: 100%; + width: 100%; + display: flex; + align-items: center; + justify-content: center; + position: relative; + background: radial-gradient(circle, #222, rgba(0, 0, 0, 0)); +} + +@media (min-width: 80em) { + .inner-docs img { + height: 30%; + width: auto; + } + .common-docs { + transform: skewX(-11deg); + } + #cs-docs::before, #lua-docs::before { + transform: skewX(11deg); + } + .common-docs:hover { + transform: skewX(-11deg) scale(1.05); + } + .inner-docs { + transform: skewX(11deg); + } + .inner-docs img { + height: 30%; + width: auto; + } +} +@media (max-width: 80em) { + .common-docs { + transform: skewX(-9deg); + } + #cs-docs::before, #lua-docs::before { + transform: skewX(9deg); + } + .common-docs:hover { + transform: skewX(-9deg) scale(1.05); + } + .inner-docs { + transform: skewX(9deg); + } + .inner-docs img { + height: 30%; + width: auto; + } +} +@media (max-width: 70em) { + .common-docs { + transform: skewX(0deg); + } + #cs-docs::before, #lua-docs::before { + transform: skewX(0deg); + } + .common-docs:hover { + transform: skewX(0deg) scale(1.05); + } + .inner-docs { + transform: skewX(0deg); + } + .inner-docs img { + height: auto; + width: 50%; + } +} diff --git a/docs-landing-page/lua_logo.png b/luacs-docs/landing-page/lua_logo.png similarity index 100% rename from docs-landing-page/lua_logo.png rename to luacs-docs/landing-page/lua_logo.png diff --git a/docs/.gitignore b/luacs-docs/lua/.gitignore similarity index 66% rename from docs/.gitignore rename to luacs-docs/lua/.gitignore index 97249b860..097e7e227 100644 --- a/docs/.gitignore +++ b/luacs-docs/lua/.gitignore @@ -1,2 +1,2 @@ -html +build lua_modules diff --git a/docs/baseluadocs/Affliction.lua b/luacs-docs/lua/baseluadocs/Affliction.lua similarity index 100% rename from docs/baseluadocs/Affliction.lua rename to luacs-docs/lua/baseluadocs/Affliction.lua diff --git a/docs/baseluadocs/AfflictionPrefab.lua b/luacs-docs/lua/baseluadocs/AfflictionPrefab.lua similarity index 100% rename from docs/baseluadocs/AfflictionPrefab.lua rename to luacs-docs/lua/baseluadocs/AfflictionPrefab.lua diff --git a/docs/baseluadocs/AnimController.lua b/luacs-docs/lua/baseluadocs/AnimController.lua similarity index 100% rename from docs/baseluadocs/AnimController.lua rename to luacs-docs/lua/baseluadocs/AnimController.lua diff --git a/docs/baseluadocs/Character.lua b/luacs-docs/lua/baseluadocs/Character.lua similarity index 100% rename from docs/baseluadocs/Character.lua rename to luacs-docs/lua/baseluadocs/Character.lua diff --git a/docs/baseluadocs/CharacterHealth.lua b/luacs-docs/lua/baseluadocs/CharacterHealth.lua similarity index 100% rename from docs/baseluadocs/CharacterHealth.lua rename to luacs-docs/lua/baseluadocs/CharacterHealth.lua diff --git a/docs/baseluadocs/CharacterInfo.lua b/luacs-docs/lua/baseluadocs/CharacterInfo.lua similarity index 100% rename from docs/baseluadocs/CharacterInfo.lua rename to luacs-docs/lua/baseluadocs/CharacterInfo.lua diff --git a/docs/baseluadocs/CharacterInventory.lua b/luacs-docs/lua/baseluadocs/CharacterInventory.lua similarity index 100% rename from docs/baseluadocs/CharacterInventory.lua rename to luacs-docs/lua/baseluadocs/CharacterInventory.lua diff --git a/docs/baseluadocs/Client.lua b/luacs-docs/lua/baseluadocs/Client.lua similarity index 100% rename from docs/baseluadocs/Client.lua rename to luacs-docs/lua/baseluadocs/Client.lua diff --git a/docs/baseluadocs/Entity.Spawner.lua b/luacs-docs/lua/baseluadocs/Entity.Spawner.lua similarity index 100% rename from docs/baseluadocs/Entity.Spawner.lua rename to luacs-docs/lua/baseluadocs/Entity.Spawner.lua diff --git a/docs/baseluadocs/Entity.lua b/luacs-docs/lua/baseluadocs/Entity.lua similarity index 100% rename from docs/baseluadocs/Entity.lua rename to luacs-docs/lua/baseluadocs/Entity.lua diff --git a/docs/baseluadocs/GameScreen.lua b/luacs-docs/lua/baseluadocs/GameScreen.lua similarity index 100% rename from docs/baseluadocs/GameScreen.lua rename to luacs-docs/lua/baseluadocs/GameScreen.lua diff --git a/docs/baseluadocs/GameSession.lua b/luacs-docs/lua/baseluadocs/GameSession.lua similarity index 100% rename from docs/baseluadocs/GameSession.lua rename to luacs-docs/lua/baseluadocs/GameSession.lua diff --git a/docs/baseluadocs/GameSettings.lua b/luacs-docs/lua/baseluadocs/GameSettings.lua similarity index 100% rename from docs/baseluadocs/GameSettings.lua rename to luacs-docs/lua/baseluadocs/GameSettings.lua diff --git a/docs/baseluadocs/Hull.lua b/luacs-docs/lua/baseluadocs/Hull.lua similarity index 100% rename from docs/baseluadocs/Hull.lua rename to luacs-docs/lua/baseluadocs/Hull.lua diff --git a/docs/baseluadocs/Inventory.lua b/luacs-docs/lua/baseluadocs/Inventory.lua similarity index 100% rename from docs/baseluadocs/Inventory.lua rename to luacs-docs/lua/baseluadocs/Inventory.lua diff --git a/docs/baseluadocs/Item.lua b/luacs-docs/lua/baseluadocs/Item.lua similarity index 100% rename from docs/baseluadocs/Item.lua rename to luacs-docs/lua/baseluadocs/Item.lua diff --git a/docs/baseluadocs/ItemInventory.lua b/luacs-docs/lua/baseluadocs/ItemInventory.lua similarity index 100% rename from docs/baseluadocs/ItemInventory.lua rename to luacs-docs/lua/baseluadocs/ItemInventory.lua diff --git a/docs/baseluadocs/ItemPrefab.lua b/luacs-docs/lua/baseluadocs/ItemPrefab.lua similarity index 100% rename from docs/baseluadocs/ItemPrefab.lua rename to luacs-docs/lua/baseluadocs/ItemPrefab.lua diff --git a/docs/baseluadocs/Job.lua b/luacs-docs/lua/baseluadocs/Job.lua similarity index 100% rename from docs/baseluadocs/Job.lua rename to luacs-docs/lua/baseluadocs/Job.lua diff --git a/docs/baseluadocs/JobPrefab.lua b/luacs-docs/lua/baseluadocs/JobPrefab.lua similarity index 100% rename from docs/baseluadocs/JobPrefab.lua rename to luacs-docs/lua/baseluadocs/JobPrefab.lua diff --git a/docs/baseluadocs/Level.lua b/luacs-docs/lua/baseluadocs/Level.lua similarity index 100% rename from docs/baseluadocs/Level.lua rename to luacs-docs/lua/baseluadocs/Level.lua diff --git a/docs/baseluadocs/NetLobbyScreen.lua b/luacs-docs/lua/baseluadocs/NetLobbyScreen.lua similarity index 100% rename from docs/baseluadocs/NetLobbyScreen.lua rename to luacs-docs/lua/baseluadocs/NetLobbyScreen.lua diff --git a/docs/baseluadocs/ServerSettings.lua b/luacs-docs/lua/baseluadocs/ServerSettings.lua similarity index 100% rename from docs/baseluadocs/ServerSettings.lua rename to luacs-docs/lua/baseluadocs/ServerSettings.lua diff --git a/docs/baseluadocs/Submarine.lua b/luacs-docs/lua/baseluadocs/Submarine.lua similarity index 100% rename from docs/baseluadocs/Submarine.lua rename to luacs-docs/lua/baseluadocs/Submarine.lua diff --git a/docs/baseluadocs/SubmarineInfo.lua b/luacs-docs/lua/baseluadocs/SubmarineInfo.lua similarity index 100% rename from docs/baseluadocs/SubmarineInfo.lua rename to luacs-docs/lua/baseluadocs/SubmarineInfo.lua diff --git a/docs/baseluadocs/WayPoint.lua b/luacs-docs/lua/baseluadocs/WayPoint.lua similarity index 100% rename from docs/baseluadocs/WayPoint.lua rename to luacs-docs/lua/baseluadocs/WayPoint.lua diff --git a/docs/baseluadocs/World.lua b/luacs-docs/lua/baseluadocs/World.lua similarity index 100% rename from docs/baseluadocs/World.lua rename to luacs-docs/lua/baseluadocs/World.lua diff --git a/docs/config.ld b/luacs-docs/lua/config.ld similarity index 99% rename from docs/config.ld rename to luacs-docs/lua/config.ld index bd278528e..545535ce6 100644 --- a/docs/config.ld +++ b/luacs-docs/lua/config.ld @@ -6,7 +6,7 @@ file = { module_file = {} -dir = "html" +dir = "build" project = "LuaForBarotrauma" title = "LuaForBarotrauma Documentation" diff --git a/docs/css/highlight.css b/luacs-docs/lua/css/highlight.css similarity index 99% rename from docs/css/highlight.css rename to luacs-docs/lua/css/highlight.css index 61447dba9..087149f5a 100644 --- a/docs/css/highlight.css +++ b/luacs-docs/lua/css/highlight.css @@ -10,7 +10,7 @@ * @author Zeno Rocha */ - .hljs { +.hljs { display: block; overflow-x: auto; padding: 0.5em; diff --git a/luacs-docs/lua/css/ldoc.css b/luacs-docs/lua/css/ldoc.css new file mode 100644 index 000000000..122b60cc8 --- /dev/null +++ b/luacs-docs/lua/css/ldoc.css @@ -0,0 +1,543 @@ + +:root { + --content-width: 100%; + --sidebar-width: 25%; + + --padding-big: 48px; + --padding-normal: 24px; + --padding-small: 16px; + --padding-tiny: 10px; + + --font-massive: 32px; + --font-huge: 24px; + --font-big: 18px; + --font-normal: 16px; + --font-tiny: 12px; + + --font-style-normal: Segoe UI, Helvetica, Arial, sans-serif; + --font-style-code: Consolas, monospace; + + --color-accent: rgb(47, 100, 74); + --color-accent-dark: rgb(33, 33, 33); + --color-white: rgb(255, 255, 255); + --color-offwhite: rgb(200, 200, 200); + --color-white-accent: rgb(203, 190, 209); + --color-black: rgb(0, 0, 0); + --color-lightgrey: rgb(160, 160, 160); + --color-background-light: rgb(245, 245, 245); + --color-background-dark: rgb(33, 33, 33); + --color-background-dark-ish: rgb(44, 44, 44); + --color-outline: rgb(149, 34, 160); + --color-good: #5190ff; +} + +* { + padding: 0; + margin: 0; + box-sizing: border-box; +} + +body { + background-color: var(--color-background-dark); + font-family: var(--font-style-normal); + + display: flex; + flex-direction: column; +} + +a { + color: inherit; + text-decoration: inherit; +} + +h1, h2, h3, h4 { + font-weight: 400; +} + +ul li { + margin-left: var(--padding-small); +} + +/* landing */ +.landing { + background-color: var(--color-accent); + color: var(--color-white); + + padding: 128px 0 128px 0; +} + +.landing h1 { + margin: 0; + padding: 0; + border: none; + + font-weight: 100; + font-size: var(--font-massive); + text-align: center; +} + +.wrapper { + padding: var(--padding-small); +} + +details { + user-select: none; +} + +details summary { + outline: none; +} + +code { + font-family: "Source Code Pro", monospace; + font-size: 85%; + white-space: pre; + tab-size: 4; + -moz-tab-size: 4; + padding: 1px 4px; + background-color: #282a36; + outline-style: solid; + outline-color: black; + outline-width: 2px; +} + +pre { + background-color: rgb(0, 0, 0, 1); + margin-top: var(--padding-small); + padding: 2px; + overflow: auto; +} + +pre code { + background-color: transparent; +} + +span.realm { + width: 14px; + height: 14px; + border-radius: 3px; + display: inline-block; + margin-right: 5px; +} + +span.realm.shared { + background: linear-gradient(45deg, #f80 0%, #f80 50%, #08f 51%, #08f 100%); +} + +span.realm.client { + background-color: #f80; +} + +span.realm.server { + background-color: #08f; +} + + +.colorful-label { + color: rgb(31, 141, 155); +} + +/* wrapper element for sidebar/content */ +main { + display: flex; + flex-direction: row; + justify-content: space-between; + align-items: flex-start; + + width: var(--content-width); + margin: auto; +} + +/* sidebar */ +nav { + color: var(--color-offwhite); + background-color: var(--color-background-dark); + + position: fixed; + display: flex; + flex-direction: column; + + width: var(--sidebar-width); + height: 100%; +} + +/* sidebar header */ +nav header { + color: var(--color-white); + background-color: var(--color-accent); + + padding: var(--padding-small); +} + +nav header h1 { + font-size: var(--font-huge); + font-weight: 100; + text-align: center; + + margin-bottom: var(--padding-small); +} + +#search { + background-color: var(--color-accent-dark); + color: var(--color-white); + + border: none; + font-size: var(--font-normal); + outline: none; + + width: 100%; + padding: 6px; +} + +#search::placeholder { + color: var(--color-white-accent); +} + +#search::-webkit-search-cancel-button { + display: none; +} + +/* sidebar contents */ +nav section { + padding: var(--padding-small); + overflow: auto; +} + +nav section ul { + list-style-type: none; +} + +nav section::-webkit-scrollbar, +pre::-webkit-scrollbar { + width: 8px; + height: 8px; +} + +nav section::-webkit-scrollbar-track, +pre::-webkit-scrollbar-track { + background: transparent; +} + +nav section::-webkit-scrollbar-thumb { + background-color: var(--color-lightgrey); +} + +pre::-webkit-scrollbar-thumb { + background-color: var(--color-lightgrey); +} + +/* sidebar contents category */ +nav section details.category { + padding-top: var(--padding-tiny); +} + +nav section details.category > ul > li { + margin: 0; + line-height: 1.5; +} + +nav section details.category > ul > li a { + display: inline-block; + width: 90%; +} + +nav section details.category:first-of-type { + padding-top: calc(var(--padding-tiny) * -1); +} + +nav section details.category summary::-webkit-details-marker { + opacity: 0.5; + cursor: pointer; +} + +nav section details.category summary h2 { + color: var(--color-accent); + + font-size: var(--font-big); + letter-spacing: 2px; + text-transform: uppercase; + cursor: pointer; + + padding-bottom: var(--padding-tiny); +} + +/* content */ +article { + background-color: var(--color-background-dark-ish); + color: white; + width: calc(100% - var(--sidebar-width)); + min-height: 100vh; + margin-left: var(--sidebar-width); +} + +article .wrapper > *:first-child { + margin-top: 0; +} + +/* header */ +article header { + color: rgb(255, 255, 255); + background-color: var(--color-accent); + padding: var(--padding-tiny); +} + +article header h1 { + border-bottom: 1px solid rgba(255, 255, 255, 0.25); + padding-bottom: 8px; + font-family: var(--font-style-code); + margin: 0; +} + +article header h2 { + padding-top: var(--padding-tiny); + margin: 0; + font-size: var(--font-normal); + font-weight: normal; +} + +article header.module a { + color: white !important; + text-decoration: underline; +} + +details.category > summary { + list-style: none; +} + +details.category > summary::-webkit-details-marker { + display: none; +} + +article h1 { + font-size: 28px; + font-weight: 600; + border-bottom: 1px solid rgba(0, 0, 0, 0.25); + margin-top: 24px; + margin-bottom: 16px; + padding-bottom: 8px; +} + +article h2 { + font-size: 20px; + font-weight: 600; + margin-top: 12px; +} + +article h3 { + color: var(--color-good); + margin-top: var(--padding-tiny); + text-transform: uppercase; +} + +article p { + margin-top: var(--padding-small); +} + +article p a, +article ul li a, +article h1 a, +article h2 a { + color: var(--color-good); + font-weight: 600; +} + +article h1.title { + color: rgb(255, 255, 255); + background-color: var(--color-accent); + margin-top: var(--padding-small); + margin-bottom: 0; + padding: var(--padding-tiny); + + font-size: var(--font-big); + font-weight: 100; + letter-spacing: 2px; + text-transform: uppercase; +} + +a.reference { + color: var(--color-good); + float: right; + margin-top: 8px; + padding-left: 8px; + font-size: 14px; + font-weight: 600; +} + +.notice { + --color-notice-background: var(--color-accent); + --color-notice-text: var(--color-notice-background); + + margin-top: var(--padding-tiny); + border: 2px solid var(--color-notice-background); +} + +.notice.error { + --color-notice-background: rgb(194, 52, 130); +} + +.notice.warning { + --color-notice-background: rgb(224, 169, 112); + --color-notice-text: rgb(167, 104, 37); +} + +.notice .title { + color: var(--color-white); + background-color: var(--color-notice-background); + + padding: var(--padding-tiny); + font-size: var(--font-normal); + + text-transform: uppercase; + letter-spacing: 2px; +} + +.notice p { + color: var(--color-notice-text); + + margin: 0 !important; + padding: var(--padding-tiny); +} + +/* function/table */ +.method { + display: flex; + flex-flow: column; + padding: var(--padding-tiny); + margin-top: var(--padding-small); +} + +.method header { + color: white; + background-color: inherit; + padding: 0; + order: -1; +} + +.method header .anchor { + color: inherit; + text-decoration: inherit; +} + +.method:target { + background-color: var(--color-background-dark); + + outline: solid; + outline-width: 1px; + outline-color: var(--color-accent); +} + +.method header:target { + background-color: var(--color-accent); +} + +.method header h1 { + font-family: "Source Code Pro", monospace; + padding-bottom: var(--padding-tiny); + border-bottom: 1px solid var(--color-accent); + font-size: 20px; +} + +.method header p:first-of-type { + margin-top: var(--padding-tiny); +} + +.method h3 { + color: var(--color-good); + font-size: var(--font-normal); + letter-spacing: 2px; + text-transform: uppercase; +} + +.method pre { + margin-top: var(--padding-tiny); +} + +@media only screen and (max-width: 1100px) { + main nav { + position: inherit; + } + + main article { + margin-left: 0; + } +} + +.method ul { + margin-top: var(--padding-tiny); + background-color: inherit; +} + +.method ul li { + list-style: none; + margin: 4px 0 0 var(--padding-normal); +} + +.method ul li:first-of-type { + margin-top: 0; +} + +.method ul li p { + margin: 4px 0 0 var(--padding-normal); +} + +.method ul li pre { + margin: 4px 0 0 var(--padding-normal); +} + +.method ul li a { + color: rgb(115, 53, 142); + font-weight: 600; +} + +/* we have to manually specify these instead of making a shared class since you cannot customize the parameter class in ldoc */ +.parameter, .type, .default { + display: inline-block; + color: rgb(255, 255, 255) !important; + + padding: 4px; + font-size: 14px; + font-family: "Source Code Pro", monospace; +} + +.parameter { + background-color: rgb(115, 53, 142); +} + +.type { + background-color: rgb(31, 141, 155); +} + +a.type { + font-weight: 300 !important; + text-decoration: underline; +} + +.default { + background-color: rgb(193, 114, 11); +} + +.type a { + padding: 0; +} + +.or { + color: rgba(115, 53, 142, 0.5); + background-color: inherit; + + width: calc(100% - 32px); + height: 8px; + margin: 0 0 8px 32px; + + text-align: center; + font-weight: 600; + border-bottom: 1px solid rgba(115, 53, 142, 0.5); +} + +.or span { + background-color: inherit; + padding: 0 8px 0 8px; +} + +.strikethrough { + text-decoration: line-through; +} diff --git a/luacs-docs/lua/js/app.js b/luacs-docs/lua/js/app.js new file mode 100644 index 000000000..d5941fac8 --- /dev/null +++ b/luacs-docs/lua/js/app.js @@ -0,0 +1,138 @@ +const skippedCategories = ["manual"]; + +class Node { + constructor(name, element, expandable, noAutoCollapse, children = []) { + this.name = name; + this.element = element; + this.expandable = expandable; + this.noAutoCollapse = noAutoCollapse; + this.children = children; + } + + AddChild(name, element, expandable, noAutoCollapse, children) { + let newNode = new Node(name, element, expandable, noAutoCollapse, children); + this.children.push(newNode); + + return newNode; + } +} + +class SearchManager { + constructor(input, contents) { + this.input = input; + this.input.addEventListener("input", event => { + this.OnInputUpdated(this.input.value.toLowerCase().replace(/:/g, ".")); + }); + + // setup search tree + this.tree = new Node("", document.createElement("null"), true, true); + this.entries = {}; + + const categoryElements = contents.querySelectorAll(".category"); + + // iterate each kind (hooks/libraries/classes/etc) + for (const category of categoryElements) { + const nameElement = category.querySelector(":scope > summary > h2"); + + if (!nameElement) { + continue; + } + + const categoryName = nameElement.textContent.trim().toLowerCase(); + + if (skippedCategories.includes(categoryName)) { + continue; + } + + let categoryNode = this.tree.AddChild(categoryName, category, true, true); + const sectionElements = category.querySelectorAll(":scope > ul > li"); + + for (const section of sectionElements) { + const entryElements = section.querySelectorAll(":scope > details > ul > li > a"); + const sectionName = section.querySelector(":scope > details > summary > a") + .textContent + .trim() + .toLowerCase(); + + let sectionNode = categoryNode.AddChild(sectionName, section.querySelector(":scope > details"), true); + + for (let i = 0; i < entryElements.length; i++) { + const entryElement = entryElements[i]; + const entryName = entryElement.textContent.trim().toLowerCase(); + + sectionNode.AddChild(sectionName + "." + entryName, entryElement.parentElement); + } + } + } + } + + ResetVisibility(current) { + current.element.style.display = ""; + + if (current.noAutoCollapse) { + current.element.open = true; + } + else if (current.expandable) { + current.element.open = false; + } + + for (let node of current.children) { + this.ResetVisibility(node); + } + } + + Search(input, current) { + let matched = false; + + if (current.name.indexOf(input) != -1) { + matched = true; + } + + for (let node of current.children) { + let childMatched = this.Search(input, node); + matched = matched || childMatched; + } + + if (matched) { + current.element.style.display = ""; + + if (current.expandable) { + current.element.open = true; + } + } else { + current.element.style.display = "none"; + + if (current.expandable) { + current.element.open = false; + } + } + + return matched; + } + + OnInputUpdated(input) { + if (input.length <= 1) { + this.ResetVisibility(this.tree); + return; + } + + this.Search(input, this.tree); + } +} + +window.onload = function() { + const openDetails = document.querySelector(".category > ul > li > details[open]"); + + if (openDetails) { + openDetails.scrollIntoView(); + } +} + +document.addEventListener("DOMContentLoaded", function() { + const searchInput = document.getElementById("search"); + const contents = document.querySelector("body > main > nav > section"); + + if (searchInput && contents) { + new SearchManager(searchInput, contents); + } +}); diff --git a/docs/js/highlight.min.js b/luacs-docs/lua/js/highlight.min.js similarity index 100% rename from docs/js/highlight.min.js rename to luacs-docs/lua/js/highlight.min.js diff --git a/docs/libs/ldoc b/luacs-docs/lua/libs/ldoc similarity index 100% rename from docs/libs/ldoc rename to luacs-docs/lua/libs/ldoc diff --git a/docs/lua/File.lua b/luacs-docs/lua/lua/File.lua similarity index 100% rename from docs/lua/File.lua rename to luacs-docs/lua/lua/File.lua diff --git a/docs/lua/Game.lua b/luacs-docs/lua/lua/Game.lua similarity index 100% rename from docs/lua/Game.lua rename to luacs-docs/lua/lua/Game.lua diff --git a/docs/lua/Hooks.lua b/luacs-docs/lua/lua/Hooks.lua similarity index 100% rename from docs/lua/Hooks.lua rename to luacs-docs/lua/lua/Hooks.lua diff --git a/docs/lua/Networking.lua b/luacs-docs/lua/lua/Networking.lua similarity index 100% rename from docs/lua/Networking.lua rename to luacs-docs/lua/lua/Networking.lua diff --git a/docs/lua/Signal.lua b/luacs-docs/lua/lua/Signal.lua similarity index 100% rename from docs/lua/Signal.lua rename to luacs-docs/lua/lua/Signal.lua diff --git a/docs/lua/Steam.lua b/luacs-docs/lua/lua/Steam.lua similarity index 100% rename from docs/lua/Steam.lua rename to luacs-docs/lua/lua/Steam.lua diff --git a/docs/lua/Timer.lua b/luacs-docs/lua/lua/Timer.lua similarity index 100% rename from docs/lua/Timer.lua rename to luacs-docs/lua/lua/Timer.lua diff --git a/docs/lua/Vectors.lua b/luacs-docs/lua/lua/Vectors.lua similarity index 100% rename from docs/lua/Vectors.lua rename to luacs-docs/lua/lua/Vectors.lua diff --git a/docs/lua/enums/CauseOfDeathType.lua b/luacs-docs/lua/lua/enums/CauseOfDeathType.lua similarity index 100% rename from docs/lua/enums/CauseOfDeathType.lua rename to luacs-docs/lua/lua/enums/CauseOfDeathType.lua diff --git a/docs/lua/enums/ChatMessageType.lua b/luacs-docs/lua/lua/enums/ChatMessageType.lua similarity index 100% rename from docs/lua/enums/ChatMessageType.lua rename to luacs-docs/lua/lua/enums/ChatMessageType.lua diff --git a/docs/lua/enums/ClientPermissions.lua b/luacs-docs/lua/lua/enums/ClientPermissions.lua similarity index 100% rename from docs/lua/enums/ClientPermissions.lua rename to luacs-docs/lua/lua/enums/ClientPermissions.lua diff --git a/docs/lua/enums/DisconnectReason.lua b/luacs-docs/lua/lua/enums/DisconnectReason.lua similarity index 100% rename from docs/lua/enums/DisconnectReason.lua rename to luacs-docs/lua/lua/enums/DisconnectReason.lua diff --git a/docs/lua/enums/Hook.HookMethodType.lua b/luacs-docs/lua/lua/enums/Hook.HookMethodType.lua similarity index 100% rename from docs/lua/enums/Hook.HookMethodType.lua rename to luacs-docs/lua/lua/enums/Hook.HookMethodType.lua diff --git a/docs/lua/enums/InvSlotType.lua b/luacs-docs/lua/lua/enums/InvSlotType.lua similarity index 100% rename from docs/lua/enums/InvSlotType.lua rename to luacs-docs/lua/lua/enums/InvSlotType.lua diff --git a/docs/lua/enums/LimbType.lua b/luacs-docs/lua/lua/enums/LimbType.lua similarity index 100% rename from docs/lua/enums/LimbType.lua rename to luacs-docs/lua/lua/enums/LimbType.lua diff --git a/docs/lua/enums/NetEntityEvent.Type.lua b/luacs-docs/lua/lua/enums/NetEntityEvent.Type.lua similarity index 100% rename from docs/lua/enums/NetEntityEvent.Type.lua rename to luacs-docs/lua/lua/enums/NetEntityEvent.Type.lua diff --git a/docs/lua/enums/RandSync.lua b/luacs-docs/lua/lua/enums/RandSync.lua similarity index 100% rename from docs/lua/enums/RandSync.lua rename to luacs-docs/lua/lua/enums/RandSync.lua diff --git a/docs/lua/enums/ServerLogMessageType.lua b/luacs-docs/lua/lua/enums/ServerLogMessageType.lua similarity index 100% rename from docs/lua/enums/ServerLogMessageType.lua rename to luacs-docs/lua/lua/enums/ServerLogMessageType.lua diff --git a/docs/lua/enums/SpawnType.lua b/luacs-docs/lua/lua/enums/SpawnType.lua similarity index 100% rename from docs/lua/enums/SpawnType.lua rename to luacs-docs/lua/lua/enums/SpawnType.lua diff --git a/docs/lua/generated/Affliction.lua b/luacs-docs/lua/lua/generated/Affliction.lua similarity index 100% rename from docs/lua/generated/Affliction.lua rename to luacs-docs/lua/lua/generated/Affliction.lua diff --git a/docs/lua/generated/AfflictionPrefab.lua b/luacs-docs/lua/lua/generated/AfflictionPrefab.lua similarity index 100% rename from docs/lua/generated/AfflictionPrefab.lua rename to luacs-docs/lua/lua/generated/AfflictionPrefab.lua diff --git a/docs/lua/generated/AnimController.lua b/luacs-docs/lua/lua/generated/AnimController.lua similarity index 100% rename from docs/lua/generated/AnimController.lua rename to luacs-docs/lua/lua/generated/AnimController.lua diff --git a/docs/lua/generated/Character.lua b/luacs-docs/lua/lua/generated/Character.lua similarity index 100% rename from docs/lua/generated/Character.lua rename to luacs-docs/lua/lua/generated/Character.lua diff --git a/docs/lua/generated/CharacterHealth.lua b/luacs-docs/lua/lua/generated/CharacterHealth.lua similarity index 100% rename from docs/lua/generated/CharacterHealth.lua rename to luacs-docs/lua/lua/generated/CharacterHealth.lua diff --git a/docs/lua/generated/CharacterInfo.lua b/luacs-docs/lua/lua/generated/CharacterInfo.lua similarity index 100% rename from docs/lua/generated/CharacterInfo.lua rename to luacs-docs/lua/lua/generated/CharacterInfo.lua diff --git a/docs/lua/generated/CharacterInventory.lua b/luacs-docs/lua/lua/generated/CharacterInventory.lua similarity index 100% rename from docs/lua/generated/CharacterInventory.lua rename to luacs-docs/lua/lua/generated/CharacterInventory.lua diff --git a/docs/lua/generated/Client.lua b/luacs-docs/lua/lua/generated/Client.lua similarity index 100% rename from docs/lua/generated/Client.lua rename to luacs-docs/lua/lua/generated/Client.lua diff --git a/docs/lua/generated/Entity.Spawner.lua b/luacs-docs/lua/lua/generated/Entity.Spawner.lua similarity index 100% rename from docs/lua/generated/Entity.Spawner.lua rename to luacs-docs/lua/lua/generated/Entity.Spawner.lua diff --git a/docs/lua/generated/Entity.lua b/luacs-docs/lua/lua/generated/Entity.lua similarity index 100% rename from docs/lua/generated/Entity.lua rename to luacs-docs/lua/lua/generated/Entity.lua diff --git a/docs/lua/generated/GameScreen.lua b/luacs-docs/lua/lua/generated/GameScreen.lua similarity index 100% rename from docs/lua/generated/GameScreen.lua rename to luacs-docs/lua/lua/generated/GameScreen.lua diff --git a/docs/lua/generated/GameSession.lua b/luacs-docs/lua/lua/generated/GameSession.lua similarity index 100% rename from docs/lua/generated/GameSession.lua rename to luacs-docs/lua/lua/generated/GameSession.lua diff --git a/docs/lua/generated/GameSettings.lua b/luacs-docs/lua/lua/generated/GameSettings.lua similarity index 100% rename from docs/lua/generated/GameSettings.lua rename to luacs-docs/lua/lua/generated/GameSettings.lua diff --git a/docs/lua/generated/Hull.lua b/luacs-docs/lua/lua/generated/Hull.lua similarity index 100% rename from docs/lua/generated/Hull.lua rename to luacs-docs/lua/lua/generated/Hull.lua diff --git a/docs/lua/generated/Inventory.lua b/luacs-docs/lua/lua/generated/Inventory.lua similarity index 100% rename from docs/lua/generated/Inventory.lua rename to luacs-docs/lua/lua/generated/Inventory.lua diff --git a/docs/lua/generated/Item.lua b/luacs-docs/lua/lua/generated/Item.lua similarity index 100% rename from docs/lua/generated/Item.lua rename to luacs-docs/lua/lua/generated/Item.lua diff --git a/docs/lua/generated/ItemInventory.lua b/luacs-docs/lua/lua/generated/ItemInventory.lua similarity index 100% rename from docs/lua/generated/ItemInventory.lua rename to luacs-docs/lua/lua/generated/ItemInventory.lua diff --git a/docs/lua/generated/ItemPrefab.lua b/luacs-docs/lua/lua/generated/ItemPrefab.lua similarity index 100% rename from docs/lua/generated/ItemPrefab.lua rename to luacs-docs/lua/lua/generated/ItemPrefab.lua diff --git a/docs/lua/generated/Job.lua b/luacs-docs/lua/lua/generated/Job.lua similarity index 100% rename from docs/lua/generated/Job.lua rename to luacs-docs/lua/lua/generated/Job.lua diff --git a/docs/lua/generated/JobPrefab.lua b/luacs-docs/lua/lua/generated/JobPrefab.lua similarity index 100% rename from docs/lua/generated/JobPrefab.lua rename to luacs-docs/lua/lua/generated/JobPrefab.lua diff --git a/docs/lua/generated/Level.lua b/luacs-docs/lua/lua/generated/Level.lua similarity index 100% rename from docs/lua/generated/Level.lua rename to luacs-docs/lua/lua/generated/Level.lua diff --git a/docs/lua/generated/NetLobbyScreen.lua b/luacs-docs/lua/lua/generated/NetLobbyScreen.lua similarity index 100% rename from docs/lua/generated/NetLobbyScreen.lua rename to luacs-docs/lua/lua/generated/NetLobbyScreen.lua diff --git a/docs/lua/generated/ServerSettings.lua b/luacs-docs/lua/lua/generated/ServerSettings.lua similarity index 100% rename from docs/lua/generated/ServerSettings.lua rename to luacs-docs/lua/lua/generated/ServerSettings.lua diff --git a/docs/lua/generated/Submarine.lua b/luacs-docs/lua/lua/generated/Submarine.lua similarity index 100% rename from docs/lua/generated/Submarine.lua rename to luacs-docs/lua/lua/generated/Submarine.lua diff --git a/docs/lua/generated/SubmarineInfo.lua b/luacs-docs/lua/lua/generated/SubmarineInfo.lua similarity index 100% rename from docs/lua/generated/SubmarineInfo.lua rename to luacs-docs/lua/lua/generated/SubmarineInfo.lua diff --git a/docs/lua/generated/WayPoint.lua b/luacs-docs/lua/lua/generated/WayPoint.lua similarity index 100% rename from docs/lua/generated/WayPoint.lua rename to luacs-docs/lua/lua/generated/WayPoint.lua diff --git a/docs/lua/generated/World.lua b/luacs-docs/lua/lua/generated/World.lua similarity index 100% rename from docs/lua/generated/World.lua rename to luacs-docs/lua/lua/generated/World.lua diff --git a/docs/manual/common-questions.md b/luacs-docs/lua/manual/common-questions.md similarity index 100% rename from docs/manual/common-questions.md rename to luacs-docs/lua/manual/common-questions.md diff --git a/docs/manual/getting-started.md b/luacs-docs/lua/manual/getting-started.md similarity index 100% rename from docs/manual/getting-started.md rename to luacs-docs/lua/manual/getting-started.md diff --git a/docs/manual/how-to-use-hooks.md b/luacs-docs/lua/manual/how-to-use-hooks.md similarity index 100% rename from docs/manual/how-to-use-hooks.md rename to luacs-docs/lua/manual/how-to-use-hooks.md diff --git a/docs/manual/installing-lua-for-barotrauma-manually.md b/luacs-docs/lua/manual/installing-lua-for-barotrauma-manually.md similarity index 100% rename from docs/manual/installing-lua-for-barotrauma-manually.md rename to luacs-docs/lua/manual/installing-lua-for-barotrauma-manually.md diff --git a/docs/manual/lua-examples.md b/luacs-docs/lua/manual/lua-examples.md similarity index 100% rename from docs/manual/lua-examples.md rename to luacs-docs/lua/manual/lua-examples.md diff --git a/luacs-docs/lua/scripts/build.ps1 b/luacs-docs/lua/scripts/build.ps1 new file mode 100644 index 000000000..71b286024 --- /dev/null +++ b/luacs-docs/lua/scripts/build.ps1 @@ -0,0 +1,19 @@ +Import-Module $PSScriptRoot/../../scripts/location.ps1 + +try { + Change-Location $PSScriptRoot/.. + + Remove-Item -Force -Recurse ./build | Out-Null + New-Item -ItemType Directory ./build | Out-Null + Copy-Item -Path ./css/. -Destination ./build -Recurse -Force | Out-Null + Copy-Item -Path ./js/. -Destination ./build -Recurse -Force | Out-Null + + if ((Get-Command "lua_modules/bin/ldoc" -ErrorAction SilentlyContinue) -eq $null) { + echo "ldoc not found; please run docs/scripts/install.ps1" + exit 1 + } + + lua_modules/bin/ldoc . +} finally { + Restore-Location +} diff --git a/docs/scripts/build.sh b/luacs-docs/lua/scripts/build.sh similarity index 78% rename from docs/scripts/build.sh rename to luacs-docs/lua/scripts/build.sh index df79e31cd..a08f0df88 100755 --- a/docs/scripts/build.sh +++ b/luacs-docs/lua/scripts/build.sh @@ -10,10 +10,10 @@ if [[ ! -x "$ldoc_path" ]]; then exit 1 fi -rm -rf ./html -mkdir ./html +rm -rf ./build +mkdir ./build -cp -r ./js/. ./html -cp -r ./css/. ./html +cp -r ./js/. ./build +cp -r ./css/. ./build "$ldoc_path" . diff --git a/docs/scripts/install.ps1 b/luacs-docs/lua/scripts/install.ps1 similarity index 83% rename from docs/scripts/install.ps1 rename to luacs-docs/lua/scripts/install.ps1 index 262e7f93c..c028d722f 100644 --- a/docs/scripts/install.ps1 +++ b/luacs-docs/lua/scripts/install.ps1 @@ -1,5 +1,7 @@ +Import-Module $PSScriptRoot/../../scripts/location.ps1 + try { - cd $PSScriptRoot/.. + Change-Location $PSScriptRoot/.. $lua_binary = $env:LUA_BINARY if ($lua_binary -eq $null) { @@ -31,11 +33,11 @@ try { ) try { - cd ./libs/ldoc + Change-Location ./libs/ldoc luarocks @luarocks_args make } finally { - popd + Restore-Location } } finally { - popd + Restore-Location } diff --git a/docs/scripts/install.sh b/luacs-docs/lua/scripts/install.sh similarity index 100% rename from docs/scripts/install.sh rename to luacs-docs/lua/scripts/install.sh diff --git a/luacs-docs/lua/scripts/serve.ps1 b/luacs-docs/lua/scripts/serve.ps1 new file mode 100644 index 000000000..07b40bc67 --- /dev/null +++ b/luacs-docs/lua/scripts/serve.ps1 @@ -0,0 +1,14 @@ +Import-Module $PSScriptRoot/../../scripts/location.ps1 + +try { + Change-Location $PSScriptRoot/.. + + if ((Get-Command "python3" -ErrorAction SilentlyContinue) -eq $null) { + echo "python3 not found" + exit 1 + } + + python3 ../scripts/http_server.py ./build --port 8000 +} finally { + Restore-Location +} diff --git a/docs/scripts/serve.sh b/luacs-docs/lua/scripts/serve.sh similarity index 77% rename from docs/scripts/serve.sh rename to luacs-docs/lua/scripts/serve.sh index 198bca784..1c62a7171 100755 --- a/docs/scripts/serve.sh +++ b/luacs-docs/lua/scripts/serve.sh @@ -8,4 +8,4 @@ if ! command -v "python3" &> /dev/null; then exit 1 fi -python3 -m http.server -d html +python3 ../scripts/http_server.py ./build --port 8000 diff --git a/luacs-docs/lua/templates/landing.ltp b/luacs-docs/lua/templates/landing.ltp new file mode 100644 index 000000000..8080a8eec --- /dev/null +++ b/luacs-docs/lua/templates/landing.ltp @@ -0,0 +1,24 @@ + +
    +

    Lua For Barotrauma Documentation

    +
    + +
    +

    +

    Welcome to the Lua For Barotrauma documentation!

    +

    This is a work in progress documentation, not everything is documented here, but because Barotrauma classes are exposed to Lua, you can check the Barotrauma source code for functions and fields that you can access.

    + +

    Installing Lua For Barotrauma

    +

    Downloading and using the Core Content Package from Workshop should be enough to have it installed, but if you want to install it manually, you can check out this guide. +

    + +

    Getting Started

    +

    If you want to get started with Lua For Barotrauma modding, check out this guide. +

    + +

    Links

    +

    + Github
    + Discord Server +

    +
    diff --git a/luacs-docs/lua/templates/ldoc.ltp b/luacs-docs/lua/templates/ldoc.ltp new file mode 100644 index 000000000..fe92540f7 --- /dev/null +++ b/luacs-docs/lua/templates/ldoc.ltp @@ -0,0 +1,138 @@ + +{% +local baseUrl = ldoc.css:gsub("ldoc.css", "") +local repo = "https://github.com/evilfactory/Barotrauma-lua-attempt/" +local pageTitle = mod and (ldoc.display_name(mod) .. " - " .. ldoc.title) or ldoc.title + +local oldmarkup = ldoc.markup +function ldoc.markup(text, item) + return oldmarkup(text, item, ldoc.plain) +end + +function ldoc.url(path) + return baseUrl .. path +end + +function ldoc.realm_icon(realm) + return "" +end + +function ldoc.sidebar_item(item, module) + local text = "" + + local deprecated = item.tags.deprecated + if deprecated then + text = text .. "" + else + text = text .. "" + end + + text = text .. ldoc.realm_icon(item.tags.realm[1]) + text = text .. "" + + if ldoc.is_kind_classmethod(module.kind) then + text = text .. item.name:gsub(".+:", "") + else + text = text .. item.name:gsub(module.name .. ".", "") + end + + text = text + .. "" + .. "" + return text +end + +function ldoc.item_header(item) + local text = "" + + text = text .. "" + + local deprecated = item.tags.deprecated + if deprecated then + text = text .. "

    " + else + text = text .. "

    " + end + + text = text + .. ldoc.realm_icon(item.tags.realm[1]) + .. ldoc.display_name(item) + .. "

    " + .. "
    " + + return text +end + +function ldoc.is_kind_classmethod(kind) + return kind ~= "libraries" +end + +function ldoc.repo_reference(item) + return repo .. "tree/master" .. item.file.filename:gsub(item.file.base, "/gamemode") .. "#L" .. item.lineno +end + +local function moduleDescription(mod) + if (mod.type == "topic") then + return mod.body:gsub(mod.display_name, ""):gsub("#", ""):sub(1, 256) .. "..." + end + + return mod.summary +end +%} + + + + {{pageTitle}} + + + + + + {% if (mod) then %} + + {% else %} + + {% end %} + + + + + + + +
    + {(templates/sidebar.ltp)} + +
    + {% if (ldoc.root) then -- we're rendering the landing page (index.html) %} + {(templates/landing.ltp)} + {% elseif (ldoc.body) then -- we're rendering non-code elements %} +
    + {* ldoc.body *} +
    + {% elseif (module) then -- we're rendering libary contents %} +
    + {(templates/module.ltp)} +
    + {% end %} +
    +
    + + + + + + diff --git a/luacs-docs/lua/templates/module.ltp b/luacs-docs/lua/templates/module.ltp new file mode 100644 index 000000000..17055615c --- /dev/null +++ b/luacs-docs/lua/templates/module.ltp @@ -0,0 +1,152 @@ + +
    +

    {{mod.name}}

    +

    {* ldoc.markup(mod.summary) *}

    +
    + +

    {* ldoc.markup(mod.description) *}

    + +{% + local kinds = {} + local kindsIpairs = {} + for kind, items in mod.kinds() do + local name = kind + if kind == "Tables" then + name = "Fields" + end + + for item in items() do + if kinds[name] == nil then + kinds[name] = {} + + local value = {} + value.kind = name + value.items = kinds[name] + table.insert(kindsIpairs, value) + end + kinds[name][item] = true + end + end + + +%} + +{% for i, value in ipairs(kindsIpairs) do + local kind = value.kind + local items = value.items +%} +

    {{kind}}

    + + {% for item, _ in pairs(items) do %} +
    +
    + {* ldoc.item_header(item) *} + + {% if item.tags.deprecated then %} +
    +
    Deprecated
    +

    This API is deprecated and shouldn't be used anymore.

    +
    + {% end %} + + {% if (item.tags.internal) then %} +
    +
    Internal
    +

    This is an internal function! You are able to use it, but you risk unintended side effects if used incorrectly.

    +
    + {% end %} + + {% if (ldoc.descript(item):len() == 0) then %} +
    +
    Incomplete
    +

    Documentation for this section is incomplete and needs expanding.

    +
    + {% else %} +

    {* ldoc.markup(ldoc.descript(item)) *}

    + {% end %} +
    + + {# function arguments #} + {% if (item.params and #item.params > 0) then %} + {% local subnames = mod.kinds:type_of(item).subnames %} + + {% if (subnames) then %} +

    {{subnames}}

    + {% end %} + + {% for argument in ldoc.modules.iter(item.params) do %} + {% local argument, sublist = item:subparam(argument) %} + +
      + {% for argumentName in ldoc.modules.iter(argument) do %} + {% local displayName = item:display_name_of(argumentName) %} + {% local type = ldoc.typename(item:type_of_param(argumentName)) %} + {% local default = item:default_of_param(argumentName) %} + +
    • + {{displayName}} + + {% if (type ~= "") then %} + {* type *} + {% end %} + + {% if (default and default ~= true) then %} + default: {{default}} + {% elseif (default) then %} + optional + {% end %} + +

      {* ldoc.markup(item.params.map[argumentName]) *}

      +
    • + {% end %} +
    + {% end %} + {% end %} + + {# function returns #} + {% if ((not ldoc.no_return_or_parms) and item.retgroups) then %} + {% local groups = item.retgroups %} + +

    Returns

    +
      + {% for i, group in ldoc.ipairs(groups) do %} + {% for returnValue in group:iter() do %} + {% local type, ctypes = item:return_type(returnValue) %} + {% type = ldoc.typename(type) %} + +
    • + {% if (type ~= "") then %} + {* type *} + {% else -- we'll assume that it will return a variable type if none is set %} + any + {% end %} + +

      {* ldoc.markup(returnValue.text) *}

      +
    • + {% end %} + + {% if (i ~= #groups) then %} +
      OR
      + {% end %} + {% end %} +
    + {% end %} + + {% if (item.usage) then -- function usage %} +

    Example Usage

    + {% for usage in ldoc.modules.iter(item.usage) do %} +
    {* usage *}
    + {% end %} + {% end %} + + {% if (item.see) then %} +

    See Also

    +
      + {% for see in ldoc.modules.iter(item.see) do %} +
    • {{see.label}}
    • + {% end %} +
    + {% end %} +
    + {% end %} +{% end %} diff --git a/luacs-docs/lua/templates/sidebar.ltp b/luacs-docs/lua/templates/sidebar.ltp new file mode 100644 index 000000000..17b305563 --- /dev/null +++ b/luacs-docs/lua/templates/sidebar.ltp @@ -0,0 +1,102 @@ + +{% +local function isKindExpandable(kind) + return kind ~= "Manual" +end +%} + + diff --git a/luacs-docs/scripts/http_server.py b/luacs-docs/scripts/http_server.py new file mode 100644 index 000000000..aa5b1e3d1 --- /dev/null +++ b/luacs-docs/scripts/http_server.py @@ -0,0 +1,63 @@ +import os +import http.server +import argparse + +def Route(s): + try: + prefix, root = s.split(":", 1) + except: + raise argparse.ArgumentTypeError("Route mapping must be in the : format: " + s) + return (prefix, root) + +parser = argparse.ArgumentParser() +parser.add_argument("root", type=str) +parser.add_argument("--port", type=int, default=8000) +parser.add_argument("--route", type=Route, dest="routes", action="extend", nargs="*") + +class RequestHandler(http.server.SimpleHTTPRequestHandler): + base_dir = None + routes = [] + def translate_path(self, path): + path.lstrip() + for prefix, rootDir in self.routes: + if path.startswith(prefix): + # print("matched route: " + prefix) + path = path[len(prefix):] + root = rootDir + break + + path.lstrip() + if root == None: + raise Exception("No route matches path: " + path) + + # Make sure we don't have a path that starts with /, otherwise + # os.path.join() would resolve it to the root of the filesystem. + if path.startswith("/"): + path = "." + path + + resolved_path = os.path.join(self.base_dir, root, path) + + # print("base_dir: " + self.base_dir) + # print("root: " + root) + # print("path: " + path) + # print("resolved_path: " + resolved_path) + + return resolved_path + +if __name__ == "__main__": + args = parser.parse_args() + + # Make sure we have at least one route + if not args.routes: + args.routes = [("/", "")] + + # Routes listed first should have precedence over the rest + args.routes.reverse() + + RequestHandler.base_dir = args.root + RequestHandler.routes = args.routes + port = args.port + + httpd = http.server.HTTPServer(("127.0.0.1", port), RequestHandler) + print(f"Listening on port {port} (http://127.0.0.1:{port})") + httpd.serve_forever() diff --git a/luacs-docs/scripts/location.ps1 b/luacs-docs/scripts/location.ps1 new file mode 100644 index 000000000..d265e213f --- /dev/null +++ b/luacs-docs/scripts/location.ps1 @@ -0,0 +1,14 @@ +[System.Collections.ArrayList]$Locations = @() + +function Change-Location($path) { + $loc = Get-Location + $Locations.Add($loc) + Set-Location $path +} + +function Restore-Location { + $idx = $Locations.Count - 1 + $loc = $Locations[$idx] + $Locations.RemoveAt($idx) + Set-Location $loc +} From 897466d295a8359f55907b0403642a4e2128e58f Mon Sep 17 00:00:00 2001 From: peelz Date: Wed, 3 Aug 2022 21:34:42 -0400 Subject: [PATCH 14/35] Move hooks docs to their own section --- .gitmodules | 2 +- luacs-docs/lua/css/ldoc.css | 29 +++++++++++++ luacs-docs/lua/libs/ldoc | 2 +- luacs-docs/lua/lua/Hooks.lua | 11 ++++- luacs-docs/lua/templates/module.ltp | 12 +++++- luacs-docs/lua/templates/sidebar.ltp | 62 +++++++++++++++++----------- 6 files changed, 88 insertions(+), 30 deletions(-) diff --git a/.gitmodules b/.gitmodules index 568b2ac40..7717574dc 100644 --- a/.gitmodules +++ b/.gitmodules @@ -3,4 +3,4 @@ url = https://github.com/evilfactory/moonsharp.git [submodule "luacs-docs/lua/libs/ldoc"] path = luacs-docs/lua/libs/ldoc - url = https://github.com/impulsh/LDoc.git + url = https://github.com/evilfactory/LDoc.git diff --git a/luacs-docs/lua/css/ldoc.css b/luacs-docs/lua/css/ldoc.css index 122b60cc8..29c99dddb 100644 --- a/luacs-docs/lua/css/ldoc.css +++ b/luacs-docs/lua/css/ldoc.css @@ -300,6 +300,35 @@ article header.module a { text-decoration: underline; } +article header.section { + color: rgb(255, 255, 255); + background-color: var(--color-accent); + padding: var(--padding-tiny); + margin-top: var(--padding-small); + margin-bottom: 0; +} + +article header.section h1 { + padding: 0; + border: 0; + font-size: var(--font-big); + font-weight: 100; + letter-spacing: 2px; + text-transform: uppercase; +} + +article header.section h2 { + padding: 0; + margin: 0; + font-size: var(--font-normal); + font-weight: normal; +} + +article header.section .section-separator { + margin: var(--padding-tiny) 0; + border-bottom: 1px solid rgba(255, 255, 255, 0.25); +} + details.category > summary { list-style: none; } diff --git a/luacs-docs/lua/libs/ldoc b/luacs-docs/lua/libs/ldoc index 69ef8c976..d1d4e3700 160000 --- a/luacs-docs/lua/libs/ldoc +++ b/luacs-docs/lua/libs/ldoc @@ -1 +1 @@ -Subproject commit 69ef8c976e3ac94146f87e99c8885211128a1c13 +Subproject commit d1d4e3700ad0de2e74a5aa350a73af2fb19a071b diff --git a/luacs-docs/lua/lua/Hooks.lua b/luacs-docs/lua/lua/Hooks.lua index 7b916d761..d282e23c7 100644 --- a/luacs-docs/lua/lua/Hooks.lua +++ b/luacs-docs/lua/lua/Hooks.lua @@ -1,7 +1,7 @@ -- luacheck: ignore 111 --[[-- -Hooks are basically functions that get called when events happen in-game, like chat messages. +The Hook API allow you to listen to game events and modify the behavior/logic of the game. ]] -- @code Hook -- @pragma nostrip @@ -50,6 +50,13 @@ function Hook.Call(eventName, parameters) end -- end, Hook.HookMethodType.After) function Hook.HookMethod(className, methodName, callback) end +--- Hooks +-- @summary +-- Hooks are functions that get called when events happen in-game, e.g. chat messages. +-- +-- These can be used with `Hook.Add` and `Hook.Call`. +-- @section hook + --- Game's fixed update rate, gets called normally 60 times a second. -- @realm shared function think() end @@ -248,4 +255,4 @@ function human.CPRSuccess(animController) end --- Called after the CPR skill check fails. -- @realm shared -function human.CPRFailed(animController) end \ No newline at end of file +function human.CPRFailed(animController) end diff --git a/luacs-docs/lua/templates/module.ltp b/luacs-docs/lua/templates/module.ltp index 17055615c..ad19c7f95 100644 --- a/luacs-docs/lua/templates/module.ltp +++ b/luacs-docs/lua/templates/module.ltp @@ -9,7 +9,7 @@ {% local kinds = {} local kindsIpairs = {} - for kind, items in mod.kinds() do + for kind, items, _, summary in mod.kinds() do local name = kind if kind == "Tables" then name = "Fields" @@ -21,6 +21,7 @@ local value = {} value.kind = name + value.summary = summary value.items = kinds[name] table.insert(kindsIpairs, value) end @@ -33,9 +34,16 @@ {% for i, value in ipairs(kindsIpairs) do local kind = value.kind + local summary = value.summary local items = value.items %} -

    {{kind}}

    +
    +

    {{kind}}

    + {% if summary ~= nil then %} +
    +

    {* ldoc.markup(summary) *}

    + {% end %} +
    {% for item, _ in pairs(items) do %}
    diff --git a/luacs-docs/lua/templates/sidebar.ltp b/luacs-docs/lua/templates/sidebar.ltp index 17b305563..ff9c44aa9 100644 --- a/luacs-docs/lua/templates/sidebar.ltp +++ b/luacs-docs/lua/templates/sidebar.ltp @@ -3,6 +3,26 @@ local function isKindExpandable(kind) return kind ~= "Manual" end + +local function isSectionEmpty(mod, sections) + if type(sections) == "table" then + for _, v in pairs(mod.items) do + for _, section in pairs(sections) do + if v.section_id == section then + return false + end + end + end + else + local section = sections + for _, v in pairs(mod.items) do + if v.section_id == section then + return false + end + end + end + return true +end %}
    {% end %} From 7e99cdef291f037aa4935ef5d96a3430896efa2a Mon Sep 17 00:00:00 2001 From: peelz Date: Wed, 3 Aug 2022 21:34:42 -0400 Subject: [PATCH 23/35] Implement rudimentary cache busting for lua docs --- luacs-docs/lua/templates/ldoc.ltp | 17 +++++++++++++---- luacs-docs/scripts/http_server.py | 15 ++++++++++++++- 2 files changed, 27 insertions(+), 5 deletions(-) diff --git a/luacs-docs/lua/templates/ldoc.ltp b/luacs-docs/lua/templates/ldoc.ltp index 2173af01e..d1b41430f 100644 --- a/luacs-docs/lua/templates/ldoc.ltp +++ b/luacs-docs/lua/templates/ldoc.ltp @@ -1,5 +1,7 @@ {% +math.randomseed(os.time()) + local baseUrl = ldoc.css:gsub("ldoc.css", "") local repo = "https://github.com/evilfactory/LuaCsForBarotrauma" local pageTitle = mod and (ldoc.display_name(mod) .. " - " .. ldoc.title) or ldoc.title @@ -13,6 +15,13 @@ function ldoc.url(path) return baseUrl .. path end +function ldoc.asset_url(path) + local uuid = string.gsub("xxxxxxx", "x", function(c) + return string.format("%x", math.random(0, 0xf)) + end) + return baseUrl .. path .. "?v=" .. uuid +end + function ldoc.realm_icon(realm) return "" end @@ -97,8 +106,8 @@ end {% end %} - - + + @@ -120,8 +129,8 @@ end