diff --git a/.dockerignore b/.dockerignore new file mode 100644 index 0000000000..24e31346b4 --- /dev/null +++ b/.dockerignore @@ -0,0 +1,8 @@ +.dockerignore +.git +.gitignore +.vs +.vscode +packages +*/bin +*/obj diff --git a/.github/CONTRIBUTING.md b/.github/CONTRIBUTING.md index 15f8ebe73a..71f8dd3c10 100644 --- a/.github/CONTRIBUTING.md +++ b/.github/CONTRIBUTING.md @@ -53,15 +53,10 @@ As mentioned before, you are expected to follow these specifications in order to ### Object Oriented Code As C# is an object-oriented language, code must be object-oriented when possible in order to be more flexible when adding content to it. If you don't know what "object-oriented" means, we highly recommend you do some light research to grasp the basics. -### Follow the [C# coding guidelines](https://docs.microsoft.com/en-us/dotnet/csharp/programming-guide/inside-a-program/coding-conventions) +### Tabs, not spaces +You must use tabs to indent your code, NOT SPACES. -With the following amendments for a focus on minimal code - -- Tabs, not spaces -- Use `var` whenever possible -- Prefer `using` statements to inline namespace imports -- One line blocks should not have braces where unneccessary (nested `if` statements may circumvent this to avoid confusion) -- Do not use the LINQ query syntax (The functions are acceptable) +(You may use spaces to align something, but you should tab to the block level first, then add the remaining spaces) ### No hacky code Hacky code, such as adding specific checks, is highly discouraged and only allowed when there is ***no*** other option. (Protip: 'I couldn't immediately think of a proper way so thus there must be no other option' is not gonna cut it here! If you can't think of anything else, say that outright and admit that you need help with it. Maintainers exist for exactly that reason.) @@ -73,8 +68,8 @@ Copying code from one place to another may be suitable for small, short-time pro Instead you can use object orientation, or simply placing repeated code in a function, to obey this specification easily. -### No duplicated magic numbers or strings -This means stuff like having a "mode" variable for an object set to "1" or "2" with no clear indicator of what that means. If it's used in more than one place, make these consts with a name that more clearly states what it's for. This is clearer and enhances readability of your code! Get used to doing it! +### No magic numbers or strings +This means stuff like having a "mode" variable for an object set to "1" or "2" with no clear indicator of what that means. Make these #defines with a name that more clearly states what it's for. This is clearer and enhances readability of your code! Get used to doing it! ### Do not commit modifications to the version numbers in AssemblyInfo.global.cs This file will be updated by maintainers when they deem it prudent to release a new version. For reference here is the version format we use 3.\.\.\ The criteria for changing a version number is as follows @@ -115,9 +110,13 @@ void Hello() This prevents nesting levels from getting deeper then they need to be. ### Other Notes -* Code should always be modular -* You are expected to help maintain the code that you add, meaning that if there is a problem then you are likely to be approached in order to fix any bugs. -* Non-prototype code must be unit tested with 100% code coverage +* Code should be modular where possible; if you are working on a new addition, then strongly consider putting it in its own file unless it makes sense to put it with similar ones. + +* Bloated code may be necessary to add a certain feature, which means there has to be a judgement over whether the feature is worth having or not. You can help make this decision easier by making sure your code is modular. + +* You are expected to help maintain the code that you add, meaning that if there is a problem then you are likely to be approached in order to fix any issues, runtimes, or bugs. + +* If you used regex to replace code during development of your code, post the regex in your PR for the benefit of future developers and downstream users. ## Pull Request Process diff --git a/.gitignore b/.gitignore index 90b3b65a9c..5074f2b86e 100644 --- a/.gitignore +++ b/.gitignore @@ -1,16 +1,17 @@ -#Visual studio stuff -.vscode/* -.vs/* - -#VS stuff - -*/bin/* -*/obj/* -*/Debug/* -*/Release/* - -packages/* +[Oo]bj/ +[Bb]in/ +.nuget/ +_ReSharper.* +packages/ +artifacts/ +.vs/ *.user -*.dmb -*.int +*.suo +*.userprefs +*DS_Store +*.sln.ide /TestResults +/src/Tgstation.Server.Host/appsettings.Development.json +/tests/DMAPI/travistester.lk +/tests/DMAPI/travistester.int +/tests/DMAPI/travistester.dmb diff --git a/.travis.yml b/.travis.yml index b54966f76b..3643b77270 100644 --- a/.travis.yml +++ b/.travis.yml @@ -1,14 +1,12 @@ -language: csharp +language: generic sudo: false - +git: + depth: 1 env: global: - BYOND_MAJOR="511" - BYOND_MINOR="1385" - - DMEName="Tools/travistester.dme" - matrix: - - BUILD_CLIENT=true - - BUILD_CLIENT=false + - DMEName="tests/DMAPI/travistester.dme" cache: directories: @@ -21,7 +19,7 @@ addons: - libstdc++6:i386 install: - - if [ "$BUILD_CLIENT" = true ]; then nuget restore; else ./Tools/install_byond.sh; fi + - build/install_byond.sh script: - - if [ "$BUILD_CLIENT" = true ]; then msbuild /p:Configuration=Release-Client TGStationServer3.sln; else ./Tools/build_byond.sh; fi + - tests/DMAPI/build_byond.sh diff --git a/AssemblyInfo.global.cs b/AssemblyInfo.global.cs deleted file mode 100644 index 333df9ca5d..0000000000 --- a/AssemblyInfo.global.cs +++ /dev/null @@ -1,10 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; - -//You cannot one definition the version number -//Believe me, I've tried, the compiler hates it so much -[assembly: AssemblyVersion("3.2.3.7")] -[assembly: AssemblyFileVersion("3.2.3.7")] -[assembly: AssemblyInformationalVersion("3.2.3.7")] - -[assembly: InternalsVisibleTo("TGS.Tests")] diff --git a/README.md b/README.md index 10d30ea886..85a09ea251 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,4 @@ -# Tgstation Toolkit: +# tgstation-server v4: [![Build status](https://ci.appveyor.com/api/projects/status/7t1h7bvuha0p9j5f/branch/master?svg=true)](https://ci.appveyor.com/project/Cyberboss/tgstation-server-tools/branch/master) [![Build Status](https://travis-ci.org/tgstation/tgstation-server.svg?branch=master)](https://travis-ci.org/tgstation/tgstation-server) [![codecov](https://codecov.io/gh/tgstation/tgstation-server/branch/master/graph/badge.svg)](https://codecov.io/gh/tgstation/tgstation-server) [![Waffle.io - Columns and their card count](https://badge.waffle.io/tgstation/tgstation-server.png?columns=all)](https://waffle.io/tgstation/tgstation-server?utm_source=badge) @@ -9,223 +9,12 @@ [![forthebadge](http://forthebadge.com/images/badges/built-with-love.svg)](http://forthebadge.com) [![forthebadge](http://forthebadge.com/images/badges/60-percent-of-the-time-works-every-time.svg)](http://forthebadge.com) -This is a toolset to manage a production server of /tg/Station13 (and its forks). It includes the ability to update the server without having to stop or shutdown the server (the update will take effect next round) the ability start the server and restart it if it crashes, as well as systems for fixing errors and merging GitHub Pull Requests locally. +This is a toolset to manage a production BYOND server. It includes the ability to update the server without having to stop or shutdown the server (the update will take effect on a "reboot" of the server) the ability start the server and restart it if it crashes, as well as systems for fixing errors and merging GitHub Pull Requests locally. -Generally, updates force a live tracking of the configured git repo, resetting local modifications. If you plan to make modifications, set up a new git repo to store your version of the code in, and point this script to that in the config (explained below). This can be on github or a local repo using file:/// urls. +Generally, updates force a live tracking of the configured git repo, resetting local modifications. If you plan to make modifications, set up a new git repo to store your version of the code in, and point this script to that in the config (explained below). This can be on GitHub or a local repo using file:/// urls. -Requires python 2.7/3.6 to be installed for changelog generation - -### Legacy Server -* The old cmd script server can be found in the legacy tree - - -## Installing -1. Either compile from source (requires .NET Framework 4.5.2, Nuget, and WiX toolset 4.0) or download the latest [release from github](https://github.com/tgstation/tgstation-server-tools/releases) -1. Unzip setup files -1. Run the installer or use the console server - -## Installing (GUI): -1. Launch `TGControlPanel.exe` as an administrator. A shortcut can be found on your desktop -1. Use the `Create Instance` button to create a new server instance -1. Go to the `Repository` Tab and set the remote address and branch of the git you with to track. Note: To grab the tgstation repo, use the following URL: git://github.com/tgstation/tgstation.git -1. Hit the clone button -1. While waiting go to the `BYOND` tab and install the BYOND version you wish -1. You may also configure an IRC and/or discord bot for the server on the chat tab -1. Once the clone is complete you may set up a committer identity, user name, and password on the `Repository` tab for pushing changelog updates -1. Go to the `Server` tab and click the `Initialize Game Folders` button -1. Optionally change the `Project Path` Setting from tgstation to wherever the dme/dmb pair are in your repository -1. Optionally tick the Autostart box if you wish to have your server start with Windows -1. When game folder initialization is complete, click the `Copy from Repo and Compile` option - -## Installing (CL example): -This process is identical to the above steps in command line mode. You can always learn more about a command using `?` i.e. `repo ?` -1. Launch TGCommandLine.exe as an administrator (running with no parameters puts you in interactive mode) -1. `service set-python-path C:\Python27` (If Your Python Install is in Program Files, You must encase the path in Quotes, Ex: "C:\Program Files\Python36") -1. `service create-instance "TGS" D:\tgstation` -1. `instance` And enter `TGS`. If you aren't using interactive mode, the following commands must be suffixed with `--instance TGS` -1. `repo setup https://github.com/tgstation/tgstation master` -1. `byond update 511.1385` -1. `irc nick TGS3Test` -1. `irc set-auth-mode channel-mode` -1. `irc set-auth-level %` -1. `irc setup-auth NickServ "id hunter2"` Yes this is the real password, please use it only for testing -1. `irc join botbus dev` -1. `irc join botbus admin` -1. `irc join botbus wd` -1. `irc join botbus game` -1. `irc enable` -1. `discord set-token Rjfa93jlksjfj934jlkjasf8a08wfl.asdjfj08e44` See https://discordapp.com/developers/docs/topics/oauth2#bots -1. `discord set-auth-mode role-id` -1. `discord addmin 192837419273409` See how to get a role id: https://www.reddit.com/r/discordapp/comments/5bezg2/role_id/. Note that if you `discord set-auth-mode user-id` you'll need to use user ids (Enable developer mode, right click user, `Copy ID`) -1. `discord join 12341234453235 dev` This is a channel id (Enable developer mode, right click channel, `Copy ID`) -1. `discord join 34563456344245 dev` -1. `discord join 23452362574456 admin` -1. `discord join 23452362574456 wd` -1. `discord join 53457345736788 game` -1. `discord enable` -1. `repo set-name tgstation-server` These two lines specify the changelog's committer identity. They are not mirrored in the GUI -1. `repo set-email tgstation-server@tgstation13.org` -1. `repo set-credentials` And follow the prompts -1. `dm project-name tgstation` -1. `dd autostart on` -1. `repo status` To check the clone job status -1. `dm initialize --wait` -1. `dd start` - -## Setting up Multi-User - -1. Create windows accounts for those you wish to have access to the service -1. Join them in a common windows group -1. Run TGCommandLine.exe as an administrator -1. `admin set-group --instance ""` - -Note: Due to internal functionality, a user who has access to at least one server instance will be able to view the metadata (Name, path, Logging ID, and Enabled status) of all instances. - -## Setting up Remote access - -1. Obtain an SSL certificate to secure the connection (this is beyond the scope of this guide) -1. Either stick with the default port `38607` or change it with `service set-port ` -1. [Bind the SSL certificate to the port](https://docs.microsoft.com/en-us/dotnet/framework/wcf/feature-details/how-to-configure-a-port-with-an-ssl-certificate) - - e.g. `netsh http add sslcert ipport=0.0.0.0: certhash= appid={F32EDA25-0855-411C-AF5E-F0D042917E2D}` - - The `appid` GUID actually doesn't matter, but for sanity, you should use the GUID of TGServerService.exe as printed above - - Power shell users remember to quit the appid: `netsh http add sslcert ipport=0.0.0.0: certhash= appid="{F32EDA25-0855-411C-AF5E-F0D042917E2D}"` as {} has special meaning in powershell -1. Ensure the port can be acccessed from the internet -1. Log in from any computer using a username and password from the service computer in either the CLI or GUI - -## Updating - -The service supports updates while running a DreamDaemon instance. Simply install an updated version as you normally would and process ownership will transfer smoothly to the new service. - -### Folders and Files (None of these should be touched): -* `Game//` - * This will house two copies of the game code, one for updating and one for live. When updating, it will automatically swap them. - -* `Static/` - * This contains the `data/` and `config/` folders from the code. They are stored here and a symbolic link is created in the `Game` folders pointing to here. - * Resetting the repository will create a backup of this and reinitalize it - -* `Game/Live/` - * This is a symbolic link pointing to current "live" folder. - * When the server is updated, we just point this to the updating folder so that the update takes place next round. - -* `Diagnostics` - * This contains various timestamped diagnostic information for DreamDaemon invocations - -* `Repository/` - * This contains the actual git repository, all changes in here will be overwritten during update operations. - -* `RepoKey/` - * This contains ssh key information for automatic changelog pushing. - -* `EventHandlers/` - * This contains batch files that run after certain events, currently only precompile and postcompile events are implemented. If you'd like an event handler you should create a file in this folder with the following name: `{event_name}.bat` for example: if I want an event handler for precompile, I would name it `precompile.bat`. - -* `BYOND/` - * This contains the actual BYOND installation the server uses - -* `BYOND_staging/` - * This appears when a BYOND update is queued but can't currently be applied due to usage of the current BYOND version. It will be applied at the first possible moment. Restarting the service deletes this folder - -* `BYOND_revision.zip` - * This is a queued update downloaded from BYOND, it will be unzipped into BYOND_staging and deleted. Restarting the service deletes this file - -* `TGDreamDaemonBridge.dll` - * This is the .dll the TGS3 API `call()()`s into to RPC the server instance that runs it - * Instance -> DreamDaemon communication is achieved via `world/Topic()` - -* `prtestjob.json` - * This contains information about current test merged pull requests in the Repository folder - -* `TGS3.json` - * This is a copy of `TGS3.json` from the Repository. If a repostory change creates differences between the two, update operations will be blocked until the user confirms they want to change it - -* `Instance.json` - * The internal configuration settings for a server instance. Note that access to this file bypasses API user restrictions - -### Codebase integration -To get the TGS3 API for your code base, import the 3 .dm files in the `DMAPI` folder into your include structure, then fill out the configuration as documented in the comments of server_tools.dm. Then, you may want to add a `TGS3.json` file to specify any static directories and .dlls your codebase uses, along with the optional changelog compile options. Any changes to `TGS3.json` will block compiler operations until a server operator manually approves them. - -### Starting the game server: -To run the game server, open the `Server` tab of the control panel and click either `Start` - -It will restart the game server if it shutdowns for any reason, giving up after 5 tries in under a minute - -### Updating the server: -To update the server, open the `Server` tab of the control panel. Click `Update Server`. (it will git pull, compile, all that jazz). Note that this button won't clear test merges. - -(Note: Updating automatically does a code reset, clearing ALL changes to the local git repo, including manual changes (This will not change any data in the `Static/` folder)) - -Updates do not require the server to be shutdown, changes will apply next round if the server is currently running. - -All DM compilation will log to the server what commit they happened at and create a backup tag in the local repository. - -### Locally merge GitHub Pull Requests (PR test merge): -This feature currently only works if github is the remote (git server). - -Running these will merge the pull request then recompile the server, changes take effect the next round if the server is currently running. - -There are two flavors to this. -* The manual method is to use the repo page's merge PR button for fine grain control over PR merging. Each time this button is pressed, the latest commit of the specified PR# will be merged into the repo -* The managed method is the Server page's `Test Merge Manager` which uses the GitHub API to get information about available PRs. - * Open PRs are listed by default - * Currently test merged PRs are checked off and listed at the top when it's opened -* If a PR contains a label that contains the text `test` (case-insensitive) it will be listed on top and marked as `TESTING REQUESTED` - * If a PR has been updated since it was test merged, two entries for it will appear. One listed as `OUTDATED` and specifying the commit it was merged at - * You can change the initial update action of the server with the radio buttons in the bottom left. `Update to Remote` fully resets and updates the server before merging PRs. `Update To Origin` does the same based off the local repository's `origin` remote. `No Update` will merge the PRs without any prior action - * Checking off PRs here and then hitting apply will run the selected update action, optionally generate and push a changelog (see below), merge the PRs, and then compile. `Update Server` will keep any active merged PRs until they are manually removed or merged on the `origin` remote. - * Given that the control panel uses GitHub's API to populate the `Test Merge Manager` you may be prompted for your credentials if you make too many requests. This will create a personal access token with public access on your account specifying its use to bypass the GitHub API rate limit. - -You can clear all active test merges using `Reset to Origin Branch` in the `Repository` tab and the using `Copy from Repo and Compile` in the server tab (explained below). You can also use the `Reset All Test Merges` button on the server tab for a concise solution. - -### The Compiler -* `Server` -> `Copy from Repo and Compile` - * Copies the local repository code, compiles it, and stages it to apply next round -* `Server` -> `Initialize Game Folders` - * Requires the server not be running, rebuilds the `Game` folder and then does `Copy from Repo and Compile` - * Required on first setup - -### Starting everything when the computer/server boots -Just tick the `Autostart` option in the `Server` tab or run `dd autostart on` on the command line. As it's a windows service, it will automatically run without having to log ing - -### Enabling the BYOND Webclient -Just tick the `Webclient` option in the `Server` tab or run `dd webclient on` on the command line. - -### Static Configuration -* The `Static Files` page of the control panel lets you modify all files in directories you specified in `TGS3.json` -* The files are modified here using the Windows credentials of the active user, feel free to manually set ACLs on them -* You may optionally rebuild the entire `Static` folder from the repository using the `Recreate Static Directory` button. This will copy the original files from the repository based on the current `TGS3.json`. This requires DreamDaemon not be running - -### Modifying Your Code - -Any `.dm` files included in the root level of the `Static` directory are automatically copied over and included before anything else in your `.dme` for compilation. Use this to configure compile options as you see fit. - -### Moving, Renaming, and Detaching instances - -* Instances can be renamed, but this requires a temporary offlining of them (this includes interface access, DreamDaemon, and chat bots) -* Detaching an instance simply removes it from the main server's configuration and control, leaving it free for the user to manipulate -* Importing an instance will work as long as - 1. No other instance currently in the server has the same name - 1. It is not the same path as another instance - Note that importing an instance as a different windows user (this is always different across machines) will result in a loss of chat configuration due to the encryption scheme - -### Viewing Server Logs -* Service logs are stored in the Windows event viewer under `Windows Logs` -> `Application`. You'll need to filter this list for `TG Station Server` -* Every event type is keyed with an ID. A complete listing of these IDs and their purpose can be found [here](https://github.com/tgstation/tgstation-server/blob/master/TGS.Server/EventID.cs). Event IDs from different instances are offset by the logging ID the instance was assigned when it was started. -* You can also import the custom view `View TGS3 Logs.xml` in this folder to have them automatically filtered -* Servers running in console mode only use std_out - -### Enabling upstream changelog generation -* The repository will automatically create an ssh version of the initial origin remote and can optionally push generated changelogs to your git through it -* The repository can only authenticate using ssh public key authentication -* To enable this feature, simply create `public_key.txt` and `private_key.txt` in a folder called RepoKey in the server directory -* The private key must be in `-----BEGIN RSA PRIVATE KEY-----` format and the public key must be in `ssh-rsa` format. You can generate a keypair like this using the converter in the dropdown menu of PuTTYGen. See github guidelines for setting this up here: https://help.github.com/articles/connecting-to-github-with-ssh/ -* The server will be able to read these files regardless of their permissions, so the responsibility is on you to set their ACL's so they can't be read by those that shouldn't - -### Synchronized test merge commits -* An instance can push branchless test merge commits to GitHub if the `RepoKey` folder is setup -* This pushes all test merge commits to the remote branch `___TGS3TempBranch` and then deletes it -* This provides public reference information about the test merge commit and time since it will appear in the PR in questing -* To enable this, check the `Sync Commits` button on the `Repository` page of the control panel or run `repo push-testmerges on --instance ""` from TGCommandLine +### Legacy Servers +* Versions 3 and 4 can be found in the `legacy/` directory ## CONTRIBUTING diff --git a/TGS.CommandLine/AdminCommands.cs b/TGS.CommandLine/AdminCommands.cs deleted file mode 100644 index 41addc7b7b..0000000000 --- a/TGS.CommandLine/AdminCommands.cs +++ /dev/null @@ -1,114 +0,0 @@ -using System.Collections.Generic; -using TGS.Interface; - -namespace TGS.CommandLine -{ - sealed class AdminCommand : RootCommand - { - public AdminCommand() - { - Keyword = "admin"; - Children = new Command[] { new AdminViewGroupCommand(), new AdminSetGroupCommand(), new AdminClearGroupCommand(), new AdminRecreateStaticCommand() }; - } - public override string GetHelpText() - { - return "Manage instance authentication"; - } - } - - class AdminRecreateStaticCommand : ConsoleCommand - { - public AdminRecreateStaticCommand() - { - Keyword = "recreate-static-directory"; - } - - protected override ExitCode Run(IList parameters) - { - var res = Instance.Administration.RecreateStaticFolder(); - OutputProc(res ?? "Success"); - return res == null ? ExitCode.Normal : ExitCode.ServerError; - } - - public override string GetHelpText() - { - return "Backup the current static directory and repopulate it from the TGS3.json in the repository"; - } - } - - class AdminViewGroupCommand : ConsoleCommand - { - public AdminViewGroupCommand() - { - Keyword = "view-group"; - } - public override string GetHelpText() - { - return "Print the name of the windows group that is allowed to use the service"; - } - - protected override ExitCode Run(IList parameters) - { - var group = Instance.Administration.GetCurrentAuthorizedGroup(); - OutputProc(group ?? "ERROR"); - return group != null ? ExitCode.Normal : ExitCode.ServerError; - } - } - - class AdminSetGroupCommand : ConsoleCommand - { - public AdminSetGroupCommand() - { - Keyword = "set-group"; - RequiredParameters = 1; - } - - public override string GetHelpText() - { - return "Set the windows group allowed to use the service"; - } - - public override string GetArgumentString() - { - return ""; - } - protected override ExitCode Run(IList parameters) - { - var result = Instance.Administration.SetAuthorizedGroup(parameters[0]); - if(result != null) - { - OutputProc("Group set to: " + result); - return ExitCode.Normal; - } - else - { - OutputProc("Failed to find a group named: " + parameters[0]); - return ExitCode.ServerError; - } - } - } - - class AdminClearGroupCommand : ConsoleCommand - { - public AdminClearGroupCommand() - { - Keyword = "clear-group"; - } - - public override string GetHelpText() - { - return "Clears the groups allowed to use the service, leaving only windows administrators"; - } - - protected override ExitCode Run(IList parameters) - { - var res = Instance.Administration.SetAuthorizedGroup(null); - if(res != "ADMIN") - { - OutputProc("Failed to clear the group??? We are currently set to: " + res); - return ExitCode.ServerError; - } - return ExitCode.Normal; - } - } -} diff --git a/TGS.CommandLine/App.config b/TGS.CommandLine/App.config deleted file mode 100644 index 88fa4027bd..0000000000 --- a/TGS.CommandLine/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/TGS.CommandLine/BYONDCommands.cs b/TGS.CommandLine/BYONDCommands.cs deleted file mode 100644 index 84e1209781..0000000000 --- a/TGS.CommandLine/BYONDCommands.cs +++ /dev/null @@ -1,138 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using TGS.Interface; - -namespace TGS.CommandLine -{ - class BYONDCommand : RootCommand - { - public BYONDCommand() - { - Keyword = "byond"; - Children = new Command[] { new BYONDUpdateCommand(), new BYONDVersionCommand(), new BYONDStatusCommand() }; - } - public override string GetHelpText() - { - return "Manage BYOND installation"; - } - } - - class BYONDVersionCommand : ConsoleCommand - { - public BYONDVersionCommand() - { - Keyword = "version"; - } - protected override ExitCode Run(IList parameters) - { - var type = ByondVersion.Installed; - if (parameters.Count > 0) - if (parameters[0].ToLower() == "--staged") - type = ByondVersion.Staged; - else if (parameters[0].ToLower() == "--latest") - type = ByondVersion.Latest; - OutputProc(Instance.Byond.GetVersion(type) ?? "Unistalled"); - return ExitCode.Normal; - } - public override string GetArgumentString() - { - return "[--staged|--latest]"; - } - - public override string GetHelpText() - { - return "Print the currently installed BYOND version"; - } - } - - - class BYONDStatusCommand : ConsoleCommand - { - public BYONDStatusCommand() - { - Keyword = "status"; - } - protected override ExitCode Run(IList parameters) - { - switch (Instance.Byond.CurrentStatus()) - { - case ByondStatus.Downloading: - OutputProc("Downloading update..."); - break; - case ByondStatus.Idle: - OutputProc("Updater Idle"); - break; - case ByondStatus.Staged: - OutputProc("Update staged and awaiting server restart"); - break; - case ByondStatus.Staging: - OutputProc("Staging update..."); - break; - case ByondStatus.Starting: - OutputProc("Starting update..."); - break; - case ByondStatus.Updating: - OutputProc("Applying update..."); - break; - default: - OutputProc("Limmexing (This is an error)."); - return ExitCode.ServerError; - } - return ExitCode.Normal; - } - public override string GetHelpText() - { - return "Print the current status of the BYOND updater"; - } - } - - class BYONDUpdateCommand : ConsoleCommand - { - public BYONDUpdateCommand() - { - Keyword = "update"; - RequiredParameters = 2; - } - protected override ExitCode Run(IList parameters) - { - int Major = 0, Minor = 0; - try - { - Major = Convert.ToInt32(parameters[0]); - Minor = Convert.ToInt32(parameters[1]); - } - catch - { - OutputProc("Please enter version as ."); - return ExitCode.BadCommand; - } - - var BYOND = Instance.Byond; - if (!BYOND.UpdateToVersion(Major, Minor)) - - { - OutputProc("Failed to begin update!"); - return ExitCode.ServerError; - } - - var stat = BYOND.CurrentStatus(); - while (stat != ByondStatus.Idle && stat != ByondStatus.Staged) - { - Thread.Sleep(100); - stat = BYOND.CurrentStatus(); - } - var res = BYOND.GetError(); - OutputProc(res ?? (stat == ByondStatus.Staged ? "Update staged and will apply next DD reboot" : "Update finished")); - return res == null ? ExitCode.Normal : ExitCode.ServerError; - } - public override string GetArgumentString() - { - return " "; - } - public override string GetHelpText() - { - return "Updates the BYOND installation to the specified version"; - } - } -} diff --git a/TGS.CommandLine/ChatCommands.cs b/TGS.CommandLine/ChatCommands.cs deleted file mode 100644 index 6e87e34944..0000000000 --- a/TGS.CommandLine/ChatCommands.cs +++ /dev/null @@ -1,721 +0,0 @@ -using System; -using System.Collections.Generic; -using TGS.Interface; - -namespace TGS.CommandLine -{ - class IRCCommand : RootCommand - { - public IRCCommand() - { - Keyword = "irc"; - Children = new Command[] { new IRCNickCommand(), new IRCAuthCommand(), new IRCDisableAuthCommand(), new IRCServerCommand(), new ChatJoinCommand(ChatProvider.IRC), new ChatPartCommand(ChatProvider.IRC), new ChatListAdminsCommand(ChatProvider.IRC), new ChatReconnectCommand(ChatProvider.IRC), new ChatAddminCommand(ChatProvider.IRC), new ChatDeadminCommand(ChatProvider.IRC), new ChatEnableCommand(ChatProvider.IRC), new ChatDisableCommand(ChatProvider.IRC), new ChatStatusCommand(ChatProvider.IRC), new IRCAuthModeCommand(), new IRCAuthLevelCommand() }; - } - public override string GetHelpText() - { - return "Manages the IRC bot"; - } - } - class DiscordCommand : RootCommand - { - public DiscordCommand() - { - Keyword = "discord"; - Children = new Command[] { new DiscordSetTokenCommand(), new ChatJoinCommand(ChatProvider.Discord), new ChatPartCommand(ChatProvider.Discord), new ChatListAdminsCommand(ChatProvider.Discord), new ChatReconnectCommand(ChatProvider.Discord), new ChatAddminCommand(ChatProvider.Discord), new ChatDeadminCommand(ChatProvider.Discord), new ChatEnableCommand(ChatProvider.Discord), new ChatDisableCommand(ChatProvider.Discord), new ChatStatusCommand(ChatProvider.Discord) , new DiscordAuthModeCommand() }; - } - public override string GetHelpText() - { - return "Manages the Discord bot"; - } - } - class IRCNickCommand : ConsoleCommand - { - public IRCNickCommand() - { - Keyword = "nick"; - RequiredParameters = 1; - } - - public override string GetArgumentString() - { - return ""; - } - public override string GetHelpText() - { - return "Sets the IRC nickname"; - } - - protected override ExitCode Run(IList parameters) - { - var Chat = Instance.Chat; - Chat.SetProviderInfo(new IRCSetupInfo(Chat.ProviderInfos()[(int)ChatProvider.IRC]) - { - Nickname = parameters[0], - }); - return ExitCode.Normal; - } - } - - class ChatJoinCommand : ConsoleCommand - { - readonly int providerIndex; - public ChatJoinCommand(ChatProvider pI) - { - Keyword = "join"; - RequiredParameters = 2; - providerIndex = (int)pI; - } - - public override string GetArgumentString() - { - return " "; - } - public override string GetHelpText() - { - return "Joins a channel for listening and broadcasting of the specified message type (Developer, Watchdog, Game, Admin)"; - } - - protected override ExitCode Run(IList parameters) - { - var IRC = Instance.Chat; - var info = IRC.ProviderInfos()[providerIndex]; - List channels; - - switch (parameters[1].ToLower()) - { - case "dev": - channels = info.DevChannels; - break; - case "wd": - channels = info.WatchdogChannels; - break; - case "game": - channels = info.GameChannels; - break; - case "admin": - channels = info.AdminChannels; - break; - default: - OutputProc("Invalid parameter: " + parameters[1]); - return ExitCode.BadCommand; - } - - var lowerParam = parameters[0].ToLower(); - foreach (var I in channels) - { - if (I.ToLower() == lowerParam) - { - OutputProc("Already in this channel!"); - return ExitCode.BadCommand; - } - } - channels.Add(parameters[0]); - switch (parameters[1].ToLower()) - { - case "dev": - info.DevChannels = channels; - break; - case "wd": - info.WatchdogChannels = channels; - break; - case "game": - info.GameChannels = channels; - break; - case "admin": - info.AdminChannels = channels; - break; - } - var res = IRC.SetProviderInfo(info); - if(res != null) - { - OutputProc(res); - return ExitCode.ServerError; - } - return ExitCode.Normal; - } - } - - class ChatPartCommand : ConsoleCommand - { - readonly int providerIndex; - public ChatPartCommand(ChatProvider pI) - { - Keyword = "part"; - RequiredParameters = 2; - providerIndex = (int)pI; - } - - public override string GetArgumentString() - { - return " "; - } - public override string GetHelpText() - { - return "Stops listening and broadcasting on a channel for the specified message type (Developer, Watchdog, Game, Admin)"; - } - protected override ExitCode Run(IList parameters) - { - var IRC = Instance.Chat; - var info = IRC.ProviderInfos()[providerIndex]; - List channels; - - switch (parameters[1].ToLower()) - { - case "dev": - channels = info.DevChannels; - break; - case "wd": - channels = info.WatchdogChannels; - break; - case "game": - channels = info.GameChannels; - break; - case "admin": - channels = info.AdminChannels; - break; - default: - OutputProc("Invalid parameter: " + parameters[1]); - return ExitCode.BadCommand; - } - var lowerParam = parameters[0].ToLower(); - if ((ChatProvider)providerIndex == ChatProvider.IRC && lowerParam[0] != '#') - lowerParam = "#" + lowerParam; - channels.Remove(lowerParam); - switch (parameters[1].ToLower()) - { - case "dev": - info.DevChannels = channels; - break; - case "wd": - info.WatchdogChannels = channels; - break; - case "game": - info.GameChannels = channels; - break; - case "admin": - info.AdminChannels = channels; - break; - } - var res = IRC.SetProviderInfo(info); - if (res != null) - { - OutputProc(res); - return ExitCode.ServerError; - } - return ExitCode.Normal; - } - } - class ChatListAdminsCommand : ConsoleCommand - { - readonly int providerIndex; - public ChatListAdminsCommand(ChatProvider pI) - { - Keyword = "list-admins"; - providerIndex = (int)pI; - } - - public override string GetHelpText() - { - return "List users which can use restricted commands in the admin channel"; - } - - protected override ExitCode Run(IList parameters) - { - var info = Instance.Chat.ProviderInfos()[providerIndex]; - string authType; - switch ((ChatProvider)providerIndex) - { - case ChatProvider.IRC: - if (info.AdminsAreSpecial) - authType = "Mode:"; - else - authType = "Nicknames:"; - break; - case ChatProvider.Discord: - if (info.AdminsAreSpecial) - authType = "Role IDs:"; - else - authType = "User IDs:"; - break; - default: - OutputProc(String.Format("Invalid provider: {0}!", providerIndex)); - return ExitCode.ServerError; - } - OutputProc("Authorized " + authType); - if (info.AdminsAreSpecial && (ChatProvider)providerIndex == ChatProvider.IRC) - switch(new IRCSetupInfo(info).AuthLevel) - { - case IRCMode.Voice: - OutputProc("+"); - break; - case IRCMode.Halfop: - OutputProc("%"); - break; - case IRCMode.Op: - OutputProc("@"); - break; - case IRCMode.Owner: - OutputProc("~"); - break; - } - else - foreach (var I in info.AdminList) - OutputProc(I); - return ExitCode.Normal; - } - } - class ChatReconnectCommand : ConsoleCommand - { - readonly ChatProvider providerIndex; - public ChatReconnectCommand(ChatProvider pI) - { - Keyword = "reconnect"; - providerIndex = pI; - } - - public override string GetHelpText() - { - return "Restablish the chat connection"; - } - - protected override ExitCode Run(IList parameters) - { - var res = Instance.Chat.Reconnect(providerIndex); - if (res != null) - { - OutputProc("Error: " + res); - return ExitCode.ServerError; - } - return ExitCode.Normal; - } - } - class ChatAddminCommand : ConsoleCommand - { - readonly int providerIndex; - public ChatAddminCommand(ChatProvider pI) - { - Keyword = "addmin"; - RequiredParameters = 1; - providerIndex = (int)pI; - } - - public override string GetArgumentString() - { - return "[nick]"; - } - public override string GetHelpText() - { - return "Add a user which can use restricted commands in the admin channels"; - } - protected override ExitCode Run(IList parameters) - { - var IRC = Instance.Chat; - var info = IRC.ProviderInfos()[providerIndex]; - var newmin = parameters[0].ToLower(); - - if (info.AdminsAreSpecial && (ChatProvider)providerIndex == ChatProvider.IRC) - { - OutputProc("Invalid auth mode for this command!"); - return ExitCode.BadCommand; - } - - if (info.AdminList.Contains(newmin)) - { - OutputProc(parameters[0] + " is already an admin!"); - return ExitCode.BadCommand; - } - var al = info.AdminList; - al.Add(newmin); - info.AdminList = al; - var res = IRC.SetProviderInfo(info); - if (res != null) - { - OutputProc(res); - return ExitCode.ServerError; - } - return ExitCode.Normal; - } - } - class IRCAuthModeCommand : ConsoleCommand - { - public IRCAuthModeCommand() - { - Keyword = "set-auth-mode"; - RequiredParameters = 1; - } - - public override string GetArgumentString() - { - return ""; - } - public override string GetHelpText() - { - return "Switch between admin command authorization via user channel mode or nicknames"; - } - protected override ExitCode Run(IList parameters) - { - var IRC = Instance.Chat; - var info = IRC.ProviderInfos()[(int)ChatProvider.IRC]; - var lowerparam = parameters[0].ToLower(); - if (lowerparam == "channel-mode") - info.AdminsAreSpecial = true; - else if (lowerparam == "nickname") - info.AdminsAreSpecial = false; - else - { - OutputProc("Invalid parameter: " + parameters[0]); - return ExitCode.BadCommand; - } - var res = IRC.SetProviderInfo(info); - if (res != null) - { - OutputProc(res); - return ExitCode.ServerError; - } - return ExitCode.Normal; - } - } - class DiscordAuthModeCommand : ConsoleCommand - { - public DiscordAuthModeCommand() - { - Keyword = "set-auth-mode"; - RequiredParameters = 1; - } - - public override string GetArgumentString() - { - return ""; - } - public override string GetHelpText() - { - return "Switch between admin command authorization via user roles or individual users"; - } - protected override ExitCode Run(IList parameters) - { - var IRC = Instance.Chat; - var info = IRC.ProviderInfos()[(int)ChatProvider.Discord]; - var lowerparam = parameters[0].ToLower(); - if (lowerparam == "role-id") - info.AdminsAreSpecial = true; - else if (lowerparam == "user-id") - info.AdminsAreSpecial = false; - else - { - OutputProc("Invalid parameter: " + parameters[0]); - return ExitCode.BadCommand; - } - var res = IRC.SetProviderInfo(info); - if (res != null) - { - OutputProc(res); - return ExitCode.ServerError; - } - return ExitCode.Normal; - } - } - class IRCAuthLevelCommand : ConsoleCommand - { - public IRCAuthLevelCommand() - { - Keyword = "set-auth-level"; - RequiredParameters = 1; - } - - public override string GetArgumentString() - { - return "<+|%|@|~>"; - } - public override string GetHelpText() - { - return "Set the required channel mode for users to use admin commands"; - } - protected override ExitCode Run(IList parameters) - { - var IRC = Instance.Chat; - var info = new IRCSetupInfo(IRC.ProviderInfos()[(int)ChatProvider.IRC]); - switch (parameters[0]) - { - case "+": - info.AuthLevel = IRCMode.Voice; - break; - case "%": - info.AuthLevel = IRCMode.Halfop; - break; - case "@": - info.AuthLevel = IRCMode.Op; - break; - case "~": - info.AuthLevel = IRCMode.Owner; - break; - default: - OutputProc("Invalid parameter: " + parameters[0]); - return ExitCode.BadCommand; - } - - var res = IRC.SetProviderInfo(info); - if (res != null) - { - OutputProc(res); - return ExitCode.ServerError; - } - return ExitCode.Normal; - } - } - class ChatDeadminCommand : ConsoleCommand - { - readonly int providerIndex; - public ChatDeadminCommand(ChatProvider pI) - { - Keyword = "deadmin"; - RequiredParameters = 1; - providerIndex = (int)pI; - } - public override string GetArgumentString() - { - return ""; - } - public override string GetHelpText() - { - return "Remove a user which can use restricted commands in the admin channels"; - } - protected override ExitCode Run(IList parameters) - { - var IRC = Instance.Chat; - var info = IRC.ProviderInfos()[providerIndex]; - var newmin = parameters[0].ToLower(); - - if (info.AdminsAreSpecial && (ChatProvider)providerIndex == ChatProvider.IRC) - { - OutputProc("Invalid auth mode for this command!"); - return ExitCode.BadCommand; - } - - if (!info.AdminList.Contains(newmin)) - { - OutputProc(parameters[0] + " is not an admin!"); - return ExitCode.BadCommand; - } - - var al = info.AdminList; - al.Remove(newmin); - info.AdminList = al; - var res = IRC.SetProviderInfo(info); - if (res != null) - { - OutputProc(res); - return ExitCode.ServerError; - } - return ExitCode.Normal; - } - } - - class IRCAuthCommand : ConsoleCommand - { - public IRCAuthCommand() - { - Keyword = "setup-auth"; - RequiredParameters = 2; - } - - public override string GetArgumentString() - { - return " "; - } - public override string GetHelpText() - { - return "Set the authentication message to send to target for identification. e.g. NickServ \"identify hunter2\""; - } - protected override ExitCode Run(IList parameters) - { - var IRC = Instance.Chat; - IRC.SetProviderInfo(new IRCSetupInfo(IRC.ProviderInfos()[(int)ChatProvider.IRC]) - { - AuthTarget = parameters[0], - AuthMessage = parameters[1] - }); - return ExitCode.Normal; - } - } - - class IRCDisableAuthCommand : ConsoleCommand - { - public IRCDisableAuthCommand() - { - Keyword = "disable-auth"; - } - public override string GetHelpText() - { - return "Turns off IRC authentication"; - } - protected override ExitCode Run(IList parameters) - { - var IRC = Instance.Chat; - IRC.SetProviderInfo(new IRCSetupInfo(IRC.ProviderInfos()[(int)ChatProvider.IRC]) - { - AuthTarget = null, - AuthMessage = null, - }); - return ExitCode.Normal; - } - } - - class ChatStatusCommand : ConsoleCommand - { - readonly int providerIndex; - public ChatStatusCommand(ChatProvider pI) - { - Keyword = "status"; - providerIndex = (int)pI; - } - public override string GetHelpText() - { - return "Lists channels and connections status"; - } - protected override ExitCode Run(IList parameters) - { - var IRC = Instance.Chat; - var info = IRC.ProviderInfos()[providerIndex]; - OutputProc("Currently configured channels:"); - OutputProc("Admin:"); - foreach (var I in info.AdminChannels) - OutputProc("\t" + I); - OutputProc("Watchdog:"); - foreach (var I in info.WatchdogChannels) - OutputProc("\t" + I); - OutputProc("Game:"); - foreach (var I in info.GameChannels) - OutputProc("\t" + I); - OutputProc("Developer:"); - foreach (var I in info.DevChannels) - OutputProc("\t" + I); - OutputProc("Chat bot is: " + (!info.Enabled ? "Disabled" : IRC.Connected(info.Provider) ? "Connected" : "Disconnected")); - return ExitCode.Normal; - } - } - class ChatEnableCommand : ConsoleCommand - { - readonly int providerIndex; - public ChatEnableCommand(ChatProvider pI) - { - Keyword = "enable"; - providerIndex = (int)pI; - } - - public override string GetHelpText() - { - return "Enables the chat bot"; - } - - protected override ExitCode Run(IList parameters) - { - var Chat = Instance.Chat; - var info = Chat.ProviderInfos()[providerIndex]; - info.Enabled = true; - var res = Chat.SetProviderInfo(info); - if (res != null) - { - OutputProc(res); - return ExitCode.ServerError; - } - return ExitCode.Normal; - } - } - class ChatDisableCommand : ConsoleCommand - { - readonly int providerIndex; - public ChatDisableCommand(ChatProvider pI) - { - Keyword = "disable"; - providerIndex = (int)pI; - } - - public override string GetHelpText() - { - return "Disables the chat bot"; - } - - protected override ExitCode Run(IList parameters) - { - var Chat = Instance.Chat; - var info = Chat.ProviderInfos()[providerIndex]; - info.Enabled = false; - var res = Chat.SetProviderInfo(info); - if (res != null) - { - OutputProc(res); - return ExitCode.ServerError; - } - return ExitCode.Normal; - } - } - - class IRCServerCommand : ConsoleCommand - { - public IRCServerCommand() - { - Keyword = "set-server"; - RequiredParameters = 1; - } - public override string GetArgumentString() - { - return ":"; - } - public override string GetHelpText() - { - return "Sets the IRC server"; - } - - protected override ExitCode Run(IList parameters) - { - var splits = parameters[0].Split(':'); - if(splits.Length < 2) - { - OutputProc("Invalid parameter!"); - return ExitCode.BadCommand; - } - var Chat = Instance.Chat; - var PI = new IRCSetupInfo(Chat.ProviderInfos()[(int)ChatProvider.IRC]) - { - URL = splits[0] - }; - try - { - PI.Port = Convert.ToUInt16(splits[1]); - } - catch - { - OutputProc("Invalid port number!"); - return ExitCode.BadCommand; - } - - var res = Chat.SetProviderInfo(PI); - OutputProc(res ?? "Success"); - return res == null ? ExitCode.Normal : ExitCode.ServerError; - } - } - - class DiscordSetTokenCommand : ConsoleCommand - { - public DiscordSetTokenCommand() - { - Keyword = "set-token"; - RequiredParameters = 1; - } - public override string GetArgumentString() - { - return ""; - } - public override string GetHelpText() - { - return "Sets the discord API bot token"; - } - protected override ExitCode Run(IList parameters) - { - var Chat = Instance.Chat; - var res = Chat.SetProviderInfo(new DiscordSetupInfo(Chat.ProviderInfos()[(int)ChatProvider.Discord]) { BotToken = parameters[0] }); - OutputProc(res ?? "Success"); - return res == null ? ExitCode.Normal : ExitCode.ServerError; - } - } -} \ No newline at end of file diff --git a/TGS.CommandLine/ConfigCommands.cs b/TGS.CommandLine/ConfigCommands.cs deleted file mode 100644 index 77c47bfaf9..0000000000 --- a/TGS.CommandLine/ConfigCommands.cs +++ /dev/null @@ -1,174 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using TGS.Interface; - -namespace TGS.CommandLine -{ - class ConfigCommand : RootCommand - { - public ConfigCommand() - { - Keyword = "config"; - Children = new Command[] { new ConfigDeleteCommand(), new ConfigServerDirectoryCommand(), new ConfigDownloadCommand(), new ConfigUploadCommand(), new ConfigListCommand() }; - } - public override string GetHelpText() - { - return "Manage settings"; - } - } - class ConfigDeleteCommand : ConsoleCommand - { - public ConfigDeleteCommand() - { - Keyword = "delete"; - RequiredParameters = 1; - } - - protected override ExitCode Run(IList parameters) - { - var res = Instance.Config.DeleteFile(parameters[0], out bool unauthorized); - if (res != null) - { - OutputProc(res); - return ExitCode.ServerError; - } - return ExitCode.Normal; - } - public override string GetArgumentString() - { - return ""; - } - public override string GetHelpText() - { - return "Deletes the specified file from the static tree."; - } - } - - class ConfigListCommand : ConsoleCommand - { - public ConfigListCommand() - { - Keyword = "list"; - } - - public override string GetHelpText() - { - return "Lists the contents of the server's static directory, optionally specifying a subdirectory"; - } - - public override string GetArgumentString() - { - return "[path to subdirectory]"; - } - protected override ExitCode Run(IList parameters) - { - var list = Instance.Config.ListStaticDirectory(parameters.Count > 0 ? parameters[0] : null, out string error, out bool unauthorized); - if(list == null) - { - OutputProc(error); - return ExitCode.ServerError; - } - if (list.Count == 0) - OutputProc("The static directory is empty!"); - else - foreach (var I in list) - OutputProc(I); - return ExitCode.Normal; - } - } - - class ConfigServerDirectoryCommand : ConsoleCommand - { - public ConfigServerDirectoryCommand() - { - Keyword = "server-dir"; - } - - protected override ExitCode Run(IList parameters) - { - OutputProc(Instance.ServerDirectory()); - return ExitCode.Normal; - } - - public override string GetHelpText() - { - return "Print the directory the server is installed in"; - } - } - - class ConfigDownloadCommand : ConsoleCommand - { - public ConfigDownloadCommand() - { - Keyword = "download"; - RequiredParameters = 2; - } - - protected override ExitCode Run(IList parameters) - { - var bytes = Instance.Config.ReadText(parameters[0], parameters.Count > 2 && parameters[2].ToLower() == "--repo", out string error, out bool unauthorized); - if(bytes == null) - { - OutputProc("Error: " + error); - return ExitCode.ServerError; - } - - try - { - File.WriteAllText(parameters[1], bytes); - } - catch (Exception e) - { - OutputProc("Error: " + e.ToString()); - } - - return ExitCode.Normal; - } - public override string GetArgumentString() - { - return " [--repo]"; - } - public override string GetHelpText() - { - return "Downloads the specified file from the static tree and writes it to out file. --repo will fetch it from the repository instead of the Static directory"; - } - } - - class ConfigUploadCommand : ConsoleCommand - { - public ConfigUploadCommand() - { - Keyword = "upload"; - RequiredParameters = 2; - } - - protected override ExitCode Run(IList parameters) - { - try - { - var res = Instance.Config.WriteText(parameters[0], File.ReadAllText(parameters[1]), null, out bool unauthorized); - if (res != null) - { - OutputProc("Error: " + res); - return ExitCode.ServerError; - } - } - catch (Exception e) - { - OutputProc("Error: " + e.ToString()); - } - return ExitCode.Normal; - } - - public override string GetArgumentString() - { - return " [--repo]"; - } - - public override string GetHelpText() - { - return "Uploads the specified file to the static tree from source file"; - } - } -} diff --git a/TGS.CommandLine/ConsoleCommand.cs b/TGS.CommandLine/ConsoleCommand.cs deleted file mode 100644 index 56b0381ee1..0000000000 --- a/TGS.CommandLine/ConsoleCommand.cs +++ /dev/null @@ -1,16 +0,0 @@ -using TGS.Interface; - -namespace TGS.CommandLine -{ - abstract class ConsoleCommand : Command - { - /// - /// The currently in use by the - /// - public static IServer Server; - /// - /// The currently in use by the - /// - public static IInstance Instance; - } -} diff --git a/TGS.CommandLine/DDCommands.cs b/TGS.CommandLine/DDCommands.cs deleted file mode 100644 index 41ad8fe94d..0000000000 --- a/TGS.CommandLine/DDCommands.cs +++ /dev/null @@ -1,314 +0,0 @@ -using System; -using System.Collections.Generic; -using TGS.Interface; - -namespace TGS.CommandLine -{ - class DDCommand : RootCommand - { - public DDCommand() - { - Keyword = "dd"; - Children = new Command[] { new DDStartCommand(), new DDStopCommand(), new DDRestartCommand(), new DDStatusCommand(), new DDAutostartCommand(), new DDPortCommand(), new DDSecurityCommand(), new DDWorldAnnounceCommand(), new DDWebclientCommand() }; - } - public override string GetHelpText() - { - return "Manage DreamDaemon"; - } - } - - class DDWorldAnnounceCommand : ConsoleCommand - { - public DDWorldAnnounceCommand() - { - Keyword = "announce"; - RequiredParameters = 1; - } - - public override string GetHelpText() - { - return "Sends a message all players on the server"; - } - - public override string GetArgumentString() - { - return ""; - } - - protected override ExitCode Run(IList parameters) - { - var res = Instance.DreamDaemon.WorldAnnounce(String.Join(" ", parameters)); - OutputProc(res ?? "Success!"); - return res == null ? ExitCode.Normal : ExitCode.ServerError; - } - } - - class DDStartCommand : ConsoleCommand - { - public DDStartCommand() - { - Keyword = "start"; - } - - public override string GetHelpText() - { - return "Starts the server and watchdog"; - } - - protected override ExitCode Run(IList parameters) - { - var res = Instance.DreamDaemon.Start(); - OutputProc(res ?? "Success!"); - return res == null ? ExitCode.Normal : ExitCode.ServerError; - } - } - - class DDStopCommand : ConsoleCommand - { - public DDStopCommand() - { - Keyword = "stop"; - } - public override string GetArgumentString() - { - return "[--graceful]"; - } - - public override string GetHelpText() - { - return "Stops the server and watchdog optionally waiting for the current round to end"; - } - - protected override ExitCode Run(IList parameters) - { - var DD = Instance.DreamDaemon; - if (parameters.Count > 0 && parameters[0].ToLower() == "--graceful") - { - if (DD.DaemonStatus() != DreamDaemonStatus.Online) - { - OutputProc("Error: The game is not currently running!"); - return ExitCode.ServerError; - } - DD.RequestStop(); - return ExitCode.Normal; - } - var res = DD.Stop(); - OutputProc(res ?? "Success!"); - return res == null ? ExitCode.Normal : ExitCode.ServerError; - } - } - class DDRestartCommand : ConsoleCommand - { - public DDRestartCommand() - { - Keyword = "restart"; - } - - public override string GetArgumentString() - { - return "[--graceful]"; - } - protected override ExitCode Run(IList parameters) - { - var DD = Instance.DreamDaemon; - if (parameters.Count > 0 && parameters[0].ToLower() == "--graceful") - { - if (DD.DaemonStatus() != DreamDaemonStatus.Online) - { - OutputProc("Error: The game is not currently running!"); - return ExitCode.ServerError; - } - DD.RequestRestart(); - return ExitCode.Normal; - } - var res = DD.Restart(); - OutputProc(res ?? "Success!"); - return res == null ? ExitCode.Normal : ExitCode.ServerError; - } - - public override string GetHelpText() - { - return "Restarts the server and watchdog optionally waiting for the current round to end"; - } - } - class DDStatusCommand : ConsoleCommand - { - public DDStatusCommand() - { - Keyword = "status"; - } - - public override string GetHelpText() - { - return "Gets the current status of the watchdog and server"; - } - - protected override ExitCode Run(IList parameters) - { - var DD = Instance.DreamDaemon; - OutputProc(DD.StatusString(true)); - if (DD.ShutdownInProgress()) - OutputProc("The server will shutdown once the current round completes."); - var pc = DD.PlayerCount(); - if (pc != -1) - OutputProc(pc + " connected clients"); - return ExitCode.Normal; - } - } - - class DDAutostartCommand : ConsoleCommand - { - public DDAutostartCommand() - { - Keyword = "autostart"; - RequiredParameters = 1; - } - - protected override ExitCode Run(IList parameters) - { - var DD = Instance.DreamDaemon; - switch (parameters[0].ToLower()) - { - case "on": - DD.SetAutostart(true); - break; - case "off": - DD.SetAutostart(false); - break; - case "check": - OutputProc("Autostart is: " + (DD.Autostart() ? "On" : "Off")); - break; - default: - OutputProc("Invalid parameter: " + parameters[0]); - return ExitCode.BadCommand; - } - return ExitCode.Normal; - } - - public override string GetArgumentString() - { - return ""; - } - public override string GetHelpText() - { - return "Change or check autostarting of the game server with the service"; - } - } - class DDWebclientCommand : ConsoleCommand - { - public DDWebclientCommand() - { - Keyword = "webclient"; - RequiredParameters = 1; - } - - protected override ExitCode Run(IList parameters) - { - var DD = Instance.DreamDaemon; - switch (parameters[0].ToLower()) - { - case "on": - DD.SetWebclient(true); - break; - case "off": - DD.SetWebclient(false); - break; - case "check": - OutputProc("Webclient is: " + (DD.Webclient() ? "Enabled" : "Disabled")); - break; - default: - OutputProc("Invalid parameter: " + parameters[0]); - return ExitCode.BadCommand; - } - return ExitCode.Normal; - } - - public override string GetArgumentString() - { - return ""; - } - public override string GetHelpText() - { - return "Change or check if the BYOND webclient is enabled for the game server"; - } - } - - class DDPortCommand : ConsoleCommand - { - public DDPortCommand() - { - Keyword = "set-port"; - RequiredParameters = 1; - } - - protected override ExitCode Run(IList parameters) - { - ushort port; - try - { - port = Convert.ToUInt16(parameters[0]); - } - catch - { - OutputProc("Invalid port number!"); - return ExitCode.BadCommand; - } - - Instance.DreamDaemon.SetPort(port); - return ExitCode.Normal; - } - - public override string GetArgumentString() - { - return ""; - } - - public override string GetHelpText() - { - return "Sets the port DreamDaemon will open the server on. Requires a server restart to apply and queues a graceful one up"; - } - } - - class DDSecurityCommand : ConsoleCommand - { - public DDSecurityCommand() - { - Keyword = "set-security"; - RequiredParameters = 1; - } - - protected override ExitCode Run(IList parameters) - { - DreamDaemonSecurity sec; - switch (parameters[0].ToLower()) - { - case "safe": - sec = DreamDaemonSecurity.Safe; - break; - case "ultra": - case "ultrasafe": - sec = DreamDaemonSecurity.Ultrasafe; - break; - case "trust": - case "trusted": - sec = DreamDaemonSecurity.Trusted; - break; - default: - OutputProc("Invalid security word!"); - return ExitCode.BadCommand; - } - Instance.DreamDaemon.SetSecurityLevel(sec); - return ExitCode.Normal; - } - - public override string GetArgumentString() - { - return ""; - } - - public override string GetHelpText() - { - return "Sets the visibility option for the DreamDaemon world"; - } - } -} diff --git a/TGS.CommandLine/DMCommands.cs b/TGS.CommandLine/DMCommands.cs deleted file mode 100644 index 47d9959e4f..0000000000 --- a/TGS.CommandLine/DMCommands.cs +++ /dev/null @@ -1,218 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using TGS.Interface; - -namespace TGS.CommandLine -{ - class DMCommand : RootCommand - { - public DMCommand() - { - Keyword = "dm"; - Children = new Command[] { new DMCompileCommand(), new DMInitializeCommand(), new DMStatusCommand(), new DMSetProjectNameCommand(), new DMCancelCommand() }; - } - public override string GetHelpText() - { - return "Manage compiling the server"; - } - } - - class DMCompileCommand : ConsoleCommand - { - public DMCompileCommand() - { - Keyword = "compile"; - } - - protected override ExitCode Run(IList parameters) - { - var DM = Instance.Compiler; - var stat = DM.GetStatus(); - if (stat != CompilerStatus.Initialized) - { - OutputProc("Error: Compiler is " + ((stat == CompilerStatus.Uninitialized) ? "unintialized!" : "busy with another task!")); - return ExitCode.ServerError; - } - - if (Instance.Byond.GetVersion(ByondVersion.Installed) == null) - { - Console.Write("Error: BYOND is not installed!"); - return ExitCode.ServerError; - } - - if (!DM.Compile()) - { - OutputProc("Error: Unable to start compilation!"); - var err = DM.CompileError(); - if (err != null) - OutputProc(err); - return ExitCode.ServerError; - } - OutputProc("Compile job started"); - if (parameters.Count > 0 && parameters[0] == "--wait") - { - do - { - Thread.Sleep(1000); - } while (DM.GetStatus() == CompilerStatus.Compiling); - var res = DM.CompileError(); - OutputProc(res ?? "Compilation successful"); - if (res != null) - return ExitCode.ServerError; - } - return ExitCode.Normal; - } - - public override string GetArgumentString() - { - return "[--wait]"; - } - - public override string GetHelpText() - { - return "Starts a compile/update job optionally waiting for completion"; - } - } - class DMStatusCommand : ConsoleCommand - { - public DMStatusCommand() - { - Keyword = "status"; - } - - void ShowError() - { - var error = Instance.Compiler.CompileError(); - if (error != null) - OutputProc("Last error: " + error); - } - - protected override ExitCode Run(IList parameters) - { - var DM = Instance.Compiler; - OutputProc(String.Format("Target Project: /{0}.dme", DM.ProjectName())); - Console.Write("Compilier is currently: "); - switch (DM.GetStatus()) - { - case CompilerStatus.Compiling: - OutputProc("Compiling..."); - break; - case CompilerStatus.Initialized: - OutputProc("Idle"); - ShowError(); - break; - case CompilerStatus.Initializing: - OutputProc("Setting up..."); - break; - case CompilerStatus.Uninitialized: - OutputProc("Uninitialized"); - ShowError(); - break; - default: - OutputProc("Seizing the means of production (This is an error)."); - return ExitCode.ServerError; - } - return ExitCode.Normal; - } - - public override string GetHelpText() - { - return "Get the current status of the compiler"; - } - } - - class DMSetProjectNameCommand : ConsoleCommand - { - public DMSetProjectNameCommand() - { - Keyword = "project-name"; - RequiredParameters = 1; - } - public override string GetArgumentString() - { - return ""; - } - - public override string GetHelpText() - { - return "Set the relative path of the .dme/.dmb to compile/run"; - } - - protected override ExitCode Run(IList parameters) - { - Instance.Compiler.SetProjectName(parameters[0]); - return ExitCode.Normal; - } - } - - class DMInitializeCommand : ConsoleCommand - { - public DMInitializeCommand() - { - Keyword = "initialize"; - } - - public override string GetArgumentString() - { - return "[--wait]"; - } - - public override string GetHelpText() - { - return "Starts an initialization job optionally waiting for completion"; - } - - protected override ExitCode Run(IList parameters) - { - var DM = Instance.Compiler; - var stat = DM.GetStatus(); - if (stat == CompilerStatus.Compiling || stat == CompilerStatus.Initializing) - { - OutputProc("Error: Compiler is " + ((stat == CompilerStatus.Initializing) ? "already initialized!" : " already running!")); - return ExitCode.ServerError; - } - if (!DM.Initialize()) - { - OutputProc("Error: Unable to start initialization!"); - var err = DM.CompileError(); - if (err != null) - OutputProc(err); - return ExitCode.ServerError; - } - OutputProc("Initialize job started"); - if (parameters.Count > 0 && parameters[0] == "--wait") - { - do - { - Thread.Sleep(1000); - } while (DM.GetStatus() == CompilerStatus.Initializing); - var res = DM.CompileError(); - OutputProc(res ?? "Initialization successful"); - if (res != null) - return ExitCode.ServerError; - } - return ExitCode.Normal; - } - } - - class DMCancelCommand : ConsoleCommand - { - public DMCancelCommand() - { - Keyword = "cancel"; - } - - protected override ExitCode Run(IList parameters) - { - var res = Instance.Compiler.Cancel(); - OutputProc(res ?? "Success!"); - return ExitCode.Normal; //because failing cancellation implys it's already cancelled - } - - public override string GetHelpText() - { - return "Cancels the current compilation job"; - } - } -} diff --git a/TGS.CommandLine/Program.cs b/TGS.CommandLine/Program.cs deleted file mode 100644 index 40e2ae456b..0000000000 --- a/TGS.CommandLine/Program.cs +++ /dev/null @@ -1,282 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using TGS.Interface; -using TGS.Interface.Components; - -namespace TGS.CommandLine -{ - class Program - { - static bool interactive = false, saidSrvVersion = false; - static IClient currentInterface; - static Command.ExitCode RunCommandLine(IList argsAsList) - { - //first lookup the connection string - bool badConnectionString = false; - for (var I = 0; I < argsAsList.Count - 1; ++I) { - var lowerarg = argsAsList[I].ToLower(); - if (lowerarg == "-c" || lowerarg == "--connect") - { - var connectionString = argsAsList[I + 1]; - var splits = connectionString.Split('@'); - var userpass = splits[0].Split(':'); - if (splits.Length != 2 || userpass.Length != 2) - { - badConnectionString = true; - break; - } - var addrport = splits[1].Split(':'); - if (addrport.Length != 2) - { - badConnectionString = true; - break; - } - var username = userpass[0]; - var password = userpass[1]; - var address = addrport[0]; - ushort port; - try - { - port = Convert.ToUInt16(addrport[1]); - } - catch - { - badConnectionString = true; - break; - } - if(String.IsNullOrWhiteSpace(username) || String.IsNullOrWhiteSpace(password) || String.IsNullOrWhiteSpace(address)) - { - badConnectionString = true; - break; - } - argsAsList.RemoveAt(I); - argsAsList.RemoveAt(I); - ReplaceInterface(new Client(new RemoteLoginInfo(address, port, username, password))); - break; - } - } - - if (badConnectionString) - { - Console.WriteLine("Remote connection usage: <-c/--connect> username:password@address:port"); - return Command.ExitCode.BadCommand; - } - - var res = currentInterface.ConnectionStatus(out string error); - if (!res.HasFlag(ConnectivityLevel.Connected)) - { - Console.WriteLine("Unable to connect to service: " + error); - Console.WriteLine("Remote connection usage: <-c/--connect> username:password@address:port"); - return Command.ExitCode.ConnectionError; - } - - if (!res.HasFlag(ConnectivityLevel.Authenticated)) - { - Console.WriteLine("Authentication error: Username/password/windows identity is not authorized!"); - return Command.ExitCode.ConnectionError; - } - - if (!SentVMMWarning && currentInterface.VersionMismatch(out error)) - { - SentVMMWarning = true; - Console.WriteLine(error); - } - else if (interactive && !saidSrvVersion) - { - Console.WriteLine("Connectd to service version: " + currentInterface.Server.Version); - saidSrvVersion = true; - } - - try - { - return new CLICommand(currentInterface).DoRun(argsAsList); - } - catch (Exception e) - { - Console.WriteLine("Error: " + e.ToString()); - return Command.ExitCode.ConnectionError; - }; - } - - static void ReplaceInterface(IClient I) - { - currentInterface = I; - ConsoleCommand.Server = I.Server; - ConsoleCommand.Instance = null; - saidSrvVersion = false; - } - - public static string ReadLineSecure() - { - string result = ""; - while (true) - { - ConsoleKeyInfo i = Console.ReadKey(true); - if (i.Key == ConsoleKey.Enter) - { - break; - } - else if (i.Key == ConsoleKey.Backspace) - { - if (result.Length > 0) - { - result = result.Substring(0, result.Length - 1); - Console.Write("\b \b"); - } - } - else - { - result += i.KeyChar; - Console.Write("*"); - } - } - Console.WriteLine(); - return result; - } - static bool SentVMMWarning = false; - static string AcceptedBadCert; - static bool BadCertificateInteractive(string message) - { - if (AcceptedBadCert == message) - return true; - Console.WriteLine(message); - Console.Write("Do you wish to continue? NOT RECCOMENDED! (y/N): "); - var result = Console.ReadLine().Trim().ToLower(); - if (result == "y" || result == "yes") - { - AcceptedBadCert = message; - return true; - } - return false; - } - - /// - /// Tries to set 's to , outputting appropriate messages - /// - /// The name of the to test - /// If , does not output on success - /// if the connection was made, otherwise - static bool CheckInstanceConnectivity(string instanceName, bool silentSuccess) - { - var res = currentInterface.Server.Instances.Where(x => x.Metadata.Name == instanceName).FirstOrDefault(); - if (res != null) - { - ConsoleCommand.Instance = res; - if (!silentSuccess) - Console.WriteLine("Successfully conected to instance!"); - return true; - } - Console.WriteLine("Unable to connect to instance! Does it exist?"); - return false; - } - - static int Main(string[] args) - { - ReplaceInterface(new Client()); - Command.OutputProcVar.Value = Console.WriteLine; - if (args.Length != 0) - { - var argsAsList = new List(args); - for (var I = 0; I < argsAsList.Count - 1; ++I) - { - if (argsAsList[I].ToLower() == "--instance") - { - if (!CheckInstanceConnectivity(args[I + 1], true)) - return (int)Command.ExitCode.ConnectionError; - argsAsList.RemoveRange(I, 2); - break; - } - else if (argsAsList[I].ToLower() == "--disable-ssl-verification") //im just not even going to document this because i hate it so much - { - argsAsList.RemoveAt(I); - --I; - Client.SetBadCertificateHandler(_ => false); - } - } - return (int)RunCommandLine(argsAsList); - } - //interactive mode - Client.SetBadCertificateHandler(BadCertificateInteractive); - Console.WriteLine("Type 'instance' to connect to a server instance"); - Console.WriteLine("Type 'remote' to connect to a remote service"); - while (true) - { - Console.Write("Enter command: "); - var NextCommand = Console.ReadLine(); - switch (NextCommand.ToLower()) - { - case "instance": - Console.Write("Enter instance name: "); - CheckInstanceConnectivity(Console.ReadLine(), false); - break; - case "remote": - SentVMMWarning = false; - Console.Write("Enter server address: "); - var address = Console.ReadLine(); - Console.Write("Enter server port: "); - ushort port; - try{ - port = Convert.ToUInt16(Console.ReadLine()); - } - catch - { - Console.WriteLine("Error: Bad port!"); - break; - } - Console.Write("Enter username: "); - var username = Console.ReadLine(); - Console.Write("Enter password: "); - var password = ReadLineSecure(); - ReplaceInterface(new Client(new RemoteLoginInfo(address, port, username, password))); - var res = currentInterface.ConnectionStatus(out string error); - if (!res.HasFlag(ConnectivityLevel.Connected)) - { - Console.WriteLine("Unable to connect: " + error); - ReplaceInterface(new Client()); - } - else if (!res.HasFlag(ConnectivityLevel.Authenticated)) - { - Console.WriteLine("Authentication error: Username/password/windows identity is not authorized! Returning to local mode..."); - ReplaceInterface(new Client()); - } - else - { - Console.WriteLine("Connected remotely"); - Console.WriteLine("Type 'instance' to connect to a server instance"); - if (currentInterface.VersionMismatch(out error)) - { - SentVMMWarning = true; - Console.WriteLine(error); - } - Console.WriteLine("Type 'disconnect' to return to local mode"); - } - break; - case "disconnect": - SentVMMWarning = false; - ReplaceInterface(new Client()); - Console.WriteLine("Switch to local mode"); - break; - case "quit": - case "exit": - return (int)Command.ExitCode.Normal; - case "debug-upgrade": - currentInterface.Server.Management.PrepareForUpdate(); - return (int)Command.ExitCode.Normal; - default: - //linq voodoo to get quoted strings - var formattedCommand = NextCommand.Split('"') - .Select((element, index) => index % 2 == 0 // If even index - ? element.Split(new[] { ' ' }, StringSplitOptions.RemoveEmptyEntries) // Split the item - : new string[] { element }) // Keep the entire item - .SelectMany(element => element).ToList(); - - formattedCommand = formattedCommand.Select(x => x.Trim()).ToList(); - formattedCommand.Remove(""); - RunCommandLine(formattedCommand); - break; - } - } - } - } -} diff --git a/TGS.CommandLine/Properties/AssemblyInfo.cs b/TGS.CommandLine/Properties/AssemblyInfo.cs deleted file mode 100644 index ba9129eaba..0000000000 --- a/TGS.CommandLine/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.Reflection; -using System.Runtime.CompilerServices; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("TGStation Server Commandline")] -[assembly: AssemblyDescription("CLI for the TG Station Server Service")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("9ad1f086-a83e-4d14-a844-58a9471106b6")] diff --git a/TGS.CommandLine/RepoCommands.cs b/TGS.CommandLine/RepoCommands.cs deleted file mode 100644 index 30a7a0613d..0000000000 --- a/TGS.CommandLine/RepoCommands.cs +++ /dev/null @@ -1,407 +0,0 @@ -using System; -using System.Collections.Generic; -using TGS.Interface; - -namespace TGS.CommandLine -{ - class RepoCommand : RootCommand - { - public RepoCommand() - { - Keyword = "repo"; - Children = new Command[] { new RepoSetupCommand(), new RepoUpdateCommand(), new RepoGenChangelogCommand(), new RepoPushChangelogCommand(), new RepoSetEmailCommand(), new RepoSetNameCommand(), new RepoMergePRCommand(), new RepoListPRsCommand(), new RepoStatusCommand(), new RepoListBackupsCommand(), new RepoCheckoutCommand(), new RepoResetCommand(), new RepoUpdateJsonCommand(), new RepoSetPushTestmergeCommitsCommand() }; - } - public override string GetHelpText() - { - return "Manage the git repository"; - } - } - - class RepoSetPushTestmergeCommitsCommand : ConsoleCommand - { - public RepoSetPushTestmergeCommitsCommand() - { - Keyword = "push-testmerges"; - RequiredParameters = 1; - } - public override string GetHelpText() - { - return "Set if a temporary branch is to the remote when we make testmerge commits and then delete it"; - } - - public override string GetArgumentString() - { - return ""; - } - - protected override ExitCode Run(IList parameters) - { - switch (parameters[0].ToLower()) - { - case "on": - Instance.Repository.SetPushTestmergeCommits(true); - break; - case "off": - Instance.Repository.SetPushTestmergeCommits(false); - break; - default: - OutputProc("Invalid option!"); - return ExitCode.BadCommand; - } - return ExitCode.Normal; - } - } - - class RepoUpdateJsonCommand : ConsoleCommand - { - public RepoUpdateJsonCommand() - { - Keyword = "update-json"; - } - - public override string GetHelpText() - { - return "Updates the cached TGS3.json with the one from the repo. Compilation is blocked if these two do not match."; - } - - protected override ExitCode Run(IList parameters) - { - var res = Instance.Repository.UpdateTGS3Json(); - if (res != null) - { - OutputProc(res); - return ExitCode.ServerError; - } - return ExitCode.Normal; - } - } - - class RepoSetupCommand : ConsoleCommand - { - public RepoSetupCommand() - { - Keyword = "setup"; - RequiredParameters = 1; - } - protected override ExitCode Run(IList parameters) - { - var res = Instance.Repository.Setup(parameters[0], parameters.Count > 1 ? parameters[1] : "master"); - if (res != null) - { - OutputProc("Error: " + res); - return ExitCode.ServerError; - } - OutputProc("Setting up repo. This will take a while..."); - return ExitCode.Normal; - } - public override string GetArgumentString() - { - return " [branchname]"; - } - public override string GetHelpText() - { - return "Clean up everything and clones the repo at git-url with optional branch name"; - } - } - - class RepoStatusCommand : ConsoleCommand - { - public RepoStatusCommand() - { - Keyword = "status"; - } - protected override ExitCode Run(IList parameters) - { - var Repo = Instance.Repository; - var busy = Repo.OperationInProgress(); - if (!busy) - { - OutputProc("Repo: Idle"); - var head = Repo.GetHead(false, out string error); - if (head == null) - head = "Error: " + error; - var remotehead = Repo.GetHead(true, out error); - if (remotehead == null) - remotehead = "Error: " + error; - var branch = Repo.GetBranch(out error); - if (branch == null) - branch = "Error: " + error; - var remote = Repo.GetRemote(out error); - if (remote == null) - remote = "Error: " + error; - OutputProc("Remote: " + remote + " (" + remotehead + ")"); - OutputProc("Branch: " + branch); - OutputProc("HEAD: " + head); - OutputProc("Push testmerge commits: " + (Repo.PushTestmergeCommits() ? "ON" : "OFF")); - OutputProc(String.Format("Committer Identity: {0} ({1})", Repo.GetCommitterName(), Repo.GetCommitterEmail())); - } - else - { - OutputProc("Repo: Busy"); - var progress = Repo.CheckoutProgress(); - if (progress != -1) - { - var eqs = ""; - for (var I = 0; I < progress / 10; ++I) - eqs += "="; - var dshs = ""; - for (var I = 0; I < 10 - (progress / 10); ++I) - eqs += "-"; - OutputProc(String.Format("Progress: [{0}{1}] {2}%", eqs, dshs, progress)); - } - } - return ExitCode.Normal; - } - public override string GetHelpText() - { - return "Shows the busy status of the repo, remote, branch, and HEAD information"; - } - } - - class RepoResetCommand : ConsoleCommand - { - public RepoResetCommand() - { - Keyword = "reset"; - } - protected override ExitCode Run(IList parameters) - { - var result = Instance.Repository.Reset(parameters.Count > 0 && parameters[0].ToLower() == "--origin"); - OutputProc(result ?? "Success!"); - return result == null ? ExitCode.Normal : ExitCode.ServerError; - } - public override string GetArgumentString() - { - return "[--origin]"; - } - public override string GetHelpText() - { - return "Hard resets the repo. If a target is specified, the current branch is reset to that branch"; - } - } - - class RepoUpdateCommand : ConsoleCommand - { - public RepoUpdateCommand() - { - Keyword = "update"; - RequiredParameters = 1; - } - protected override ExitCode Run(IList parameters) - { - bool hard; - switch (parameters[0].ToLower()) - { - case "hard": - hard = true; - break; - case "merge": - hard = false; - break; - default: - OutputProc("Invalid parameter: " + parameters[0]); - return ExitCode.BadCommand; - } - var res = Instance.Repository.Update(hard); - OutputProc(res ?? "Success"); - return res == null ? ExitCode.Normal : ExitCode.ServerError; - } - public override string GetHelpText() - { - return "Updates the current branch the repo is on either via a merge or hard reset"; - } - public override string GetArgumentString() - { - return ""; - } - } - class RepoGenChangelogCommand : ConsoleCommand - { - public RepoGenChangelogCommand() - { - Keyword = "gen-changelog"; - } - protected override ExitCode Run(IList parameters) - { - var result = Instance.Repository.GenerateChangelog(out string error); - OutputProc(error ?? "Success!"); - if (result != null) - OutputProc(result); - return error == null ? ExitCode.Normal : ExitCode.ServerError; - } - - public override string GetHelpText() - { - return "Compiles the html changelog"; - } - } - class RepoPushChangelogCommand : ConsoleCommand - { - public RepoPushChangelogCommand() - { - Keyword = "push-changelog"; - } - protected override ExitCode Run(IList parameters) - { - var result = Instance.Repository.SynchronizePush(); - if(result != null) - OutputProc(result); - return result == null ? ExitCode.Normal : ExitCode.ServerError; - } - - public override string GetHelpText() - { - return "Pushes the html changelog if the SSH authentication is configured correctly"; - } - } - class RepoSetEmailCommand : ConsoleCommand - { - public RepoSetEmailCommand() - { - Keyword = "set-email"; - RequiredParameters = 1; - } - protected override ExitCode Run(IList parameters) - { - Instance.Repository.SetCommitterEmail(parameters[0]); - return ExitCode.Normal; - } - - public override string GetArgumentString() - { - return ""; - } - public override string GetHelpText() - { - return "Set the e-mail used for commits"; - } - } - class RepoSetNameCommand : ConsoleCommand - { - public RepoSetNameCommand() - { - Keyword = "set-name"; - RequiredParameters = 1; - } - protected override ExitCode Run(IList parameters) - { - Instance.Repository.SetCommitterName(parameters[0]); - return ExitCode.Normal; - } - public override string GetArgumentString() - { - return ""; - } - public override string GetHelpText() - { - return "Set the name used for commits"; - } - } - - class RepoMergePRCommand : ConsoleCommand - { - public RepoMergePRCommand() - { - Keyword = "merge-pr"; - RequiredParameters = 1; - } - - protected override ExitCode Run(IList parameters) - { - ushort PR; - try - { - PR = Convert.ToUInt16(parameters[0]); - } - catch - { - OutputProc("Invalid PR Number!"); - return ExitCode.BadCommand; - } - var res = Instance.Repository.MergePullRequest(PR); - OutputProc(res ?? "Success"); - return res == null ? ExitCode.Normal : ExitCode.ServerError; - } - - public override string GetArgumentString() - { - return ""; - } - public override string GetHelpText() - { - return "Merge the given pull request from the origin repository into the current branch. Only supported with github remotes"; - } - } - - class RepoListPRsCommand : ConsoleCommand - { - public RepoListPRsCommand() - { - Keyword = "list-prs"; - } - public override string GetHelpText() - { - return "Lists currently merge pull requests"; - } - protected override ExitCode Run(IList parameters) - { - var data = Instance.Repository.MergedPullRequests(out string error); - if (data == null) - { - OutputProc(error); - return ExitCode.ServerError; - } - if (data.Count == 0) - OutputProc("None!"); - else - foreach (var I in data) - OutputProc(String.Format("#{0}: {2} by {3} at commit {1}", I.Number, I.Sha, I.Title, I.Author)); - return ExitCode.Normal; - } - } - - class RepoListBackupsCommand : ConsoleCommand - { - public RepoListBackupsCommand() - { - Keyword = "list-backups"; - } - public override string GetHelpText() - { - return "Lists backup tags created by compilation"; - } - protected override ExitCode Run(IList parameters) - { - var data = Instance.Repository.ListBackups(out string error); - if (data == null) - { - OutputProc(error); - return ExitCode.ServerError; - } - if (data.Count == 0) - OutputProc("None!"); - else - foreach (var I in data) - OutputProc(String.Format("{0} at commit {1}", I.Key, I.Value)); - return ExitCode.Normal; - } - } - class RepoCheckoutCommand : ConsoleCommand - { - public RepoCheckoutCommand() - { - Keyword = "checkout"; - RequiredParameters = 1; - } - public override string GetHelpText() - { - return "Checks out the targeted object"; - } - protected override ExitCode Run(IList parameters) - { - var res = Instance.Repository.Checkout(parameters[0]); - OutputProc(res ?? "Success"); - return res == null ? ExitCode.Normal : ExitCode.ServerError; - } - } -} diff --git a/TGS.CommandLine/RootCommands.cs b/TGS.CommandLine/RootCommands.cs deleted file mode 100644 index 309ebca351..0000000000 --- a/TGS.CommandLine/RootCommands.cs +++ /dev/null @@ -1,191 +0,0 @@ -using System; -using System.Collections.Generic; -using TGS.Interface; - -namespace TGS.CommandLine -{ - class CLICommand : RootCommand - { - public CLICommand(IClient I) - { - var tmp = ConsoleCommand.Instance != null ? new List { new UpdateCommand(), new TestmergeCommand(), new RepoCommand(), new BYONDCommand(), new DMCommand(), new DDCommand(), new ConfigCommand(), new IRCCommand(), new DiscordCommand(), new AutoUpdateCommand(), new SetAutoUpdateCommand() } : new List(); - if (ConsoleCommand.Instance?.Administration != null) - tmp.Add(new AdminCommand()); - if (ConsoleCommand.Server.Management != null) - tmp.Add(new ServiceCommand()); - Children = tmp.ToArray(); - } - public override void PrintHelp() - { - OutputProc("/tg/station 13 Server Command Line"); - base.PrintHelp(); - } - } - class AutoUpdateCommand : ConsoleCommand - { - public AutoUpdateCommand() - { - Keyword = "auto-update"; - } - - public override string GetHelpText() - { - return "Get the interval in minutes that the server automatically updates"; - } - - protected override ExitCode Run(IList parameters) - { - var res = Instance.Repository.AutoUpdateInterval(); - OutputProc(res == 0 ? "OFF" : String.Format("Auto updating every {0} minutes", res)); - return ExitCode.Normal; - } - } - class SetAutoUpdateCommand : ConsoleCommand - { - public SetAutoUpdateCommand() - { - Keyword = "set-auto-update"; - RequiredParameters = 1; - } - - public override string GetArgumentString() - { - return ""; - } - - public override string GetHelpText() - { - return "Set the interval in minutes that the server automatically updates"; - } - - protected override ExitCode Run(IList parameters) - { - ulong NewInterval; - if (parameters[0].ToLower() == "off") - NewInterval = 0; - else - try - { - NewInterval = Convert.ToUInt64(parameters[0]); - } - catch - { - OutputProc("Invalid interval specified!"); - return ExitCode.BadCommand; - } - - Instance.Repository.SetAutoUpdateInterval(NewInterval); - return ExitCode.Normal; - } - - } - class UpdateCommand : ConsoleCommand - { - public UpdateCommand() - { - Keyword = "update"; - RequiredParameters = 1; - } - protected override ExitCode Run(IList parameters) - { - var gen_cl = parameters.Count > 1 && parameters[1].ToLower() == "--cl"; - var Repo = Instance.Repository; - switch (parameters[0].ToLower()) - { - case "hard": - var res = Repo.Update(true); - if (res != null) - { - OutputProc(res); - return ExitCode.ServerError; - } - break; - case "merge": - res = Repo.Update(false); - if (res != null) - { - OutputProc(res); - return ExitCode.ServerError; - } - break; - default: - OutputProc("Please specify hard or merge"); - return ExitCode.BadCommand; - } - if (gen_cl) - { - Repo.GenerateChangelog(out string res); - if (res != null) - OutputProc(res); - else - { - res = Repo.SynchronizePush(); - if (res != null) - OutputProc(res); - } - } - var resu = Instance.Compiler.Compile(true); - OutputProc(resu ? "Compilation started!" : "Compilation could not be started!"); - return resu ? ExitCode.Normal : ExitCode.ServerError; - } - - public override string GetArgumentString() - { - return " [--cl]"; - } - - public override string GetHelpText() - { - return "Updates the server fully, optionally generating and pushing a changelog. Runs asynchronously once compilation starts"; - } - } - - class TestmergeCommand : ConsoleCommand - { - public TestmergeCommand() - { - Keyword = "testmerge"; - RequiredParameters = 1; - } - protected override ExitCode Run(IList parameters) - { - ushort tm; - try - { - tm = Convert.ToUInt16(parameters[0]); - if (tm == 0) - throw new Exception(); - } - catch - { - OutputProc("Invalid tesmerge #: " + parameters[0]); - return ExitCode.BadCommand; - } - var Repo = Instance.Repository; - var res = Repo.MergePullRequest(tm); - if (res != null) - { - OutputProc(res); - return ExitCode.ServerError; - } - Repo.GenerateChangelog(out res); - if (res != null) - { - OutputProc(res); - return ExitCode.ServerError; - } - var resu = Instance.Compiler.Compile(true); - OutputProc(resu ? "Compilation started!" : "Compilation could not be started!"); - return resu ? ExitCode.Normal : ExitCode.ServerError; - } - public override string GetArgumentString() - { - return ""; - } - - public override string GetHelpText() - { - return "Merges the specified pull request and updates the server"; - } - } -} diff --git a/TGS.CommandLine/ServiceCommands.cs b/TGS.CommandLine/ServiceCommands.cs deleted file mode 100644 index d1a3274d60..0000000000 --- a/TGS.CommandLine/ServiceCommands.cs +++ /dev/null @@ -1,434 +0,0 @@ -using System; -using System.Collections.Generic; -using TGS.Interface; -using TGS.Interface.Components; - -namespace TGS.CommandLine -{ - /// - /// Used for managing the components - /// - class ServiceCommand : RootCommand - { - /// - /// Construct a - /// - public ServiceCommand() - { - Keyword = "service"; - Children = new Command[] { new ServiceCreateInstanceCommand(), new ServiceDetachInstanceCommand(), new ServiceEnableInstanceCommand(), new ServiceImportInstanceCommand(), new ServiceListInstancesCommand(), new ServicePythonPathCommand(), new ServiceSetPythonPathCommand(), new ServiceSetRemoteAccessPortCommand(), new ServiceRemoteAccessPortCommand(), new ServiceDisableInstanceCommand(), new ServiceRenameInstanceCommand() }; - } - - /// - public override string GetHelpText() - { - return "Manage service wide settings"; - } - } - - /// - /// Command for calling - /// - class ServiceCreateInstanceCommand : ConsoleCommand - { - /// - /// Construct a - /// - public ServiceCreateInstanceCommand() - { - Keyword = "create-instance"; - RequiredParameters = 2; - } - - /// - public override string GetHelpText() - { - return "Creates a new instance at the given path"; - } - - /// - public override string GetArgumentString() - { - return " "; - } - - /// - protected override ExitCode Run(IList parameters) - { - var res = Server.InstanceManager.CreateInstance(parameters[0], parameters[1]); - if (res != null) - { - OutputProc(res); - return ExitCode.ServerError; - } - return ExitCode.Normal; - } - } - - /// - /// Command for calling - /// - class ServiceListInstancesCommand : ConsoleCommand - { - /// - /// Construct a - /// - public ServiceListInstancesCommand() - { - Keyword = "list-instances"; - } - - /// - public override string GetHelpText() - { - return "Lists all instances"; - } - - /// - protected override ExitCode Run(IList parameters) - { - foreach (var I in Server.Instances) - OutputProc(I.ToString()); - return ExitCode.Normal; - } - } - - /// - /// Command for calling - /// - class ServiceDetachInstanceCommand : ConsoleCommand - { - /// - /// Construct a - /// - public ServiceDetachInstanceCommand() - { - Keyword = "detach-instance"; - RequiredParameters = 1; - } - - /// - public override string GetHelpText() - { - return "Detaches an instance"; - } - - /// - public override string GetArgumentString() - { - return ""; - } - - /// - protected override ExitCode Run(IList parameters) - { - var res = Server.InstanceManager.DetachInstance(parameters[0]); - if (res != null) - { - OutputProc(res); - return ExitCode.ServerError; - } - return ExitCode.Normal; - } - } - - /// - /// Command for calling - /// - class ServiceImportInstanceCommand : ConsoleCommand - { - /// - /// Construct a - /// - public ServiceImportInstanceCommand() - { - Keyword = "import-instance"; - RequiredParameters = 1; - } - - /// - public override string GetHelpText() - { - return "Imports an instance (Chat settings will be lost if they are from a different windows installation)"; - } - - /// - public override string GetArgumentString() - { - return ""; - } - - /// - protected override ExitCode Run(IList parameters) - { - var res = Server.InstanceManager.ImportInstance(parameters[0]); - if (res != null) - { - OutputProc(res); - return ExitCode.ServerError; - } - return ExitCode.Normal; - } - } - - /// - /// Command for calling - /// - class ServicePythonPathCommand : ConsoleCommand - { - /// - /// Construct a - /// - public ServicePythonPathCommand() - { - Keyword = "python"; - } - - /// - public override string GetHelpText() - { - return "Displays configured path the service uses for python"; - } - - /// - protected override ExitCode Run(IList parameters) - { - var res = Server.Management.PythonPath(); - if (res != null) - { - OutputProc(res); - return ExitCode.ServerError; - } - return ExitCode.Normal; - } - } - - /// - /// Command for calling - /// - class ServiceSetPythonPathCommand : ConsoleCommand - { - /// - /// Construct a - /// - public ServiceSetPythonPathCommand() - { - Keyword = "set-python"; - RequiredParameters = 1; - } - - /// - public override string GetHelpText() - { - return "Sets the path to the python installation"; - } - - /// - public override string GetArgumentString() - { - return ""; - } - - /// - protected override ExitCode Run(IList parameters) - { - Server.Management.SetPythonPath(parameters[0]); - return ExitCode.Normal; - } - } - - /// - /// Command for calling with a parameter - /// - class ServiceEnableInstanceCommand : ConsoleCommand - { - /// - /// Construct a - /// - public ServiceEnableInstanceCommand() - { - Keyword = "enable-instance"; - RequiredParameters = 1; - } - - /// - public override string GetHelpText() - { - return "Enables the specified instance"; - } - - /// - public override string GetArgumentString() - { - return ""; - } - - /// - protected override ExitCode Run(IList parameters) - { - var res = Server.InstanceManager.SetInstanceEnabled(parameters[0], true); - if (res != null) - { - OutputProc(res); - return ExitCode.ServerError; - } - return ExitCode.Normal; - } - } - - /// - /// Command for calling with a parameter - /// - class ServiceDisableInstanceCommand : ConsoleCommand - { - /// - /// Construct a - /// - public ServiceDisableInstanceCommand() - { - Keyword = "disable-instance"; - RequiredParameters = 1; - } - - /// - public override string GetHelpText() - { - return "Disables the specified instance"; - } - - /// - public override string GetArgumentString() - { - return ""; - } - - /// - protected override ExitCode Run(IList parameters) - { - var res = Server.InstanceManager.SetInstanceEnabled(parameters[0], false); - if (res != null) - { - OutputProc(res); - return ExitCode.ServerError; - } - return ExitCode.Normal; - } - } - - /// - /// Command for calling - /// - class ServiceRemoteAccessPortCommand : ConsoleCommand - { - /// - /// Construct a - /// - public ServiceRemoteAccessPortCommand() - { - Keyword = "port"; - } - - /// - public override string GetHelpText() - { - return "Displays the service's remote access port"; - } - - /// - protected override ExitCode Run(IList parameters) - { - OutputProc(Server.Management.RemoteAccessPort().ToString()); - return ExitCode.Normal; - } - } - - /// - /// Command for calling - /// - class ServiceSetRemoteAccessPortCommand : ConsoleCommand - { - /// - /// Construct a - /// - public ServiceSetRemoteAccessPortCommand() - { - Keyword = "set-port"; - RequiredParameters = 1; - } - - /// - public override string GetHelpText() - { - return "Sets the service's remote access port"; - } - - /// - public override string GetArgumentString() - { - return ""; - } - - /// - protected override ExitCode Run(IList parameters) - { - ushort port; - try - { - port = Convert.ToUInt16(parameters[0]); - } - catch - { - OutputProc("Invalid port number!"); - return ExitCode.BadCommand; - } - - var res = Server.Management.SetRemoteAccessPort(port); - if (res != null) - { - OutputProc(res); - return ExitCode.ServerError; - } - OutputProc("Change will be applied after service restart"); - return ExitCode.Normal; - } - } - - /// - /// Command for calling - /// - class ServiceRenameInstanceCommand : ConsoleCommand - { - /// - /// Construct a - /// - public ServiceRenameInstanceCommand() - { - Keyword = "rename-instance"; - RequiredParameters = 1; - } - - /// - public override string GetHelpText() - { - return "Renames an instance. Will temporarily disable the instance if it is active"; - } - - /// - public override string GetArgumentString() - { - return " "; - } - - /// - protected override ExitCode Run(IList parameters) - { - var res = Server.InstanceManager.RenameInstance(parameters[0], parameters[1]); - if (res != null) - { - OutputProc(res); - return ExitCode.ServerError; - } - return ExitCode.Normal; - } - } -} diff --git a/TGS.CommandLine/TGS.CommandLine.csproj b/TGS.CommandLine/TGS.CommandLine.csproj deleted file mode 100644 index d0369c4c5d..0000000000 --- a/TGS.CommandLine/TGS.CommandLine.csproj +++ /dev/null @@ -1,74 +0,0 @@ - - - - - Debug - AnyCPU - {89191F69-B18E-4B59-B72E-E12F9B6811A0} - Exe - TGS.CommandLine - TGCommandLine - v4.5.2 - 512 - true - - - - - - tgs.ico - - - true - bin\Debug\ - DEBUG;TRACE - full - AnyCPU - prompt - MinimumRecommendedRules.ruleset - true - - - bin\Release\ - TRACE - bin\x86\Release\TGS.CommandLine.xml - true - true - pdbonly - AnyCPU - prompt - MinimumRecommendedRules.ruleset - true - - - false - - - - - - - - - - - - - - - - - - - - - - {ac4e7e8b-f83a-481c-a8b0-8fa4e8ae59ab} - TGS.Interface - - - - - - - \ No newline at end of file diff --git a/TGS.ControlPanel/App.config b/TGS.ControlPanel/App.config deleted file mode 100644 index e5a8ddbf55..0000000000 --- a/TGS.ControlPanel/App.config +++ /dev/null @@ -1,71 +0,0 @@ - - - - - -
-
-
- - - - - - - - - - 0 - - - 0 - - - True - - - 0 - - - False - - - - - - - - - - - 0 - - - - - https://github.com/tgstation/tgstation.git - - - tgstation - - - 2337 - - - master - - - False - - - C:\Program Files (x86)\BYOND\bin - - - tgstation-server - - - tgstation-server@users.noreply.github.com - - - - \ No newline at end of file diff --git a/TGS.ControlPanel/ControlPanel/ByondPage.cs b/TGS.ControlPanel/ControlPanel/ByondPage.cs deleted file mode 100644 index f5ec5546d7..0000000000 --- a/TGS.ControlPanel/ControlPanel/ByondPage.cs +++ /dev/null @@ -1,100 +0,0 @@ -using System; -using System.Windows.Forms; -using TGS.Interface; -using TGS.Interface.Components; - -namespace TGS.ControlPanel -{ - partial class ControlPanel - { - string lastReadError = null; - void InitBYONDPage() - { - var BYOND = Instance.Byond; - var CV = BYOND.GetVersion(ByondVersion.Installed); - if (CV == null) - CV = BYOND.GetVersion(ByondVersion.Staged); - if (CV != null) - { - var splits = CV.Split('.'); - if(splits.Length == 2) - { - try - { - var Major = Convert.ToInt32(splits[0]); - var Minor = Convert.ToInt32(splits[1]); - MajorVersionNumeric.Value = Major; - MinorVersionNumeric.Value = Minor; - } - catch { } - } - } - - var latestVer = BYOND.GetVersion(ByondVersion.Latest); - LatestVersionLabel.Text = latestVer; - - try - { - var splits = latestVer.Split('.'); - var maj = Convert.ToInt32(splits[0]); - MinorVersionNumeric.Value = Convert.ToInt32(splits[1]); - MajorVersionNumeric.Value = maj; - } - catch { } - } - private void UpdateButton_Click(object sender, EventArgs e) - { - UpdateBYONDButtons(); - if (!Instance.Byond.UpdateToVersion((int)MajorVersionNumeric.Value, (int)MinorVersionNumeric.Value)) - MessageBox.Show("Unable to begin update, there is another operation in progress."); - } - - private void BYONDRefreshButton_Click(object sender, EventArgs e) - { - UpdateBYONDButtons(); - } - void UpdateBYONDButtons() - { - var BYOND = Instance.Byond; - - VersionLabel.Text = BYOND.GetVersion(ByondVersion.Installed) ?? "Not Installed"; - - StagedVersionTitle.Visible = false; - StagedVersionLabel.Visible = false; - switch (BYOND.CurrentStatus()) - { - case ByondStatus.Idle: - case ByondStatus.Starting: - StatusLabel.Text = "Idle"; - UpdateButton.Enabled = true; - break; - case ByondStatus.Downloading: - StatusLabel.Text = "Downloading..."; - UpdateButton.Enabled = false; - break; - case ByondStatus.Staging: - StatusLabel.Text = "Staging..."; - UpdateButton.Enabled = false; - break; - case ByondStatus.Staged: - StagedVersionTitle.Visible = true; - StagedVersionLabel.Visible = true; - StagedVersionLabel.Text = BYOND.GetVersion(ByondVersion.Staged) ?? "Unknown"; - StatusLabel.Text = "Staged and waiting for BYOND to shutdown..."; - UpdateButton.Enabled = true; - break; - case ByondStatus.Updating: - StatusLabel.Text = "Applying update..."; - UpdateButton.Enabled = false; - break; - } - var error = Instance.Byond.GetError(); - if (error != lastReadError) - { - lastReadError = error; - if (error != null) - MessageBox.Show("An error occurred: " + lastReadError); - } - } - } -} diff --git a/TGS.ControlPanel/ControlPanel/ChatPage.cs b/TGS.ControlPanel/ControlPanel/ChatPage.cs deleted file mode 100644 index d512d0d6ef..0000000000 --- a/TGS.ControlPanel/ControlPanel/ChatPage.cs +++ /dev/null @@ -1,218 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Windows.Forms; -using TGS.Interface; -using TGS.Interface.Components; - -namespace TGS.ControlPanel -{ - partial class ControlPanel - { - bool updatingChat = false; - - ChatProvider ModifyingProvider - { - get { return (ChatProvider)Properties.Settings.Default.LastChatProvider; } - set { Properties.Settings.Default.LastChatProvider = (int)value; } - } - - void LoadChatPage() - { - updatingChat = true; - var Chat = Instance.Chat; - var PI = Chat.ProviderInfos()[(int)ModifyingProvider]; - ChatAdminsTextBox.Visible = true; - IRCModesComboBox.Visible = false; - switch (ModifyingProvider) - { - case ChatProvider.Discord: - var DPI = new DiscordSetupInfo(PI); - DiscordProviderSwitch.Select(); - AuthField1.Text = DPI.BotToken; //it's invisible so whatever - AuthField1Title.Text = "Bot Token:"; - AuthField2.Visible = false; - AuthField2Title.Visible = false; - ChatServerText.Visible = false; - ChatPortSelector.Visible = false; - ChatServerTitle.Visible = false; - ChatPortTitle.Visible = false; - ChatNicknameText.Visible = false; - ChatNicknameTitle.Visible = false; - - ChatAdminsTitle.Text = String.Format("Admin {0} IDs:", DPI.AdminsAreSpecial ? "Role" : "User"); - ChannelsTitle.Text = "Broadcast/Listening Channel IDs:"; - AdminModeNormal.Text = "User IDs"; - AdminModeSpecial.Text = "Role IDs"; - break; - case ChatProvider.IRC: - var IRC = new IRCSetupInfo(PI); - IRCProviderSwitch.Select(); - AuthField1.Text = IRC.AuthTarget; - AuthField2.Text = IRC.AuthMessage; - AuthField2.Visible = true; - AuthField2Title.Visible = true; - AuthField1Title.Text = "Auth Target:"; - AuthField2Title.Text = "Auth Message:"; - ChatServerText.Visible = true; - ChatPortSelector.Visible = true; - ChatServerTitle.Visible = true; - ChatPortTitle.Visible = true; - ChatServerText.Text = IRC.URL; - ChatPortSelector.Value = IRC.Port; - ChatNicknameText.Visible = true; - ChatNicknameTitle.Visible = true; - ChatNicknameText.Text = IRC.Nickname; - ChatAdminsTitle.Text = String.Format("Admin {0}:", IRC.AdminsAreSpecial ? "Req Mode" : "Nicknames"); - ChannelsTitle.Text = "Broadcast/Listening Channels:"; - AdminModeNormal.Text = "Nicknames"; - AdminModeSpecial.Text = "Channel Mode"; - if (IRC.AdminsAreSpecial) - { - ChatAdminsTextBox.Visible = false; - IRCModesComboBox.Visible = true; - IRCModesComboBox.SelectedIndex = (int)IRC.AuthLevel; - } - break; - default: - Properties.Settings.Default.LastChatProvider = (int)ChatProvider.IRC; - LoadChatPage(); - return; - } - - AdminModeNormal.Checked = !PI.AdminsAreSpecial; - AdminModeSpecial.Checked = PI.AdminsAreSpecial; - ChatEnabledCheckbox.Checked = PI.Enabled; - if (!PI.Enabled) - ChatStatusLabel.Text = "Disabled"; - else if (Chat.Connected(ModifyingProvider)) - ChatStatusLabel.Text = "Connected"; - else - ChatStatusLabel.Text = "Disconnected"; - ChatReconnectButton.Enabled = PI.Enabled; - - AssignListToTextbox(PI.AdminList, ChatAdminsTextBox); - AssignListToTextbox(PI.WatchdogChannels, WDChannelsTextbox); - AssignListToTextbox(PI.AdminChannels, AdminChannelsTextbox); - AssignListToTextbox(PI.DevChannels, DevChannelsTextbox); - AssignListToTextbox(PI.GameChannels, GameChannelsTextbox); - updatingChat = false; - } - - static void AssignListToTextbox(IList a, TextBox b) - { - b.Text = ""; - foreach (var I in a) - b.Text += I + Environment.NewLine; - } - - private void ChatRefreshButton_Click(object sender, EventArgs e) - { - LoadChatPage(); - } - - private void ChatReconnectButton_Click(object sender, EventArgs e) - { - Instance.Chat.Reconnect(ModifyingProvider); - LoadChatPage(); - } - - static string[] SplitByLine(TextBox t) - { - var channels = t.Text.Split('\n'); - - var finalChannels = new List(); - foreach (var I in channels) - { - var trimmed = I.Trim(); - if(trimmed != "") - finalChannels.Add(trimmed); - } - return finalChannels.ToArray(); - } - - private void DiscordProviderSwitch_CheckedChanged(object sender, EventArgs e) - { - if (!updatingChat && DiscordProviderSwitch.Checked) - { - ModifyingProvider = ChatProvider.Discord; - LoadChatPage(); - } - } - - private void IRCProviderSwitch_CheckedChanged(object sender, EventArgs e) - { - if (!updatingChat && IRCProviderSwitch.Checked) - { - ModifyingProvider = ChatProvider.IRC; - LoadChatPage(); - } - } - void SetAdminsAreSpecial(bool value) - { - var Chat = Instance.Chat; - var PI = Chat.ProviderInfos()[(int)ModifyingProvider]; - PI.AdminsAreSpecial = value; - var res = Chat.SetProviderInfo(PI); - if (res != null) - MessageBox.Show(res); - LoadChatPage(); - } - - private void AdminModeNormal_CheckedChanged(object sender, EventArgs e) - { - if (!updatingChat && AdminModeNormal.Checked) - SetAdminsAreSpecial(false); - } - - private void AdminModeSpecial_CheckedChanged(object sender, EventArgs e) - { - if (!updatingChat && AdminModeSpecial.Checked) - SetAdminsAreSpecial(true); - } - - private void ChatApplyButton_Click(object sender, EventArgs e) - { - string res = null; - ChatSetupInfo wip = null; - switch (ModifyingProvider) - { - case ChatProvider.Discord: - wip = new DiscordSetupInfo() - { - BotToken = AuthField1.Text - }; - break; - case ChatProvider.IRC: - wip = new IRCSetupInfo() - { - AuthMessage = AuthField2.Text, - AuthTarget = AuthField1.Text, - Nickname = ChatNicknameText.Text, - URL = ChatServerText.Text, - Port = (ushort)ChatPortSelector.Value, - AuthLevel = (IRCMode)IRCModesComboBox.SelectedIndex, - }; - break; - default: - res = "You really shouldn't be able to read this."; - break; - } - - if (res == null) - { - wip.AdminChannels = new List(AdminChannelsTextbox.Text.Split(Environment.NewLine.ToCharArray())); - wip.WatchdogChannels = new List(WDChannelsTextbox.Text.Split(Environment.NewLine.ToCharArray())); - wip.DevChannels = new List(DevChannelsTextbox.Text.Split(Environment.NewLine.ToCharArray())); - wip.GameChannels = new List(GameChannelsTextbox.Text.Split(Environment.NewLine.ToCharArray())); - wip.AdminList = new List(ChatAdminsTextBox.Text.Split(Environment.NewLine.ToCharArray())); - wip.Enabled = ChatEnabledCheckbox.Checked; - wip.AdminsAreSpecial = AdminModeSpecial.Checked; - - res = Instance.Chat.SetProviderInfo(wip); - } - if (res != null) - MessageBox.Show(res); - LoadChatPage(); - } - } -} diff --git a/TGS.ControlPanel/ControlPanel/ControlPanel.Designer.cs b/TGS.ControlPanel/ControlPanel/ControlPanel.Designer.cs deleted file mode 100644 index ba5d05108f..0000000000 --- a/TGS.ControlPanel/ControlPanel/ControlPanel.Designer.cs +++ /dev/null @@ -1,1882 +0,0 @@ -namespace TGS.ControlPanel -{ - partial class ControlPanel - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - Cleanup(); - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(ControlPanel)); - this.RepoBGW = new System.ComponentModel.BackgroundWorker(); - this.ServerStartBGW = new System.ComponentModel.BackgroundWorker(); - this.ChatPanel = new System.Windows.Forms.TabPage(); - this.label5 = new System.Windows.Forms.Label(); - this.label4 = new System.Windows.Forms.Label(); - this.label3 = new System.Windows.Forms.Label(); - this.label2 = new System.Windows.Forms.Label(); - this.IRCModesComboBox = new System.Windows.Forms.ComboBox(); - this.label1 = new System.Windows.Forms.Label(); - this.AdminChannelsTextbox = new System.Windows.Forms.TextBox(); - this.WDChannelsTextbox = new System.Windows.Forms.TextBox(); - this.GameChannelsTextbox = new System.Windows.Forms.TextBox(); - this.DevChannelsTextbox = new System.Windows.Forms.TextBox(); - this.ChatNicknameText = new System.Windows.Forms.TextBox(); - this.ChatServerText = new System.Windows.Forms.TextBox(); - this.AuthField2 = new System.Windows.Forms.TextBox(); - this.AuthField1 = new System.Windows.Forms.TextBox(); - this.ChatAdminsTextBox = new System.Windows.Forms.TextBox(); - this.ChatRefreshButton = new System.Windows.Forms.Button(); - this.ChatNicknameTitle = new System.Windows.Forms.Label(); - this.ChatPortSelector = new System.Windows.Forms.NumericUpDown(); - this.ChatPortTitle = new System.Windows.Forms.Label(); - this.ChatServerTitle = new System.Windows.Forms.Label(); - this.ChatApplyButton = new System.Windows.Forms.Button(); - this.AuthField2Title = new System.Windows.Forms.Label(); - this.AuthField1Title = new System.Windows.Forms.Label(); - this.ChatReconnectButton = new System.Windows.Forms.Button(); - this.ChatStatusLabel = new System.Windows.Forms.Label(); - this.ChatStatusTitle = new System.Windows.Forms.Label(); - this.ChatEnabledCheckbox = new System.Windows.Forms.CheckBox(); - this.ChatProviderSelectorPanel = new System.Windows.Forms.Panel(); - this.DiscordProviderSwitch = new System.Windows.Forms.RadioButton(); - this.IRCProviderSwitch = new System.Windows.Forms.RadioButton(); - this.ChatProviderTitle = new System.Windows.Forms.Label(); - this.ChannelsTitle = new System.Windows.Forms.Label(); - this.ChatAdminsTitle = new System.Windows.Forms.Label(); - this.panel1 = new System.Windows.Forms.Panel(); - this.AdminModeSpecial = new System.Windows.Forms.RadioButton(); - this.AdminModeNormal = new System.Windows.Forms.RadioButton(); - this.ServerPanel = new System.Windows.Forms.TabPage(); - this.AutoUpdateMLabel = new System.Windows.Forms.Label(); - this.AutoUpdateInterval = new System.Windows.Forms.NumericUpDown(); - this.AutoUpdateCheckbox = new System.Windows.Forms.CheckBox(); - this.ServerPageRefreshButton = new System.Windows.Forms.Button(); - this.AutostartCheckbox = new System.Windows.Forms.CheckBox(); - this.WebclientCheckBox = new System.Windows.Forms.CheckBox(); - this.WorldAnnounceButton = new System.Windows.Forms.Button(); - this.WorldAnnounceField = new System.Windows.Forms.TextBox(); - this.projectNameText = new System.Windows.Forms.TextBox(); - this.WorldAnnounceLabel = new System.Windows.Forms.Label(); - this.SecuritySelector = new System.Windows.Forms.ComboBox(); - this.SecurityTitle = new System.Windows.Forms.Label(); - this.RemoveAllTestMergesButton = new System.Windows.Forms.Button(); - this.ServerPathLabel = new System.Windows.Forms.Label(); - this.CompileCancelButton = new System.Windows.Forms.Button(); - this.ProjectPathLabel = new System.Windows.Forms.Label(); - this.PortLabel = new System.Windows.Forms.Label(); - this.PortSelector = new System.Windows.Forms.NumericUpDown(); - this.TestMergeManagerButton = new System.Windows.Forms.Button(); - this.UpdateServerButton = new System.Windows.Forms.Button(); - this.ServerGRestartButton = new System.Windows.Forms.Button(); - this.ServerGStopButton = new System.Windows.Forms.CheckBox(); - this.ServerRestartButton = new System.Windows.Forms.Button(); - this.ServerStopButton = new System.Windows.Forms.Button(); - this.ServerStartButton = new System.Windows.Forms.Button(); - this.CompilerStatusLabel = new System.Windows.Forms.Label(); - this.CompilerLabel = new System.Windows.Forms.Label(); - this.compileButton = new System.Windows.Forms.Button(); - this.initializeButton = new System.Windows.Forms.Button(); - this.ServerStatusLabel = new System.Windows.Forms.Label(); - this.ServerStatusTitle = new System.Windows.Forms.Label(); - this.BYONDPanel = new System.Windows.Forms.TabPage(); - this.BYONDRefreshButton = new System.Windows.Forms.Button(); - this.LatestVersionLabel = new System.Windows.Forms.Label(); - this.LatestVersionTitle = new System.Windows.Forms.Label(); - this.StagedVersionLabel = new System.Windows.Forms.Label(); - this.StagedVersionTitle = new System.Windows.Forms.Label(); - this.StatusLabel = new System.Windows.Forms.Label(); - this.VersionLabel = new System.Windows.Forms.Label(); - this.VersionTitle = new System.Windows.Forms.Label(); - this.MinorVersionLabel = new System.Windows.Forms.Label(); - this.MajorVersionLabel = new System.Windows.Forms.Label(); - this.UpdateButton = new System.Windows.Forms.Button(); - this.MinorVersionNumeric = new System.Windows.Forms.NumericUpDown(); - this.MajorVersionNumeric = new System.Windows.Forms.NumericUpDown(); - this.RepoPanel = new System.Windows.Forms.TabPage(); - this.SyncCommitsCheckBox = new System.Windows.Forms.CheckBox(); - this.TGSJsonUpdate = new System.Windows.Forms.Button(); - this.RepoRefreshButton = new System.Windows.Forms.Button(); - this.BackupTagsList = new System.Windows.Forms.ListBox(); - this.ResetRemote = new System.Windows.Forms.Button(); - this.RecloneButton = new System.Windows.Forms.Button(); - this.RepoBranchTextBox = new System.Windows.Forms.TextBox(); - this.RepoRemoteTextBox = new System.Windows.Forms.TextBox(); - this.RepoGenChangelogButton = new System.Windows.Forms.Button(); - this.TestmergeSelector = new System.Windows.Forms.NumericUpDown(); - this.TestMergeListLabel = new System.Windows.Forms.ListBox(); - this.CurrentRevisionLabel = new System.Windows.Forms.Label(); - this.RepoApplyButton = new System.Windows.Forms.Button(); - this.HardReset = new System.Windows.Forms.Button(); - this.UpdateRepoButton = new System.Windows.Forms.Button(); - this.MergePRButton = new System.Windows.Forms.Button(); - this.IdentityLabel = new System.Windows.Forms.Label(); - this.TestMergeListTitle = new System.Windows.Forms.Label(); - this.RemoteNameTitle = new System.Windows.Forms.Label(); - this.BranchNameTitle = new System.Windows.Forms.Label(); - this.CurrentRevisionTitle = new System.Windows.Forms.Label(); - this.CloneRepositoryButton = new System.Windows.Forms.Button(); - this.RepoProgressBarLabel = new System.Windows.Forms.Label(); - this.RepoProgressBar = new System.Windows.Forms.ProgressBar(); - this.Panels = new System.Windows.Forms.TabControl(); - this.StaticPanel = new System.Windows.Forms.TabPage(); - this.RecreateStaticButton = new System.Windows.Forms.Button(); - this.StaticFileDownloadButton = new System.Windows.Forms.Button(); - this.StaticFilesRefreshButton = new System.Windows.Forms.Button(); - this.StaticFileUploadButton = new System.Windows.Forms.Button(); - this.StaticFileEditTextbox = new System.Windows.Forms.TextBox(); - this.StaticFileDeleteButton = new System.Windows.Forms.Button(); - this.StaticFileSaveButton = new System.Windows.Forms.Button(); - this.StaticFileCreateButton = new System.Windows.Forms.Button(); - this.label6 = new System.Windows.Forms.Label(); - this.StaticFileListBox = new System.Windows.Forms.ListBox(); - this.ChatPanel.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.ChatPortSelector)).BeginInit(); - this.ChatProviderSelectorPanel.SuspendLayout(); - this.panel1.SuspendLayout(); - this.ServerPanel.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.AutoUpdateInterval)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.PortSelector)).BeginInit(); - this.BYONDPanel.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.MinorVersionNumeric)).BeginInit(); - ((System.ComponentModel.ISupportInitialize)(this.MajorVersionNumeric)).BeginInit(); - this.RepoPanel.SuspendLayout(); - ((System.ComponentModel.ISupportInitialize)(this.TestmergeSelector)).BeginInit(); - this.Panels.SuspendLayout(); - this.StaticPanel.SuspendLayout(); - this.SuspendLayout(); - // - // RepoBGW - // - this.RepoBGW.WorkerReportsProgress = true; - this.RepoBGW.WorkerSupportsCancellation = true; - // - // ServerStartBGW - // - this.ServerStartBGW.DoWork += new System.ComponentModel.DoWorkEventHandler(this.ServerStartBGW_DoWork); - // - // ChatPanel - // - this.ChatPanel.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(39)))), ((int)(((byte)(40)))), ((int)(((byte)(34))))); - this.ChatPanel.Controls.Add(this.label5); - this.ChatPanel.Controls.Add(this.label4); - this.ChatPanel.Controls.Add(this.label3); - this.ChatPanel.Controls.Add(this.label2); - this.ChatPanel.Controls.Add(this.IRCModesComboBox); - this.ChatPanel.Controls.Add(this.label1); - this.ChatPanel.Controls.Add(this.AdminChannelsTextbox); - this.ChatPanel.Controls.Add(this.WDChannelsTextbox); - this.ChatPanel.Controls.Add(this.GameChannelsTextbox); - this.ChatPanel.Controls.Add(this.DevChannelsTextbox); - this.ChatPanel.Controls.Add(this.ChatNicknameText); - this.ChatPanel.Controls.Add(this.ChatServerText); - this.ChatPanel.Controls.Add(this.AuthField2); - this.ChatPanel.Controls.Add(this.AuthField1); - this.ChatPanel.Controls.Add(this.ChatAdminsTextBox); - this.ChatPanel.Controls.Add(this.ChatRefreshButton); - this.ChatPanel.Controls.Add(this.ChatNicknameTitle); - this.ChatPanel.Controls.Add(this.ChatPortSelector); - this.ChatPanel.Controls.Add(this.ChatPortTitle); - this.ChatPanel.Controls.Add(this.ChatServerTitle); - this.ChatPanel.Controls.Add(this.ChatApplyButton); - this.ChatPanel.Controls.Add(this.AuthField2Title); - this.ChatPanel.Controls.Add(this.AuthField1Title); - this.ChatPanel.Controls.Add(this.ChatReconnectButton); - this.ChatPanel.Controls.Add(this.ChatStatusLabel); - this.ChatPanel.Controls.Add(this.ChatStatusTitle); - this.ChatPanel.Controls.Add(this.ChatEnabledCheckbox); - this.ChatPanel.Controls.Add(this.ChatProviderSelectorPanel); - this.ChatPanel.Controls.Add(this.ChatProviderTitle); - this.ChatPanel.Controls.Add(this.ChannelsTitle); - this.ChatPanel.Controls.Add(this.ChatAdminsTitle); - this.ChatPanel.Controls.Add(this.panel1); - this.ChatPanel.Location = new System.Drawing.Point(4, 22); - this.ChatPanel.Name = "ChatPanel"; - this.ChatPanel.Padding = new System.Windows.Forms.Padding(3); - this.ChatPanel.Size = new System.Drawing.Size(868, 366); - this.ChatPanel.TabIndex = 4; - this.ChatPanel.Text = "Chat"; - // - // label5 - // - this.label5.AutoSize = true; - this.label5.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label5.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.label5.Location = new System.Drawing.Point(686, 219); - this.label5.Name = "label5"; - this.label5.Size = new System.Drawing.Size(148, 18); - this.label5.TabIndex = 43; - this.label5.Text = "Game Messages:"; - this.label5.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // label4 - // - this.label4.AutoSize = true; - this.label4.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label4.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.label4.Location = new System.Drawing.Point(516, 219); - this.label4.Name = "label4"; - this.label4.Size = new System.Drawing.Size(136, 18); - this.label4.TabIndex = 42; - this.label4.Text = "Developer Info:"; - this.label4.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // label3 - // - this.label3.AutoSize = true; - this.label3.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label3.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.label3.Location = new System.Drawing.Point(686, 61); - this.label3.Name = "label3"; - this.label3.Size = new System.Drawing.Size(135, 18); - this.label3.TabIndex = 41; - this.label3.Text = "Administration:"; - this.label3.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // label2 - // - this.label2.AutoSize = true; - this.label2.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label2.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.label2.Location = new System.Drawing.Point(516, 61); - this.label2.Name = "label2"; - this.label2.Size = new System.Drawing.Size(153, 18); - this.label2.TabIndex = 40; - this.label2.Text = "Server Watchdog:"; - this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // IRCModesComboBox - // - this.IRCModesComboBox.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.IRCModesComboBox.FormattingEnabled = true; - this.IRCModesComboBox.Items.AddRange(new object[] { - "Voice - +", - "Halfop - %", - "Op - @", - "Owner - ~"}); - this.IRCModesComboBox.Location = new System.Drawing.Point(369, 170); - this.IRCModesComboBox.Name = "IRCModesComboBox"; - this.IRCModesComboBox.Size = new System.Drawing.Size(141, 21); - this.IRCModesComboBox.TabIndex = 38; - this.IRCModesComboBox.Visible = false; - // - // label1 - // - this.label1.AutoSize = true; - this.label1.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.label1.Location = new System.Drawing.Point(30, 174); - this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(110, 18); - this.label1.TabIndex = 39; - this.label1.Text = "Admin Auth:"; - this.label1.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // AdminChannelsTextbox - // - this.AdminChannelsTextbox.Location = new System.Drawing.Point(689, 82); - this.AdminChannelsTextbox.Multiline = true; - this.AdminChannelsTextbox.Name = "AdminChannelsTextbox"; - this.AdminChannelsTextbox.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; - this.AdminChannelsTextbox.Size = new System.Drawing.Size(167, 120); - this.AdminChannelsTextbox.TabIndex = 37; - // - // WDChannelsTextbox - // - this.WDChannelsTextbox.Location = new System.Drawing.Point(516, 82); - this.WDChannelsTextbox.Multiline = true; - this.WDChannelsTextbox.Name = "WDChannelsTextbox"; - this.WDChannelsTextbox.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; - this.WDChannelsTextbox.Size = new System.Drawing.Size(167, 120); - this.WDChannelsTextbox.TabIndex = 36; - // - // GameChannelsTextbox - // - this.GameChannelsTextbox.Location = new System.Drawing.Point(689, 240); - this.GameChannelsTextbox.Multiline = true; - this.GameChannelsTextbox.Name = "GameChannelsTextbox"; - this.GameChannelsTextbox.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; - this.GameChannelsTextbox.Size = new System.Drawing.Size(167, 120); - this.GameChannelsTextbox.TabIndex = 35; - // - // DevChannelsTextbox - // - this.DevChannelsTextbox.Location = new System.Drawing.Point(516, 240); - this.DevChannelsTextbox.Multiline = true; - this.DevChannelsTextbox.Name = "DevChannelsTextbox"; - this.DevChannelsTextbox.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; - this.DevChannelsTextbox.Size = new System.Drawing.Size(167, 120); - this.DevChannelsTextbox.TabIndex = 34; - // - // ChatNicknameText - // - this.ChatNicknameText.Location = new System.Drawing.Point(174, 251); - this.ChatNicknameText.Name = "ChatNicknameText"; - this.ChatNicknameText.Size = new System.Drawing.Size(151, 20); - this.ChatNicknameText.TabIndex = 31; - // - // ChatServerText - // - this.ChatServerText.Location = new System.Drawing.Point(174, 199); - this.ChatServerText.Name = "ChatServerText"; - this.ChatServerText.Size = new System.Drawing.Size(151, 20); - this.ChatServerText.TabIndex = 27; - // - // AuthField2 - // - this.AuthField2.Location = new System.Drawing.Point(174, 309); - this.AuthField2.Name = "AuthField2"; - this.AuthField2.Size = new System.Drawing.Size(151, 20); - this.AuthField2.TabIndex = 22; - // - // AuthField1 - // - this.AuthField1.Location = new System.Drawing.Point(174, 283); - this.AuthField1.Name = "AuthField1"; - this.AuthField1.Size = new System.Drawing.Size(151, 20); - this.AuthField1.TabIndex = 21; - // - // ChatAdminsTextBox - // - this.ChatAdminsTextBox.Location = new System.Drawing.Point(369, 49); - this.ChatAdminsTextBox.Multiline = true; - this.ChatAdminsTextBox.Name = "ChatAdminsTextBox"; - this.ChatAdminsTextBox.ScrollBars = System.Windows.Forms.ScrollBars.Vertical; - this.ChatAdminsTextBox.Size = new System.Drawing.Size(141, 311); - this.ChatAdminsTextBox.TabIndex = 10; - // - // ChatRefreshButton - // - this.ChatRefreshButton.Location = new System.Drawing.Point(174, 335); - this.ChatRefreshButton.Name = "ChatRefreshButton"; - this.ChatRefreshButton.Size = new System.Drawing.Size(112, 25); - this.ChatRefreshButton.TabIndex = 32; - this.ChatRefreshButton.Text = "Refresh"; - this.ChatRefreshButton.UseVisualStyleBackColor = true; - this.ChatRefreshButton.Click += new System.EventHandler(this.ChatRefreshButton_Click); - // - // ChatNicknameTitle - // - this.ChatNicknameTitle.AutoSize = true; - this.ChatNicknameTitle.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.ChatNicknameTitle.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.ChatNicknameTitle.Location = new System.Drawing.Point(30, 250); - this.ChatNicknameTitle.Name = "ChatNicknameTitle"; - this.ChatNicknameTitle.Size = new System.Drawing.Size(94, 18); - this.ChatNicknameTitle.TabIndex = 30; - this.ChatNicknameTitle.Text = "Nickname:"; - this.ChatNicknameTitle.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // ChatPortSelector - // - this.ChatPortSelector.Location = new System.Drawing.Point(174, 225); - this.ChatPortSelector.Maximum = new decimal(new int[] { - 65535, - 0, - 0, - 0}); - this.ChatPortSelector.Minimum = new decimal(new int[] { - 1, - 0, - 0, - 0}); - this.ChatPortSelector.Name = "ChatPortSelector"; - this.ChatPortSelector.Size = new System.Drawing.Size(151, 20); - this.ChatPortSelector.TabIndex = 29; - this.ChatPortSelector.Value = new decimal(new int[] { - 1, - 0, - 0, - 0}); - // - // ChatPortTitle - // - this.ChatPortTitle.AutoSize = true; - this.ChatPortTitle.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.ChatPortTitle.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.ChatPortTitle.Location = new System.Drawing.Point(30, 227); - this.ChatPortTitle.Name = "ChatPortTitle"; - this.ChatPortTitle.Size = new System.Drawing.Size(48, 18); - this.ChatPortTitle.TabIndex = 28; - this.ChatPortTitle.Text = "Port:"; - this.ChatPortTitle.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // ChatServerTitle - // - this.ChatServerTitle.AutoSize = true; - this.ChatServerTitle.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.ChatServerTitle.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.ChatServerTitle.Location = new System.Drawing.Point(30, 201); - this.ChatServerTitle.Name = "ChatServerTitle"; - this.ChatServerTitle.Size = new System.Drawing.Size(66, 18); - this.ChatServerTitle.TabIndex = 26; - this.ChatServerTitle.Text = "Server:"; - this.ChatServerTitle.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // ChatApplyButton - // - this.ChatApplyButton.Location = new System.Drawing.Point(56, 335); - this.ChatApplyButton.Name = "ChatApplyButton"; - this.ChatApplyButton.Size = new System.Drawing.Size(112, 25); - this.ChatApplyButton.TabIndex = 25; - this.ChatApplyButton.Text = "Apply"; - this.ChatApplyButton.UseVisualStyleBackColor = true; - this.ChatApplyButton.Click += new System.EventHandler(this.ChatApplyButton_Click); - // - // AuthField2Title - // - this.AuthField2Title.AutoSize = true; - this.AuthField2Title.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.AuthField2Title.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.AuthField2Title.Location = new System.Drawing.Point(30, 311); - this.AuthField2Title.Name = "AuthField2Title"; - this.AuthField2Title.Size = new System.Drawing.Size(45, 18); - this.AuthField2Title.TabIndex = 24; - this.AuthField2Title.Text = "AF2:"; - this.AuthField2Title.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // AuthField1Title - // - this.AuthField1Title.AutoSize = true; - this.AuthField1Title.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.AuthField1Title.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.AuthField1Title.Location = new System.Drawing.Point(30, 285); - this.AuthField1Title.Name = "AuthField1Title"; - this.AuthField1Title.Size = new System.Drawing.Size(45, 18); - this.AuthField1Title.TabIndex = 23; - this.AuthField1Title.Text = "AF1:"; - this.AuthField1Title.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // ChatReconnectButton - // - this.ChatReconnectButton.Enabled = false; - this.ChatReconnectButton.Location = new System.Drawing.Point(174, 141); - this.ChatReconnectButton.Name = "ChatReconnectButton"; - this.ChatReconnectButton.Size = new System.Drawing.Size(112, 25); - this.ChatReconnectButton.TabIndex = 18; - this.ChatReconnectButton.Text = "Reconnect"; - this.ChatReconnectButton.UseVisualStyleBackColor = true; - this.ChatReconnectButton.Click += new System.EventHandler(this.ChatReconnectButton_Click); - // - // ChatStatusLabel - // - this.ChatStatusLabel.AutoSize = true; - this.ChatStatusLabel.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.ChatStatusLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.ChatStatusLabel.Location = new System.Drawing.Point(171, 113); - this.ChatStatusLabel.Name = "ChatStatusLabel"; - this.ChatStatusLabel.Size = new System.Drawing.Size(82, 18); - this.ChatStatusLabel.TabIndex = 17; - this.ChatStatusLabel.Text = "Unknown"; - this.ChatStatusLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // ChatStatusTitle - // - this.ChatStatusTitle.AutoSize = true; - this.ChatStatusTitle.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.ChatStatusTitle.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.ChatStatusTitle.Location = new System.Drawing.Point(30, 113); - this.ChatStatusTitle.Name = "ChatStatusTitle"; - this.ChatStatusTitle.Size = new System.Drawing.Size(112, 18); - this.ChatStatusTitle.TabIndex = 16; - this.ChatStatusTitle.Text = "Chat Status:"; - this.ChatStatusTitle.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // ChatEnabledCheckbox - // - this.ChatEnabledCheckbox.AutoSize = true; - this.ChatEnabledCheckbox.Font = new System.Drawing.Font("Verdana", 12F); - this.ChatEnabledCheckbox.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.ChatEnabledCheckbox.Location = new System.Drawing.Point(32, 142); - this.ChatEnabledCheckbox.Name = "ChatEnabledCheckbox"; - this.ChatEnabledCheckbox.Size = new System.Drawing.Size(136, 22); - this.ChatEnabledCheckbox.TabIndex = 15; - this.ChatEnabledCheckbox.Text = "Chat Enabled"; - this.ChatEnabledCheckbox.UseVisualStyleBackColor = true; - // - // ChatProviderSelectorPanel - // - this.ChatProviderSelectorPanel.Controls.Add(this.DiscordProviderSwitch); - this.ChatProviderSelectorPanel.Controls.Add(this.IRCProviderSwitch); - this.ChatProviderSelectorPanel.Location = new System.Drawing.Point(23, 49); - this.ChatProviderSelectorPanel.Name = "ChatProviderSelectorPanel"; - this.ChatProviderSelectorPanel.Size = new System.Drawing.Size(302, 50); - this.ChatProviderSelectorPanel.TabIndex = 14; - // - // DiscordProviderSwitch - // - this.DiscordProviderSwitch.AutoSize = true; - this.DiscordProviderSwitch.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.DiscordProviderSwitch.Location = new System.Drawing.Point(220, 17); - this.DiscordProviderSwitch.Name = "DiscordProviderSwitch"; - this.DiscordProviderSwitch.Size = new System.Drawing.Size(61, 17); - this.DiscordProviderSwitch.TabIndex = 13; - this.DiscordProviderSwitch.TabStop = true; - this.DiscordProviderSwitch.Text = "Discord"; - this.DiscordProviderSwitch.UseVisualStyleBackColor = true; - this.DiscordProviderSwitch.CheckedChanged += new System.EventHandler(this.DiscordProviderSwitch_CheckedChanged); - // - // IRCProviderSwitch - // - this.IRCProviderSwitch.AutoSize = true; - this.IRCProviderSwitch.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.IRCProviderSwitch.Location = new System.Drawing.Point(14, 17); - this.IRCProviderSwitch.Name = "IRCProviderSwitch"; - this.IRCProviderSwitch.Size = new System.Drawing.Size(116, 17); - this.IRCProviderSwitch.TabIndex = 12; - this.IRCProviderSwitch.TabStop = true; - this.IRCProviderSwitch.Text = "Internet Relay Chat"; - this.IRCProviderSwitch.UseVisualStyleBackColor = true; - this.IRCProviderSwitch.CheckedChanged += new System.EventHandler(this.IRCProviderSwitch_CheckedChanged); - // - // ChatProviderTitle - // - this.ChatProviderTitle.AutoSize = true; - this.ChatProviderTitle.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.ChatProviderTitle.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.ChatProviderTitle.Location = new System.Drawing.Point(20, 15); - this.ChatProviderTitle.Name = "ChatProviderTitle"; - this.ChatProviderTitle.Size = new System.Drawing.Size(144, 18); - this.ChatProviderTitle.TabIndex = 13; - this.ChatProviderTitle.Text = "Editing Provider:"; - this.ChatProviderTitle.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // ChannelsTitle - // - this.ChannelsTitle.AutoSize = true; - this.ChannelsTitle.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.ChannelsTitle.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.ChannelsTitle.Location = new System.Drawing.Point(565, 15); - this.ChannelsTitle.Name = "ChannelsTitle"; - this.ChannelsTitle.Size = new System.Drawing.Size(234, 18); - this.ChannelsTitle.TabIndex = 11; - this.ChannelsTitle.Text = "Listen/Broadcast Channels:"; - this.ChannelsTitle.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // ChatAdminsTitle - // - this.ChatAdminsTitle.AutoSize = true; - this.ChatAdminsTitle.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.ChatAdminsTitle.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.ChatAdminsTitle.Location = new System.Drawing.Point(366, 15); - this.ChatAdminsTitle.Name = "ChatAdminsTitle"; - this.ChatAdminsTitle.Size = new System.Drawing.Size(75, 18); - this.ChatAdminsTitle.TabIndex = 9; - this.ChatAdminsTitle.Text = "Admins:"; - this.ChatAdminsTitle.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // panel1 - // - this.panel1.Controls.Add(this.AdminModeSpecial); - this.panel1.Controls.Add(this.AdminModeNormal); - this.panel1.Location = new System.Drawing.Point(174, 168); - this.panel1.Name = "panel1"; - this.panel1.Size = new System.Drawing.Size(202, 25); - this.panel1.TabIndex = 15; - // - // AdminModeSpecial - // - this.AdminModeSpecial.AutoSize = true; - this.AdminModeSpecial.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.AdminModeSpecial.Location = new System.Drawing.Point(87, 8); - this.AdminModeSpecial.Name = "AdminModeSpecial"; - this.AdminModeSpecial.Size = new System.Drawing.Size(94, 17); - this.AdminModeSpecial.TabIndex = 13; - this.AdminModeSpecial.TabStop = true; - this.AdminModeSpecial.Text = "Channel Mode"; - this.AdminModeSpecial.UseVisualStyleBackColor = true; - // - // AdminModeNormal - // - this.AdminModeNormal.AutoSize = true; - this.AdminModeNormal.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.AdminModeNormal.Location = new System.Drawing.Point(3, 8); - this.AdminModeNormal.Name = "AdminModeNormal"; - this.AdminModeNormal.Size = new System.Drawing.Size(78, 17); - this.AdminModeNormal.TabIndex = 12; - this.AdminModeNormal.TabStop = true; - this.AdminModeNormal.Text = "Nicknames"; - this.AdminModeNormal.UseVisualStyleBackColor = true; - this.AdminModeNormal.CheckedChanged += new System.EventHandler(this.AdminModeNormal_CheckedChanged); - // - // ServerPanel - // - this.ServerPanel.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(39)))), ((int)(((byte)(40)))), ((int)(((byte)(34))))); - this.ServerPanel.Controls.Add(this.AutoUpdateMLabel); - this.ServerPanel.Controls.Add(this.AutoUpdateInterval); - this.ServerPanel.Controls.Add(this.AutoUpdateCheckbox); - this.ServerPanel.Controls.Add(this.ServerPageRefreshButton); - this.ServerPanel.Controls.Add(this.AutostartCheckbox); - this.ServerPanel.Controls.Add(this.WebclientCheckBox); - this.ServerPanel.Controls.Add(this.WorldAnnounceButton); - this.ServerPanel.Controls.Add(this.WorldAnnounceField); - this.ServerPanel.Controls.Add(this.projectNameText); - this.ServerPanel.Controls.Add(this.WorldAnnounceLabel); - this.ServerPanel.Controls.Add(this.SecuritySelector); - this.ServerPanel.Controls.Add(this.SecurityTitle); - this.ServerPanel.Controls.Add(this.RemoveAllTestMergesButton); - this.ServerPanel.Controls.Add(this.ServerPathLabel); - this.ServerPanel.Controls.Add(this.CompileCancelButton); - this.ServerPanel.Controls.Add(this.ProjectPathLabel); - this.ServerPanel.Controls.Add(this.PortLabel); - this.ServerPanel.Controls.Add(this.PortSelector); - this.ServerPanel.Controls.Add(this.TestMergeManagerButton); - this.ServerPanel.Controls.Add(this.UpdateServerButton); - this.ServerPanel.Controls.Add(this.ServerGRestartButton); - this.ServerPanel.Controls.Add(this.ServerGStopButton); - this.ServerPanel.Controls.Add(this.ServerRestartButton); - this.ServerPanel.Controls.Add(this.ServerStopButton); - this.ServerPanel.Controls.Add(this.ServerStartButton); - this.ServerPanel.Controls.Add(this.CompilerStatusLabel); - this.ServerPanel.Controls.Add(this.CompilerLabel); - this.ServerPanel.Controls.Add(this.compileButton); - this.ServerPanel.Controls.Add(this.initializeButton); - this.ServerPanel.Controls.Add(this.ServerStatusLabel); - this.ServerPanel.Controls.Add(this.ServerStatusTitle); - this.ServerPanel.Location = new System.Drawing.Point(4, 22); - this.ServerPanel.Name = "ServerPanel"; - this.ServerPanel.Padding = new System.Windows.Forms.Padding(3); - this.ServerPanel.Size = new System.Drawing.Size(868, 366); - this.ServerPanel.TabIndex = 2; - this.ServerPanel.Text = "Server"; - // - // AutoUpdateMLabel - // - this.AutoUpdateMLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.AutoUpdateMLabel.AutoSize = true; - this.AutoUpdateMLabel.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.AutoUpdateMLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.AutoUpdateMLabel.Location = new System.Drawing.Point(433, 136); - this.AutoUpdateMLabel.Name = "AutoUpdateMLabel"; - this.AutoUpdateMLabel.Size = new System.Drawing.Size(21, 18); - this.AutoUpdateMLabel.TabIndex = 47; - this.AutoUpdateMLabel.Text = "M"; - this.AutoUpdateMLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - this.AutoUpdateMLabel.Visible = false; - // - // AutoUpdateInterval - // - this.AutoUpdateInterval.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.AutoUpdateInterval.Location = new System.Drawing.Point(365, 135); - this.AutoUpdateInterval.Maximum = new decimal(new int[] { - 276447231, - 23283, - 0, - 0}); - this.AutoUpdateInterval.Minimum = new decimal(new int[] { - 1, - 0, - 0, - 0}); - this.AutoUpdateInterval.Name = "AutoUpdateInterval"; - this.AutoUpdateInterval.Size = new System.Drawing.Size(62, 20); - this.AutoUpdateInterval.TabIndex = 46; - this.AutoUpdateInterval.Value = new decimal(new int[] { - 1, - 0, - 0, - 0}); - this.AutoUpdateInterval.Visible = false; - this.AutoUpdateInterval.ValueChanged += new System.EventHandler(this.AutoUpdateInterval_ValueChanged); - // - // AutoUpdateCheckbox - // - this.AutoUpdateCheckbox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.AutoUpdateCheckbox.AutoSize = true; - this.AutoUpdateCheckbox.Font = new System.Drawing.Font("Verdana", 12F); - this.AutoUpdateCheckbox.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.AutoUpdateCheckbox.Location = new System.Drawing.Point(228, 134); - this.AutoUpdateCheckbox.Name = "AutoUpdateCheckbox"; - this.AutoUpdateCheckbox.Size = new System.Drawing.Size(131, 22); - this.AutoUpdateCheckbox.TabIndex = 45; - this.AutoUpdateCheckbox.Text = "Auto-Update"; - this.AutoUpdateCheckbox.UseVisualStyleBackColor = true; - this.AutoUpdateCheckbox.Visible = false; - this.AutoUpdateCheckbox.CheckedChanged += new System.EventHandler(this.AutoUpdateCheckbox_CheckedChanged); - // - // ServerPageRefreshButton - // - this.ServerPageRefreshButton.Anchor = System.Windows.Forms.AnchorStyles.Top; - this.ServerPageRefreshButton.Location = new System.Drawing.Point(626, 13); - this.ServerPageRefreshButton.Name = "ServerPageRefreshButton"; - this.ServerPageRefreshButton.Size = new System.Drawing.Size(118, 28); - this.ServerPageRefreshButton.TabIndex = 43; - this.ServerPageRefreshButton.Text = "Refresh"; - this.ServerPageRefreshButton.UseVisualStyleBackColor = true; - this.ServerPageRefreshButton.Click += new System.EventHandler(this.ServerPageRefreshButton_Click); - // - // AutostartCheckbox - // - this.AutostartCheckbox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.AutostartCheckbox.AutoSize = true; - this.AutostartCheckbox.Font = new System.Drawing.Font("Verdana", 12F); - this.AutostartCheckbox.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.AutostartCheckbox.Location = new System.Drawing.Point(754, 16); - this.AutostartCheckbox.Name = "AutostartCheckbox"; - this.AutostartCheckbox.Size = new System.Drawing.Size(104, 22); - this.AutostartCheckbox.TabIndex = 15; - this.AutostartCheckbox.Text = "Autostart"; - this.AutostartCheckbox.UseVisualStyleBackColor = true; - this.AutostartCheckbox.CheckedChanged += new System.EventHandler(this.AutostartCheckbox_CheckedChanged); - // - // WebclientCheckBox - // - this.WebclientCheckBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.WebclientCheckBox.AutoSize = true; - this.WebclientCheckBox.Font = new System.Drawing.Font("Verdana", 12F); - this.WebclientCheckBox.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.WebclientCheckBox.Location = new System.Drawing.Point(754, 57); - this.WebclientCheckBox.Name = "WebclientCheckBox"; - this.WebclientCheckBox.Size = new System.Drawing.Size(108, 22); - this.WebclientCheckBox.TabIndex = 42; - this.WebclientCheckBox.Text = "Webclient"; - this.WebclientCheckBox.UseVisualStyleBackColor = true; - this.WebclientCheckBox.CheckedChanged += new System.EventHandler(this.WebclientCheckBox_CheckedChanged); - // - // WorldAnnounceButton - // - this.WorldAnnounceButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.WorldAnnounceButton.Location = new System.Drawing.Point(785, 133); - this.WorldAnnounceButton.Name = "WorldAnnounceButton"; - this.WorldAnnounceButton.Size = new System.Drawing.Size(77, 20); - this.WorldAnnounceButton.TabIndex = 41; - this.WorldAnnounceButton.Text = "Send"; - this.WorldAnnounceButton.UseVisualStyleBackColor = true; - this.WorldAnnounceButton.Click += new System.EventHandler(this.WorldAnnounceButton_Click); - // - // WorldAnnounceField - // - this.WorldAnnounceField.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.WorldAnnounceField.Location = new System.Drawing.Point(566, 134); - this.WorldAnnounceField.Name = "WorldAnnounceField"; - this.WorldAnnounceField.Size = new System.Drawing.Size(213, 20); - this.WorldAnnounceField.TabIndex = 40; - // - // projectNameText - // - this.projectNameText.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.projectNameText.Location = new System.Drawing.Point(566, 163); - this.projectNameText.Name = "projectNameText"; - this.projectNameText.Size = new System.Drawing.Size(296, 20); - this.projectNameText.TabIndex = 29; - // - // WorldAnnounceLabel - // - this.WorldAnnounceLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.WorldAnnounceLabel.AutoSize = true; - this.WorldAnnounceLabel.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.WorldAnnounceLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.WorldAnnounceLabel.Location = new System.Drawing.Point(466, 135); - this.WorldAnnounceLabel.Name = "WorldAnnounceLabel"; - this.WorldAnnounceLabel.Size = new System.Drawing.Size(94, 18); - this.WorldAnnounceLabel.TabIndex = 39; - this.WorldAnnounceLabel.Text = "Announce:"; - this.WorldAnnounceLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // SecuritySelector - // - this.SecuritySelector.DropDownStyle = System.Windows.Forms.ComboBoxStyle.DropDownList; - this.SecuritySelector.FormattingEnabled = true; - this.SecuritySelector.Items.AddRange(new object[] { - "Trusted", - "Safe", - "Ultrasafe"}); - this.SecuritySelector.Location = new System.Drawing.Point(101, 134); - this.SecuritySelector.Name = "SecuritySelector"; - this.SecuritySelector.Size = new System.Drawing.Size(121, 21); - this.SecuritySelector.TabIndex = 38; - this.SecuritySelector.SelectedIndexChanged += new System.EventHandler(this.SecuritySelector_SelectedIndexChanged); - // - // SecurityTitle - // - this.SecurityTitle.AutoSize = true; - this.SecurityTitle.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.SecurityTitle.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.SecurityTitle.Location = new System.Drawing.Point(15, 139); - this.SecurityTitle.Name = "SecurityTitle"; - this.SecurityTitle.Size = new System.Drawing.Size(80, 18); - this.SecurityTitle.TabIndex = 37; - this.SecurityTitle.Text = "Security:"; - this.SecurityTitle.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // RemoveAllTestMergesButton - // - this.RemoveAllTestMergesButton.Anchor = System.Windows.Forms.AnchorStyles.Top; - this.RemoveAllTestMergesButton.Location = new System.Drawing.Point(489, 94); - this.RemoveAllTestMergesButton.Name = "RemoveAllTestMergesButton"; - this.RemoveAllTestMergesButton.Size = new System.Drawing.Size(142, 28); - this.RemoveAllTestMergesButton.TabIndex = 36; - this.RemoveAllTestMergesButton.Text = "Remove All Test Merges"; - this.RemoveAllTestMergesButton.UseVisualStyleBackColor = true; - this.RemoveAllTestMergesButton.Click += new System.EventHandler(this.RemoveAllTestMergesButton_Click); - // - // ServerPathLabel - // - this.ServerPathLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.ServerPathLabel.AutoSize = true; - this.ServerPathLabel.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.ServerPathLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.ServerPathLabel.Location = new System.Drawing.Point(15, 165); - this.ServerPathLabel.Name = "ServerPathLabel"; - this.ServerPathLabel.Size = new System.Drawing.Size(109, 18); - this.ServerPathLabel.TabIndex = 33; - this.ServerPathLabel.Text = "Server Path:"; - this.ServerPathLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // CompileCancelButton - // - this.CompileCancelButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.CompileCancelButton.Enabled = false; - this.CompileCancelButton.Location = new System.Drawing.Point(382, 302); - this.CompileCancelButton.Name = "CompileCancelButton"; - this.CompileCancelButton.Size = new System.Drawing.Size(69, 31); - this.CompileCancelButton.TabIndex = 31; - this.CompileCancelButton.Text = "Cancel"; - this.CompileCancelButton.UseVisualStyleBackColor = true; - this.CompileCancelButton.Click += new System.EventHandler(this.CompileCancelButton_Click); - // - // ProjectPathLabel - // - this.ProjectPathLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.ProjectPathLabel.AutoSize = true; - this.ProjectPathLabel.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.ProjectPathLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.ProjectPathLabel.Location = new System.Drawing.Point(445, 165); - this.ProjectPathLabel.Name = "ProjectPathLabel"; - this.ProjectPathLabel.Size = new System.Drawing.Size(115, 18); - this.ProjectPathLabel.TabIndex = 30; - this.ProjectPathLabel.Text = "Project Path:"; - this.ProjectPathLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // PortLabel - // - this.PortLabel.AutoSize = true; - this.PortLabel.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.PortLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.PortLabel.Location = new System.Drawing.Point(4, 60); - this.PortLabel.Name = "PortLabel"; - this.PortLabel.Size = new System.Drawing.Size(48, 18); - this.PortLabel.TabIndex = 28; - this.PortLabel.Text = "Port:"; - this.PortLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // PortSelector - // - this.PortSelector.Location = new System.Drawing.Point(59, 60); - this.PortSelector.Maximum = new decimal(new int[] { - 65535, - 0, - 0, - 0}); - this.PortSelector.Minimum = new decimal(new int[] { - 1, - 0, - 0, - 0}); - this.PortSelector.Name = "PortSelector"; - this.PortSelector.Size = new System.Drawing.Size(60, 20); - this.PortSelector.TabIndex = 27; - this.PortSelector.Value = new decimal(new int[] { - 1, - 0, - 0, - 0}); - this.PortSelector.ValueChanged += new System.EventHandler(this.PortSelector_ValueChanged); - // - // TestMergeManagerButton - // - this.TestMergeManagerButton.Anchor = System.Windows.Forms.AnchorStyles.Top; - this.TestMergeManagerButton.Location = new System.Drawing.Point(343, 94); - this.TestMergeManagerButton.Name = "TestMergeManagerButton"; - this.TestMergeManagerButton.Size = new System.Drawing.Size(142, 28); - this.TestMergeManagerButton.TabIndex = 24; - this.TestMergeManagerButton.Text = "Test Merge Manager"; - this.TestMergeManagerButton.UseVisualStyleBackColor = true; - this.TestMergeManagerButton.Click += new System.EventHandler(this.TestMergeManagerButton_Click); - // - // UpdateServerButton - // - this.UpdateServerButton.Anchor = System.Windows.Forms.AnchorStyles.Top; - this.UpdateServerButton.Location = new System.Drawing.Point(180, 94); - this.UpdateServerButton.Name = "UpdateServerButton"; - this.UpdateServerButton.Size = new System.Drawing.Size(157, 28); - this.UpdateServerButton.TabIndex = 21; - this.UpdateServerButton.Text = "Update Server"; - this.UpdateServerButton.UseVisualStyleBackColor = true; - this.UpdateServerButton.Click += new System.EventHandler(this.UpdateServerButton_Click); - // - // ServerGRestartButton - // - this.ServerGRestartButton.Anchor = System.Windows.Forms.AnchorStyles.Top; - this.ServerGRestartButton.Location = new System.Drawing.Point(626, 54); - this.ServerGRestartButton.Name = "ServerGRestartButton"; - this.ServerGRestartButton.Size = new System.Drawing.Size(118, 28); - this.ServerGRestartButton.TabIndex = 20; - this.ServerGRestartButton.Text = "Graceful Restart"; - this.ServerGRestartButton.UseVisualStyleBackColor = true; - this.ServerGRestartButton.Click += new System.EventHandler(this.ServerGRestartButton_Click); - // - // ServerGStopButton - // - this.ServerGStopButton.Anchor = System.Windows.Forms.AnchorStyles.Top; - this.ServerGStopButton.Appearance = System.Windows.Forms.Appearance.Button; - this.ServerGStopButton.Location = new System.Drawing.Point(502, 54); - this.ServerGStopButton.Name = "ServerGStopButton"; - this.ServerGStopButton.Size = new System.Drawing.Size(118, 28); - this.ServerGStopButton.TabIndex = 19; - this.ServerGStopButton.Text = "Graceful Stop"; - this.ServerGStopButton.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - this.ServerGStopButton.UseVisualStyleBackColor = true; - this.ServerGStopButton.CheckedChanged += new System.EventHandler(this.ServerGStopButton_Checked); - // - // ServerRestartButton - // - this.ServerRestartButton.Anchor = System.Windows.Forms.AnchorStyles.Top; - this.ServerRestartButton.Location = new System.Drawing.Point(378, 54); - this.ServerRestartButton.Name = "ServerRestartButton"; - this.ServerRestartButton.Size = new System.Drawing.Size(118, 28); - this.ServerRestartButton.TabIndex = 18; - this.ServerRestartButton.Text = "Restart"; - this.ServerRestartButton.UseVisualStyleBackColor = true; - this.ServerRestartButton.Click += new System.EventHandler(this.ServerRestartButton_Click); - // - // ServerStopButton - // - this.ServerStopButton.Anchor = System.Windows.Forms.AnchorStyles.Top; - this.ServerStopButton.Location = new System.Drawing.Point(254, 54); - this.ServerStopButton.Name = "ServerStopButton"; - this.ServerStopButton.Size = new System.Drawing.Size(118, 28); - this.ServerStopButton.TabIndex = 17; - this.ServerStopButton.Text = "Stop"; - this.ServerStopButton.UseVisualStyleBackColor = true; - this.ServerStopButton.Click += new System.EventHandler(this.ServerStopButton_Click); - // - // ServerStartButton - // - this.ServerStartButton.Anchor = System.Windows.Forms.AnchorStyles.Top; - this.ServerStartButton.Location = new System.Drawing.Point(130, 54); - this.ServerStartButton.Name = "ServerStartButton"; - this.ServerStartButton.Size = new System.Drawing.Size(118, 28); - this.ServerStartButton.TabIndex = 16; - this.ServerStartButton.Text = "Start"; - this.ServerStartButton.UseVisualStyleBackColor = true; - this.ServerStartButton.Click += new System.EventHandler(this.ServerStartButton_Click); - // - // CompilerStatusLabel - // - this.CompilerStatusLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.CompilerStatusLabel.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.CompilerStatusLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.CompilerStatusLabel.Location = new System.Drawing.Point(110, 271); - this.CompilerStatusLabel.Name = "CompilerStatusLabel"; - this.CompilerStatusLabel.Size = new System.Drawing.Size(618, 28); - this.CompilerStatusLabel.TabIndex = 14; - this.CompilerStatusLabel.Text = "Idle"; - this.CompilerStatusLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // CompilerLabel - // - this.CompilerLabel.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.CompilerLabel.AutoSize = true; - this.CompilerLabel.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.CompilerLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.CompilerLabel.Location = new System.Drawing.Point(379, 203); - this.CompilerLabel.Name = "CompilerLabel"; - this.CompilerLabel.Size = new System.Drawing.Size(80, 18); - this.CompilerLabel.TabIndex = 13; - this.CompilerLabel.Text = "Compiler"; - this.CompilerLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // compileButton - // - this.compileButton.Anchor = System.Windows.Forms.AnchorStyles.Bottom; - this.compileButton.Enabled = false; - this.compileButton.Location = new System.Drawing.Point(456, 240); - this.compileButton.Name = "compileButton"; - this.compileButton.Size = new System.Drawing.Size(159, 28); - this.compileButton.TabIndex = 12; - this.compileButton.Text = "Copy from Repo and Compile"; - this.compileButton.UseVisualStyleBackColor = true; - this.compileButton.Click += new System.EventHandler(this.CompileButton_Click); - // - // initializeButton - // - this.initializeButton.Anchor = System.Windows.Forms.AnchorStyles.Bottom; - this.initializeButton.Enabled = false; - this.initializeButton.Location = new System.Drawing.Point(219, 240); - this.initializeButton.Name = "initializeButton"; - this.initializeButton.Size = new System.Drawing.Size(159, 28); - this.initializeButton.TabIndex = 11; - this.initializeButton.Text = "Initialize Game Folders"; - this.initializeButton.UseVisualStyleBackColor = true; - this.initializeButton.Click += new System.EventHandler(this.InitializeButton_Click); - // - // ServerStatusLabel - // - this.ServerStatusLabel.AutoSize = true; - this.ServerStatusLabel.Font = new System.Drawing.Font("Verdana", 10F); - this.ServerStatusLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.ServerStatusLabel.Location = new System.Drawing.Point(136, 19); - this.ServerStatusLabel.Name = "ServerStatusLabel"; - this.ServerStatusLabel.Size = new System.Drawing.Size(73, 17); - this.ServerStatusLabel.TabIndex = 9; - this.ServerStatusLabel.Text = "Unknown"; - this.ServerStatusLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // ServerStatusTitle - // - this.ServerStatusTitle.AutoSize = true; - this.ServerStatusTitle.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.ServerStatusTitle.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.ServerStatusTitle.Location = new System.Drawing.Point(15, 17); - this.ServerStatusTitle.Name = "ServerStatusTitle"; - this.ServerStatusTitle.Size = new System.Drawing.Size(125, 18); - this.ServerStatusTitle.TabIndex = 8; - this.ServerStatusTitle.Text = "Server Status:"; - this.ServerStatusTitle.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // BYONDPanel - // - this.BYONDPanel.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(39)))), ((int)(((byte)(40)))), ((int)(((byte)(34))))); - this.BYONDPanel.Controls.Add(this.BYONDRefreshButton); - this.BYONDPanel.Controls.Add(this.LatestVersionLabel); - this.BYONDPanel.Controls.Add(this.LatestVersionTitle); - this.BYONDPanel.Controls.Add(this.StagedVersionLabel); - this.BYONDPanel.Controls.Add(this.StagedVersionTitle); - this.BYONDPanel.Controls.Add(this.StatusLabel); - this.BYONDPanel.Controls.Add(this.VersionLabel); - this.BYONDPanel.Controls.Add(this.VersionTitle); - this.BYONDPanel.Controls.Add(this.MinorVersionLabel); - this.BYONDPanel.Controls.Add(this.MajorVersionLabel); - this.BYONDPanel.Controls.Add(this.UpdateButton); - this.BYONDPanel.Controls.Add(this.MinorVersionNumeric); - this.BYONDPanel.Controls.Add(this.MajorVersionNumeric); - this.BYONDPanel.Location = new System.Drawing.Point(4, 22); - this.BYONDPanel.Name = "BYONDPanel"; - this.BYONDPanel.Size = new System.Drawing.Size(868, 366); - this.BYONDPanel.TabIndex = 1; - this.BYONDPanel.Text = "BYOND"; - // - // BYONDRefreshButton - // - this.BYONDRefreshButton.Location = new System.Drawing.Point(462, 252); - this.BYONDRefreshButton.Name = "BYONDRefreshButton"; - this.BYONDRefreshButton.Size = new System.Drawing.Size(118, 28); - this.BYONDRefreshButton.TabIndex = 14; - this.BYONDRefreshButton.Text = "Refresh"; - this.BYONDRefreshButton.UseVisualStyleBackColor = true; - this.BYONDRefreshButton.Click += new System.EventHandler(this.BYONDRefreshButton_Click); - // - // LatestVersionLabel - // - this.LatestVersionLabel.AutoSize = true; - this.LatestVersionLabel.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.LatestVersionLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.LatestVersionLabel.Location = new System.Drawing.Point(425, 93); - this.LatestVersionLabel.Name = "LatestVersionLabel"; - this.LatestVersionLabel.Size = new System.Drawing.Size(82, 18); - this.LatestVersionLabel.TabIndex = 13; - this.LatestVersionLabel.Text = "Unknown"; - this.LatestVersionLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; - // - // LatestVersionTitle - // - this.LatestVersionTitle.AutoSize = true; - this.LatestVersionTitle.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.LatestVersionTitle.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.LatestVersionTitle.Location = new System.Drawing.Point(286, 93); - this.LatestVersionTitle.Name = "LatestVersionTitle"; - this.LatestVersionTitle.Size = new System.Drawing.Size(133, 18); - this.LatestVersionTitle.TabIndex = 12; - this.LatestVersionTitle.Text = "Latest Version:"; - this.LatestVersionTitle.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // StagedVersionLabel - // - this.StagedVersionLabel.AutoSize = true; - this.StagedVersionLabel.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.StagedVersionLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.StagedVersionLabel.Location = new System.Drawing.Point(425, 131); - this.StagedVersionLabel.Name = "StagedVersionLabel"; - this.StagedVersionLabel.Size = new System.Drawing.Size(82, 18); - this.StagedVersionLabel.TabIndex = 11; - this.StagedVersionLabel.Text = "Unknown"; - this.StagedVersionLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; - this.StagedVersionLabel.Visible = false; - // - // StagedVersionTitle - // - this.StagedVersionTitle.AutoSize = true; - this.StagedVersionTitle.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.StagedVersionTitle.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.StagedVersionTitle.Location = new System.Drawing.Point(282, 131); - this.StagedVersionTitle.Name = "StagedVersionTitle"; - this.StagedVersionTitle.Size = new System.Drawing.Size(137, 18); - this.StagedVersionTitle.TabIndex = 10; - this.StagedVersionTitle.Text = "Staged version:"; - this.StagedVersionTitle.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - this.StagedVersionTitle.Visible = false; - // - // StatusLabel - // - this.StatusLabel.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.StatusLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.StatusLabel.Location = new System.Drawing.Point(303, 320); - this.StatusLabel.Name = "StatusLabel"; - this.StatusLabel.Size = new System.Drawing.Size(253, 37); - this.StatusLabel.TabIndex = 9; - this.StatusLabel.Text = "Idle"; - this.StatusLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // VersionLabel - // - this.VersionLabel.AutoSize = true; - this.VersionLabel.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.VersionLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.VersionLabel.Location = new System.Drawing.Point(425, 56); - this.VersionLabel.Name = "VersionLabel"; - this.VersionLabel.Size = new System.Drawing.Size(82, 18); - this.VersionLabel.TabIndex = 8; - this.VersionLabel.Text = "Unknown"; - this.VersionLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; - // - // VersionTitle - // - this.VersionTitle.AutoSize = true; - this.VersionTitle.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.VersionTitle.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.VersionTitle.Location = new System.Drawing.Point(265, 56); - this.VersionTitle.Name = "VersionTitle"; - this.VersionTitle.Size = new System.Drawing.Size(154, 18); - this.VersionTitle.TabIndex = 7; - this.VersionTitle.Text = "Installed Version:"; - this.VersionTitle.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // MinorVersionLabel - // - this.MinorVersionLabel.AutoSize = true; - this.MinorVersionLabel.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.MinorVersionLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.MinorVersionLabel.Location = new System.Drawing.Point(521, 180); - this.MinorVersionLabel.Name = "MinorVersionLabel"; - this.MinorVersionLabel.Size = new System.Drawing.Size(59, 18); - this.MinorVersionLabel.TabIndex = 6; - this.MinorVersionLabel.Text = "Minor:"; - this.MinorVersionLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // MajorVersionLabel - // - this.MajorVersionLabel.AutoSize = true; - this.MajorVersionLabel.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.MajorVersionLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.MajorVersionLabel.Location = new System.Drawing.Point(276, 180); - this.MajorVersionLabel.Name = "MajorVersionLabel"; - this.MajorVersionLabel.Size = new System.Drawing.Size(60, 18); - this.MajorVersionLabel.TabIndex = 5; - this.MajorVersionLabel.Text = "Major:"; - this.MajorVersionLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // UpdateButton - // - this.UpdateButton.Location = new System.Drawing.Point(268, 252); - this.UpdateButton.Name = "UpdateButton"; - this.UpdateButton.Size = new System.Drawing.Size(118, 28); - this.UpdateButton.TabIndex = 3; - this.UpdateButton.Text = "Update"; - this.UpdateButton.UseVisualStyleBackColor = true; - this.UpdateButton.Click += new System.EventHandler(this.UpdateButton_Click); - // - // MinorVersionNumeric - // - this.MinorVersionNumeric.Location = new System.Drawing.Point(490, 210); - this.MinorVersionNumeric.Maximum = new decimal(new int[] { - 10000, - 0, - 0, - 0}); - this.MinorVersionNumeric.Name = "MinorVersionNumeric"; - this.MinorVersionNumeric.Size = new System.Drawing.Size(120, 20); - this.MinorVersionNumeric.TabIndex = 2; - this.MinorVersionNumeric.Value = new decimal(new int[] { - 1381, - 0, - 0, - 0}); - // - // MajorVersionNumeric - // - this.MajorVersionNumeric.Location = new System.Drawing.Point(245, 210); - this.MajorVersionNumeric.Maximum = new decimal(new int[] { - 1000, - 0, - 0, - 0}); - this.MajorVersionNumeric.Name = "MajorVersionNumeric"; - this.MajorVersionNumeric.Size = new System.Drawing.Size(120, 20); - this.MajorVersionNumeric.TabIndex = 1; - this.MajorVersionNumeric.Value = new decimal(new int[] { - 511, - 0, - 0, - 0}); - // - // RepoPanel - // - this.RepoPanel.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(39)))), ((int)(((byte)(40)))), ((int)(((byte)(34))))); - this.RepoPanel.Controls.Add(this.SyncCommitsCheckBox); - this.RepoPanel.Controls.Add(this.TGSJsonUpdate); - this.RepoPanel.Controls.Add(this.RepoRefreshButton); - this.RepoPanel.Controls.Add(this.BackupTagsList); - this.RepoPanel.Controls.Add(this.ResetRemote); - this.RepoPanel.Controls.Add(this.RecloneButton); - this.RepoPanel.Controls.Add(this.RepoBranchTextBox); - this.RepoPanel.Controls.Add(this.RepoRemoteTextBox); - this.RepoPanel.Controls.Add(this.RepoGenChangelogButton); - this.RepoPanel.Controls.Add(this.TestmergeSelector); - this.RepoPanel.Controls.Add(this.TestMergeListLabel); - this.RepoPanel.Controls.Add(this.CurrentRevisionLabel); - this.RepoPanel.Controls.Add(this.RepoApplyButton); - this.RepoPanel.Controls.Add(this.HardReset); - this.RepoPanel.Controls.Add(this.UpdateRepoButton); - this.RepoPanel.Controls.Add(this.MergePRButton); - this.RepoPanel.Controls.Add(this.IdentityLabel); - this.RepoPanel.Controls.Add(this.TestMergeListTitle); - this.RepoPanel.Controls.Add(this.RemoteNameTitle); - this.RepoPanel.Controls.Add(this.BranchNameTitle); - this.RepoPanel.Controls.Add(this.CurrentRevisionTitle); - this.RepoPanel.Controls.Add(this.CloneRepositoryButton); - this.RepoPanel.Controls.Add(this.RepoProgressBarLabel); - this.RepoPanel.Controls.Add(this.RepoProgressBar); - this.RepoPanel.Location = new System.Drawing.Point(4, 22); - this.RepoPanel.Name = "RepoPanel"; - this.RepoPanel.Padding = new System.Windows.Forms.Padding(3); - this.RepoPanel.Size = new System.Drawing.Size(868, 366); - this.RepoPanel.TabIndex = 0; - this.RepoPanel.Text = "Repository"; - // - // SyncCommitsCheckBox - // - this.SyncCommitsCheckBox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.SyncCommitsCheckBox.AutoSize = true; - this.SyncCommitsCheckBox.Font = new System.Drawing.Font("Verdana", 12F); - this.SyncCommitsCheckBox.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.SyncCommitsCheckBox.Location = new System.Drawing.Point(574, 16); - this.SyncCommitsCheckBox.Name = "SyncCommitsCheckBox"; - this.SyncCommitsCheckBox.Size = new System.Drawing.Size(142, 22); - this.SyncCommitsCheckBox.TabIndex = 46; - this.SyncCommitsCheckBox.Text = "Sync Commits"; - this.SyncCommitsCheckBox.UseVisualStyleBackColor = true; - this.SyncCommitsCheckBox.Visible = false; - // - // TGSJsonUpdate - // - this.TGSJsonUpdate.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.TGSJsonUpdate.Location = new System.Drawing.Point(722, 212); - this.TGSJsonUpdate.Name = "TGSJsonUpdate"; - this.TGSJsonUpdate.Size = new System.Drawing.Size(140, 29); - this.TGSJsonUpdate.TabIndex = 36; - this.TGSJsonUpdate.Text = "Update TGS3.json"; - this.TGSJsonUpdate.UseVisualStyleBackColor = true; - this.TGSJsonUpdate.Visible = false; - this.TGSJsonUpdate.Click += new System.EventHandler(this.TGSJsonUpdate_Click); - // - // RepoRefreshButton - // - this.RepoRefreshButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.RepoRefreshButton.Location = new System.Drawing.Point(722, 296); - this.RepoRefreshButton.Name = "RepoRefreshButton"; - this.RepoRefreshButton.Size = new System.Drawing.Size(140, 29); - this.RepoRefreshButton.TabIndex = 35; - this.RepoRefreshButton.Text = "Refresh"; - this.RepoRefreshButton.UseVisualStyleBackColor = true; - this.RepoRefreshButton.Visible = false; - this.RepoRefreshButton.Click += new System.EventHandler(this.RepoRefreshButton_Click); - // - // BackupTagsList - // - this.BackupTagsList.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.BackupTagsList.Items.AddRange(new object[] { - "None"}); - this.BackupTagsList.Location = new System.Drawing.Point(122, 142); - this.BackupTagsList.Name = "BackupTagsList"; - this.BackupTagsList.ScrollAlwaysVisible = true; - this.BackupTagsList.Size = new System.Drawing.Size(535, 95); - this.BackupTagsList.TabIndex = 34; - this.BackupTagsList.Visible = false; - // - // ResetRemote - // - this.ResetRemote.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.ResetRemote.Location = new System.Drawing.Point(722, 81); - this.ResetRemote.Name = "ResetRemote"; - this.ResetRemote.Size = new System.Drawing.Size(140, 29); - this.ResetRemote.TabIndex = 33; - this.ResetRemote.Text = "Reset To Remote"; - this.ResetRemote.UseVisualStyleBackColor = true; - this.ResetRemote.Visible = false; - this.ResetRemote.Click += new System.EventHandler(this.ResetRemote_Click); - // - // RecloneButton - // - this.RecloneButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.RecloneButton.Location = new System.Drawing.Point(722, 247); - this.RecloneButton.Name = "RecloneButton"; - this.RecloneButton.Size = new System.Drawing.Size(140, 29); - this.RecloneButton.TabIndex = 32; - this.RecloneButton.Text = "Reclone"; - this.RecloneButton.UseVisualStyleBackColor = true; - this.RecloneButton.Visible = false; - this.RecloneButton.Click += new System.EventHandler(this.RecloneButton_Click); - // - // RepoBranchTextBox - // - this.RepoBranchTextBox.Location = new System.Drawing.Point(122, 70); - this.RepoBranchTextBox.Name = "RepoBranchTextBox"; - this.RepoBranchTextBox.Size = new System.Drawing.Size(535, 20); - this.RepoBranchTextBox.TabIndex = 15; - this.RepoBranchTextBox.Visible = false; - // - // RepoRemoteTextBox - // - this.RepoRemoteTextBox.Location = new System.Drawing.Point(122, 44); - this.RepoRemoteTextBox.Name = "RepoRemoteTextBox"; - this.RepoRemoteTextBox.Size = new System.Drawing.Size(535, 20); - this.RepoRemoteTextBox.TabIndex = 14; - this.RepoRemoteTextBox.Visible = false; - // - // RepoGenChangelogButton - // - this.RepoGenChangelogButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.RepoGenChangelogButton.Location = new System.Drawing.Point(722, 177); - this.RepoGenChangelogButton.Name = "RepoGenChangelogButton"; - this.RepoGenChangelogButton.Size = new System.Drawing.Size(140, 29); - this.RepoGenChangelogButton.TabIndex = 27; - this.RepoGenChangelogButton.Text = "Generate Changelog"; - this.RepoGenChangelogButton.UseVisualStyleBackColor = true; - this.RepoGenChangelogButton.Visible = false; - this.RepoGenChangelogButton.Click += new System.EventHandler(this.RepoGenChangelogButton_Click); - // - // TestmergeSelector - // - this.TestmergeSelector.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.TestmergeSelector.Location = new System.Drawing.Point(722, 151); - this.TestmergeSelector.Maximum = new decimal(new int[] { - 1000000, - 0, - 0, - 0}); - this.TestmergeSelector.Name = "TestmergeSelector"; - this.TestmergeSelector.Size = new System.Drawing.Size(140, 20); - this.TestmergeSelector.TabIndex = 22; - this.TestmergeSelector.Visible = false; - // - // TestMergeListLabel - // - this.TestMergeListLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.TestMergeListLabel.HorizontalScrollbar = true; - this.TestMergeListLabel.Items.AddRange(new object[] { - "None"}); - this.TestMergeListLabel.Location = new System.Drawing.Point(122, 260); - this.TestMergeListLabel.Name = "TestMergeListLabel"; - this.TestMergeListLabel.ScrollAlwaysVisible = true; - this.TestMergeListLabel.Size = new System.Drawing.Size(535, 95); - this.TestMergeListLabel.TabIndex = 21; - this.TestMergeListLabel.Visible = false; - // - // CurrentRevisionLabel - // - this.CurrentRevisionLabel.AutoSize = true; - this.CurrentRevisionLabel.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.CurrentRevisionLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.CurrentRevisionLabel.Location = new System.Drawing.Point(162, 14); - this.CurrentRevisionLabel.Name = "CurrentRevisionLabel"; - this.CurrentRevisionLabel.Size = new System.Drawing.Size(82, 18); - this.CurrentRevisionLabel.TabIndex = 20; - this.CurrentRevisionLabel.Text = "Unknown"; - this.CurrentRevisionLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - this.CurrentRevisionLabel.Visible = false; - // - // RepoApplyButton - // - this.RepoApplyButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.RepoApplyButton.Location = new System.Drawing.Point(722, 331); - this.RepoApplyButton.Name = "RepoApplyButton"; - this.RepoApplyButton.Size = new System.Drawing.Size(140, 29); - this.RepoApplyButton.TabIndex = 17; - this.RepoApplyButton.Text = "Apply Changes"; - this.RepoApplyButton.UseVisualStyleBackColor = true; - this.RepoApplyButton.Visible = false; - this.RepoApplyButton.Click += new System.EventHandler(this.RepoApplyButton_Click); - // - // HardReset - // - this.HardReset.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.HardReset.Location = new System.Drawing.Point(722, 46); - this.HardReset.Name = "HardReset"; - this.HardReset.Size = new System.Drawing.Size(140, 29); - this.HardReset.TabIndex = 13; - this.HardReset.Text = "Reset To Origin Branch"; - this.HardReset.UseVisualStyleBackColor = true; - this.HardReset.Visible = false; - this.HardReset.Click += new System.EventHandler(this.HardReset_Click); - // - // UpdateRepoButton - // - this.UpdateRepoButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.UpdateRepoButton.Location = new System.Drawing.Point(722, 11); - this.UpdateRepoButton.Name = "UpdateRepoButton"; - this.UpdateRepoButton.Size = new System.Drawing.Size(140, 29); - this.UpdateRepoButton.TabIndex = 12; - this.UpdateRepoButton.Text = "Merge from Remote"; - this.UpdateRepoButton.UseVisualStyleBackColor = true; - this.UpdateRepoButton.Visible = false; - this.UpdateRepoButton.Click += new System.EventHandler(this.UpdateRepoButton_Click); - // - // MergePRButton - // - this.MergePRButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.MergePRButton.Location = new System.Drawing.Point(722, 116); - this.MergePRButton.Name = "MergePRButton"; - this.MergePRButton.Size = new System.Drawing.Size(140, 29); - this.MergePRButton.TabIndex = 11; - this.MergePRButton.Text = "Merge Pull Request"; - this.MergePRButton.UseVisualStyleBackColor = true; - this.MergePRButton.Visible = false; - this.MergePRButton.Click += new System.EventHandler(this.TestMergeButton_Click); - // - // IdentityLabel - // - this.IdentityLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.IdentityLabel.AutoSize = true; - this.IdentityLabel.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.IdentityLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.IdentityLabel.Location = new System.Drawing.Point(6, 142); - this.IdentityLabel.Name = "IdentityLabel"; - this.IdentityLabel.Size = new System.Drawing.Size(116, 18); - this.IdentityLabel.TabIndex = 8; - this.IdentityLabel.Text = "Backup Tags:"; - this.IdentityLabel.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; - this.IdentityLabel.Visible = false; - // - // TestMergeListTitle - // - this.TestMergeListTitle.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.TestMergeListTitle.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.TestMergeListTitle.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.TestMergeListTitle.Location = new System.Drawing.Point(6, 260); - this.TestMergeListTitle.Name = "TestMergeListTitle"; - this.TestMergeListTitle.Size = new System.Drawing.Size(110, 41); - this.TestMergeListTitle.TabIndex = 6; - this.TestMergeListTitle.Text = "Active Test Merges:"; - this.TestMergeListTitle.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; - this.TestMergeListTitle.Visible = false; - // - // RemoteNameTitle - // - this.RemoteNameTitle.AutoSize = true; - this.RemoteNameTitle.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.RemoteNameTitle.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.RemoteNameTitle.Location = new System.Drawing.Point(6, 46); - this.RemoteNameTitle.Name = "RemoteNameTitle"; - this.RemoteNameTitle.Size = new System.Drawing.Size(78, 18); - this.RemoteNameTitle.TabIndex = 5; - this.RemoteNameTitle.Text = "Remote:"; - this.RemoteNameTitle.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; - this.RemoteNameTitle.Visible = false; - // - // BranchNameTitle - // - this.BranchNameTitle.AutoSize = true; - this.BranchNameTitle.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.BranchNameTitle.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.BranchNameTitle.Location = new System.Drawing.Point(6, 72); - this.BranchNameTitle.Name = "BranchNameTitle"; - this.BranchNameTitle.Size = new System.Drawing.Size(70, 18); - this.BranchNameTitle.TabIndex = 4; - this.BranchNameTitle.Text = "Branch:"; - this.BranchNameTitle.TextAlign = System.Drawing.ContentAlignment.MiddleLeft; - this.BranchNameTitle.Visible = false; - // - // CurrentRevisionTitle - // - this.CurrentRevisionTitle.AutoSize = true; - this.CurrentRevisionTitle.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.CurrentRevisionTitle.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.CurrentRevisionTitle.Location = new System.Drawing.Point(6, 14); - this.CurrentRevisionTitle.Name = "CurrentRevisionTitle"; - this.CurrentRevisionTitle.Size = new System.Drawing.Size(150, 18); - this.CurrentRevisionTitle.TabIndex = 3; - this.CurrentRevisionTitle.Text = "Current Revision:"; - this.CurrentRevisionTitle.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - this.CurrentRevisionTitle.Visible = false; - // - // CloneRepositoryButton - // - this.CloneRepositoryButton.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.CloneRepositoryButton.Location = new System.Drawing.Point(311, 191); - this.CloneRepositoryButton.Name = "CloneRepositoryButton"; - this.CloneRepositoryButton.Size = new System.Drawing.Size(229, 34); - this.CloneRepositoryButton.TabIndex = 2; - this.CloneRepositoryButton.Text = "Clone Repository"; - this.CloneRepositoryButton.UseVisualStyleBackColor = true; - this.CloneRepositoryButton.Visible = false; - this.CloneRepositoryButton.Click += new System.EventHandler(this.CloneRepositoryButton_Click); - // - // RepoProgressBarLabel - // - this.RepoProgressBarLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.RepoProgressBarLabel.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.RepoProgressBarLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.RepoProgressBarLabel.Location = new System.Drawing.Point(184, 142); - this.RepoProgressBarLabel.Name = "RepoProgressBarLabel"; - this.RepoProgressBarLabel.Size = new System.Drawing.Size(499, 46); - this.RepoProgressBarLabel.TabIndex = 1; - this.RepoProgressBarLabel.Text = "Searching for Repository..."; - this.RepoProgressBarLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // RepoProgressBar - // - this.RepoProgressBar.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.RepoProgressBar.Location = new System.Drawing.Point(184, 191); - this.RepoProgressBar.Name = "RepoProgressBar"; - this.RepoProgressBar.Size = new System.Drawing.Size(499, 23); - this.RepoProgressBar.Style = System.Windows.Forms.ProgressBarStyle.Marquee; - this.RepoProgressBar.TabIndex = 0; - // - // Panels - // - this.Panels.Anchor = ((System.Windows.Forms.AnchorStyles)((((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left) - | System.Windows.Forms.AnchorStyles.Right))); - this.Panels.Controls.Add(this.RepoPanel); - this.Panels.Controls.Add(this.BYONDPanel); - this.Panels.Controls.Add(this.ServerPanel); - this.Panels.Controls.Add(this.ChatPanel); - this.Panels.Controls.Add(this.StaticPanel); - this.Panels.Cursor = System.Windows.Forms.Cursors.Default; - this.Panels.Location = new System.Drawing.Point(12, 12); - this.Panels.Name = "Panels"; - this.Panels.SelectedIndex = 0; - this.Panels.Size = new System.Drawing.Size(876, 392); - this.Panels.TabIndex = 3; - // - // StaticPanel - // - this.StaticPanel.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(39)))), ((int)(((byte)(40)))), ((int)(((byte)(34))))); - this.StaticPanel.Controls.Add(this.RecreateStaticButton); - this.StaticPanel.Controls.Add(this.StaticFileDownloadButton); - this.StaticPanel.Controls.Add(this.StaticFilesRefreshButton); - this.StaticPanel.Controls.Add(this.StaticFileUploadButton); - this.StaticPanel.Controls.Add(this.StaticFileEditTextbox); - this.StaticPanel.Controls.Add(this.StaticFileDeleteButton); - this.StaticPanel.Controls.Add(this.StaticFileSaveButton); - this.StaticPanel.Controls.Add(this.StaticFileCreateButton); - this.StaticPanel.Controls.Add(this.label6); - this.StaticPanel.Controls.Add(this.StaticFileListBox); - this.StaticPanel.Location = new System.Drawing.Point(4, 22); - this.StaticPanel.Name = "StaticPanel"; - this.StaticPanel.Padding = new System.Windows.Forms.Padding(3); - this.StaticPanel.Size = new System.Drawing.Size(868, 366); - this.StaticPanel.TabIndex = 5; - this.StaticPanel.Text = "Static Files"; - // - // RecreateStaticButton - // - this.RecreateStaticButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.RecreateStaticButton.Location = new System.Drawing.Point(471, 6); - this.RecreateStaticButton.Name = "RecreateStaticButton"; - this.RecreateStaticButton.Size = new System.Drawing.Size(144, 22); - this.RecreateStaticButton.TabIndex = 33; - this.RecreateStaticButton.Text = "Recreate Static Directory"; - this.RecreateStaticButton.UseVisualStyleBackColor = true; - this.RecreateStaticButton.Click += new System.EventHandler(this.RecreateStaticButton_Click); - // - // StaticFileDownloadButton - // - this.StaticFileDownloadButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.StaticFileDownloadButton.Location = new System.Drawing.Point(296, 6); - this.StaticFileDownloadButton.Name = "StaticFileDownloadButton"; - this.StaticFileDownloadButton.Size = new System.Drawing.Size(71, 22); - this.StaticFileDownloadButton.TabIndex = 32; - this.StaticFileDownloadButton.Text = "Download"; - this.StaticFileDownloadButton.UseVisualStyleBackColor = true; - this.StaticFileDownloadButton.Click += new System.EventHandler(this.StaticFileDownloadButton_Click); - // - // StaticFilesRefreshButton - // - this.StaticFilesRefreshButton.Location = new System.Drawing.Point(65, 6); - this.StaticFilesRefreshButton.Name = "StaticFilesRefreshButton"; - this.StaticFilesRefreshButton.Size = new System.Drawing.Size(71, 22); - this.StaticFilesRefreshButton.TabIndex = 31; - this.StaticFilesRefreshButton.Text = "Refresh"; - this.StaticFilesRefreshButton.UseVisualStyleBackColor = true; - this.StaticFilesRefreshButton.Click += new System.EventHandler(this.StaticFilesRefreshButton_Click); - // - // StaticFileUploadButton - // - this.StaticFileUploadButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.StaticFileUploadButton.Location = new System.Drawing.Point(219, 6); - this.StaticFileUploadButton.Name = "StaticFileUploadButton"; - this.StaticFileUploadButton.Size = new System.Drawing.Size(71, 22); - this.StaticFileUploadButton.TabIndex = 30; - this.StaticFileUploadButton.Text = "Upload"; - this.StaticFileUploadButton.UseVisualStyleBackColor = true; - this.StaticFileUploadButton.Click += new System.EventHandler(this.StaticFileUploadButton_Click); - // - // StaticFileEditTextbox - // - this.StaticFileEditTextbox.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.StaticFileEditTextbox.Location = new System.Drawing.Point(219, 31); - this.StaticFileEditTextbox.Multiline = true; - this.StaticFileEditTextbox.Name = "StaticFileEditTextbox"; - this.StaticFileEditTextbox.ScrollBars = System.Windows.Forms.ScrollBars.Both; - this.StaticFileEditTextbox.Size = new System.Drawing.Size(643, 329); - this.StaticFileEditTextbox.TabIndex = 29; - // - // StaticFileDeleteButton - // - this.StaticFileDeleteButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.StaticFileDeleteButton.Location = new System.Drawing.Point(714, 6); - this.StaticFileDeleteButton.Name = "StaticFileDeleteButton"; - this.StaticFileDeleteButton.Size = new System.Drawing.Size(71, 22); - this.StaticFileDeleteButton.TabIndex = 28; - this.StaticFileDeleteButton.Text = "Delete"; - this.StaticFileDeleteButton.UseVisualStyleBackColor = true; - this.StaticFileDeleteButton.Click += new System.EventHandler(this.StaticFileDeleteButton_Click); - // - // StaticFileSaveButton - // - this.StaticFileSaveButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.StaticFileSaveButton.Location = new System.Drawing.Point(791, 6); - this.StaticFileSaveButton.Name = "StaticFileSaveButton"; - this.StaticFileSaveButton.Size = new System.Drawing.Size(71, 22); - this.StaticFileSaveButton.TabIndex = 27; - this.StaticFileSaveButton.Text = "Save"; - this.StaticFileSaveButton.UseVisualStyleBackColor = true; - this.StaticFileSaveButton.Click += new System.EventHandler(this.StaticFileSaveButton_Click); - // - // StaticFileCreateButton - // - this.StaticFileCreateButton.Location = new System.Drawing.Point(142, 6); - this.StaticFileCreateButton.Name = "StaticFileCreateButton"; - this.StaticFileCreateButton.Size = new System.Drawing.Size(71, 22); - this.StaticFileCreateButton.TabIndex = 26; - this.StaticFileCreateButton.Text = "Add"; - this.StaticFileCreateButton.UseVisualStyleBackColor = true; - this.StaticFileCreateButton.Click += new System.EventHandler(this.StaticFileCreateButton_Click); - // - // label6 - // - this.label6.AutoSize = true; - this.label6.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label6.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.label6.Location = new System.Drawing.Point(6, 10); - this.label6.Name = "label6"; - this.label6.Size = new System.Drawing.Size(53, 18); - this.label6.TabIndex = 14; - this.label6.Text = "Files:"; - this.label6.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // StaticFileListBox - // - this.StaticFileListBox.Anchor = ((System.Windows.Forms.AnchorStyles)(((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Bottom) - | System.Windows.Forms.AnchorStyles.Left))); - this.StaticFileListBox.FormattingEnabled = true; - this.StaticFileListBox.HorizontalScrollbar = true; - this.StaticFileListBox.Location = new System.Drawing.Point(6, 31); - this.StaticFileListBox.Name = "StaticFileListBox"; - this.StaticFileListBox.Size = new System.Drawing.Size(207, 329); - this.StaticFileListBox.TabIndex = 0; - this.StaticFileListBox.SelectedIndexChanged += new System.EventHandler(this.StaticFileListBox_SelectedIndexChanged); - // - // ControlPanel - // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(39)))), ((int)(((byte)(40)))), ((int)(((byte)(34))))); - this.ClientSize = new System.Drawing.Size(900, 415); - this.Controls.Add(this.Panels); - this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); - this.Name = "ControlPanel"; - this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; - this.Text = "/tg/station 13 Server Control Panel"; - this.ChatPanel.ResumeLayout(false); - this.ChatPanel.PerformLayout(); - ((System.ComponentModel.ISupportInitialize)(this.ChatPortSelector)).EndInit(); - this.ChatProviderSelectorPanel.ResumeLayout(false); - this.ChatProviderSelectorPanel.PerformLayout(); - this.panel1.ResumeLayout(false); - this.panel1.PerformLayout(); - this.ServerPanel.ResumeLayout(false); - this.ServerPanel.PerformLayout(); - ((System.ComponentModel.ISupportInitialize)(this.AutoUpdateInterval)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.PortSelector)).EndInit(); - this.BYONDPanel.ResumeLayout(false); - this.BYONDPanel.PerformLayout(); - ((System.ComponentModel.ISupportInitialize)(this.MinorVersionNumeric)).EndInit(); - ((System.ComponentModel.ISupportInitialize)(this.MajorVersionNumeric)).EndInit(); - this.RepoPanel.ResumeLayout(false); - this.RepoPanel.PerformLayout(); - ((System.ComponentModel.ISupportInitialize)(this.TestmergeSelector)).EndInit(); - this.Panels.ResumeLayout(false); - this.StaticPanel.ResumeLayout(false); - this.StaticPanel.PerformLayout(); - this.ResumeLayout(false); - - } - - #endregion - private System.ComponentModel.BackgroundWorker RepoBGW; - private System.ComponentModel.BackgroundWorker ServerStartBGW; - private System.Windows.Forms.TabPage ChatPanel; - private System.Windows.Forms.Label label5; - private System.Windows.Forms.Label label4; - private System.Windows.Forms.Label label3; - private System.Windows.Forms.Label label2; - private System.Windows.Forms.ComboBox IRCModesComboBox; - private System.Windows.Forms.Label label1; - private System.Windows.Forms.TextBox AdminChannelsTextbox; - private System.Windows.Forms.TextBox WDChannelsTextbox; - private System.Windows.Forms.TextBox GameChannelsTextbox; - private System.Windows.Forms.TextBox DevChannelsTextbox; - private System.Windows.Forms.TextBox ChatNicknameText; - private System.Windows.Forms.TextBox ChatServerText; - private System.Windows.Forms.TextBox AuthField2; - private System.Windows.Forms.TextBox AuthField1; - private System.Windows.Forms.TextBox ChatAdminsTextBox; - private System.Windows.Forms.Button ChatRefreshButton; - private System.Windows.Forms.Label ChatNicknameTitle; - private System.Windows.Forms.NumericUpDown ChatPortSelector; - private System.Windows.Forms.Label ChatPortTitle; - private System.Windows.Forms.Label ChatServerTitle; - private System.Windows.Forms.Button ChatApplyButton; - private System.Windows.Forms.Label AuthField2Title; - private System.Windows.Forms.Label AuthField1Title; - private System.Windows.Forms.Button ChatReconnectButton; - private System.Windows.Forms.Label ChatStatusLabel; - private System.Windows.Forms.Label ChatStatusTitle; - private System.Windows.Forms.CheckBox ChatEnabledCheckbox; - private System.Windows.Forms.Panel ChatProviderSelectorPanel; - private System.Windows.Forms.RadioButton DiscordProviderSwitch; - private System.Windows.Forms.RadioButton IRCProviderSwitch; - private System.Windows.Forms.Label ChatProviderTitle; - private System.Windows.Forms.Label ChannelsTitle; - private System.Windows.Forms.Label ChatAdminsTitle; - private System.Windows.Forms.Panel panel1; - private System.Windows.Forms.RadioButton AdminModeSpecial; - private System.Windows.Forms.RadioButton AdminModeNormal; - private System.Windows.Forms.TabPage ServerPanel; - private System.Windows.Forms.CheckBox AutostartCheckbox; - private System.Windows.Forms.CheckBox WebclientCheckBox; - private System.Windows.Forms.Button WorldAnnounceButton; - private System.Windows.Forms.TextBox WorldAnnounceField; - private System.Windows.Forms.Label WorldAnnounceLabel; - private System.Windows.Forms.ComboBox SecuritySelector; - private System.Windows.Forms.Label SecurityTitle; - private System.Windows.Forms.Button RemoveAllTestMergesButton; - private System.Windows.Forms.Label ServerPathLabel; - private System.Windows.Forms.Button CompileCancelButton; - private System.Windows.Forms.Label ProjectPathLabel; - private System.Windows.Forms.Label PortLabel; - private System.Windows.Forms.NumericUpDown PortSelector; - private System.Windows.Forms.Button TestMergeManagerButton; - private System.Windows.Forms.Button UpdateServerButton; - private System.Windows.Forms.Button ServerGRestartButton; - private System.Windows.Forms.CheckBox ServerGStopButton; - private System.Windows.Forms.Button ServerRestartButton; - private System.Windows.Forms.Button ServerStopButton; - private System.Windows.Forms.Button ServerStartButton; - private System.Windows.Forms.Label CompilerStatusLabel; - private System.Windows.Forms.Label CompilerLabel; - private System.Windows.Forms.Button compileButton; - private System.Windows.Forms.Button initializeButton; - private System.Windows.Forms.Label ServerStatusLabel; - private System.Windows.Forms.Label ServerStatusTitle; - private System.Windows.Forms.TabPage BYONDPanel; - private System.Windows.Forms.Label LatestVersionLabel; - private System.Windows.Forms.Label LatestVersionTitle; - private System.Windows.Forms.Label StagedVersionLabel; - private System.Windows.Forms.Label StagedVersionTitle; - private System.Windows.Forms.Label StatusLabel; - private System.Windows.Forms.Label VersionLabel; - private System.Windows.Forms.Label VersionTitle; - private System.Windows.Forms.Label MinorVersionLabel; - private System.Windows.Forms.Label MajorVersionLabel; - private System.Windows.Forms.Button UpdateButton; - private System.Windows.Forms.NumericUpDown MinorVersionNumeric; - private System.Windows.Forms.NumericUpDown MajorVersionNumeric; - private System.Windows.Forms.TabPage RepoPanel; - private System.Windows.Forms.Button RepoRefreshButton; - private System.Windows.Forms.ListBox BackupTagsList; - private System.Windows.Forms.Button ResetRemote; - private System.Windows.Forms.Button RecloneButton; - private System.Windows.Forms.TextBox RepoBranchTextBox; - private System.Windows.Forms.TextBox RepoRemoteTextBox; - private System.Windows.Forms.Button RepoGenChangelogButton; - private System.Windows.Forms.NumericUpDown TestmergeSelector; - private System.Windows.Forms.ListBox TestMergeListLabel; - private System.Windows.Forms.Label CurrentRevisionLabel; - private System.Windows.Forms.Button RepoApplyButton; - private System.Windows.Forms.Button HardReset; - private System.Windows.Forms.Button UpdateRepoButton; - private System.Windows.Forms.Button MergePRButton; - private System.Windows.Forms.Label IdentityLabel; - private System.Windows.Forms.Label TestMergeListTitle; - private System.Windows.Forms.Label RemoteNameTitle; - private System.Windows.Forms.Label BranchNameTitle; - private System.Windows.Forms.Label CurrentRevisionTitle; - private System.Windows.Forms.Button CloneRepositoryButton; - private System.Windows.Forms.Label RepoProgressBarLabel; - private System.Windows.Forms.ProgressBar RepoProgressBar; - private System.Windows.Forms.TabControl Panels; - private System.Windows.Forms.TabPage StaticPanel; - private System.Windows.Forms.Button StaticFileCreateButton; - private System.Windows.Forms.Label label6; - private System.Windows.Forms.ListBox StaticFileListBox; - private System.Windows.Forms.Button StaticFileUploadButton; - private System.Windows.Forms.TextBox StaticFileEditTextbox; - private System.Windows.Forms.Button StaticFileDeleteButton; - private System.Windows.Forms.Button StaticFileSaveButton; - private System.Windows.Forms.Button StaticFilesRefreshButton; - private System.Windows.Forms.Button StaticFileDownloadButton; - private System.Windows.Forms.Button TGSJsonUpdate; - private System.Windows.Forms.Button BYONDRefreshButton; - private System.Windows.Forms.Button ServerPageRefreshButton; - private System.Windows.Forms.Button RecreateStaticButton; - private System.Windows.Forms.NumericUpDown AutoUpdateInterval; - private System.Windows.Forms.CheckBox AutoUpdateCheckbox; - private System.Windows.Forms.Label AutoUpdateMLabel; - private System.Windows.Forms.TextBox projectNameText; - private System.Windows.Forms.CheckBox SyncCommitsCheckBox; - } -} diff --git a/TGS.ControlPanel/ControlPanel/ControlPanel.cs b/TGS.ControlPanel/ControlPanel/ControlPanel.cs deleted file mode 100644 index 8f9368a096..0000000000 --- a/TGS.ControlPanel/ControlPanel/ControlPanel.cs +++ /dev/null @@ -1,116 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Drawing; -using System.Windows.Forms; -using TGS.Interface; - -namespace TGS.ControlPanel -{ - /// - /// The main form - /// - sealed partial class ControlPanel : CountedForm - { - /// - /// List of instances being used by open control panels - /// - public static IDictionary InstancesInUse { get; private set; } = new Dictionary(); - - /// - /// The for this - /// - readonly IInstance Instance; - - /// - /// Constructs a - /// - /// The to use - /// The to use - public ControlPanel(IServer server, IInstance instance) - { - InitializeComponent(); - - FormClosed += ControlPanel_FormClosed; - Panels.SelectedIndexChanged += Panels_SelectedIndexChanged; - Panels.SelectedIndex += Math.Min(Properties.Settings.Default.LastPageIndex, Panels.TabCount - 1); - - Instance = instance; - - InstancesInUse.Add(Instance.Metadata.Name, this); - - Text = String.Format("TGS {0} Instance: {1}", server.Version, Instance.Metadata.Name); - - InitRepoPage(); - InitBYONDPage(); - InitServerPage(); - - UpdateSelectedPanel(); - } - - /// - /// Called when the is closed - /// - /// The sender of the event - /// The - void ControlPanel_FormClosed(object sender, FormClosedEventArgs e) - { - InstancesInUse.Remove(Instance.Metadata.Name); - } - - /// - /// Called from - /// - void Cleanup() - { - InstancesInUse.Remove(Instance.Metadata.Name); - } - - private void Main_Resize(object sender, EventArgs e) - { - Panels.Location = new Point(10, 10); - Panels.Width = ClientSize.Width - 20; - Panels.Height = ClientSize.Height - 20; - } - - /// - /// Updates the content of of - /// - void UpdateSelectedPanel() - { - switch (Panels.SelectedIndex) - { - case 0: //repo - PopulateRepoFields(); - break; - case 1: //byond - UpdateBYONDButtons(); - break; - case 2: //scp - LoadServerPage(); - break; - case 3: //chat - LoadChatPage(); - break; - case 4: //static - InitStaticPage(); - break; - } - Properties.Settings.Default.LastPageIndex = Panels.SelectedIndex; - } - - void Panels_SelectedIndexChanged(object sender, EventArgs e) - { - UpdateSelectedPanel(); - } - - bool CheckAdminWithWarning() - { - if (Instance.Administration != null) - { - MessageBox.Show("Only system administrators may use this command!"); - return false; - } - return true; - } - } -} diff --git a/TGS.ControlPanel/ControlPanel/ControlPanel.resx b/TGS.ControlPanel/ControlPanel/ControlPanel.resx deleted file mode 100644 index 74d58cbbd6..0000000000 --- a/TGS.ControlPanel/ControlPanel/ControlPanel.resx +++ /dev/null @@ -1,2185 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - 17, 17 - - - 993, 18 - - - - - AAABAAUAEBAAAAEAIABoBAAAVgAAABgYAAABACAAiAkAAL4EAAAgIAAAAQAgAKgQAABGDgAAMDAAAAEA - IACoJQAA7h4AAKCgAAABACAAqJwBAJZEAAAoAAAAEAAAACAAAAABACAAAAAAAAAEAAATCwAAEwsAAAAA - AAAAAAAAAAAAAAAAAABeTTgAWkgyA1pIMilOOyMoRjMaRz4qEMM+KhDESDUcSU07IyhaSDIpWUgxA15N - NwAAAAAAAAAAAAAAAADSzsgAPyoQAEk2HTk9KQ7ZOSUK5DciB+43Igf/OCQJ/zkkCe46JQrkPSgO2UYz - Gjk8JwwA0s3HAAAAAABVQywAbV1JAk47IytALBK6XEo0/2xcSP9lVUD/mo+C/5CEdf9jUz7/ZFQ//2BP - Ov9LOCC6SzgfK2hYRAJSPygA////AUg1HHA7JgzkOSQJ/29gTP9wYU7/6+nm/+rn5f9rXEj/U0Ap/1NB - Kv9YRzD/Z1ZC/zsmC+RINBtw////AYFzYgU/KhCxOiUK/4V4aP+hl4r/ZFQ///b19P/a1tH/c2RR/87J - w//X083/Y1M+/3BhTv8+Kg//PyoQsYFzYgVYRi8cPSkOz0MvFf/a1tH/vreu/2RTPv/08/H//Pz7//Py - 8P/+/f3/+vn5/3JjUP+Hemr/RjMa/z0pDs9XRS8cQi4VnDciB/xsXEn/9PPx/7y1rP9kUz7/9PLx//// - //////////////f29f9qW0f/oJaJ/3BhTf83Igf8Qi4VnD8qEN45JAn/saie//////+7tKv/ZFM+//Ty - 8f//////7u3q/5SJev95a1n/Y1M9/9rX0v+zq6D/OSQJ/z8qEN5EMBeFNiEF/oV4aP//////vLSr/2RT - Pv/08vH//////+7t6v+UiXr/eWtZ/2NTPv/X0s3/h3pq/zYhBf5EMBeFRzMaZTYhBfuAc2L//////7y0 - q/9kUz7/9PLx//////////////////f29f9qWkX/rqaa/4R3Zv82IQX7RzMaZUUxGIc3Igf/X085/97b - 1/+9tqz/ZFM+//Tz8f/8/Pv/8/Lw//79/f/6+fn/cWJP/5KHeP9kUz7/NyIH/0UxGIdMOSE0PikPvzkk - Cf+Lf2//in5u/2JSPf/29fT/2tbR/3JkUf/PycP/19PN/2NTPv9oWEP/OycM/z0pD79LOCA0RDEYAE89 - JSU/KxHWcGBN/1NAKf9wYU3/9vX0/+rn5P9pWkX/VEIq/1NBKv9YRzD/ZlZB/zwnDdZPPSUlRTEYAGdY - QwAAAAAAQCwSkkw5If9cSjT/bF1J/6ujl/+lm4//j4N0/4R3Z/9lVUD/YE86/0IuFP8/KxGSAAAAAGdY - QwAAAAAAQy8WAFA+JiFDLxZmPCcMnzciBvM5JAn/NyIH/zgjCP87Jgz/OCMI8zwnDZ9DLxVmTzwlIUEs - EwAAAAAAAAAAAAAAAAAAAAAAQi4VAEYyGQc+KQ+GPSgO6EEtE61CLhStPSkP6D0pDoZEMBcHQS0TAAAA - AAAAAAAAAAAAAOAHAADgBwAAgAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAB - AADAAwAAwAMAAPAPAAAoAAAAGAAAADAAAAABACAAAAAAAAAJAAATCwAAEwsAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAP///wD///8A////AP///wBVQy0AemxaA047I0NCLhTGQS0TxlE+J0aFd2cEYlI9AP// - /wD///8A////AP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF5NNwBhUDsMTToifk47 - I50+KQ+BQy8WmzwnDeg4Iwj/OCMI/z0oDupDLxacPikPgU47I51NOiF+YFA6DF1NNwAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAj4R1AB8HAABLOCBgOiUK+DYhBf82IQX/OCMI/zciB/82IQX/NiEF/zUg - Bf82IQX/NiEF/zYhBf86JQr4SzgfYB8HAACOgnMAAAAAAAAAAAAAAAAAAAAAAAAAAACMgXIACAAAAGBP - OS0/KxHVRzMa/2BPOf9eTTf/QSwT/0UyGf+Lf2//joJy/15NN/9fTjn/X085/11MNv9KNx//PysR1V9O - OC0GAAAAjIFyAAAAAAAAAAAAnpWIANrY0wFaSTIhRTIZdUAsEtc2IQb/d2lW/35xX/+Lf2//vLWs/8/K - xP/7+vr/wLqx/3NkUf9qWkb/alpG/25fS/+GeWj/ZFM+/z8qENdFMRh1WEYvIdTOyQGZjX8AbF1KAG1e - ShxFMhjMOSQJ/jciB/8zHgL/dmhV/1E/KP9oWET/+Pf2///////h3tr/UD4m/zsnDP9EMBb/QzAW/zsm - C/9BLRT/hnlp/0EtFP84Iwj+RTEYzG1dShxsXUkATDkgAE06Iig7JgzoNyIH/z4pD/9wYU3/ubKo/1RB - Kv9qWkb/+Pf3///////Hwbn/OyYL/4p+bv/c2NT/3NjT/6iek/87Jwz/e21b/1E+J/82IQb/OyYM6E06 - IihMOSAAWUcwAF1MNjs9KQ/0NSAE/2pbRv/z8vD/7evo/1JAKf9qWkb/+Pf3///////p5+T/tq6l/9vX - 0v///////////9XRy/9BLRP/in1t/25fS/81HwT/PSkP9F5NNztZRzEAAAAAAEo3H3A6JQr9NB8D/3xu - XP//////7Oro/1JAKf9qWkb/+Pf3/////////////////////////////////9XRy/9BLBP/kIR1/35w - Xv80HwP/OiUK/Uo3HnAAAAAAUkApRj4pD+M3Igf/OiUK/6iekv//////7Oro/1JAKf9qWkb/+Pf3//// - /////////////////////////////9XQy/9ALBL/k4d5/6qhlf86JQr/NyIH/z4pD+NSPyhGQy8V1jgj - CP82IQb/emxa//Tz8f//////7Oro/1JAKf9qWkb/+Pf3//////////////////f29f/x7+3/8vDu/6yj - mP83Igf/mI6A//b29P97bVv/NiEG/zgjCP9DLxXWRTEYxDgjCP82IQb/m5CD////////////7Oro/1JA - Kf9qWkb/+Pf3/////////////////6ackP9XRS7/WEYw/0IuFf91Z1T/4d7Z//////+bkIP/NiEG/zgj - CP9FMRjEUT4nXTsmDPU2IAX/XUw2/+3r6f//////7Oro/1JAKf9qWkb/+Pf3/////////////////6ac - kP9XRS7/WEYv/0IuFP92Z1T/4t/b/+7s6v9dTDb/NiAF/zsmDPVRPiddYlE7Gz8qENQ3Igb/SDUc/93a - 1f//////7Oro/1JAKf9qWkb/+Pf3//////////////////f29f/x7+3/8vDu/6yjmP83Igf/mo+B/9/c - 2P9INRz/NyIG/z8qENRhUTwbX044Jj8qEOA2IAX/WUcx/+3r6f//////7Oro/1JAKf9qWkb/+Pf3//// - /////////////////////////////9XQy/9ALBL/kod4/+/t6/9ZRzD/NiAF/z8qEOBfTjgmVEIqRjwn - DfI3IQb/TDkg/8bAuf//////7Oro/1JAKf9qWkb/+Pf3/////////////////////////////////9XR - y/9BLBP/kYV2/8nDvP9MOSH/NiEG/zwnDfJUQitGVUMsQj4pD+Q4Iwj/NiEG/047I//PysT/7uzq/1JA - Kf9qWkb/+Pf3///////p5+T/tq6l/9vX0v///////////9XRy/9CLhT/fG5c/1tKNP82IQX/OCMI/z4p - D+RVQyxCwry1Ak07I0Y/KhDUNyIH/0s4IP+akIL/jIBw/0UxGP9rW0f/+Pf3///////Fv7j/OiYL/4p+ - bv/c2NT/3NjT/6iek/87Jwz/eWpY/006Iv83Igf/PioQ1E06Ika8ta0CY1M+AEIvFQBYRjA1PCcN3lA9 - Jf96a1n/NyIH/zQeA/9qW0b/+Pf2///////e29b/TToi/zsmC/9DLxb/QzAW/zsmC/9BLRT/hnlp/0Et - FP89KA7eV0YvNUMwFgBhUDwAAAAAAG5fSwD5+PcBQCwSm0QwF/+FeGj/gnVk/3lrWf+Vinz/9fPy//// - ///39vX/ubGo/4F0Y/9yYk//alpG/25fS/+GeWj/Y1M9/zciB/9BLhSb+fr5AW5gTAAAAAAAAAAAAAAA - AABBLRMASjcfVjsmDPs7Jgv/Qy8W/047I/9aSDL/hHdm/6CWif9xYk7/cWJP/52Thv+DdWX/Xk04/11M - Nv9KNx//OCMH/zsmDPtKNx9WQS0TAAAAAAAAAAAAAAAAAAAAAABcSzUAY1I9E006ImxFMRieQCsSxzgj - CPU2IAX/NiEG/zYhBv81IAT/NSAE/zYhBv82IQb/NSAF/zciB/U+KhDHQy8Wnko3H2xiUTwTXEo0AAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAKGXiwDJxL0BalpGDT4qEHM5JQrzOCMI/zgjCP86Jgv7OyYL+zgj - CP84Iwj/OSQK8z4pD3NnV0MNt66lAZKGeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAASzggAFNBKgVBLRSBPysR60QwFrBNOyNpTzwlaUYyGbBALBLrQS0TgVA+JgVJNh0AAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAA/4H/APgAHwD4AB8A8AAPAIAAAQCAAAEAgAABAIAAAQCAAAEAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAADAMAAAwDgAAcA4AAHAPgAHwD+AH8AKAAAACAA - AABAAAAAAQAgAAAAAAAAEAAAEwsAABMLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAHlrWQCGeWkEVkQtRkUyGclEMBfJU0EpR52UhwZ7blwAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAalpHAAAAAABlVUAmcGBNQG5fTDE8Jw0dXEs1L0g1HIw9KQ7qOCMI/zgjCP8+KQ/sTToikltK - My88Jw0dbl9MMXBgTUBlVUAmAAAAAGdXQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAABaSDIAY1I9IEQwF8pBLRP5PysR7TkkCeA+KQ/rOSUK/jgjCP84Iwj/OCMI/zgj - CP85JAr+PSkP6zkkCeA/KxHtQS0T+UQwF8piUTwgWUgxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAeGpXALy2rQJKNx+MOSQK/zchBv83Igb/NyIH/zgiB/84Iwj/NyIH/zUg - Bf81IAX/NyIH/zciB/83Igf/NyIH/zciBv83Igf/OSQK/0o3Hoy6s6oCdmhWAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAANnUzwA0HwMAXk44Rz4pD+05JQr/RTEY/0g1HP9GMhn/OSQJ/zUf - BP9AKxH/e21b/3xuXP9HMxr/RjIZ/0g0HP9INBv/RzQb/0EtFP83Igf/PSgO7V5NN0czHgIA2dTPAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ2ThgAWAAAAYlE8ElRCK2BFMRfVNiEG/1xLNf+pn5T/lIl7/6ee - kv+DdmX/bl9L/763rv/5+Pf/8vHv/66mm/+Wi33/lop8/5aKfP+Wi33/mo+C/4l9bf9HNBv/Qy8W1VRC - K2BhUDsSJxAAAI+CcwAAAAAAAAAAAAAAAAByY1AAg3ZlFVE/J2lALBLFOyYM+jgjCP81IAT/a1tH/4Z5 - af83Igf/e21b//X08//39vX///////v6+f+aj4L/STYd/z4pD/89KA7/PSgO/z0pDv9DLxb/dGZT/5WK - e/8+KQ//OyYL+j8rEcVQPSVpgHJhFW5eSwAAAAAAAAAAAEw6IgBgTzpiPioQ/jgjCP84Iwj/OCMI/zMe - Av9pWUT/g3Zl/zEcAP93aVb//Pv7////////////1tLN/0UyGf83Igf/Qi4U/0MvFv9DLxb/QCwS/zci - B/83IQb/kIR1/15NN/82IAX/OCMI/z4qEP5gTzliTDkhAAAAAAAAAAAAPCgNAEMvFWk5JAn/OCMI/zgj - CP86JQr/V0Uv/66lmv+Ie2v/MRwA/3dpVv/8+/v///////////+5sqn/NSAF/0k2Hv/DvLT/1tHM/9XR - y//Mx8D/Z1dD/zMdAv92aFX/cWJP/zUgBP84Iwj/OSQJ/0MvFmk8KA0AAAAAAAAAAAAAAAAAU0AphDkk - Cf84Iwj/OCIH/4x/cP/q5+T//////4p+bv8xHAD/d2lW//z7+////////////8fBuv9oWUT/fW9d/+7s - 6v////////////////+Wi3z/Mh0B/3VmU/+YjX//NyIH/zgjCP85JAn/U0EphAAAAAAAAAAAemlYAAAA - AABPPSWYOSQJ/zgjCP87Jgv/vbat////////////in1u/zEcAP93aVb//Pv7/////////////Pz8//j3 - 9v/5+Pj//v7+/////////////////5iMf/8yHQH/d2hW/7ixp/87Jgv/OCMI/zkkCf9PPSWYAAAAAHRm - UgBcSzUAZVQ/J0EtE9M4Iwj/OCMI/z0pDv/JxL3///////////+KfW7/MRwA/3dpVv/8+/v///////// - ////////////////////////////////////////mIx+/zIdAf93aFb/xb+3/z0pD/84Iwj/OCMI/0Et - E9NjUz4nW0kzAHlrWBBJNh2oOSQJ/zgjCP82IQb/U0Ap/+He2v///////////4p9bv8xHAD/d2lW//z7 - +/////////////////////////////////////////////////+YjH7/Mh0B/3VnVP/d2dX/U0Ep/zYh - Bv84Iwj/OSQJ/0g1HKh3aVcQTjsjjDsmC/k4Iwj/NyIH/0AsEv+zq6H/////////////////in1u/zEc - AP93aVb//Pv7/////////////////////////////////////////////////4t/b/8yHAD/c2RR//v7 - +v+0raP/QCwT/zciB/84Iwj/OyYL+U47I4xFMRjoOCMH/zgjCP82IAX/hHdm//r6+f////////////// - //+KfW7/MRwA/3dpVv/8+/v///////////////////////f29f/Szsj/zcjC/87Jw/+wqJ7/Tzwk/zUf - BP+MgHD///7+//v6+v+Ed2f/NiAF/zgjCP84Iwf/RTEY6E48JJs5JAn/OCMI/zUgBf97bVv/+vn5//// - /////////////4p9bv8xHAD/d2lW//z7+///////////////////////4d7Z/1A+Jv89KQ//PioQ/zkk - Cv89KQ//nJGE/+zq5///////+vn5/3ttW/81IAX/OCMI/zkkCf9OPCSbYlI9O0AsEuk4Iwf/NyIH/0Yy - Gf/Tzsj/////////////////in1u/zEcAP93aVb//Pv7///////////////////////h3tn/UD4m/z0p - D/8+KhD/OSQJ/z0pD/+ckYT/7Oro///////Tzsj/RjIZ/zciB/84Iwf/QCwS6WJSPTuooJQFRTEYoDgj - CP84Iwj/NiEG/6KYi/////////////////+KfW7/MRwA/3dpVv/8+/v///////////////////////f2 - 9f/Szsj/zcjC/87Jw/+wqJ3/Tzwk/zUfBP+MgHD//////6KYi/82IQX/OCMI/zgjCP9FMRigqJ+TBQAA - AABOOyOLOSQJ/zgjCP84Iwj/sKid/////////////////4p9bv8xHAD/d2lW//z7+/////////////// - //////////////////////////////////+Lf2//MhwA/3NkUf/+/v7/sKid/zgjCP84Iwj/OSQJ/048 - I4sAAAAAycO7BUk1Ha04Iwj/OCIH/0ArEf/MxsD/////////////////in1u/zEcAP93aVb//Pv7//// - /////////////////////////////////////////////5iMf/8yHQH/dGVS//79/f/Mx8D/QCsR/zgi - B/84Iwj/STYdrcjEvAV8b10UQzAWzDgjCP84Iwf/PikP/62kmf/9/Pz///////////+KfW7/MRwA/3dp - Vv/8+/v/////////////////////////////////////////////////mIx+/zIdAf90ZVL/+fj3/66m - m/8+KQ//OCMH/zgjCP9EMBbMfW9eFGVVQCZBLBPhNyIH/zgjCP83Igf/Qy8V/5yShf/18/L//////4p9 - bv8xHAD/d2lW//z7+/////////////z8/P/49/b/+fj4//7+/v////////////////+YjH//Mh0B/3Rl - Uv+nnZH/Qy8W/zciB/84Iwj/NyIH/0EsE+FlVUAmhHZmDE48JIE8Jw3zOCMI/zgjCP81IAX/VkQt/9bS - zf/+/v7/in1t/zEcAP93aVb//Pv7////////////x8G6/2hZRP99b13/7uzq/////////////////5aL - fP8yHQH/cWJO/3RlUv80HwP/OCMI/zgjCP88Jw3zTTsjgYF0YwxjUj0AfG5dBE88JV4/KxHkOCMI/zUg - BP91Z1T/kIV2/3NlUv9XRS//Mx4C/3dpVv/8+/v///////////+3sKb/NSAE/0k2Hv/DvLT/1tHM/9XR - y//Mx8D/Z1dD/zMdAv92aFX/cmNP/zUgBP84Iwj/PyoQ5E48JF58blwEY1I9AAAAAAByZVEA////AF5N - OEk+KhDmNR8E/3ZnVP9tXUn/LxkA/zUfBP80HgP/d2hW//z7+////////////9LNx/9CLhX/OCMI/0Iu - FP9DLxb/Qy8W/0AsEv83Igf/NyEG/5CEdf9eTTf/NiAF/z4qEOZdTTdJ////AHBhTgAAAAAAAAAAAAAA - AACBc2MAvreuA0UxGKM1IAX/cWJP/5qQgv9pWUX/Wkgy/0g1HP9/cWD//Pv7////////////+fj4/5SJ - ev9GMhn/OyYM/zwnDf89KA7/PSkO/0MvFv90ZlP/lYp8/z4qD/84Iwj/RTEYo764sAOBdGQAAAAAAAAA - AAAAAAAAAAAAAAAAAAA6JQoASzggXDkkCfxDLxb/bF1J/4ByYf+NgXL/lop8/62kmf/g3dn///////j3 - 9v/f29f/2NTP/8/KxP/CvLT/pZyQ/5WKfP+Wi33/mo+C/4l9bf9HNBv/NyIG/zolCvxLOCBcOiUKAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAF1MNgBdTDYkQCwS4jciB/81HwT/NR8E/zciB/88Jw3/QS0T/1pI - Mv+UiHr/cGBN/0s4H/9KNx//cGBN/5aLff9fTzn/RzMa/0c0G/9BLRT/NyIH/zciBv84Iwj/QCwS4l1M - NiRdTDYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAi39wAI2AcgVWRC1lSDUcqEMvFs0/KxHqOiUK/Dgj - CP84Igf/NyEG/zUgBf81IAT/NyIH/zciB/81IAX/NSAF/zYhBv83Igf/NyIH/zolCvw+KhDqQS0UzUYy - GahUQiplkIR1BYyAcQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJuRhACzq6IDeGpYEWNT - PipCLhV5OiUK8jgjCP84Iwj/OCMI/zgjCP84Igf/OCIH/zgjCP84Iwj/OCMI/zgjCP85JAryQi4UeWFQ - OypvX0wRpJqOA4x/cQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAASzgfAFVDKwRCLhRtOiYL8jgjCP84Iwj/OiUL+0EsE+dBLRPnOyYL+zgjCP84Iwj/OiUL8kEt - E21RPicESTUdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAU0EqAGBPOgRFMhl9QS0T7Ek2HblNOiJpZFQ/MGhYQzBRPidpSzkguUIu - FOxEMRd9XEw2BFE/KAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/+B///wAA//4A - AH/8AAA//AAAP/AAAA/AAAADwAAAA8AAAAPAAAADwAAAA4AAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AACAAAABAAAAAAAAAAAAAAAAAAAAAIAAAAHgAAAH4AAAB/AAAA/wAAAP8AAAD/wAAD//gAH//8AD/ygA - AAAwAAAAYAAAAAEAIAAAAAAAACQAABMLAAATCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGxc - SACShXcEaVlFT0o3HstINBzLZFM+T4R3ZwRkUz4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AACXjH4AAAAAAGdXQyNRPiaTQCsR6zkkCf85JAn/PyoQ6048JJR+cF8v////AIl8bAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1c/OAGtcSAB0ZVMib2BMU4F0 - Y1OMgHBJSTYdIT4pDx1rW0cmXUw2dEIuFNY6JQr+OCMH/zgjCP84Iwj/OCMI/zolC/9HMxrgW0kzdWtb - RyY+KQ8dSTYdIYyAcEmBdGNTb2BMU3RlUyJrXEgAysjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfW9eAIR3 - Zw1MOSGsQS0T/0UxGP5FMRj4OSQK4zolC+FBLRPmPSkO/DgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP83Igf/PSgO/EEtE+Y6JQvhOSQK40UxGPhFMRj+QS0T/0s5IKyEd2YNfW9dAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AACgl4sAAAAAAGFQOmw9KA76NyIH/zciB/83Igf/OCMI/zgjCP84Iwf/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjB/84Iwj/OCMI/zciB/83Igf/NyIH/z0oDvpgTzpsAAAAAKKY - jAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAABXRS4Aa1tHJEQxF9U4Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zYhBf82IQX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9EMBfValpGJFZELQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAKCWiQDEvbUHVEIrmjolCv84Iwj/NiEF/zUgBP81IAT/NSAE/zUg - BP83Igf/OCMI/zgjCP82IQb/OSQJ/2BPOf9gUDr/OSQJ/zUgBP81IAT/NSAE/zUgBP81IAT/NSAE/zUg - BP81IAT/NiEG/zgjCP86JQr/VEEqmsG6sweek4cAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAl41+AODd2ARtXUleQi4U9DciB/85JAn/YE86/31v - Xf98b13/fG9d/3lrWf9KNx//NB8D/zYgBf9PPSX/n5WI/+zq5//t6+j/oZeK/21dSv94aVf/fG9d/3xv - Xf98b13/fG9d/3ttXP9wYU7/VUMs/zomC/82IQb/Qi4U9GxdSV7h3dkEl41+AAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AGtaRgCNgHAFY1I9OlNBKp9EMBbvOCMI/zgj - CP8+Kg//wbuy/7Wto/+il4v/oZeL/8nEvf+so5j/alpG/4l9bf/b19L//v79////////////7Orn/763 - rv+onpP/opiM/6OYjP+jmIz/opiM/6Sajv+wp53/w721/6ifk/9OOyP/NiEG/0QwFu9TQiqfY1M+Oo2A - cQVrW0cA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wANAAAAqKCUDlxKND9JNh2fQCwS7Tkk - Cf83Igf/OCMI/zgjCP8/KxH/wLqx/11MNv81IAT/NR8E/4+DdP/+/v7/+Pf2//v7+/////////////// - ///Qy8T/Y1I9/z0pD/84Iwj/NyIH/zciB/83Igf/NyIH/zciB/85JAn/TToh/5qPgv+2rqT/RzQb/zYh - Bv85JAn/QCwS7Uc0G59ZRzE/p52RDh4IAAD08e8AAAAAAAAAAAAAAAAAAAAAAJ+WiQCjmo0YX085sj8r - EfM4Iwj/OCMH/zgjCP84Iwj/OCMI/zgjCP8/KxH/wLqx/15NN/82IAX/NSAF/4+DdP////////////// - /////////////+zq5/9jUj3/NB8D/zgjCP84Iwj/OCIH/zgjB/84Iwf/OCMH/zgjCP84Iwj/NiEG/zsm - C/+upZr/in5u/zYgBf84Iwj/OCMH/zgjCP8/KxHzXkw3sqGXihiflIcAAAAAAAAAAAAAAAAAAAAAAIF0 - YwB/cmEtSDUc7TchBv84Iwj/OCMI/zgjCP84Iwj/OCMI/zciB/89KQ7/wLmw/15NN/82IAX/NSAF/4+D - dP///////////////////////////8C6sf88Jw3/OCMI/zkkCf8+KhD/PysR/z8rEf8/KxH/PysR/zsm - DP83Igb/OCMI/zUgBP9sXEj/tKyi/zwoDv84Iwj/OCMI/zgjCP83IQb/SDUc7YByYS2CdGQAAAAAAAAA - AAAAAAAAAAAAAFZFLgBWRC0tPysR7TgjCP84Iwj/OCMI/zgjCP83Igf/NSAE/zwoDf9rXEj/1NDK/11M - Nv82IAX/NSAF/4+DdP///////////////////////////6KYi/82IQb/NyIG/0s4IP+6s6n/y8W+/8rE - vP/KxLz/ysS9/7uzqv9hUDv/NyEG/zYhBv9RPyj/vLSr/0g0HP83Igf/OCMI/zgjCP84Iwj/PysR7VZE - LS1XRS4AAAAAAAAAAAAAAAAAAAAAAEEtEwBINBsxOSUK7zgjCP84Iwj/OCMI/zgjB/9ALBL/cGFO/7ix - p//y8O7/8fDu/1tKM/82IAX/NSAF/4+DdP///////////////////////////5aKfP8yHAH/Mx0C/089 - Jf/p5+T///////////////////////////+xqZ7/OSQK/zciBv9KNx//vreu/1NBKv83IQb/OCMI/zgj - CP84Iwj/OSUK70c0GzFBLRMAAAAAAAAAAAAAAAAAAAAAAGVUPwB+cF5eQCwS/jgiB/84Iwj/OCMI/zYg - Bf+GeWj/9/b1////////////8fDu/1tKM/82IAX/NSAF/4+DdP///////////////////////////7ix - p/93aFb/d2lX/4p+bv/x7+3///////////////////////////+/ubD/PCgN/zciBv9INBv/1tHM/4p9 - bf81IAX/OCMI/zgjCP84Igf/QCwS/n5wXl5lVD8AAAAAAAAAAAAAAAAAAAAAAFpJMwBsXEhpPysR/zgj - CP84Iwj/OCMI/zYhBv+glYj/////////////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////// - //////////////7+/v/+/f3//v79//7+/v////////////////////////////////+/uLD/PCgN/zci - Bv9HNBv/3NjT/6GYi/82IQb/OCMI/zgjCP84Iwf/PysR/2xdSWlbSTMAAAAAAAAAAAAAAAAAn5WIAAAA - AABPPSV9OiYL/zgjCP84Iwj/OCMI/zgjCP+tpZr/////////////////8fDu/1tKM/82IAX/NSAF/4+D - dP////////////////////////////////////////////////////////////////////////////// - //+/uLD/PCgN/zciBv9HNBv/3NjT/6+nnP84Iwj/OCMI/zgjCP84Iwj/OiYL/089JX0AAAAAn5SIAAAA - AAD///8AY1M+AG5fS0BFMRjnNyIH/zgjCP84Iwj/OCMI/zkkCf+8taz/////////////////8fDu/1tK - M/82IAX/NSAF/4+DdP////////////////////////////////////////////////////////////// - //////////////////+/uLD/PCgN/zciBv9HNBv/3dnU/764r/85JAn/OCMI/zgjCP84Iwj/NyIH/0Ux - GOdsXUlAYlE8AP///wB7bVsAgHJhH0s4IMY4Iwj/OCMI/zgjCP84Iwj/NyIH/0MvFf/OycP///////// - ////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////////////////////////////////////// - //////////////////////////////////+/ubD/PCgN/zciBv9HNBv/3NjT/9HMxf9DLxX/NyIH/zgj - CP84Iwj/OCMI/zgjCP9LOCDGf3JgH3ttWwCimYwNUkAomjolC/84Iwj/OCMI/zgjCP84Iwj/OCMI/5CE - df/6+vn/////////////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////////////////////// - //////////////////////////////////////////////////++t67/PCcN/zciBv9HNBv/2tbQ//38 - /P+RhXb/OCMI/zgjCP84Iwj/OCMI/zgjCP86JQv/UT8omqCWiQ1ZSDJ6PioQ9jgjB/84Iwj/OCMI/zgj - CP82IAX/bl9L/+3r6f//////////////////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////// - //////////////////////////////////////////////////////////////////+qoZX/OCMI/zci - B/9HNBv/2dXQ///////u7Or/cWJO/zYgBf84Iwj/OCMI/zgjCP84Iwf/PioP9llHMXpKNx/tNyIH/zgj - CP84Iwj/OCMI/zYhBv9UQiv/19LN////////////////////////////8fDu/1tKM/82IAX/NSAF/4+D - dP////////////////////////////////////////////7+/v/5+Pj/+fj3//n49//5+Pf/9/b1/9PO - yP9fTjj/NiEG/zUgBP9PPCT/4t/c////////////2NTP/1RCK/82IQb/OCMI/zgjCP84Iwj/NyIH/0s3 - H+1XRS+1OiUK/zgjCP84Iwj/OCMI/zUgBP9uX0z/9/b1////////////////////////////8fDu/1tK - M/82IAX/NSAF/4+DdP///////////////////////////////////////////+Th3v91Z1T/aFhE/2lZ - Rf9pWUX/ZlVB/0o3Hv83Igf/OyYL/0s4IP+flYj//Pv7////////////9/b1/25fTP81IAT/OCMI/zgj - CP84Iwj/OiUK/1dGL7VrW0hMQCsS7zgjB/84Iwj/OCMI/zciB/9FMRj/zsnC//////////////////// - ////////8fDu/1tKM/82IAX/NSAF/4+DdP///////////////////////////////////////////9vX - 0v9GMhn/NB4D/zUgBP81IAT/NSAE/zciB/82IAX/a1tH/9/c1//8+/v/////////////////zsnC/0Ux - GP83Igf/OCMI/zgjCP84Iwf/QCsR72tbR0yglooMUj8ouTgjCP84Iwj/OCMI/zgjCP81IAX/iX1t//7+ - /f//////////////////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////////////////////// - /////////////9vX0v9GMhn/NB4D/zUgBP81IAT/NSAE/zciB/82IAX/a1tH/9/c2P/8+/v///////// - ///+/f3/iX1t/zUgBf84Iwj/OCMI/zgjCP84Iwj/Uj8ouaGXiwwbAwAAZVVAYD0pD/o4Iwj/OCMI/zgj - CP82IQb/UD4m/+Dd2P//////////////////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////// - /////////////////////////////+Th3v91Z1T/aFhE/2lZRf9pWUX/ZlVB/0k2Hv83Igf/OyYL/0s4 - IP+glon//Pv7///////g3Nj/UD4m/zYhBv84Iwj/OCMI/zgjCP89KQ/6ZVVAYBsDAABtXUoAcWNPE0Et - FMY4Iwj/OCMI/zgjCP84Iwf/PSkP/8S+tv//////////////////////8fDu/1tKM/82IAX/NSAF/4+D - dP////////////////////////////////////////////7+/v/5+Pj/+fj3//n49//5+Pf/9/b1/9LO - yP9eTjj/NiEG/zUgBP9PPCT/4+Dc///////Evrb/PSkP/zgjCP84Iwj/OCMI/zgjCP9BLRTGcWJQE21d - SgCwp5wAxb63C1I/KL83Igf/OCMI/zgjCP83Igf/RzMa/9nW0P//////////////////////8fDu/1tK - M/82IAX/NSAF/4+DdP////////////////////////////////////////////////////////////// - //////////////////+qoZX/OCMI/zciB/9HNBv/2dXQ///////Z1dD/RjMa/zciB/84Iwj/OCMI/zci - B/9SQCi/xsG4C7CpngByYk8AdmdVFUUxGNU4Iwj/OCMI/zgjCP82IQb/WEYw/+vp5v////////////// - ////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////////////////////////////////////// - //////////////////////////////////++t6//PCcN/zciBv9HNBv/2dXQ///////r6eb/WEYv/zYh - Bv84Iwj/OCMI/zgjCP9FMhjVdmhVFXFjUAB+cF4Af3FgNkUxGPA3Igf/OCMI/zgjCP81IAT/b2BN//n5 - +P//////////////////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////////////////////// - //////////////////////////////////////////////////+/ubD/PCgN/zciBv9HNBv/2dXQ//// - ///5+fj/b2BN/zUgBP84Iwj/OCMI/zciB/9FMRjwf3JgNn5wXwBKNh4AXUw2Sj0oDvk4Iwj/OCMI/zgj - CP82IAX/bF1J/+7s6v//////////////////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////// - //////////////////////////////////////////////////////////////////+/uLD/PCgN/zci - Bv9HNBv/2dXQ///////v7ev/bV1J/zYgBf84Iwj/OCMI/zgjCP89KA75XUw2Sko3HgA6JgsAY1M+ejwn - Df84Iwj/OCMI/zgjCP84Iwj/OiUL/3lqWP/j4N3/////////////////8fDu/1tKM/82IAX/NSAF/4+D - dP////////////////////////////////////////////////////////////////////////////// - //+/uLD/PCgN/zciBv9HNBv/2tbR/+bk4P96bFr/OyYL/zgjCP84Iwj/OCMI/zgjCP88Jw3/ZFM+ejsm - CwAAAAAATz0ljDkkCf84Iwj/OCMI/zgjCP84Iwj/OCMI/zYhBv9hUDr/zsnC//7+/v//////8fDu/1tK - M/82IAX/NSAF/4+DdP////////////////////////////7+/v/+/f3//v79//7+/v////////////// - //////////////////+/uLD/PCgN/zciBv9JNR3/x8G5/21dSf82IAX/OCMI/zgjCP84Iwj/OCMI/zgj - CP85JAn/Tz0ljAAAAAAAAAAAcGFOREo2HtQ4Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP83Igf/emxa//Dv - 7f//////8fDu/1tKM/82IAX/NSAF/4+DdP///////////////////////////7ixp/93aFb/d2lX/4p+ - bv/x7+3///////////////////////////+/ubD/PCgN/zciBv9JNh3/urKo/047I/83IQb/OCMI/zgj - CP84Iwj/OCMI/zgjCP9JNh3Ubl9LRAAAAADU0MsAAAAAAGFRPCROOyO3OiYL/TgjB/84Iwj/OCMI/zci - B/9KNh7/x8G5/8zGv//X087/2tbR/1hHMP82IQX/NSAF/4+DdP///////////////////////////5SJ - e/8yHAH/Mx0C/089Jf/p5+T///////////////////////////+xqZ7/OSQK/zciBv9LNx//u7Oq/088 - JP83Igb/OCMI/zgjCP84Iwf/OiYL/U47I7dhUTwkAAAAANTQywAAAAAAxL61AEUxGQCAcmEUSzggiz8r - Efo4Iwj/OCMI/zciBv9PPSX/u7Oq/048JP9EMRf/UD4m/z4qEP84Iwj/NSAF/4+DdP////////////// - /////////////52Thv82IQb/NyIG/0s4IP+6s6n/y8W+/8rEvP/KxLz/ysS9/7uzqv9hUDv/NyEG/zYh - Bv9RPyj/vLWs/0g1HP83Igf/OCMI/zgjCP8/KxH6Sjcfi31vXRRDLhUAxL61AAAAAAAAAAAAAAAAAAAA - AACFeGgAnJOFCmRUP3w+KhDzOCMI/zciBv9PPCX/urKp/0k2Hf82IQb/NiEG/zgjCP84Iwj/NSAF/4+D - dP///////////////////////////7qyqf86JQr/OCMI/zkkCf8+KhD/PysR/z8rEf8/KxH/PysR/zsm - DP83Igb/OCMI/zUgBP9rXEj/tK2i/zwoDv84Iwj/OCMI/z4qEPNjUz58mY+BCoJ2ZQAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAjIBwAJyShAhJNh61OCMI/zciBv9PPSX/urKp/0c0G/80HgP/NSAF/zYh - Bv83Igf/NSAE/4+DdP///////////////////////////+Xi3/9ZSDH/NB8E/zgjCP84Iwj/OCIH/zgj - B/84Iwf/OCMH/zgjCP84Iwj/NiEG/zsmC/+tpZr/in5u/zYgBf84Iwj/OCMI/0o3HrWckoUIjIBwAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnJGEACgRAABUQituOyYL/zciB/9KNh7/x8G5/6GX - iv+BdGP/bV5K/1hHMP9KNx7/OycM/5CEdf/////////////////////////////////Fv7f/XUw2/zwo - Df83Igf/NyIH/zciB/83Igf/NyIH/zciB/85JAn/TDoh/5qPgf+2rqT/RzQb/zciB/84Iwj/OyYL/1RC - K24oEQAAnJKGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF1MNgBiUjwtQCsR6Dci - B/86JQr/ZVVA/4p+bv+imIz/same/722rf+/uK//uLGn/9XRy//5+Pj///////////////////////z8 - +//9/fz/7evo/8bAuP+so5j/p52S/6Sajv+imIz/opiM/6Sajv+wp5z/w721/6ifk/9OOyP/NiEG/zgj - CP84Iwj/QCsR6GJSPC1dTDYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHhq - VwCNgnIIRTIYszgjCP84Iwj/NiAF/zUgBP82IQb/OyYM/0MwFv9QPSb/ZFQ//3FiT/9+cF//4d3Z//f2 - 9f/c2NP/saie/31vXf98b13/rqWa/9vY0//5+Pf/4+Hd/4x/cP97blz/fG9d/3ttXP9wYU7/VUMs/zom - C/83Igb/OCMI/zgjCP84Iwj/RTIZs46Dcwh4a1gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAMG5sAAvGQAAXEo0ejsmDP83Igf/NyIH/zgjCP84Iwj/OCMI/zciB/83IQb/NSAF/zUg - BP82IQX/YE85/21eSv9JNh3/OSQJ/zUgBP81IAT/OCMI/0g0HP9rW0f/YE86/zYgBf81HwT/NSAE/zUg - BP81IAT/NiEG/zgjCP84Iwj/NyIH/zciB/87Jgz/XEs1ei4YAADEvrQAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABYRjAAbl5LLFNBKrZEMRfaQi4U9DwnDfw5JQr/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/NiEF/zUgBf83Igf/OCMI/zgjCP84Iwj/OCMI/zciB/81IAX/NiEF/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zkkCf87Jgz8QCwS9EIuFdpRPie2bl9MLFhHMQAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARTEYALGpngp6bFoaa1xIO2JS - PWRRPyiMTjsjvzolC/A4Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP86JQrwTjsjv088JIxdSzVkZFM+O3FiTxqil4sKNCAGAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAPb19AD///8BnpOGCEIuFWY7JgzwOCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zsmC/BBLRRmnZOGCP///wH29fQAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkMrAG5dSQJGMxlmPCcN7zgjCP84Iwj/OCMI/zgj - CP84Iwf/OCMI/zsmC/87Jgv/OCMI/zciB/84Iwj/OCMI/zgjCP84Iwj/PCcM70QwF2ZmVkMCUT8oAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFlHMABxYU4CSTYdZT0o - Du84Igf/OCMJ/zwnDP1AKxHmSTYdvV1MNoleTTiJTDkhvUEtE+Y8KA39OCQJ/zciB/88KA3vSDUcZWlb - RgJUQywAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AABjUj0Af3FiA007InRFMhjvTjsjw11NN3xlVUAyi39vC////wH///8BlIl7C21dSTJjUz58UT8ow0cz - Gu9MOCB0eGpWA19ONwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAD///gf//8AAP//8A///wAA//AAAA//AAD/4AAAB/8AAP/gAAAH/wAA/8AAAAP/ - AAD/gAAAAf8AAP8AAAAA/wAA/AAAAAA/AADwAAAAAA8AAOAAAAAABwAA4AAAAAAHAADgAAAAAAcAAOAA - AAAABwAA4AAAAAAHAADgAAAAAAcAAOAAAAAABwAAwAAAAAADAACAAAAAAAEAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAQAAgAAAAAABAACAAAAAAAEAAIAA - AAAAAQAAgAAAAAABAACAAAAAAAEAAIAAAAAAAQAAgAAAAAABAACAAAAAAAEAAMAAAAAAAwAA4AAAAAAH - AADwAAAAAA8AAPgAAAAAHwAA/AAAAAA/AAD8AAAAAD8AAPwAAAAAPwAA/gAAAAB/AAD+AAAAAH8AAP8A - AAAA/wAA//AAAA//AAD//AAAP/8AAP/+AAB//wAA//8AAP//AAAoAAAAoAAAAEABAAABACAAAAAAAACQ - AQATCwAAEwsAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AHNjUf9zY1H/////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8Am5CD/z4pD/84Iwj/OCMI/zsnDP+NgXL/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wDCvLT/UD0m/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/0w4IP+2raP/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wBvXkv/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/2dX - RP////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AI2Bcv87Jgv/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OSQJ/4FzYv////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8Atq2j/0k1Hf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/RjMZ/6eek/////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AY1Q//zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/X085/9DMxv////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wCNgXL/OSQJ/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/gXNi/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AKee - k/9DLxf/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9AKxL/m5CD//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8ApJuQ/3ttW/+Ie2r/kIN0/5eMfv+il4z/q6OW/7Ssov+9tq3/yMK6/9DMxv////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8AysS+/1hHMP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9YRzD/ycS9/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wDQzMb/yMK6/722rf+0rKL/q6OW/6KXjP+XjH7/kIN0/4h7 - av97bVv/pJuQ/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ADolCv84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9CLhT/Sjcf/1NA - KP9fTzn/Xk02/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9cSzX/X085/1NAKP9KNx//Qi4U/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zolCv////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AGJRPf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/X085/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AKmg - lP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP+poJT/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wBDLxf/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Qi4U//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wB6alj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP96bFr/////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDHwrn/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/8O9tf////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8AU0Ao/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9QPSb/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8AlYp9/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/5eMfv////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ADwoDf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP87Jgv/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AGpbRv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/2ZWQv////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ALKq - oP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/sqqg//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wBINRz/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0YzGf////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wCDd2X/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OSQJ/1dGLv/Iwrr/yMK6/1dGLv84Iwj/OSQJ/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/gnRk/////wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDOyML/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/QCsS/5uQg/////////////// - ////////p56T/0IuFP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP/MxsD/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8AWEcw/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/emxa////////////////////////////////////////////iHtq/zkk - Cf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/VUQt/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8An5aI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/2ZW - Qv96bFr/emxa/3psWv96bFr/emxa/3psWv96bFr/emxa/3psWv96bFr/emxa/3psWv96bFr/dGRT/0Yz - Gf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9eTTb/0MzG//// - ///////////////////////////////////////////////////QzMb/Xk02/zgjCP84Iwj/Qi4U/1xL - Nf9pWUX/dGRT/3psWv96bFr/emxa/3psWv96bFr/emxa/3psWv96bFr/emxa/3psWv96bFr/emxa/3ps - Wv96bFr/emxa/3psWv9uYEz/ZlVB/048JP86JQr/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+floj/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Av7ev/z8qEP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/6adkf////////////////////////////// - ////////////////////////////////////////////////////////RjMZ/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/0YzGf+tpJj///////////////////////////////////////// - ///////////////////////////////////KxL7///////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////8/Lxf+hl4n/Y1M+/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/PyoQ/7+3r/////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wC0rKL/V0Yu/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP////////////////////////////////////////////////////////////// - /////////////////////////////3xuXf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OiUK/4l+ - bv////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////iX5u/zsnDP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/V0Yu/7Ss - ov////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AKeek/9OPCT/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj///////// - ////////opeM/6KXjP+il4z/opeM/6KXjP+il4z/opeM/6KXjP+il4z/opeM/6KXjP/JxL3///////// - //+Ie2r/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/ZlZC//////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////8O9 - tf+2raP/qJ2S/6KXjP+il4z/opeM/6KXjP+il4z/opeM/6KXjP+il4z/opeM/6KXjP+il4z/opeM/6KX - jP+il4z/opeM/6Sajv+yqZ//wLmx/87Iwv//////////////////////////////////////altG/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Tjwk/6eek/////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8Am5CD/0c0G/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy///////////////////////Szsj/ycS9/722 - rf+2raP/wryz//////////////////////////////////////////////////////////////////// - /////////////////////////////7Cnnf92Z1X/RjMZ/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/1VDLP+Qg3T///////////////////////////+ajoH/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/RzQb/52RhP////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wCQg3T/QS0T/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////pp2R/0Yz - Gf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9tX0r//////////////////////6uhlf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/QS0T/5CEdf////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AI2Bcv8+KQ//OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj///////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////ZlVB/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zsnDP+kmo7///////// - ////////lIh5/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/PCgN/4FzYv////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AgXNi/zol - Cv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////VUMs/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/42Bcv////////////////9mVUH/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OSQJ/3Nl - Uv////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AMnEvf9sXEj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////ZlZC/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/qJ2S/////////////////zolCv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/21dSf/KxL7/////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Awry0/2NT - Pv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj///////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////pp2R/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/z0p - Dv////////////////9/cmD/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/1xLNf+2raP/////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8AubOq/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////0c0 - G/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/dGZU/////////////////zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/7mxqP////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AKmglP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - /////////////////////////////////////////////6uhlf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP////////////////9eTTb/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP+poJT/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wCZkIL/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj///////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////9lVUD/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/k4d3////////////m5CD/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/m5CD/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8AjIBx/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - ////////////////////////////////////////////////////////////////////////PCgN/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/1VELf///////////9HM - x/85JAn/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/4yAcf////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AHtuXP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////0MzG/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/////////////////OSQJ/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9/cmD/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wBvXkv/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj///////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - /////////////6adkf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/PCgN/21d - Sf9tXUn/bV1J/21dSf9tXUn/bV1J/21dSf9tXUn/bV1J/21dSf9tXUn/bV1J/21dSf9tXUn/bV1J/21d - Sf9tXUn/YlI8/0MvF/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/8S+t////////////1dGLv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/b15L/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8AX085/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/SDUc/////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////+PgnT/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0g1HP////////////////////////////// - ////////////////////////////////////////////////////////////////////////wbux/1E+ - J/85JAn/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+onZL///////// - //9wYU3/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/19POf////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AFA+Jv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9QPSb/q6OW//////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////gnRk/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9INRz///////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////U0Ao/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/mo6B////////////hXdn/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9QPib/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wBCLhT/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/1pIMf+2raP///////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - /////////////3ZnVf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/SDUc//// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////8jCuv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv///////////5CEdf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Qi4U/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8AOCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/X043/7ato////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////9pWkX/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0g1HP////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////UT4n/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+Bc2L///////// - //+YjYD/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////ADgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9wYU3/zsjC//////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////aFdD/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9INRz///////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////3doVf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV////////////nZGE/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wA4Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OiUK/3xuXf////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - /////////////2hXQ/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/SDUc//// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+DeGb/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf///////////7KqoP86JQr/OSQJ/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wDPycL/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/RjMZ//////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////9oV0P/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0g1HP////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////0UxGP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP/PycL/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8AwLmx/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/1tKNf////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////aFdD/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9INRz///////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////9bSTT/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/wLmx/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ALKpn/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9qW0b///////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf//////////////////////aVpF/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/7KqoP////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wCimY3/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/empY//////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////3doVf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+kmo7/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8AlIh5/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/4d7a/////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////+GeWj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/lIp7/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AIV3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP+XjH7///////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf//////////////////////lot8/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/4Z5aP////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wB1aFT/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/pp2R//////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////6SbkP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8AZ1dE/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/7Ssov////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////+yqqD/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/Z1dE/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AEYzGf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP/CvLT///////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf//////////////////////wry0/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0YzGf////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AG9eS/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/0czH//////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////9DMxv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/altG/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AJuQ - g/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI//////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV////////////////////////////OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP+bkIP/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AMzGwP86JQr/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf///////////////////////////zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OSQJ/8jC - uv////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wBMOCD/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP85JAn///////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - //////////////////85JAn/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9JNR3/////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wBsXEj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/RTEY//////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV////////////////////////////QzAW/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/2lZRf////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wCXjH7/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/1RC - Kv////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf///////////////////////////1NAKP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/mI2A/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wDGwLj/OSQJ/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+elIb///////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - //////////////////+elIb/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zkkCf/GwLj/////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8ARzQb/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zkkCf9sXEj///////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV/////////////////////////////////25g - TP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/RjMZ/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8AZlZC/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9KNx////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf//////////////////////////////////////TToi/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9iUT3/////AP///wD///8A////AP///wD///8A////AP///wD///8Ak4d3/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP87Jwz/zsjC//////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////g3dl/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////////////////////9POyP88KA3/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/5KH - eP////8A////AP///wD///8A////AP///wD///8Aw762/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/opmN//////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////3doVf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////////////////// - ////////opeM/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/wLmx/////wD///8A////AP// - /wD///8A////AEYzGf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/cWJP//////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////9iUjz/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf////////////////////////////////////////////////92Z1X/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/0UxGP////8A////AP///wD///8A////AGNUP/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/Tjwk//////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////PCgN/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////////////////////////////////////1E+J/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/YVA7/////wD///8A////AI2Bcv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OycM/9DM - xv////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////ubGo/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////////////////// - ////////////////////////PCgN/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+PgnT/////AMK8 - tP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/6adkf////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////1pIMf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3tuXP///////////////////////////////////////////////////////////6ad - kf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/7+3r/9TQCj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////4l8 - bP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+QhHX///////// - ////////////////////////////////////////////////////////d2hV/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9TQCj/lYp9/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/1A9Jv////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////3tuXP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/wryz//////////////////////////////////// - //////////////////////////////////9TQCj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/l4x+//// - /wA7Jwz/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //+5s6r/hnlo/0ArEv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/UT4n//////////////////////////////////////////////////////////////////// - /////////////zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OyYL/////wD///8Aemxa/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/w721//////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////8O9tf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OiUK/8nEvf////////////// - /////////////////////////////////////////////////////////////8K8tP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3ttW/////8A////AM7Iwv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/29eS/////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ///////////////////DvbX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/QCsS/7u1rP////////////////////////////////////////////// - //////////////////////////////////9tX0r/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP/Py8X/////AP// - /wD///8AYU86/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj///////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////w721/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9HNBv/hnlo//// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9eTTb/////AP///wD///8A////ALSsov84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/4p+cP////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////8O9tf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/XEs1/722rf////////////////////////////////////////////// - ////////////////////////////////////////////////////////iX5u/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/tKyi/////wD///8A////AP///wD///8ASTYe/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP8/KhD///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ///////////////////DvbX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/3xu - Xf////////////////////////////////////////////////////////////////////////////// - /////////////////////////////0EtE/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/SDUc/////wD///8A////AP// - /wD///8A////AJiNgP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/6adkf////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////w721/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP98bl3///////////////////////// - /////////////////////////////////////////////////////////////////////////////6Sb - kP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/5qOgf////8A////AP///wD///8A////AP///wD///8AOycM/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9QPib///////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////8O9tf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/fG5d//////////////////////////////////////////////////// - //////////////////////////////////////////////////9TQCj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zwo - Df////8A////AP///wD///8A////AP///wD///8A////AHxuXf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/8G7 - sf////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ///////////////////DvbX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/3xu - Xf////////////////////////////////////////////////////////////////////////////// - ///////////////////AubH/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP98bl3/////AP///wD///8A////AP// - /wD///8A////AP///wDSzsj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9sXEj///////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////w721/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9cSzX/vbat//////////////////// - ////////////////////////////////////////////////////////////////////////aVpF/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/087I/////wD///8A////AP///wD///8A////AP///wD///8A////AGJS - PP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI//////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////8O9tf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/SDUc/4d7a/////////////////////////////// - /////////////////////////////////////////////zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/X085//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wC2r6T/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP+He2v///////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ///////////////////DvbX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/QS0T/722rf////////////////////////////////////////////// - /////////////4Z6af84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/7avpP////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AEo3H/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/QCsS//////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////w721/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP87Jwz/ysS+//////////////////////////////////////////////////////9AKxL/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/0k2Hv////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wCbkIP/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+kmo7///////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////ubGo/4J0ZP8/KhD/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/1NAKP////////////// - //////////////////////////////////+il4z/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+bkIP/////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ADwoDf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/bmBM//////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////3RmVP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/wry0//////////////////////////////////// - ////////bmBM/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP88KA3/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wB/cmD/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/42Bcv////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////g3hm/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/5GGdv///////////////////////////////////////////4yAcf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/f3Fh/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////ADgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+vp5z///////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //9XRi7/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP97blz///////// - //////////////////////////////////+tpJj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wA4Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/0MzG//////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////ubOq/zkkCf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////////////////// - ////////zsjC/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wDMxsD/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OSQJ//////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////89KQ7/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf////////////////////////////////////////////////84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/zcjB/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8Aq6GV/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/0w4IP////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////Y1Q//zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - ////////////////////////////////////////Sjcf/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/6ujlv////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AIl8bP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9tX0r///////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////3doVf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////////////////// - /////////////2xcSP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+Jfm7/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wBoV0P/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/joRz//////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+DeGb/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf////////////////////////////////////////////////+NgXL/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/Z1dE/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8ARzQb/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/7Cnnf////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - ////////////////////////////////////////r6ec/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0YzGf////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ADgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP/RzMf///////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////////////////// - /////////////9DMxv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AMrEvv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj///////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf//////////////////////////////////////////////////////OSQJ/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/8rGv/////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wCpoJT/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/TToi//////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////////////////////////////////////0w4IP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+poJT/////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AiHtq/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/29e - S/////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////////////////// - //////////////////9tX0r/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/h3tr/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AGZVQf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+Qg3T///////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf//////////////////////////////////////////////////////joRz/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/2ZWQv////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wBGMxn/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/qJ2S//////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////////////////////////////////////6idkv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9GMxn/////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AOCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/z0p - Dv+2r6T///////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////////////////// - /////////////722rf9AKxL/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8AycS9/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/5SKe/////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf///////////////////////////////////////////6KXjP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP/KxL7/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AKee - k/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/cGFN//////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////////////////////3VoVP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/qaCU//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wCGemn/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9UQir///////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV/////////////////////////////////1hH - MP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/4h7av////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8AZVVA/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0Iu - FP/CvLP///////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf//////////////////////x8C4/0UxGP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9mVUH/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AEUx - GP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OiUK/6KXjP////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - ////////p56T/zolCv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/QzAW//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wA4Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/fnFf//////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV////////////q6OW/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP////8A////AP///wD///8A////AP// - /wD///8A////AP///wDIwrr/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9fTjf///////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf///////////52RhP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/yMK6/////wD///8A////AP///wD///8A////AP///wD///8A////AHxu - Xf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0k2 - Hv/Py8X//////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - //+dkYT/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/fG5d//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Ap56T/zolCv84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Tjwk//////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////Z1dE/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9INRz///////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV////////////nZGE/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/m5CD/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wDEvrf/Qy8X/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9RPif/hnlo/3xuXf////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - /////////////2VVQP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/SDUc//// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf///////////52RhP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9DLxf/xL63/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wBVRC3/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9QPSb///////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////9nV0T/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0g1HP////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////g3hm/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - //+dkYT/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9VRC3/////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AHNj - Uf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////ZVVA/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9INRz///////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////3doVf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV////////////nZGE/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9zY1H/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Alot8/zgjCP85JAn/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/52RhP///////////8G7sf+7s6v///////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - /////////////2dXRP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/SDUc//// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////9QPSb/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/4FzYv///////////5iNgP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+Wi3z/////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wC7s6v/PyoQ/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+dkYT///////// - //93aFX/OCMI/zgjCP84Iwj/RjMZ/2JSPP98bl3/mI2A/7Ssov/OyML//////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////9pWUX/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0g1HP////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ///JxL3/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //+QhHX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/z4pD/+2raP/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wBPPCX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/nZGE////////////d2hV/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////dGZU/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9INRz///////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////U0Ao/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/mo6B////////////hXdn/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/088 - Jf////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AGpb - Rv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/52RhP///////////3doVf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - /////////////4J0ZP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/SDUc//// - //////////////////////////////////////////////////////////////////////////////// - ///////////////////CvLT/U0Ao/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/6idkv///////////3BhTf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/2ZVQf////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AiX5u/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+dkYT///////// - //93aFX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////+OhHP/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zwoDf9tXUn/bV1J/21dSf9tXUn/bV1J/21d - Sf9tXUn/bV1J/21dSf9tXUn/bV1J/21dSf9tXUn/bV1J/21dSf9tXUn/bV1J/2JRPf9FMRj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP/Dvrb///////// - //9XRi7/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/4l+bv////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wCvp5z/PCgN/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/nZGE////////////d2hV/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////raSY/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/////////////////OyYL/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OyYL/6mglP////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AM/Lxf9JNh7/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/52RhP///////////3doVf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////85JAn/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/VUMs////////////0MzG/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/STYe/8/Lxf////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AFVE - Lf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+dkYT///////// - //93aFX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - ////////////////////////////////////////////////////////////////////////Qi4U/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/5GGdv///////////5uQ - g/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/VUQt//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wCpoJT/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/nZGE////////////d2hV/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - /////////////////////////////////////////////39yYP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP////////////////9fTzn/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/6mglP////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////ADgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/52RhP///////////3doVf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - ///////////////////OyML/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9zZVL/////////////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wBiUT3/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+dkYT///////// - //93aFX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////3Bh - Tf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP89KQ7/////////////////gXNi/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9jVD//////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8ApJqO/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/nZGE////////////d2hV/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////Qy8X/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/pp2R/////////////////zolCv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/pJuQ/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wA4Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/52RhP///////////3doVf84Iwj/OSQJ/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////8rEvv8+KQ//OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//// - /////////////2ZWQv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8AX043/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+Sh3j///////// - ////////wry0/6adkf+JfGz/bV1J/1A9Jv85JAn/OCMI/zgjCP84Iwj/OCMI/zgjCP85JAn/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////z8nC/0k2Hv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP87Jgv/opeM/////////////////5aLfP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/19ON/////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AJ+WiP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/ZVVA//////////////////////////////////// - ///////////////////AubH/pJqO/4Z6af9qW0b/TToi/zgjCP84Iwj/OCMI/zkkCf84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////hnlo/zsm - C/84Iwj/OCMI/zkkCf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9qW0b//////////////////////6ujlv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+floj/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8AOCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP+PgnT///////////////////////////////////////////////////////// - /////////////////////////////722rf+gl4v/g3hm/2hXQ/9JNh7/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////npSG/2lZRf8/KhD/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/VUMs/4+CdP///////////////////////////5uQ - g/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AFpIMf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP8+KQ//W0k0/3doVf+Uinv/sKme/87Iwv////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////AubH/sqqg/6adkf+il4z/opeM/6KX - jP+il4z/opeM/6KXjP+il4z/opeM/6KXjP+il4z/opeM/6KXjP+il4z/opeM/6KXjP+imY3/sKme/7+3 - r//OyML//////////////////////////////////////2xcSP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Wkgx/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wCZkIL/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/0EtE/9eTTb/emxa/5eMfv+0rKL/0czH//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////jIBx/zwoDf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/5mQgv////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////ADgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/Qy8X/2FPOv98bl3/mZCC/7ewpv////////////////////////////// - //+2r6T///////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////0MzG/6KZjf9lVUD/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wBUQir/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9GMxn/Y1M+/3VoVP9INRz/OCMI/6ujlv////////////// - /////////////////////////////////////////////////////////////62kmP9xYk//PyoQ/z8q - EP9xYk//raSY//////////////////////////////////////////////////////////////////// - ////////sKme/3psWv96bFr/emxa/3psWv96bFr/emxa/3psWv96bFr/emxa/3psWv96bFr/bmBM/2ZV - Qf9OPCT/OyYL/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9UQir/////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AlIh5/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/qJ2S//////////////////////////////////// - //////////////////+il4z/ZlVB/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/WEcw/5SK - e//Py8X/////////////////////////////////////////////////raSY/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/lot8/////wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wA5JAn/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP+elIb////////////////////////////CvLT/iHtq/006Iv84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0c0G/96bFr/tq2j//// - ////////////////////////pJqO/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8ATzwl/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/52R - hP//////r6ec/3NlUv89KQ7/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP87Jgv/ZlVB/6KXjP//////opmN/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/048JP////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AI+C - dP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP+Qg3T/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wDOyML/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/0MzG//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AEw4IP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Sjcf/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wCKfnD/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/4yAcf////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDCvLT/m5CD/3Nl - Uv9JNR3/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Qy8X/2ZVQf+NgXL/tq2j/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDCvLT/m5CD/3Nl - Uv9JNh7/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9DLxf/ZlVB/42B - cv+2raP/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDCvLT/m5CD/3Nl - Uv9JNh7/OCMI/zgjCP84Iwj/OCMI/zkkCf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP85JAn/OCMI/zgj - CP84Iwj/OCMI/0MwFv9mVUH/jYFy/7ato/////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDAubH/mI2A/3Bh - Tf9CLhT/OSQJ/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Qi4U/3BhTf+YjYD/wLmx/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AEw4IP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/STYe/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8ATToi/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Sjcf/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wBRPif/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/Tjwk/////wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AFNA - KP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Tzwl//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AVEIq/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/UD0m/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wBYRzD/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/VEIq/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AFpIMf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/VUQt/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AW0k0/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/V0Yu/////wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wBcTDb/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/QCsS/3ZnVf+wqZ7/sKme/3ZnVf9AKxL/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/XEw2//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AGFQO/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0k2Hv+Bc2L/vbat/////wD///8A////AP// - /wD///8A////AMnEvf+NgXL/Tzwl/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/XEs1/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8AYlE9/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zkkCf84Iwj/OCMI/zkk - Cf9fTzn/m5CD/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8Ap56T/21dSf87Jgv/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/X043/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wBjVD//OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP8/KhD/c2VS/6+nnP////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AL22 - rf+Bc2L/Qy8X/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/X085/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AGpbRv84Iwj/OCMI/zgjCP84Iwj/UD4m/4h7 - av/CvLT/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDPy8X/lIp7/1dG - Lv84Iwj/OCMI/zgjCP84Iwj/ZlVB/////wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8AbV9K/19POf+bkIP/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AKeek/9tXUn/bFtH//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - //////////5/////////////////////////+B/////////////////////////gB/////////////// - /////////8AD////////////////////////AAD///////////////////////wAAD////////////// - ////////+AAAD//////////////////////gAAAH/////////////////////4AAAAH///////////// - ////wAf+AAAAAH/gA//////////////AAAAAAAAAAAAD/////////////4AAAAAAAAAAAAH///////// - ////AAAAAAAAAAAAAP////////////8AAAAAAAAAAAAA/////////////gAAAAAAAAAAAAB///////// - ///8AAAAAAAAAAAAAD////////////wAAAAAAAAAAAAAP///////////+AAAAAAAAAAAAAAf//////// - ///4AAAAAAAAAAAAAB////////////AAAAAAAAAAAAAAD///////////4AAAAAAAAAAAAAAH//////// - ///gAAAAAAAAAAAAAAf//////////8AAAAAAAAAAAAAAA///////////gAAAAAAAAAAAAAAB//////// - //+AAAAAAAAAAAAAAAH//////////wAAAAAAAAAAAAAAAP/////////+AAAAAAAAAAAAAAAAf/////// - //gAAAAAAAAAAAAAAAAf////////4AAAAAAAAAAAAAAAAAf///////+AAAAAAAAAAAAAAAAAAf////// - /gAAAAAAAAAAAAAAAAAAf//////4AAAAAAAAAAAAAAAAAAAf/////+AAAAAAAAAAAAAAAAAAAAf///// - AAAAAAAAAAAAAAAAAAAAAP////wAAAAAAAAAAAAAAAAAAAAAP///+AAAAAAAAAAAAAAAAAAAAAAf///4 - AAAAAAAAAAAAAAAAAAAAAB////gAAAAAAAAAAAAAAAAAAAAAH///+AAAAAAAAAAAAAAAAAAAAAAf///4 - AAAAAAAAAAAAAAAAAAAAAB////gAAAAAAAAAAAAAAAAAAAAAH///+AAAAAAAAAAAAAAAAAAAAAAf///4 - AAAAAAAAAAAAAAAAAAAAAB////gAAAAAAAAAAAAAAAAAAAAAH///+AAAAAAAAAAAAAAAAAAAAAAf///4 - AAAAAAAAAAAAAAAAAAAAAB////gAAAAAAAAAAAAAAAAAAAAAH///8AAAAAAAAAAAAAAAAAAAAAAP///w - AAAAAAAAAAAAAAAAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAD///8AAAAAAAAAAAAAAAAAAAAAAP///w - AAAAAAAAAAAAAAAAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAD///8AAAAAAAAAAAAAAAAAAAAAAP///w - AAAAAAAAAAAAAAAAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAD///4AAAAAAAAAAAAAAAAAAAAAAH///A - AAAAAAAAAAAAAAAAAAAAAAP//4AAAAAAAAAAAAAAAAAAAAAAAf//gAAAAAAAAAAAAAAAAAAAAAAB//8A - AAAAAAAAAAAAAAAAAAAAAAD//gAAAAAAAAAAAAAAAAAAAAAAAH/8AAAAAAAAAAAAAAAAAAAAAAAAP/wA - AAAAAAAAAAAAAAAAAAAAAAA/+AAAAAAAAAAAAAAAAAAAAAAAAB/wAAAAAAAAAAAAAAAAAAAAAAAAD+AA - AAAAAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAAAAAAAfAAAAAAAAAAAAAAAAAAAAAAAAAA4AA - AAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAYAA - AAAAAAAAAAAAAAAAAAAAAAABwAAAAAAAAAAAAAAAAAAAAAAAAAPAAAAAAAAAAAAAAAAAAAAAAAAAA+AA - AAAAAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAD/AA - AAAAAAAAAAAAAAAAAAAAAAAP8AAAAAAAAAAAAAAAAAAAAAAAAA/4AAAAAAAAAAAAAAAAAAAAAAAAH/gA - AAAAAAAAAAAAAAAAAAAAAAAf/AAAAAAAAAAAAAAAAAAAAAAAAD/8AAAAAAAAAAAAAAAAAAAAAAAAP/4A - AAAAAAAAAAAAAAAAAAAAAAB//gAAAAAAAAAAAAAAAAAAAAAAAH//AAAAAAAAAAAAAAAAAAAAAAAA//8A - AAAAAAAAAAAAAAAAAAAAAAD//gAAAAAAAAAAAAAAAAAAAAAAAH/+AAAAAAAAAAAAAAAAAAAAAAAAf/4A - AAAAAAAAAAAAAAAAAAAAAAB//gAAAAAAAAAAAAAAAAAAAAAAAH/+AAAAAAAAAAAAAAAAAAAAAAAAf/4A - AAAAAAAAAAAAAAAAAAAAAAB//AAAAAAAAAAAAAAAAAAAAAAAAD/8AAAAAAAAAAAAAAAAAAAAAAAAP/wA - AAAAAAAAAAAAAAAAAAAAAAA//AAAAAAAAAAAAAAAAAAAAAAAAD/8AAAAAAAAAAAAAAAAAAAAAAAAP/wA - AAAAAAAAAAAAAAAAAAAAAAA/+AAAAAAAAAAAAAAAAAAAAAAAAB/4AAAAAAAAAAAAAAAAAAAAAAAAH/gA - AAAAAAAAAAAAAAAAAAAAAAAf+AAAAAAAAAAAAAAAAAAAAAAAAB/4AAAAAAAAAAAAAAAAAAAAAAAAH/gA - AAAAAAAAAAAAAAAAAAAAAAAf8AAAAAAAAAAAAAAAAAAAAAAAAA/4AAAAAAAAAAAAAAAAAAAAAAAAH/wA - AAAAAAAAAAAAAAAAAAAAAAA//gAAAAAAAAAAAAAAAAAAAAAAAH//gAAAAAAAAAAAAAAAAAAAAAAB///A - AAAAAAAAAAAAAAAAAAAAAAP//+AAAAAAAAAAAAAAAAAAAAAAB///8AAAAAAAAAAAAAAAAAAAAAAP///8 - AAAAAAAAAAAAAAAAAAAAAD////4AAAAAAAAAAAAAAAAAAAAAf////wAAAAAAAAAAAAAAAAAAAAD///// - gAAAAAAAAAAAAAAAAAAAAf/////AAAAAAAAAAAAAAAAAAAAD//////AAAAAAAAAAAAAAAAAAAA////// - 8AAAAAAAAAAAAAAAAAAAD//////4AAAAAAAAAAAAAAAAAAAf//////gAAAAAAAAAAAAAAAAAAB////// - +AAAAAAAAAAAAAAAAAAAH//////8AAAAAAAAAAAAAAAAAAA///////wAAAAAAAAAAAAAAAAAAD////// - /AAAAAAAAAAAAAAAAAAAP//////+AAAAAAAAAAAAAAAAAAB///////4AAAAAAAAAAAAAAAAAAH////// - /gAAAAAAAAAAAAAAAAAAf///////AAAAAAAAAAAAAAAAAAD///////8AAAAAAAAAAAAAAAAAAP////// - /wAAAAAAAAAAAAAAAAAA////////gAAAAAAAAAAAAAAAAAH///////+AAAAAAAAAAAAAAAAAAf////// - /4AAAAAAAAAAAAAAAAAB////////gAAAAAAAAAAAAAAAAAH////////AAAAAAAAAAAAAAAAAA/////// - /8AAAAAAAAAAAAAAAAAD////////8AAAAAAAAAAAAAAAAA//////////gAAAAAAAAAAAAAAB//////// - ///8AAAAAAAAAAAAAD/////////////gAAAAAAAAAAAH//////////////4AAAAAAAAAAH////////// - /////wAAAAAAAAAA////////////////gAAAAAAAAAH////////////////AAAAAAAAAA/////////// - /////+AAAAAAAAAH////////////////8AAAAAAAAA/////////////////4AAAAAAAAH/////////// - //////wAAAAAAAA//////////////////gAAAAAAAH//////////////////AAAH4AAA//////////// - //////+AAH/+AAH//////////////////8AD///AA///////////////////4B////gH//////////// - ///////x/////4////////// - - - \ No newline at end of file diff --git a/TGS.ControlPanel/ControlPanel/RepoPage.cs b/TGS.ControlPanel/ControlPanel/RepoPage.cs deleted file mode 100644 index 1a3f011341..0000000000 --- a/TGS.ControlPanel/ControlPanel/RepoPage.cs +++ /dev/null @@ -1,352 +0,0 @@ -using System; -using System.ComponentModel; -using System.Windows.Forms; -using System.Threading; -using TGS.Interface; -using TGS.Interface.Components; - -namespace TGS.ControlPanel -{ - partial class ControlPanel - { - enum RepoAction { - Clone, - Checkout, - Update, - Merge, - Reset, - Test, - Wait, - GenCL, - } - - RepoAction action; - string CloneRepoURL; - string CheckoutBranch; - int TestPR; - - string repoError; - - private void InitRepoPage() - { - RepoBGW.ProgressChanged += RepoBGW_ProgressChanged; - RepoBGW.RunWorkerCompleted += RepoBGW_RunWorkerCompleted; - RepoBGW.DoWork += RepoBGW_DoWork; - BackupTagsList.MouseDoubleClick += BackupTagsList_MouseDoubleClick; - } - - private void BackupTagsList_MouseDoubleClick(object sender, MouseEventArgs e) - { - int index = BackupTagsList.IndexFromPoint(e.Location); - if (index != ListBox.NoMatches) - { - var indexText = (string)BackupTagsList.Items[index]; - if (indexText == "None" || indexText == "Unknown") - return; - var tagname = indexText.Split(':')[0]; - var spaceSplits = indexText.Split(' '); - var sha = spaceSplits[spaceSplits.Length - 1]; - - if (MessageBox.Show(String.Format("Checkout tag {0} ({1})?", tagname, sha), "Restore Backup", MessageBoxButtons.YesNo) != DialogResult.Yes) - return; - - CheckoutBranch = tagname; - DoAsyncOp(RepoAction.Checkout, "Checking out " + tagname + "..."); - } - } - - private void RepoBGW_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) - { - RepoProgressBar.Value = 100; - RepoProgressBar.Style = ProgressBarStyle.Blocks; - RepoPanel.UseWaitCursor = false; - PopulateRepoFields(); - } - - private void RepoRefreshButton_Click(object sender, EventArgs e) - { - PopulateRepoFields(); - } - - private void PopulateRepoFields() - { - if (repoError != null) - MessageBox.Show("An error occured: " + repoError); - - if (RepoBusyCheck()) - return; - - var Repo = Instance.Repository; - - RepoProgressBar.Style = ProgressBarStyle.Marquee; - RepoProgressBar.Visible = false; - RemoteNameTitle.Visible = true; - RepoRemoteTextBox.Visible = true; - BranchNameTitle.Visible = true; - RepoBranchTextBox.Visible = true; - RepoRefreshButton.Visible = true; - SyncCommitsCheckBox.Visible = true; - SyncCommitsCheckBox.Checked = Repo.PushTestmergeCommits(); - - if (!Repo.Exists()) - { - //repo unavailable - RepoRemoteTextBox.Text = "git://github.com/tgstation/tgstation.git"; - RepoBranchTextBox.Text = "master"; - RepoProgressBarLabel.Text = "Unable to locate repository"; - CloneRepositoryButton.Visible = true; - } - else - { - RepoProgressBarLabel.Visible = false; - - CurrentRevisionLabel.Visible = true; - CurrentRevisionTitle.Visible = true; - IdentityLabel.Visible = true; - MergePRButton.Visible = true; - TestMergeListLabel.Visible = true; - TestMergeListTitle.Visible = true; - UpdateRepoButton.Visible = true; - BackupTagsList.Visible = true; - HardReset.Visible = true; - RepoApplyButton.Visible = true; - TestmergeSelector.Visible = true; - RepoGenChangelogButton.Visible = true; - RecloneButton.Visible = true; - ResetRemote.Visible = true; - TGSJsonUpdate.Visible = true; - - CurrentRevisionLabel.Text = Repo.GetHead(false, out string error) ?? "Unknown"; - RepoRemoteTextBox.Text = Repo.GetRemote(out error) ?? "Unknown"; - RepoBranchTextBox.Text = Repo.GetBranch(out error) ?? "Unknown"; - - var Backups = Repo.ListBackups(out error); - BackupTagsList.Items.Clear(); - if (Backups != null) - { - if (Backups.Count == 0) - BackupTagsList.Items.Add("None"); - else - foreach (var I in Backups) - BackupTagsList.Items.Add(I.Key + ": " + I.Value); - } - else - BackupTagsList.Items.Add("Unknown"); - - var PRs = Repo.MergedPullRequests(out error); - TestMergeListLabel.Items.Clear(); - if (PRs != null) - if (PRs.Count == 0) - TestMergeListLabel.Items.Add("None"); - else - foreach (var I in PRs) - TestMergeListLabel.Items.Add(String.Format("#{0}: {2} by {3} at commit {1}\r\n", I.Number, I.Sha, I.Title, I.Author)); - else - TestMergeListLabel.Items.Add("Unknown"); - } - } - - bool RepoBusyCheck() - { - if (Instance.Repository.OperationInProgress()) - { - DoAsyncOp(RepoAction.Wait, "Waiting for repository to finish another action..."); - return true; - } - return false; - } - private void RepoBGW_ProgressChanged(object sender, ProgressChangedEventArgs e) - { - var val = e.ProgressPercentage; - if (val < 0) - { - RepoProgressBar.Style = ProgressBarStyle.Marquee; - return; - } - RepoProgressBar.Style = ProgressBarStyle.Blocks; - RepoProgressBar.Value = val; - } - - private void RepoBGW_DoWork(object sender, DoWorkEventArgs e) - { - //Only for clones - var Repo = Instance.Repository; - - switch (action) { - case RepoAction.Clone: - repoError = Repo.Setup(CloneRepoURL, CheckoutBranch); - break; - case RepoAction.Checkout: - repoError = Repo.Checkout(CheckoutBranch); - break; - case RepoAction.Merge: - repoError = Repo.Update(false); - break; - case RepoAction.Reset: - repoError = Repo.Reset(true); - break; - case RepoAction.Test: - repoError = Repo.MergePullRequest(TestPR); - break; - case RepoAction.Update: - repoError = Repo.Update(true); - break; - case RepoAction.Wait: - break; - case RepoAction.GenCL: - var result = Repo.GenerateChangelog(out repoError); - if(repoError != null) - repoError += ": " + result; - break; - default: - //reeee - return; - } - - do - { - Thread.Sleep(1000); - RepoBGW.ReportProgress(Repo.CheckoutProgress()); - } while (Repo.OperationInProgress()); - } - - private void CloneRepositoryButton_Click(object sender, EventArgs e) - { - CloneRepo(); - } - - void CloneRepo() - { - CloneRepoURL = RepoRemoteTextBox.Text; - CheckoutBranch = RepoBranchTextBox.Text; - - DoAsyncOp(RepoAction.Clone, String.Format("Cloning {0} branch of {1}...", CheckoutBranch, CloneRepoURL)); - } - private void RecloneButton_Click(object sender, EventArgs e) - { - var DialogResult = MessageBox.Show("This will re-clone the repository, backup, and reset the Static configuration folders. Continue?", "Confim", MessageBoxButtons.YesNo); - if (DialogResult == DialogResult.No) - return; - CloneRepo(); - } - void DoAsyncOp(RepoAction ra, string message) - { - if (RepoBGW.IsBusy || (ra != RepoAction.Wait && RepoBusyCheck())) - return; - - SyncCommitsCheckBox.Visible = false; - CurrentRevisionLabel.Visible = false; - CurrentRevisionTitle.Visible = false; - TestMergeListLabel.Visible = false; - TestMergeListTitle.Visible = false; - RepoApplyButton.Visible = false; - UpdateRepoButton.Visible = false; - MergePRButton.Visible = false; - CloneRepositoryButton.Visible = false; - RemoteNameTitle.Visible = false; - RepoRemoteTextBox.Visible = false; - BranchNameTitle.Visible = false; - RepoBranchTextBox.Visible = false; - RepoProgressBar.Visible = true; - HardReset.Visible = false; - IdentityLabel.Visible = false; - TestmergeSelector.Visible = false; - RepoGenChangelogButton.Visible = false; - RecloneButton.Visible = false; - ResetRemote.Visible = false; - BackupTagsList.Visible = false; - RepoRefreshButton.Visible = false; - TGSJsonUpdate.Visible = false; - - RepoPanel.UseWaitCursor = true; - - RepoProgressBar.Value = 0; - RepoProgressBar.Style = ProgressBarStyle.Marquee; - - RepoProgressBarLabel.Text = message; - RepoProgressBarLabel.Visible = true; - - action = ra; - repoError = null; - - RepoBGW.RunWorkerAsync(); - } - private void RepoApplyButton_Click(object sender, EventArgs e) - { - var Repo = Instance.Repository; - - if (RepoBusyCheck()) - return; - - var remote = Repo.GetRemote(out string error); - if (remote == null) { - MessageBox.Show("Error: " + error); - return; - } - - var Reclone = remote != RepoRemoteTextBox.Text; - if (Reclone) - { - var DialogResult = MessageBox.Show("Changing the remote URL requires a re-cloning of the repository. Continue?", "Confim", MessageBoxButtons.YesNo); - if (DialogResult == DialogResult.No) - return; - } - - if (!Reclone) - { - Repo.SetPushTestmergeCommits(SyncCommitsCheckBox.Checked); - var branch = Repo.GetBranch(out error); - if(branch == null) - { - MessageBox.Show("Error: " + error); - return; - } - - CheckoutBranch = RepoBranchTextBox.Text; - if(branch != CheckoutBranch) - DoAsyncOp(RepoAction.Checkout, String.Format("Checking out {0}...", CheckoutBranch)); - } - else - CloneRepositoryButton_Click(null, null); - } - private void UpdateRepoButton_Click(object sender, EventArgs e) - { - DoAsyncOp(RepoAction.Merge, "Merging origin branch..."); - } - - private void HardReset_Click(object sender, EventArgs e) - { - DoAsyncOp(RepoAction.Reset, "Resetting to origin branch..."); - } - - private void ResetRemote_Click(object sender, EventArgs e) - { - DoAsyncOp(RepoAction.Update, "Updating and resetting to remote branch..."); - } - - private void TestMergeButton_Click(object sender, EventArgs e) - { - if (TestmergeSelector.Value == 0) - { - MessageBox.Show("Invalid PR number!"); - return; - } - TestPR = (int)TestmergeSelector.Value; - DoAsyncOp(RepoAction.Test, String.Format("Merging latest commit of PR #{0}...", TestPR)); - } - - private void RepoGenChangelogButton_Click(object sender, System.EventArgs e) - { - DoAsyncOp(RepoAction.GenCL, "Generating changelog..."); - } - - private void TGSJsonUpdate_Click(object sender, EventArgs e) - { - if (MessageBox.Show("This will update the cached TGS3.json to the current repository version, potentially redefining symlinks. Proceed?", "Json Update", MessageBoxButtons.YesNo) != DialogResult.Yes) - return; - var res = Instance.Repository.UpdateTGS3Json(); - if (res != null) - MessageBox.Show(res); - } - } -} diff --git a/TGS.ControlPanel/ControlPanel/ServerPage.cs b/TGS.ControlPanel/ControlPanel/ServerPage.cs deleted file mode 100644 index fcca62a386..0000000000 --- a/TGS.ControlPanel/ControlPanel/ServerPage.cs +++ /dev/null @@ -1,501 +0,0 @@ -using Octokit; -using System; -using System.Collections.Generic; -using System.ComponentModel; -using System.Linq; -using System.Reflection; -using System.Threading.Tasks; -using System.Windows.Forms; -using TGS.Interface; -using TGS.Interface.Components; - -namespace TGS.ControlPanel -{ - partial class ControlPanel - { - bool updatingFields = false; - - /// - /// used for checking the merged state of s - /// - GitHubClient ghclient; - - void InitServerPage() - { - projectNameText.LostFocus += ProjectNameText_LostFocus; - projectNameText.KeyDown += ProjectNameText_KeyDown; - ServerStartBGW.RunWorkerCompleted += ServerStartBGW_RunWorkerCompleted; - ghclient = new GitHubClient(new ProductHeaderValue(Assembly.GetExecutingAssembly().GetName().Name)); - var config = Properties.Settings.Default; - if (!String.IsNullOrWhiteSpace(config.GitHubAPIKey)) - ghclient.Credentials = new Credentials(Helpers.DecryptData(config.GitHubAPIKey, config.GitHubAPIKeyEntropy)); - } - - private void ServerStartBGW_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) - { - if (e.Result != null) - MessageBox.Show((string)e.Result); - LoadServerPage(); - } - - private void ProjectNameText_KeyDown(object sender, KeyEventArgs e) - { - if (e.KeyCode == Keys.Enter) - UpdateProjectName(); - } - - private void CompileCancelButton_Click(object sender, EventArgs e) - { - var res = Instance.Compiler.Cancel(); - if (res != null) - MessageBox.Show(res); - LoadServerPage(); - } - - void LoadServerPage() - { - var RepoExists = Instance.Repository.Exists(); - compileButton.Visible = RepoExists; - AutoUpdateCheckbox.Visible = RepoExists; - initializeButton.Visible = RepoExists; - AutostartCheckbox.Visible = RepoExists; - WebclientCheckBox.Visible = RepoExists; - PortSelector.Visible = RepoExists; - projectNameText.Visible = RepoExists; - CompilerStatusLabel.Visible = RepoExists; - CompileCancelButton.Visible = RepoExists; - CompilerLabel.Visible = RepoExists; - ProjectPathLabel.Visible = RepoExists; - ServerGStopButton.Visible = RepoExists; - ServerStartButton.Visible = RepoExists; - ServerGRestartButton.Visible = RepoExists; - ServerRestartButton.Visible = RepoExists; - PortLabel.Visible = RepoExists; - ServerStopButton.Visible = RepoExists; - TestMergeManagerButton.Visible = RepoExists; - UpdateServerButton.Visible = RepoExists; - RemoveAllTestMergesButton.Visible = RepoExists; - WorldAnnounceField.Visible = RepoExists; - WorldAnnounceButton.Visible = RepoExists; - WorldAnnounceLabel.Visible = RepoExists; - SyncCommitsCheckBox.Visible = RepoExists; - - if (updatingFields) - return; - - var DM = Instance.Compiler; - var DD = Instance.DreamDaemon; - var Config = Instance.Config; - var Repo = Instance.Repository; - - try - { - updatingFields = true; - - ServerPathLabel.Text = "Server Path: " + Instance.ServerDirectory(); - - SecuritySelector.SelectedIndex = (int)DD.SecurityLevel(); - - if (!RepoExists) - return; - - var interval = Repo.AutoUpdateInterval(); - var interval_not_zero = interval != 0; - AutoUpdateCheckbox.Checked = interval_not_zero; - AutoUpdateInterval.Visible = interval_not_zero; - AutoUpdateMLabel.Visible = interval_not_zero; - if (interval_not_zero) - AutoUpdateInterval.Value = interval; - - var DaeStat = DD.DaemonStatus(); - var Online = DaeStat == DreamDaemonStatus.Online; - ServerStartButton.Enabled = !Online; - ServerGStopButton.Enabled = Online; - ServerGRestartButton.Enabled = Online; - ServerStopButton.Enabled = Online; - ServerRestartButton.Enabled = Online; - - switch (DaeStat) - { - case DreamDaemonStatus.HardRebooting: - ServerStatusLabel.Text = "REBOOTING"; - break; - case DreamDaemonStatus.Offline: - ServerStatusLabel.Text = "OFFLINE"; - break; - case DreamDaemonStatus.Online: - ServerStatusLabel.Text = "ONLINE"; - var pc = DD.PlayerCount(); - if (pc != -1) - ServerStatusLabel.Text += " (" + pc + " players)"; - break; - } - - ServerGStopButton.Checked = DD.ShutdownInProgress(); - - AutostartCheckbox.Checked = DD.Autostart(); - WebclientCheckBox.Checked = DD.Webclient(); - if (!PortSelector.Focused) - PortSelector.Value = DD.Port(); - if (!projectNameText.Focused) - projectNameText.Text = DM.ProjectName(); - - UpdateServerButton.Enabled = false; - TestMergeManagerButton.Enabled = false; - RemoveAllTestMergesButton.Enabled = false; - switch (DM.GetStatus()) - { - case CompilerStatus.Compiling: - CompilerStatusLabel.Text = "Compiling..."; - compileButton.Enabled = false; - initializeButton.Enabled = false; - CompileCancelButton.Enabled = true; - break; - case CompilerStatus.Initializing: - CompilerStatusLabel.Text = "Initializing..."; - compileButton.Enabled = false; - initializeButton.Enabled = false; - CompileCancelButton.Enabled = false; - break; - case CompilerStatus.Initialized: - CompilerStatusLabel.Text = "Idle"; - initializeButton.Enabled = true; - compileButton.Enabled = true; - CompileCancelButton.Enabled = false; - UpdateServerButton.Enabled = true; - TestMergeManagerButton.Enabled = true; - RemoveAllTestMergesButton.Enabled = true; - break; - case CompilerStatus.Uninitialized: - CompilerStatusLabel.Text = "Uninitialized"; - compileButton.Enabled = false; - initializeButton.Enabled = true; - CompileCancelButton.Enabled = false; - break; - default: - CompilerStatusLabel.Text = "Unknown!"; - initializeButton.Enabled = true; - compileButton.Enabled = true; - CompileCancelButton.Enabled = true; - break; - } - var error = DM.CompileError(); - if (error != null) - MessageBox.Show("Error: " + error); - } - finally - { - updatingFields = false; - } - } - - private void ProjectNameText_LostFocus(object sender, EventArgs e) - { - UpdateProjectName(); - } - - void UpdateProjectName() - { - if (!updatingFields) - Instance.Compiler.SetProjectName(projectNameText.Text); - } - - private void PortSelector_ValueChanged(object sender, EventArgs e) - { - if (!updatingFields) - Instance.DreamDaemon.SetPort((ushort)PortSelector.Value); - } - - private void ServerPageRefreshButton_Click(object sender, EventArgs e) - { - LoadServerPage(); - } - - private void InitializeButton_Click(object sender, EventArgs e) - { - if (!Instance.Compiler.Initialize()) - MessageBox.Show("Unable to start initialization!"); - LoadServerPage(); - } - private void CompileButton_Click(object sender, EventArgs e) - { - if (!Instance.Compiler.Compile()) - MessageBox.Show("Unable to start compilation!"); - LoadServerPage(); - } - - private void AutostartCheckbox_CheckedChanged(object sender, System.EventArgs e) - { - if (!updatingFields) - Instance.DreamDaemon.SetAutostart(AutostartCheckbox.Checked); - } - private void ServerStartButton_Click(object sender, System.EventArgs e) - { - if (!ServerStartBGW.IsBusy) - ServerStartBGW.RunWorkerAsync(); - } - - private void ServerStartBGW_DoWork(object sender, DoWorkEventArgs e) - { - try - { - e.Result = Instance.DreamDaemon.Start(); - } - catch (Exception ex) - { - e.Result = ex.ToString(); - } - } - - private void ServerStopButton_Click(object sender, EventArgs e) - { - var DialogResult = MessageBox.Show("This will immediately shut down the server. Continue?", "Confim", MessageBoxButtons.YesNo); - if (DialogResult == DialogResult.No) - return; - var res = Instance.DreamDaemon.Stop(); - if (res != null) - MessageBox.Show(res); - } - - private void ServerRestartButton_Click(object sender, EventArgs e) - { - var DialogResult = MessageBox.Show("This will immediately restart the server. Continue?", "Confim", MessageBoxButtons.YesNo); - if (DialogResult == DialogResult.No) - return; - var res = Instance.DreamDaemon.Restart(); - if (res != null) - MessageBox.Show(res); - } - - private void ServerGStopButton_Checked(object sender, EventArgs e) - { - if (updatingFields) - return; - var DialogResult = MessageBox.Show("This will shut down the server when the current round ends. Continue?", "Confim", MessageBoxButtons.YesNo); - if (DialogResult == DialogResult.No) - return; - Instance.DreamDaemon.RequestStop(); - LoadServerPage(); - } - - private void ServerGRestartButton_Click(object sender, EventArgs e) - { - var DialogResult = MessageBox.Show("This will restart the server when the current round ends. Continue?", "Confim", MessageBoxButtons.YesNo); - if (DialogResult == DialogResult.No) - return; - Instance.DreamDaemon.RequestRestart(); - } - - /// - /// Launches the - /// - /// The sender of the event - /// The - void TestMergeManagerButton_Click(object sender, System.EventArgs e) - { - using (var TMM = new TestMergeManager(Instance, ghclient)) - TMM.ShowDialog(); - LoadServerPage(); - } - - /// - /// Calls - /// - /// The sender of the event - /// The - void UpdateServerButton_Click(object sender, EventArgs e) - { - UpdateServer(); - } - - /// - /// Calls with a parameter, re-merging any current s at their current commit, calls and , and starts the prompting the user with any errors that may occur. Merged s are not remerged - /// - async void UpdateServer() - { - try - { - UseWaitCursor = true; - Enabled = false; - try - { - string res = null; - var repo = Instance.Repository; - var pulls = await Task.Run(() => repo.MergedPullRequests(out res)); - - if (pulls == null) - { - MessageBox.Show(res); - return; - } - - List> pullsRequests = null; - if (Program.GetRepositoryRemote(repo, out string remoteOwner, out string remoteName)) - { - //find out which of the PRs have been merged - pullsRequests = new List>(); - foreach (var I in pulls) - pullsRequests.Add(ghclient.PullRequest.Get(remoteOwner, remoteName, I.Number)); - } - - res = await Task.Run(() => repo.Update(true)); - - if (res != null) - { - MessageBox.Show(res, "Error updating repository"); - return; - } - - await Task.Run(() => repo.GenerateChangelog(out res)); - - if (res != null) - MessageBox.Show(res, "Error generating changelog"); - - res = await Task.Run(() => repo.SynchronizePush()); - - if (res != null) - MessageBox.Show(res, "Error synchronizing commits"); - - if (pullsRequests != null) - Task.WaitAll(pullsRequests.ToArray()); - - foreach (var I in pullsRequests) - if (I.Result.Merged) - pulls.RemoveAll(x => x.Number == I.Result.Number); - - var mergeResults = await Task.Run(() => repo.MergePullRequests(pulls, true)); - var compileStartResult = await Task.Run(() => Instance.Compiler.Compile(true)); - - //Show any errors - for (var I = 0; I < mergeResults.Count(); ++I) - { - var err = mergeResults.ElementAt(I); - if (err != null) - MessageBox.Show(err, String.Format("Error re-merging PR #{0}", pulls[I].Number)); - } - - if (!compileStartResult) - MessageBox.Show(res, "Error starting compile!"); - } - finally - { - UseWaitCursor = false; - Enabled = true; - } - } - catch (ForbiddenException) - { - if (ghclient.Credentials.AuthenticationType == AuthenticationType.Anonymous) - { - if (Program.RateLimitPrompt(ghclient)) - UpdateServer(); - return; - } - else - throw; - } - LoadServerPage(); - } - - /// - /// Calls with a parameter, calls , and starts the prompting the user with any errors that may occur. - /// - /// The sender of the event - /// The - async void RemoveAllTestMergesButton_Click(object sender, EventArgs e) - { - if (MessageBox.Show("Are you sure you want to remove all test merges?", "Confirm", MessageBoxButtons.YesNo) != DialogResult.Yes) - return; - try - { - UseWaitCursor = true; - Enabled = false; - try - { - var repo = Instance.Repository; - - var res = await Task.Run(() => repo.Reset(true)); - - if (res != null) - { - MessageBox.Show(res, "Error resetting repository"); - return; - } - - await Task.Run(() => repo.GenerateChangelog(out res)); - - if (res != null) - MessageBox.Show(res, "Error generating changelog"); - - await Task.Run(() => Instance.Compiler.Compile(false)); - if (res != null) - MessageBox.Show(res, "Error starting compile!"); - } - finally - { - UseWaitCursor = false; - Enabled = true; - } - } - catch (ForbiddenException) - { - if (ghclient.Credentials.AuthenticationType == AuthenticationType.Anonymous) - { - if (Program.RateLimitPrompt(ghclient)) - UpdateServer(); - return; - } - else - throw; - } - LoadServerPage(); - } - - private void SecuritySelector_SelectedIndexChanged(object sender, EventArgs e) - { - if (!updatingFields) - if (!Instance.DreamDaemon.SetSecurityLevel((DreamDaemonSecurity)SecuritySelector.SelectedIndex)) - MessageBox.Show("Security change will be applied after next server reboot."); - } - - private void WorldAnnounceButton_Click(object sender, EventArgs e) - { - var msg = WorldAnnounceField.Text; - if (!String.IsNullOrWhiteSpace(msg)) - { - var res = Instance.DreamDaemon.WorldAnnounce(msg); - if (res != null) - { - MessageBox.Show(res); - return; - } - } - WorldAnnounceField.Text = ""; - } - - private void WebclientCheckBox_CheckedChanged(object sender, EventArgs e) - { - if (!updatingFields) - Instance.DreamDaemon.SetWebclient(WebclientCheckBox.Checked); - } - - private void AutoUpdateInterval_ValueChanged(object sender, EventArgs e) - { - if (!updatingFields) - Instance.Repository.SetAutoUpdateInterval((ulong)AutoUpdateInterval.Value); - } - - private void AutoUpdateCheckbox_CheckedChanged(object sender, EventArgs e) - { - if (updatingFields) - return; - var on = AutoUpdateCheckbox.Visible && AutoUpdateCheckbox.Checked; - AutoUpdateInterval.Visible = on; - AutoUpdateMLabel.Visible = on; - if (!on) - Instance.Repository.SetAutoUpdateInterval(0); - else - Instance.Repository.SetAutoUpdateInterval((ulong)AutoUpdateInterval.Value); - } - } -} diff --git a/TGS.ControlPanel/ControlPanel/StaticPage.cs b/TGS.ControlPanel/ControlPanel/StaticPage.cs deleted file mode 100644 index 8fef139bc2..0000000000 --- a/TGS.ControlPanel/ControlPanel/StaticPage.cs +++ /dev/null @@ -1,356 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Windows.Forms; -using TGS.Interface.Components; - -namespace TGS.ControlPanel -{ - partial class ControlPanel - { - IDictionary IndexesToPaths = new Dictionary(); - string originalCurrentFileContent; - IList EnumeratedPaths = new List() { "" }; - bool enumerating = false; - /// - /// Whether or not the static page has been initialized yet - /// - bool initializedStaticPage = false; - - enum EnumResult - { - Enumerated, - Unauthorized, - NotEnumerated, - Error, - } - - void InitStaticPage() - { - if (initializedStaticPage) - return; - if(Instance.Administration == null) - RecreateStaticButton.Visible = false; - BuildFileList(); - initializedStaticPage = true; - } - - void BuildFileList() - { - enumerating = true; - IndexesToPaths.Clear(); - StaticFileListBox.Items.Clear(); - IndexesToPaths.Add(StaticFileListBox.Items.Add("/"), "/"); - if (EnumeratePath("", Instance.Config, 1) == EnumResult.Unauthorized) - { - StaticFileListBox.Items[0] += " (UNAUTHORIZED)"; - IndexesToPaths[0] = null; - } - StaticFileListBox.SelectedIndex = 0; - enumerating = false; - } - - EnumResult EnumeratePath(string path, ITGConfig config, int level) - { - if (!EnumeratedPaths.Contains(path)) - return EnumResult.NotEnumerated; - var Enum = config.ListStaticDirectory(path, out string error, out bool unauthorized); - if(Enum == null) - { - if (unauthorized) - return EnumResult.Unauthorized; - else - { - MessageBox.Show(String.Format("Could not enumerate static path \"{0}\" error: {1}", path, error)); - return EnumResult.Error; - } - } - foreach(var I in Enum) - { - if(I[0] == '/') - { - var dir = I.Remove(0, 1); - var index = StaticFileListBox.Items.Add(DSNTimes(level) + dir + '/'); - var fullpath = path + '/' + dir; - IndexesToPaths.Add(index, fullpath); - switch(EnumeratePath(fullpath, config, level + 1)) - { - case EnumResult.Unauthorized: - StaticFileListBox.Items[index] += " (UNAUTHORIZED)"; - IndexesToPaths[index] = null; - break; - case EnumResult.NotEnumerated: - StaticFileListBox.Items[index] += " (...)"; - break; - case EnumResult.Error: - StaticFileListBox.Items[index] += " (ERROR)"; - IndexesToPaths[index] = null; - break; - } - continue; - } - - IndexesToPaths.Add(StaticFileListBox.Items.Add(DSNTimes(level) + I), Path.Combine(path, I)); - } - return EnumResult.Enumerated; - } - - string DSNTimes(int n) - { - var res = ""; - for (var I = 0; I < n; ++I) - res += " "; - return res; - } - private void StaticFilesRefreshButton_Click(object sender, EventArgs e) - { - BuildFileList(); - UpdateEditText(); - } - - private void StaticFileUploadButton_Click(object sender, EventArgs e) - { - if (StaticFileEditTextbox.Text != "Directory") - { - MessageBox.Show("Please select a directory to upload the file to."); - return; - } - var ofd = new OpenFileDialog() - { - CheckFileExists = true, - CheckPathExists = true, - DefaultExt = ".txt", - Multiselect = false, - Title = "Static File Upload", - ValidateNames = true, - Filter = "All files (*.*)|*.*", - AddExtension = false, - SupportMultiDottedExtensions = true, - }; - if (ofd.ShowDialog() != DialogResult.OK) - return; - - var fileToUpload = ofd.FileName; - - var FileName = Path.Combine(IndexesToPaths[StaticFileListBox.SelectedIndex], Path.GetFileName(fileToUpload)); - - string fileContents = null; - string error = null; - try - { - fileContents = File.ReadAllText(fileToUpload); - } - catch (Exception ex) - { - error = ex.ToString(); - } - if (error == null) - try - { - error = Instance.Config.WriteText(FileName, fileContents, null, out bool unauthorized); - } - catch (Exception ex) - { - error = "Failed to read file, most likely due to it being too large. The transfer limit is much higher on non-remote connections. " + ex.ToString(); - } - if (error != null) - MessageBox.Show("An error occurred: " + error); - BuildFileList(); - } - - private void StaticFileDownloadButton_Click(object sender, EventArgs e) - { - if (StaticFileEditTextbox.ReadOnly) - { - MessageBox.Show("Cannot download this file!"); - return; - } - var remotePath = IndexesToPaths[StaticFileListBox.SelectedIndex]; - if (remotePath == null) - return; - string text, error; - try - { - text = Instance.Config.ReadText(remotePath, false, out error, out bool unauthorized); - } - catch (Exception ex) - { - text = null; - error = "Failed to read file, most likely due to it being too large. The transfer limit is much higher on non-remote connections. " + ex.ToString(); - } - if (text != null) - { - var ofd = new SaveFileDialog() - { - CheckFileExists = false, - CheckPathExists = true, - DefaultExt = ".txt", - Title = "Static File Download", - ValidateNames = true, - Filter = "All files (*.*)|*.*", - AddExtension = false, - CreatePrompt = false, - OverwritePrompt = true, - SupportMultiDottedExtensions = true, - }; - if (ofd.ShowDialog() != DialogResult.OK) - return; - - try - { - File.WriteAllText(ofd.FileName, text); - return; - } - catch (Exception ex) - { - error = ex.ToString(); - } - } - MessageBox.Show("An error occurred: " + error); - } - - private void StaticFileDeleteButton_Click(object sender, EventArgs e) - { - if (MessageBox.Show("Are you sure you want to delete " + ((string)StaticFileListBox.SelectedItem).Trim() + "?", "Confirm", MessageBoxButtons.YesNo) != DialogResult.Yes) - return; - var res = Instance.Config.DeleteFile(IndexesToPaths[StaticFileListBox.SelectedIndex], out bool unauthorized); - if (res != null) - MessageBox.Show(res); - BuildFileList(); - } - - private void StaticFileCreateButton_Click(object sender, EventArgs e) - { - if(StaticFileEditTextbox.Text != "Directory") - { - MessageBox.Show("Please select a directory to create the file in."); - return; - } - var FileName = Program.TextPrompt("Static File/Directory Creation", "Enter the name of the file/directory:"); - if (FileName == null) - return; - - var resu = MessageBox.Show("Is this the name of a directory?", "Directory", MessageBoxButtons.YesNoCancel); - if (resu == DialogResult.Cancel) - return; - var FullFileName = Path.Combine(IndexesToPaths[StaticFileListBox.SelectedIndex], FileName); - if (resu == DialogResult.Yes) - FullFileName = Path.Combine(FullFileName, "__TGS3_CP_DIRECTORY_CREATOR__"); - var config = Instance.Config; - var res = config.WriteText(FullFileName, "", null, out bool unauthorized); - if (res != null) - MessageBox.Show(res); - if (resu == DialogResult.Yes) - { - FullFileName = Path.Combine(FullFileName, "__TGS3_CP_DIRECTORY_CREATOR__"); - config.DeleteFile(FullFileName, out unauthorized); //don't care about this - } - BuildFileList(); - } - - private void StaticFileSaveButton_Click(object sender, EventArgs e) - { - var index = StaticFileListBox.SelectedIndex; - string res; - bool unauthorized; - try - { - res = Instance.Config.WriteText(IndexesToPaths[index], StaticFileEditTextbox.Text, originalCurrentFileContent, out unauthorized); - if (res == null) - originalCurrentFileContent = StaticFileEditTextbox.Text; - } - catch (Exception ex) - { - unauthorized = false; - res = "Failed to write file, most likely due to it being too large. The transfer limit is much higher on non-remote connections. " + ex.ToString(); - } - if (res != null) - { - MessageBox.Show("Error: " + res); - var title = (string)StaticFileListBox.Items[index]; - if (unauthorized && !title.Contains(" (UNAUTHORIZED)")) - StaticFileListBox.Items[index] = title + " (UNAUTHORIZED)"; - } - UpdateEditText(); - } - - private void StaticFileListBox_SelectedIndexChanged(object sender, EventArgs e) - { - UpdateEditText(); - } - - void UpdateEditText() - { - if (enumerating) - return; - var newIndex = StaticFileListBox.SelectedIndex; - if (newIndex == -1) - return; - var path = IndexesToPaths[newIndex]; - var title = (string)StaticFileListBox.Items[newIndex]; - var authed_title = title.Replace(" (UNAUTHORIZED)", "").Replace(" (...)", ""); - if (authed_title[authed_title.Length - 1] == '/') - { - StaticFileEditTextbox.ReadOnly = true; - StaticFileEditTextbox.Text = "Directory"; - if (path != null && path != "/") - { - EnumeratedPaths.Add(path); - BuildFileList(); - enumerating = true; - StaticFileListBox.SelectedIndex = newIndex; - enumerating = false; - return; - } - } - else - { - string entry, error; - bool unauthorized; - try - { - entry = Instance.Config.ReadText(path, false, out error, out unauthorized); - } - catch(Exception e) - { - entry = null; - unauthorized = false; - error = "Failed to read file, most likely due to it being too large. The transfer limit is much higher on non-remote connections. " + e.ToString(); - } - if (entry == null) - { - StaticFileEditTextbox.ReadOnly = true; - StaticFileEditTextbox.Text = "ERROR: " + error; - if (unauthorized && !title.Contains(" (UNAUTHORIZED)")) - StaticFileListBox.Items[newIndex] = title + " (UNAUTHORIZED)"; - } - else - { - StaticFileEditTextbox.ReadOnly = false; - originalCurrentFileContent = entry; - StaticFileEditTextbox.Text = entry.Replace("\r\n", "\n").Replace("\r", "\n").Replace("\n", Environment.NewLine); - } - } - } - - private void RecreateStaticButton_Click(object sender, EventArgs e) - { - if (!CheckAdminWithWarning()) - { - RecreateStaticButton.Visible = false; - return; - } - if (MessageBox.Show("This will rename the current static directory to a backup and recreate it. Continue?", "Confirm", MessageBoxButtons.YesNo) != DialogResult.Yes) - return; - if (!CheckAdminWithWarning()) - { - RecreateStaticButton.Visible = false; - return; - } - var res = Instance.Administration.RecreateStaticFolder(); - if (res != null) - MessageBox.Show(res); - BuildFileList(); - } - } -} diff --git a/TGS.ControlPanel/CountedForm.cs b/TGS.ControlPanel/CountedForm.cs deleted file mode 100644 index 8f79f1c400..0000000000 --- a/TGS.ControlPanel/CountedForm.cs +++ /dev/null @@ -1,38 +0,0 @@ -using System.Windows.Forms; - -namespace TGS.ControlPanel -{ - /// - /// Calls when all s are d - /// -#if !DEBUG - abstract -#endif - class CountedForm : ServerOpForm - { - /// - /// The current number of active s - /// - static uint FormCount; - - /// - /// Construct a . Increments - /// - public CountedForm() - { - FormClosed += CountedForm_FormClosed; - ++FormCount; - } - - /// - /// Decrements . Calls if it reaches 0 - /// - /// The sender of the event - /// The - private void CountedForm_FormClosed(object sender, FormClosedEventArgs e) - { - if (--FormCount == 0) - Application.Exit(); - } - } -} diff --git a/TGS.ControlPanel/GitHubLoginPrompt.Designer.cs b/TGS.ControlPanel/GitHubLoginPrompt.Designer.cs deleted file mode 100644 index 6035c273a7..0000000000 --- a/TGS.ControlPanel/GitHubLoginPrompt.Designer.cs +++ /dev/null @@ -1,176 +0,0 @@ -namespace TGS.ControlPanel -{ - partial class GitHubLoginPrompt - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - this.PasswordLabel = new System.Windows.Forms.Label(); - this.UsernameLabel = new System.Windows.Forms.Label(); - this.PasswordTextBox = new System.Windows.Forms.TextBox(); - this.UsernameTextBox = new System.Windows.Forms.TextBox(); - this.DividerLabel = new System.Windows.Forms.Label(); - this.APIKeyLabel = new System.Windows.Forms.Label(); - this.APIKeyTextBox = new System.Windows.Forms.TextBox(); - this.OrLabel = new System.Windows.Forms.Label(); - this.LoginButton = new System.Windows.Forms.Button(); - this.SuspendLayout(); - // - // PasswordLabel - // - this.PasswordLabel.AutoSize = true; - this.PasswordLabel.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.PasswordLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.PasswordLabel.Location = new System.Drawing.Point(12, 41); - this.PasswordLabel.Name = "PasswordLabel"; - this.PasswordLabel.Size = new System.Drawing.Size(92, 18); - this.PasswordLabel.TabIndex = 25; - this.PasswordLabel.Text = "Password:"; - this.PasswordLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // UsernameLabel - // - this.UsernameLabel.AutoSize = true; - this.UsernameLabel.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.UsernameLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.UsernameLabel.Location = new System.Drawing.Point(12, 9); - this.UsernameLabel.Name = "UsernameLabel"; - this.UsernameLabel.Size = new System.Drawing.Size(97, 18); - this.UsernameLabel.TabIndex = 24; - this.UsernameLabel.Text = "Username:"; - this.UsernameLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // PasswordTextBox - // - this.PasswordTextBox.Location = new System.Drawing.Point(132, 41); - this.PasswordTextBox.Name = "PasswordTextBox"; - this.PasswordTextBox.Size = new System.Drawing.Size(233, 20); - this.PasswordTextBox.TabIndex = 23; - this.PasswordTextBox.UseSystemPasswordChar = true; - // - // UsernameTextBox - // - this.UsernameTextBox.Location = new System.Drawing.Point(132, 9); - this.UsernameTextBox.Name = "UsernameTextBox"; - this.UsernameTextBox.Size = new System.Drawing.Size(233, 20); - this.UsernameTextBox.TabIndex = 22; - // - // DividerLabel - // - this.DividerLabel.AutoSize = true; - this.DividerLabel.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.DividerLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.DividerLabel.Location = new System.Drawing.Point(-24, 59); - this.DividerLabel.Name = "DividerLabel"; - this.DividerLabel.Size = new System.Drawing.Size(468, 18); - this.DividerLabel.TabIndex = 26; - this.DividerLabel.Text = "______________________________________________"; - this.DividerLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // APIKeyLabel - // - this.APIKeyLabel.AutoSize = true; - this.APIKeyLabel.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.APIKeyLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.APIKeyLabel.Location = new System.Drawing.Point(12, 95); - this.APIKeyLabel.Name = "APIKeyLabel"; - this.APIKeyLabel.Size = new System.Drawing.Size(78, 18); - this.APIKeyLabel.TabIndex = 28; - this.APIKeyLabel.Text = "API Key:"; - this.APIKeyLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // APIKeyTextBox - // - this.APIKeyTextBox.Location = new System.Drawing.Point(132, 95); - this.APIKeyTextBox.Name = "APIKeyTextBox"; - this.APIKeyTextBox.Size = new System.Drawing.Size(233, 20); - this.APIKeyTextBox.TabIndex = 27; - this.APIKeyTextBox.UseSystemPasswordChar = true; - // - // OrLabel - // - this.OrLabel.AutoSize = true; - this.OrLabel.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.OrLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.OrLabel.Location = new System.Drawing.Point(157, 64); - this.OrLabel.Name = "OrLabel"; - this.OrLabel.Size = new System.Drawing.Size(32, 18); - this.OrLabel.TabIndex = 29; - this.OrLabel.Text = "OR"; - this.OrLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // LoginButton - // - this.LoginButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.LoginButton.Location = new System.Drawing.Point(145, 133); - this.LoginButton.Name = "LoginButton"; - this.LoginButton.Size = new System.Drawing.Size(88, 25); - this.LoginButton.TabIndex = 30; - this.LoginButton.Text = InitalLoginButtonText; - this.LoginButton.UseVisualStyleBackColor = true; - this.LoginButton.Click += new System.EventHandler(this.LoginButton_Click); - // - // GitHubLoginPrompt - // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(39)))), ((int)(((byte)(40)))), ((int)(((byte)(34))))); - this.ClientSize = new System.Drawing.Size(376, 170); - this.Controls.Add(this.LoginButton); - this.Controls.Add(this.OrLabel); - this.Controls.Add(this.APIKeyLabel); - this.Controls.Add(this.APIKeyTextBox); - this.Controls.Add(this.DividerLabel); - this.Controls.Add(this.PasswordLabel); - this.Controls.Add(this.UsernameLabel); - this.Controls.Add(this.PasswordTextBox); - this.Controls.Add(this.UsernameTextBox); - this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; - this.MaximizeBox = false; - this.MinimizeBox = false; - this.Name = "GitHubLoginPrompt"; - this.ShowIcon = false; - this.ShowInTaskbar = false; - this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; - this.Text = "Login To GitHub"; - this.ResumeLayout(false); - this.PerformLayout(); - - } - - #endregion - - private System.Windows.Forms.Label PasswordLabel; - private System.Windows.Forms.Label UsernameLabel; - private System.Windows.Forms.TextBox PasswordTextBox; - private System.Windows.Forms.TextBox UsernameTextBox; - private System.Windows.Forms.Label DividerLabel; - private System.Windows.Forms.Label APIKeyLabel; - private System.Windows.Forms.TextBox APIKeyTextBox; - private System.Windows.Forms.Label OrLabel; - private System.Windows.Forms.Button LoginButton; - } -} \ No newline at end of file diff --git a/TGS.ControlPanel/GitHubLoginPrompt.cs b/TGS.ControlPanel/GitHubLoginPrompt.cs deleted file mode 100644 index 8d676d1646..0000000000 --- a/TGS.ControlPanel/GitHubLoginPrompt.cs +++ /dev/null @@ -1,103 +0,0 @@ -using Octokit; -using System; -using System.Threading.Tasks; -using System.Windows.Forms; -using TGS.Interface; - -namespace TGS.ControlPanel -{ - /// - /// Used for recieving a GitHub API key for use in - /// - sealed partial class GitHubLoginPrompt : Form - { - /// - /// The text to appear on - /// - const string InitalLoginButtonText = "Login"; - - /// - /// The to use for OAuth requests - /// - readonly GitHubClient client; - - /// - /// Construct a - /// - /// The to use for requests - public GitHubLoginPrompt(GitHubClient c) - { - InitializeComponent(); - AcceptButton = LoginButton; - DialogResult = DialogResult.Cancel; - client = c; - } - - /// - /// Calls . On success, encrypts it's return value, saves it in and , and closes the . On failure, shows a and exits - /// - /// The sender of the event - /// The - async void LoginButton_Click(object sender, EventArgs e) - { - LoginButton.Text = "Logging in..."; - Enabled = false; - UseWaitCursor = true; - var APIKey = await GetAPIKey(); - if(APIKey == null) - { - MessageBox.Show("Authentication failure!"); - Enabled = true; - UseWaitCursor = false; - LoginButton.Text = "Login"; - return; - } - if (String.IsNullOrWhiteSpace(APIKeyTextBox.Text)) - //They used username authentication, let them know we made a token - MessageBox.Show("A personal access token has been created on your account for use with the Control Panel"); - - //Encrypt it and let's be on our way - var Config = Properties.Settings.Default; - Config.GitHubAPIKey = Helpers.EncryptData(APIKey, out string entropy); - Config.GitHubAPIKeyEntropy = entropy; - DialogResult = DialogResult.OK; - Close(); - } - - /// - /// Retrieves an API key from GitHub based on login information or from the and verifies it - /// - /// The GitHub API key on success, on failure - async Task GetAPIKey() - { - string APIKey; - if (String.IsNullOrWhiteSpace(APIKeyTextBox.Text)) - { - try - { - client.Credentials = new Credentials(UsernameTextBox.Text, PasswordTextBox.Text); - var token = await client.Authorization.Create(new NewAuthorization { Note = "TGS.ControlPanel token to bypass rate limiting" }); - APIKey = token.Token; - } - catch (AuthorizationException) - { - return null; - } - } - else - APIKey = APIKeyTextBox.Text; - - //validate it by pinging a random repository - client.Credentials = new Credentials(APIKey); - try - { - await client.Repository.Get("Dextraspace", "Test"); - } - catch (AuthorizationException) - { - return null; - } - return APIKey; - } - } -} diff --git a/TGS.ControlPanel/GitHubLoginPrompt.resx b/TGS.ControlPanel/GitHubLoginPrompt.resx deleted file mode 100644 index 1af7de150c..0000000000 --- a/TGS.ControlPanel/GitHubLoginPrompt.resx +++ /dev/null @@ -1,120 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - \ No newline at end of file diff --git a/TGS.ControlPanel/InstanceSelector.Designer.cs b/TGS.ControlPanel/InstanceSelector.Designer.cs deleted file mode 100644 index f9a745d34e..0000000000 --- a/TGS.ControlPanel/InstanceSelector.Designer.cs +++ /dev/null @@ -1,164 +0,0 @@ -namespace TGS.ControlPanel -{ - partial class InstanceSelector - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(InstanceSelector)); - this.InstanceListBox = new System.Windows.Forms.ListBox(); - this.CreateInstanceButton = new System.Windows.Forms.Button(); - this.ImportInstanceButton = new System.Windows.Forms.Button(); - this.RenameInstanceButton = new System.Windows.Forms.Button(); - this.DetachInstanceButton = new System.Windows.Forms.Button(); - this.RefreshButton = new System.Windows.Forms.Button(); - this.ConnectButton = new System.Windows.Forms.Button(); - this.EnabledCheckBox = new System.Windows.Forms.CheckBox(); - this.SuspendLayout(); - // - // InstanceListBox - // - this.InstanceListBox.Anchor = System.Windows.Forms.AnchorStyles.Left; - this.InstanceListBox.FormattingEnabled = true; - this.InstanceListBox.Location = new System.Drawing.Point(13, 13); - this.InstanceListBox.Name = "InstanceListBox"; - this.InstanceListBox.Size = new System.Drawing.Size(339, 238); - this.InstanceListBox.TabIndex = 0; - this.InstanceListBox.SelectedIndexChanged += new System.EventHandler(this.InstanceListBox_SelectedIndexChanged); - // - // CreateInstanceButton - // - this.CreateInstanceButton.Anchor = System.Windows.Forms.AnchorStyles.Right; - this.CreateInstanceButton.Location = new System.Drawing.Point(358, 15); - this.CreateInstanceButton.Name = "CreateInstanceButton"; - this.CreateInstanceButton.Size = new System.Drawing.Size(148, 25); - this.CreateInstanceButton.TabIndex = 14; - this.CreateInstanceButton.Text = "Create Instance"; - this.CreateInstanceButton.UseVisualStyleBackColor = true; - this.CreateInstanceButton.Click += new System.EventHandler(this.CreateInstanceButton_Click); - // - // ImportInstanceButton - // - this.ImportInstanceButton.Anchor = System.Windows.Forms.AnchorStyles.Right; - this.ImportInstanceButton.Location = new System.Drawing.Point(358, 45); - this.ImportInstanceButton.Name = "ImportInstanceButton"; - this.ImportInstanceButton.Size = new System.Drawing.Size(148, 25); - this.ImportInstanceButton.TabIndex = 15; - this.ImportInstanceButton.Text = "Import Instance"; - this.ImportInstanceButton.UseVisualStyleBackColor = true; - this.ImportInstanceButton.Click += new System.EventHandler(this.ImportInstanceButton_Click); - // - // RenameInstanceButton - // - this.RenameInstanceButton.Anchor = System.Windows.Forms.AnchorStyles.Right; - this.RenameInstanceButton.Location = new System.Drawing.Point(358, 76); - this.RenameInstanceButton.Name = "RenameInstanceButton"; - this.RenameInstanceButton.Size = new System.Drawing.Size(148, 25); - this.RenameInstanceButton.TabIndex = 16; - this.RenameInstanceButton.Text = "Rename Instance"; - this.RenameInstanceButton.UseVisualStyleBackColor = true; - this.RenameInstanceButton.Click += new System.EventHandler(this.RenameInstanceButton_Click); - // - // DetachInstanceButton - // - this.DetachInstanceButton.Anchor = System.Windows.Forms.AnchorStyles.Right; - this.DetachInstanceButton.Location = new System.Drawing.Point(358, 107); - this.DetachInstanceButton.Name = "DetachInstanceButton"; - this.DetachInstanceButton.Size = new System.Drawing.Size(148, 25); - this.DetachInstanceButton.TabIndex = 17; - this.DetachInstanceButton.Text = "Detach Instance"; - this.DetachInstanceButton.UseVisualStyleBackColor = true; - this.DetachInstanceButton.Click += new System.EventHandler(this.DetachInstanceButton_Click); - // - // RefreshButton - // - this.RefreshButton.Anchor = System.Windows.Forms.AnchorStyles.Right; - this.RefreshButton.Location = new System.Drawing.Point(358, 195); - this.RefreshButton.Name = "RefreshButton"; - this.RefreshButton.Size = new System.Drawing.Size(148, 25); - this.RefreshButton.TabIndex = 19; - this.RefreshButton.Text = "Refresh"; - this.RefreshButton.UseVisualStyleBackColor = true; - this.RefreshButton.Click += new System.EventHandler(this.RefreshButton_Click); - // - // ConnectButton - // - this.ConnectButton.Anchor = System.Windows.Forms.AnchorStyles.Right; - this.ConnectButton.Location = new System.Drawing.Point(358, 226); - this.ConnectButton.Name = "ConnectButton"; - this.ConnectButton.Size = new System.Drawing.Size(148, 25); - this.ConnectButton.TabIndex = 20; - this.ConnectButton.Text = "Connect"; - this.ConnectButton.UseVisualStyleBackColor = true; - this.ConnectButton.Click += new System.EventHandler(this.ConnectButton_Click); - // - // EnabledCheckBox - // - this.EnabledCheckBox.AutoSize = true; - this.EnabledCheckBox.ForeColor = System.Drawing.Color.White; - this.EnabledCheckBox.Location = new System.Drawing.Point(396, 149); - this.EnabledCheckBox.Name = "EnabledCheckBox"; - this.EnabledCheckBox.Size = new System.Drawing.Size(65, 17); - this.EnabledCheckBox.TabIndex = 21; - this.EnabledCheckBox.Text = "Enabled"; - this.EnabledCheckBox.UseVisualStyleBackColor = true; - this.EnabledCheckBox.CheckedChanged += new System.EventHandler(this.EnabledCheckBox_CheckedChanged); - // - // InstanceSelector - // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(39)))), ((int)(((byte)(40)))), ((int)(((byte)(34))))); - this.ClientSize = new System.Drawing.Size(518, 261); - this.Controls.Add(this.EnabledCheckBox); - this.Controls.Add(this.ConnectButton); - this.Controls.Add(this.RefreshButton); - this.Controls.Add(this.DetachInstanceButton); - this.Controls.Add(this.RenameInstanceButton); - this.Controls.Add(this.ImportInstanceButton); - this.Controls.Add(this.CreateInstanceButton); - this.Controls.Add(this.InstanceListBox); - this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); - this.Name = "InstanceSelector"; - this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; - this.Text = "Server Instances"; - this.ResumeLayout(false); - this.PerformLayout(); - - } - - #endregion - - private System.Windows.Forms.ListBox InstanceListBox; - private System.Windows.Forms.Button CreateInstanceButton; - private System.Windows.Forms.Button ImportInstanceButton; - private System.Windows.Forms.Button RenameInstanceButton; - private System.Windows.Forms.Button DetachInstanceButton; - private System.Windows.Forms.Button RefreshButton; - private System.Windows.Forms.Button ConnectButton; - private System.Windows.Forms.CheckBox EnabledCheckBox; - } -} \ No newline at end of file diff --git a/TGS.ControlPanel/InstanceSelector.cs b/TGS.ControlPanel/InstanceSelector.cs deleted file mode 100644 index 09264953a9..0000000000 --- a/TGS.ControlPanel/InstanceSelector.cs +++ /dev/null @@ -1,230 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading.Tasks; -using System.Windows.Forms; -using TGS.Interface; - -namespace TGS.ControlPanel -{ - /// - /// Form used for managing s - /// - sealed partial class InstanceSelector : CountedForm - { - /// - /// The we build instance connections from - /// - readonly IServer server; - /// - /// Used for modifying without invoking its side effects - /// - bool UpdatingEnabledCheckbox = false; - - /// - /// Construct an - /// - /// The value of - public InstanceSelector(IServer _server) - { - InitializeComponent(); - InstanceListBox.MouseDoubleClick += InstanceListBox_MouseDoubleClick; - server = _server; - RefreshInstances(); - } - - /// - /// Connects to a if it is double clicked in - /// - /// The sender of the event - /// The - void InstanceListBox_MouseDoubleClick(object sender, MouseEventArgs e) - { - TryConnectToIndexInstance(InstanceListBox.IndexFromPoint(e.Location)); - } - - /// - /// Returns the associated with 's current selected index - /// - /// The associated with 's current selected index if it exists, otherwise - InstanceMetadata GetSelectedInstanceMetadata() - { - var index = (IInstance)InstanceListBox.SelectedItem; - return index?.Metadata; - } - - /// - /// Loads the using - /// - void RefreshInstances() - { - InstanceListBox.Items.Clear(); - server.RebuildInstanceList(); - foreach(var I in server.Instances) - InstanceListBox.Items.Add(I); - var HasServerAdmin = server.InstanceManager != null; - CreateInstanceButton.Enabled = HasServerAdmin; - ImportInstanceButton.Enabled = HasServerAdmin; - RenameInstanceButton.Enabled = HasServerAdmin; - DetachInstanceButton.Enabled = HasServerAdmin; - EnabledCheckBox.Enabled = HasServerAdmin; - if(InstanceListBox.Items.Count > 0) - InstanceListBox.SelectedIndex = 0; - } - - /// - /// Tries to start a for a given - /// - /// The of to connect to - void TryConnectToIndexInstance(int index) - { - if (index == ListBox.NoMatches) - return; - var instance = (IInstance)InstanceListBox.Items[index]; - if (ControlPanel.InstancesInUse.TryGetValue(instance.Metadata.Name, out ControlPanel activeCP)) - { - activeCP.BringToFront(); - return; - } - - new ControlPanel(server, instance).Show(); - } - - /// - /// Prompts the user for parameters to - /// - /// The sender of the event - /// The - async void DetachInstanceButton_Click(object sender, EventArgs e) - { - var imd = GetSelectedInstanceMetadata(); - if (imd == null) - return; - if (MessageBox.Show(String.Format("This will dissociate the server instance at \"{0}\"! Are you sure?", imd.Path), "Instance Detach", MessageBoxButtons.YesNo) != DialogResult.Yes) - return; - string res = null; - await WrapServerOp(() => res = server.InstanceManager.DetachInstance(imd.Name)); - if (res != null) - MessageBox.Show(res); - RefreshInstances(); - } - - /// - /// Calls - /// - /// The sender of the event - /// The - void RefreshButton_Click(object sender, EventArgs e) - { - RefreshInstances(); - } - - /// - /// Prompts the user for parameters to - /// - /// The sender of the event - /// The - async void RenameInstanceButton_Click(object sender, EventArgs e) - { - var imd = GetSelectedInstanceMetadata(); - if (imd == null) - return; - var new_name = Program.TextPrompt("Instance Rename", "Enter a new name for the instance:"); - if (new_name == null) - return; - if (imd.Enabled && MessageBox.Show(String.Format("This will temporarily offline the server instance! Are you sure?", imd.Path), "Instance Restart", MessageBoxButtons.YesNo) != DialogResult.Yes) - return; - string res = null; - await WrapServerOp(() => res = server.InstanceManager.RenameInstance(imd.Name, new_name)); - if (res != null) - MessageBox.Show(res); - RefreshInstances(); - } - - /// - /// Prompts the user for parameters to - /// - /// The sender of the event - /// The - async void ImportInstanceButton_Click(object sender, EventArgs e) - { - var instance_path = Program.TextPrompt("Instance Import", "Enter the full path to the instance:"); - if (instance_path == null) - return; - string res = null; - await WrapServerOp(() => res = server.InstanceManager.ImportInstance(instance_path)); - if (res != null) - MessageBox.Show(res); - RefreshInstances(); - } - - /// - /// Prompts the user for parameters to - /// - /// The sender of the event - /// The - async void CreateInstanceButton_Click(object sender, EventArgs e) - { - var instance_name = Program.TextPrompt("Instance Creation", "Enter the name of the instance:"); - if (instance_name == null) - return; - var instance_path = Program.TextPrompt("Instance Creation", "Enter the full path to the instance:"); - if (instance_path == null) - return; - string res = null; - await WrapServerOp(() => res = server.InstanceManager.CreateInstance(instance_name, instance_path)); - if (res != null) - MessageBox.Show(res); - RefreshInstances(); - } - - /// - /// Attempts to connect the user to an based on the of - /// - /// The sender of the event - /// The - void ConnectButton_Click(object sender, EventArgs e) - { - TryConnectToIndexInstance(InstanceListBox.SelectedIndex); - } - - /// - /// Prompts the user if they want to call to either online or offline an based on its current state - /// - /// The sender of the event - /// The - async void EnabledCheckBox_CheckedChanged(object sender, EventArgs e) - { - if (UpdatingEnabledCheckbox) - return; - var enabling = EnabledCheckBox.Checked; - try - { - if (MessageBox.Show(String.Format("Are you sure you want to {0} this instance?", enabling ? "online" : "offline"), "Instance Status Change", MessageBoxButtons.YesNo) != DialogResult.Yes) - return; - string res = null; - await WrapServerOp(() => res = server.InstanceManager.SetInstanceEnabled(GetSelectedInstanceMetadata().Name, enabling)); - if (res != null) - MessageBox.Show(res); - } - finally - { - RefreshInstances(); - } - } - - /// - /// Update based on the selected 's property - /// - /// The sender of the event - /// The - void InstanceListBox_SelectedIndexChanged(object sender, EventArgs e) - { - if (InstanceListBox.SelectedIndex != ListBox.NoMatches) - { - UpdatingEnabledCheckbox = true; - EnabledCheckBox.Checked = GetSelectedInstanceMetadata().Enabled; - UpdatingEnabledCheckbox = false; - } - } - } -} diff --git a/TGS.ControlPanel/InstanceSelector.resx b/TGS.ControlPanel/InstanceSelector.resx deleted file mode 100644 index a83f34c04b..0000000000 --- a/TGS.ControlPanel/InstanceSelector.resx +++ /dev/null @@ -1,2179 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - - - AAABAAUAEBAAAAEAIABoBAAAVgAAABgYAAABACAAiAkAAL4EAAAgIAAAAQAgAKgQAABGDgAAMDAAAAEA - IACoJQAA7h4AAKCgAAABACAAqJwBAJZEAAAoAAAAEAAAACAAAAABACAAAAAAAAAEAAATCwAAEwsAAAAA - AAAAAAAAAAAAAAAAAABeTTgAWkgyA1pIMilOOyMoRjMaRz4qEMM+KhDESDUcSU07IyhaSDIpWUgxA15N - NwAAAAAAAAAAAAAAAADSzsgAPyoQAEk2HTk9KQ7ZOSUK5DciB+43Igf/OCQJ/zkkCe46JQrkPSgO2UYz - Gjk8JwwA0s3HAAAAAABVQywAbV1JAk47IytALBK6XEo0/2xcSP9lVUD/mo+C/5CEdf9jUz7/ZFQ//2BP - Ov9LOCC6SzgfK2hYRAJSPygA////AUg1HHA7JgzkOSQJ/29gTP9wYU7/6+nm/+rn5f9rXEj/U0Ap/1NB - Kv9YRzD/Z1ZC/zsmC+RINBtw////AYFzYgU/KhCxOiUK/4V4aP+hl4r/ZFQ///b19P/a1tH/c2RR/87J - w//X083/Y1M+/3BhTv8+Kg//PyoQsYFzYgVYRi8cPSkOz0MvFf/a1tH/vreu/2RTPv/08/H//Pz7//Py - 8P/+/f3/+vn5/3JjUP+Hemr/RjMa/z0pDs9XRS8cQi4VnDciB/xsXEn/9PPx/7y1rP9kUz7/9PLx//// - //////////////f29f9qW0f/oJaJ/3BhTf83Igf8Qi4VnD8qEN45JAn/saie//////+7tKv/ZFM+//Ty - 8f//////7u3q/5SJev95a1n/Y1M9/9rX0v+zq6D/OSQJ/z8qEN5EMBeFNiEF/oV4aP//////vLSr/2RT - Pv/08vH//////+7t6v+UiXr/eWtZ/2NTPv/X0s3/h3pq/zYhBf5EMBeFRzMaZTYhBfuAc2L//////7y0 - q/9kUz7/9PLx//////////////////f29f9qWkX/rqaa/4R3Zv82IQX7RzMaZUUxGIc3Igf/X085/97b - 1/+9tqz/ZFM+//Tz8f/8/Pv/8/Lw//79/f/6+fn/cWJP/5KHeP9kUz7/NyIH/0UxGIdMOSE0PikPvzkk - Cf+Lf2//in5u/2JSPf/29fT/2tbR/3JkUf/PycP/19PN/2NTPv9oWEP/OycM/z0pD79LOCA0RDEYAE89 - JSU/KxHWcGBN/1NAKf9wYU3/9vX0/+rn5P9pWkX/VEIq/1NBKv9YRzD/ZlZB/zwnDdZPPSUlRTEYAGdY - QwAAAAAAQCwSkkw5If9cSjT/bF1J/6ujl/+lm4//j4N0/4R3Z/9lVUD/YE86/0IuFP8/KxGSAAAAAGdY - QwAAAAAAQy8WAFA+JiFDLxZmPCcMnzciBvM5JAn/NyIH/zgjCP87Jgz/OCMI8zwnDZ9DLxVmTzwlIUEs - EwAAAAAAAAAAAAAAAAAAAAAAQi4VAEYyGQc+KQ+GPSgO6EEtE61CLhStPSkP6D0pDoZEMBcHQS0TAAAA - AAAAAAAAAAAAAOAHAADgBwAAgAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAB - AADAAwAAwAMAAPAPAAAoAAAAGAAAADAAAAABACAAAAAAAAAJAAATCwAAEwsAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAP///wD///8A////AP///wBVQy0AemxaA047I0NCLhTGQS0TxlE+J0aFd2cEYlI9AP// - /wD///8A////AP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF5NNwBhUDsMTToifk47 - I50+KQ+BQy8WmzwnDeg4Iwj/OCMI/z0oDupDLxacPikPgU47I51NOiF+YFA6DF1NNwAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAj4R1AB8HAABLOCBgOiUK+DYhBf82IQX/OCMI/zciB/82IQX/NiEF/zUg - Bf82IQX/NiEF/zYhBf86JQr4SzgfYB8HAACOgnMAAAAAAAAAAAAAAAAAAAAAAAAAAACMgXIACAAAAGBP - OS0/KxHVRzMa/2BPOf9eTTf/QSwT/0UyGf+Lf2//joJy/15NN/9fTjn/X085/11MNv9KNx//PysR1V9O - OC0GAAAAjIFyAAAAAAAAAAAAnpWIANrY0wFaSTIhRTIZdUAsEtc2IQb/d2lW/35xX/+Lf2//vLWs/8/K - xP/7+vr/wLqx/3NkUf9qWkb/alpG/25fS/+GeWj/ZFM+/z8qENdFMRh1WEYvIdTOyQGZjX8AbF1KAG1e - ShxFMhjMOSQJ/jciB/8zHgL/dmhV/1E/KP9oWET/+Pf2///////h3tr/UD4m/zsnDP9EMBb/QzAW/zsm - C/9BLRT/hnlp/0EtFP84Iwj+RTEYzG1dShxsXUkATDkgAE06Iig7JgzoNyIH/z4pD/9wYU3/ubKo/1RB - Kv9qWkb/+Pf3///////Hwbn/OyYL/4p+bv/c2NT/3NjT/6iek/87Jwz/e21b/1E+J/82IQb/OyYM6E06 - IihMOSAAWUcwAF1MNjs9KQ/0NSAE/2pbRv/z8vD/7evo/1JAKf9qWkb/+Pf3///////p5+T/tq6l/9vX - 0v///////////9XRy/9BLRP/in1t/25fS/81HwT/PSkP9F5NNztZRzEAAAAAAEo3H3A6JQr9NB8D/3xu - XP//////7Oro/1JAKf9qWkb/+Pf3/////////////////////////////////9XRy/9BLBP/kIR1/35w - Xv80HwP/OiUK/Uo3HnAAAAAAUkApRj4pD+M3Igf/OiUK/6iekv//////7Oro/1JAKf9qWkb/+Pf3//// - /////////////////////////////9XQy/9ALBL/k4d5/6qhlf86JQr/NyIH/z4pD+NSPyhGQy8V1jgj - CP82IQb/emxa//Tz8f//////7Oro/1JAKf9qWkb/+Pf3//////////////////f29f/x7+3/8vDu/6yj - mP83Igf/mI6A//b29P97bVv/NiEG/zgjCP9DLxXWRTEYxDgjCP82IQb/m5CD////////////7Oro/1JA - Kf9qWkb/+Pf3/////////////////6ackP9XRS7/WEYw/0IuFf91Z1T/4d7Z//////+bkIP/NiEG/zgj - CP9FMRjEUT4nXTsmDPU2IAX/XUw2/+3r6f//////7Oro/1JAKf9qWkb/+Pf3/////////////////6ac - kP9XRS7/WEYv/0IuFP92Z1T/4t/b/+7s6v9dTDb/NiAF/zsmDPVRPiddYlE7Gz8qENQ3Igb/SDUc/93a - 1f//////7Oro/1JAKf9qWkb/+Pf3//////////////////f29f/x7+3/8vDu/6yjmP83Igf/mo+B/9/c - 2P9INRz/NyIG/z8qENRhUTwbX044Jj8qEOA2IAX/WUcx/+3r6f//////7Oro/1JAKf9qWkb/+Pf3//// - /////////////////////////////9XQy/9ALBL/kod4/+/t6/9ZRzD/NiAF/z8qEOBfTjgmVEIqRjwn - DfI3IQb/TDkg/8bAuf//////7Oro/1JAKf9qWkb/+Pf3/////////////////////////////////9XR - y/9BLBP/kYV2/8nDvP9MOSH/NiEG/zwnDfJUQitGVUMsQj4pD+Q4Iwj/NiEG/047I//PysT/7uzq/1JA - Kf9qWkb/+Pf3///////p5+T/tq6l/9vX0v///////////9XRy/9CLhT/fG5c/1tKNP82IQX/OCMI/z4p - D+RVQyxCwry1Ak07I0Y/KhDUNyIH/0s4IP+akIL/jIBw/0UxGP9rW0f/+Pf3///////Fv7j/OiYL/4p+ - bv/c2NT/3NjT/6iek/87Jwz/eWpY/006Iv83Igf/PioQ1E06Ika8ta0CY1M+AEIvFQBYRjA1PCcN3lA9 - Jf96a1n/NyIH/zQeA/9qW0b/+Pf2///////e29b/TToi/zsmC/9DLxb/QzAW/zsmC/9BLRT/hnlp/0Et - FP89KA7eV0YvNUMwFgBhUDwAAAAAAG5fSwD5+PcBQCwSm0QwF/+FeGj/gnVk/3lrWf+Vinz/9fPy//// - ///39vX/ubGo/4F0Y/9yYk//alpG/25fS/+GeWj/Y1M9/zciB/9BLhSb+fr5AW5gTAAAAAAAAAAAAAAA - AABBLRMASjcfVjsmDPs7Jgv/Qy8W/047I/9aSDL/hHdm/6CWif9xYk7/cWJP/52Thv+DdWX/Xk04/11M - Nv9KNx//OCMH/zsmDPtKNx9WQS0TAAAAAAAAAAAAAAAAAAAAAABcSzUAY1I9E006ImxFMRieQCsSxzgj - CPU2IAX/NiEG/zYhBv81IAT/NSAE/zYhBv82IQb/NSAF/zciB/U+KhDHQy8Wnko3H2xiUTwTXEo0AAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAKGXiwDJxL0BalpGDT4qEHM5JQrzOCMI/zgjCP86Jgv7OyYL+zgj - CP84Iwj/OSQK8z4pD3NnV0MNt66lAZKGeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAASzggAFNBKgVBLRSBPysR60QwFrBNOyNpTzwlaUYyGbBALBLrQS0TgVA+JgVJNh0AAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAA/4H/APgAHwD4AB8A8AAPAIAAAQCAAAEAgAABAIAAAQCAAAEAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAADAMAAAwDgAAcA4AAHAPgAHwD+AH8AKAAAACAA - AABAAAAAAQAgAAAAAAAAEAAAEwsAABMLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAHlrWQCGeWkEVkQtRkUyGclEMBfJU0EpR52UhwZ7blwAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAalpHAAAAAABlVUAmcGBNQG5fTDE8Jw0dXEs1L0g1HIw9KQ7qOCMI/zgjCP8+KQ/sTToikltK - My88Jw0dbl9MMXBgTUBlVUAmAAAAAGdXQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAABaSDIAY1I9IEQwF8pBLRP5PysR7TkkCeA+KQ/rOSUK/jgjCP84Iwj/OCMI/zgj - CP85JAr+PSkP6zkkCeA/KxHtQS0T+UQwF8piUTwgWUgxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAeGpXALy2rQJKNx+MOSQK/zchBv83Igb/NyIH/zgiB/84Iwj/NyIH/zUg - Bf81IAX/NyIH/zciB/83Igf/NyIH/zciBv83Igf/OSQK/0o3Hoy6s6oCdmhWAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAANnUzwA0HwMAXk44Rz4pD+05JQr/RTEY/0g1HP9GMhn/OSQJ/zUf - BP9AKxH/e21b/3xuXP9HMxr/RjIZ/0g0HP9INBv/RzQb/0EtFP83Igf/PSgO7V5NN0czHgIA2dTPAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ2ThgAWAAAAYlE8ElRCK2BFMRfVNiEG/1xLNf+pn5T/lIl7/6ee - kv+DdmX/bl9L/763rv/5+Pf/8vHv/66mm/+Wi33/lop8/5aKfP+Wi33/mo+C/4l9bf9HNBv/Qy8W1VRC - K2BhUDsSJxAAAI+CcwAAAAAAAAAAAAAAAAByY1AAg3ZlFVE/J2lALBLFOyYM+jgjCP81IAT/a1tH/4Z5 - af83Igf/e21b//X08//39vX///////v6+f+aj4L/STYd/z4pD/89KA7/PSgO/z0pDv9DLxb/dGZT/5WK - e/8+KQ//OyYL+j8rEcVQPSVpgHJhFW5eSwAAAAAAAAAAAEw6IgBgTzpiPioQ/jgjCP84Iwj/OCMI/zMe - Av9pWUT/g3Zl/zEcAP93aVb//Pv7////////////1tLN/0UyGf83Igf/Qi4U/0MvFv9DLxb/QCwS/zci - B/83IQb/kIR1/15NN/82IAX/OCMI/z4qEP5gTzliTDkhAAAAAAAAAAAAPCgNAEMvFWk5JAn/OCMI/zgj - CP86JQr/V0Uv/66lmv+Ie2v/MRwA/3dpVv/8+/v///////////+5sqn/NSAF/0k2Hv/DvLT/1tHM/9XR - y//Mx8D/Z1dD/zMdAv92aFX/cWJP/zUgBP84Iwj/OSQJ/0MvFmk8KA0AAAAAAAAAAAAAAAAAU0AphDkk - Cf84Iwj/OCIH/4x/cP/q5+T//////4p+bv8xHAD/d2lW//z7+////////////8fBuv9oWUT/fW9d/+7s - 6v////////////////+Wi3z/Mh0B/3VmU/+YjX//NyIH/zgjCP85JAn/U0EphAAAAAAAAAAAemlYAAAA - AABPPSWYOSQJ/zgjCP87Jgv/vbat////////////in1u/zEcAP93aVb//Pv7/////////////Pz8//j3 - 9v/5+Pj//v7+/////////////////5iMf/8yHQH/d2hW/7ixp/87Jgv/OCMI/zkkCf9PPSWYAAAAAHRm - UgBcSzUAZVQ/J0EtE9M4Iwj/OCMI/z0pDv/JxL3///////////+KfW7/MRwA/3dpVv/8+/v///////// - ////////////////////////////////////////mIx+/zIdAf93aFb/xb+3/z0pD/84Iwj/OCMI/0Et - E9NjUz4nW0kzAHlrWBBJNh2oOSQJ/zgjCP82IQb/U0Ap/+He2v///////////4p9bv8xHAD/d2lW//z7 - +/////////////////////////////////////////////////+YjH7/Mh0B/3VnVP/d2dX/U0Ep/zYh - Bv84Iwj/OSQJ/0g1HKh3aVcQTjsjjDsmC/k4Iwj/NyIH/0AsEv+zq6H/////////////////in1u/zEc - AP93aVb//Pv7/////////////////////////////////////////////////4t/b/8yHAD/c2RR//v7 - +v+0raP/QCwT/zciB/84Iwj/OyYL+U47I4xFMRjoOCMH/zgjCP82IAX/hHdm//r6+f////////////// - //+KfW7/MRwA/3dpVv/8+/v///////////////////////f29f/Szsj/zcjC/87Jw/+wqJ7/Tzwk/zUf - BP+MgHD///7+//v6+v+Ed2f/NiAF/zgjCP84Iwf/RTEY6E48JJs5JAn/OCMI/zUgBf97bVv/+vn5//// - /////////////4p9bv8xHAD/d2lW//z7+///////////////////////4d7Z/1A+Jv89KQ//PioQ/zkk - Cv89KQ//nJGE/+zq5///////+vn5/3ttW/81IAX/OCMI/zkkCf9OPCSbYlI9O0AsEuk4Iwf/NyIH/0Yy - Gf/Tzsj/////////////////in1u/zEcAP93aVb//Pv7///////////////////////h3tn/UD4m/z0p - D/8+KhD/OSQJ/z0pD/+ckYT/7Oro///////Tzsj/RjIZ/zciB/84Iwf/QCwS6WJSPTuooJQFRTEYoDgj - CP84Iwj/NiEG/6KYi/////////////////+KfW7/MRwA/3dpVv/8+/v///////////////////////f2 - 9f/Szsj/zcjC/87Jw/+wqJ3/Tzwk/zUfBP+MgHD//////6KYi/82IQX/OCMI/zgjCP9FMRigqJ+TBQAA - AABOOyOLOSQJ/zgjCP84Iwj/sKid/////////////////4p9bv8xHAD/d2lW//z7+/////////////// - //////////////////////////////////+Lf2//MhwA/3NkUf/+/v7/sKid/zgjCP84Iwj/OSQJ/048 - I4sAAAAAycO7BUk1Ha04Iwj/OCIH/0ArEf/MxsD/////////////////in1u/zEcAP93aVb//Pv7//// - /////////////////////////////////////////////5iMf/8yHQH/dGVS//79/f/Mx8D/QCsR/zgi - B/84Iwj/STYdrcjEvAV8b10UQzAWzDgjCP84Iwf/PikP/62kmf/9/Pz///////////+KfW7/MRwA/3dp - Vv/8+/v/////////////////////////////////////////////////mIx+/zIdAf90ZVL/+fj3/66m - m/8+KQ//OCMH/zgjCP9EMBbMfW9eFGVVQCZBLBPhNyIH/zgjCP83Igf/Qy8V/5yShf/18/L//////4p9 - bv8xHAD/d2lW//z7+/////////////z8/P/49/b/+fj4//7+/v////////////////+YjH//Mh0B/3Rl - Uv+nnZH/Qy8W/zciB/84Iwj/NyIH/0EsE+FlVUAmhHZmDE48JIE8Jw3zOCMI/zgjCP81IAX/VkQt/9bS - zf/+/v7/in1t/zEcAP93aVb//Pv7////////////x8G6/2hZRP99b13/7uzq/////////////////5aL - fP8yHQH/cWJO/3RlUv80HwP/OCMI/zgjCP88Jw3zTTsjgYF0YwxjUj0AfG5dBE88JV4/KxHkOCMI/zUg - BP91Z1T/kIV2/3NlUv9XRS//Mx4C/3dpVv/8+/v///////////+3sKb/NSAE/0k2Hv/DvLT/1tHM/9XR - y//Mx8D/Z1dD/zMdAv92aFX/cmNP/zUgBP84Iwj/PyoQ5E48JF58blwEY1I9AAAAAAByZVEA////AF5N - OEk+KhDmNR8E/3ZnVP9tXUn/LxkA/zUfBP80HgP/d2hW//z7+////////////9LNx/9CLhX/OCMI/0Iu - FP9DLxb/Qy8W/0AsEv83Igf/NyEG/5CEdf9eTTf/NiAF/z4qEOZdTTdJ////AHBhTgAAAAAAAAAAAAAA - AACBc2MAvreuA0UxGKM1IAX/cWJP/5qQgv9pWUX/Wkgy/0g1HP9/cWD//Pv7////////////+fj4/5SJ - ev9GMhn/OyYM/zwnDf89KA7/PSkO/0MvFv90ZlP/lYp8/z4qD/84Iwj/RTEYo764sAOBdGQAAAAAAAAA - AAAAAAAAAAAAAAAAAAA6JQoASzggXDkkCfxDLxb/bF1J/4ByYf+NgXL/lop8/62kmf/g3dn///////j3 - 9v/f29f/2NTP/8/KxP/CvLT/pZyQ/5WKfP+Wi33/mo+C/4l9bf9HNBv/NyIG/zolCvxLOCBcOiUKAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAF1MNgBdTDYkQCwS4jciB/81HwT/NR8E/zciB/88Jw3/QS0T/1pI - Mv+UiHr/cGBN/0s4H/9KNx//cGBN/5aLff9fTzn/RzMa/0c0G/9BLRT/NyIH/zciBv84Iwj/QCwS4l1M - NiRdTDYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAi39wAI2AcgVWRC1lSDUcqEMvFs0/KxHqOiUK/Dgj - CP84Igf/NyEG/zUgBf81IAT/NyIH/zciB/81IAX/NSAF/zYhBv83Igf/NyIH/zolCvw+KhDqQS0UzUYy - GahUQiplkIR1BYyAcQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJuRhACzq6IDeGpYEWNT - PipCLhV5OiUK8jgjCP84Iwj/OCMI/zgjCP84Igf/OCIH/zgjCP84Iwj/OCMI/zgjCP85JAryQi4UeWFQ - OypvX0wRpJqOA4x/cQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAASzgfAFVDKwRCLhRtOiYL8jgjCP84Iwj/OiUL+0EsE+dBLRPnOyYL+zgjCP84Iwj/OiUL8kEt - E21RPicESTUdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAU0EqAGBPOgRFMhl9QS0T7Ek2HblNOiJpZFQ/MGhYQzBRPidpSzkguUIu - FOxEMRd9XEw2BFE/KAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/+B///wAA//4A - AH/8AAA//AAAP/AAAA/AAAADwAAAA8AAAAPAAAADwAAAA4AAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AACAAAABAAAAAAAAAAAAAAAAAAAAAIAAAAHgAAAH4AAAB/AAAA/wAAAP8AAAD/wAAD//gAH//8AD/ygA - AAAwAAAAYAAAAAEAIAAAAAAAACQAABMLAAATCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGxc - SACShXcEaVlFT0o3HstINBzLZFM+T4R3ZwRkUz4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AACXjH4AAAAAAGdXQyNRPiaTQCsR6zkkCf85JAn/PyoQ6048JJR+cF8v////AIl8bAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1c/OAGtcSAB0ZVMib2BMU4F0 - Y1OMgHBJSTYdIT4pDx1rW0cmXUw2dEIuFNY6JQr+OCMH/zgjCP84Iwj/OCMI/zolC/9HMxrgW0kzdWtb - RyY+KQ8dSTYdIYyAcEmBdGNTb2BMU3RlUyJrXEgAysjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfW9eAIR3 - Zw1MOSGsQS0T/0UxGP5FMRj4OSQK4zolC+FBLRPmPSkO/DgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP83Igf/PSgO/EEtE+Y6JQvhOSQK40UxGPhFMRj+QS0T/0s5IKyEd2YNfW9dAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AACgl4sAAAAAAGFQOmw9KA76NyIH/zciB/83Igf/OCMI/zgjCP84Iwf/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjB/84Iwj/OCMI/zciB/83Igf/NyIH/z0oDvpgTzpsAAAAAKKY - jAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAABXRS4Aa1tHJEQxF9U4Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zYhBf82IQX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9EMBfValpGJFZELQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAKCWiQDEvbUHVEIrmjolCv84Iwj/NiEF/zUgBP81IAT/NSAE/zUg - BP83Igf/OCMI/zgjCP82IQb/OSQJ/2BPOf9gUDr/OSQJ/zUgBP81IAT/NSAE/zUgBP81IAT/NSAE/zUg - BP81IAT/NiEG/zgjCP86JQr/VEEqmsG6sweek4cAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAl41+AODd2ARtXUleQi4U9DciB/85JAn/YE86/31v - Xf98b13/fG9d/3lrWf9KNx//NB8D/zYgBf9PPSX/n5WI/+zq5//t6+j/oZeK/21dSv94aVf/fG9d/3xv - Xf98b13/fG9d/3ttXP9wYU7/VUMs/zomC/82IQb/Qi4U9GxdSV7h3dkEl41+AAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AGtaRgCNgHAFY1I9OlNBKp9EMBbvOCMI/zgj - CP8+Kg//wbuy/7Wto/+il4v/oZeL/8nEvf+so5j/alpG/4l9bf/b19L//v79////////////7Orn/763 - rv+onpP/opiM/6OYjP+jmIz/opiM/6Sajv+wp53/w721/6ifk/9OOyP/NiEG/0QwFu9TQiqfY1M+Oo2A - cQVrW0cA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wANAAAAqKCUDlxKND9JNh2fQCwS7Tkk - Cf83Igf/OCMI/zgjCP8/KxH/wLqx/11MNv81IAT/NR8E/4+DdP/+/v7/+Pf2//v7+/////////////// - ///Qy8T/Y1I9/z0pD/84Iwj/NyIH/zciB/83Igf/NyIH/zciB/85JAn/TToh/5qPgv+2rqT/RzQb/zYh - Bv85JAn/QCwS7Uc0G59ZRzE/p52RDh4IAAD08e8AAAAAAAAAAAAAAAAAAAAAAJ+WiQCjmo0YX085sj8r - EfM4Iwj/OCMH/zgjCP84Iwj/OCMI/zgjCP8/KxH/wLqx/15NN/82IAX/NSAF/4+DdP////////////// - /////////////+zq5/9jUj3/NB8D/zgjCP84Iwj/OCIH/zgjB/84Iwf/OCMH/zgjCP84Iwj/NiEG/zsm - C/+upZr/in5u/zYgBf84Iwj/OCMH/zgjCP8/KxHzXkw3sqGXihiflIcAAAAAAAAAAAAAAAAAAAAAAIF0 - YwB/cmEtSDUc7TchBv84Iwj/OCMI/zgjCP84Iwj/OCMI/zciB/89KQ7/wLmw/15NN/82IAX/NSAF/4+D - dP///////////////////////////8C6sf88Jw3/OCMI/zkkCf8+KhD/PysR/z8rEf8/KxH/PysR/zsm - DP83Igb/OCMI/zUgBP9sXEj/tKyi/zwoDv84Iwj/OCMI/zgjCP83IQb/SDUc7YByYS2CdGQAAAAAAAAA - AAAAAAAAAAAAAFZFLgBWRC0tPysR7TgjCP84Iwj/OCMI/zgjCP83Igf/NSAE/zwoDf9rXEj/1NDK/11M - Nv82IAX/NSAF/4+DdP///////////////////////////6KYi/82IQb/NyIG/0s4IP+6s6n/y8W+/8rE - vP/KxLz/ysS9/7uzqv9hUDv/NyEG/zYhBv9RPyj/vLSr/0g0HP83Igf/OCMI/zgjCP84Iwj/PysR7VZE - LS1XRS4AAAAAAAAAAAAAAAAAAAAAAEEtEwBINBsxOSUK7zgjCP84Iwj/OCMI/zgjB/9ALBL/cGFO/7ix - p//y8O7/8fDu/1tKM/82IAX/NSAF/4+DdP///////////////////////////5aKfP8yHAH/Mx0C/089 - Jf/p5+T///////////////////////////+xqZ7/OSQK/zciBv9KNx//vreu/1NBKv83IQb/OCMI/zgj - CP84Iwj/OSUK70c0GzFBLRMAAAAAAAAAAAAAAAAAAAAAAGVUPwB+cF5eQCwS/jgiB/84Iwj/OCMI/zYg - Bf+GeWj/9/b1////////////8fDu/1tKM/82IAX/NSAF/4+DdP///////////////////////////7ix - p/93aFb/d2lX/4p+bv/x7+3///////////////////////////+/ubD/PCgN/zciBv9INBv/1tHM/4p9 - bf81IAX/OCMI/zgjCP84Igf/QCwS/n5wXl5lVD8AAAAAAAAAAAAAAAAAAAAAAFpJMwBsXEhpPysR/zgj - CP84Iwj/OCMI/zYhBv+glYj/////////////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////// - //////////////7+/v/+/f3//v79//7+/v////////////////////////////////+/uLD/PCgN/zci - Bv9HNBv/3NjT/6GYi/82IQb/OCMI/zgjCP84Iwf/PysR/2xdSWlbSTMAAAAAAAAAAAAAAAAAn5WIAAAA - AABPPSV9OiYL/zgjCP84Iwj/OCMI/zgjCP+tpZr/////////////////8fDu/1tKM/82IAX/NSAF/4+D - dP////////////////////////////////////////////////////////////////////////////// - //+/uLD/PCgN/zciBv9HNBv/3NjT/6+nnP84Iwj/OCMI/zgjCP84Iwj/OiYL/089JX0AAAAAn5SIAAAA - AAD///8AY1M+AG5fS0BFMRjnNyIH/zgjCP84Iwj/OCMI/zkkCf+8taz/////////////////8fDu/1tK - M/82IAX/NSAF/4+DdP////////////////////////////////////////////////////////////// - //////////////////+/uLD/PCgN/zciBv9HNBv/3dnU/764r/85JAn/OCMI/zgjCP84Iwj/NyIH/0Ux - GOdsXUlAYlE8AP///wB7bVsAgHJhH0s4IMY4Iwj/OCMI/zgjCP84Iwj/NyIH/0MvFf/OycP///////// - ////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////////////////////////////////////// - //////////////////////////////////+/ubD/PCgN/zciBv9HNBv/3NjT/9HMxf9DLxX/NyIH/zgj - CP84Iwj/OCMI/zgjCP9LOCDGf3JgH3ttWwCimYwNUkAomjolC/84Iwj/OCMI/zgjCP84Iwj/OCMI/5CE - df/6+vn/////////////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////////////////////// - //////////////////////////////////////////////////++t67/PCcN/zciBv9HNBv/2tbQ//38 - /P+RhXb/OCMI/zgjCP84Iwj/OCMI/zgjCP86JQv/UT8omqCWiQ1ZSDJ6PioQ9jgjB/84Iwj/OCMI/zgj - CP82IAX/bl9L/+3r6f//////////////////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////// - //////////////////////////////////////////////////////////////////+qoZX/OCMI/zci - B/9HNBv/2dXQ///////u7Or/cWJO/zYgBf84Iwj/OCMI/zgjCP84Iwf/PioP9llHMXpKNx/tNyIH/zgj - CP84Iwj/OCMI/zYhBv9UQiv/19LN////////////////////////////8fDu/1tKM/82IAX/NSAF/4+D - dP////////////////////////////////////////////7+/v/5+Pj/+fj3//n49//5+Pf/9/b1/9PO - yP9fTjj/NiEG/zUgBP9PPCT/4t/c////////////2NTP/1RCK/82IQb/OCMI/zgjCP84Iwj/NyIH/0s3 - H+1XRS+1OiUK/zgjCP84Iwj/OCMI/zUgBP9uX0z/9/b1////////////////////////////8fDu/1tK - M/82IAX/NSAF/4+DdP///////////////////////////////////////////+Th3v91Z1T/aFhE/2lZ - Rf9pWUX/ZlVB/0o3Hv83Igf/OyYL/0s4IP+flYj//Pv7////////////9/b1/25fTP81IAT/OCMI/zgj - CP84Iwj/OiUK/1dGL7VrW0hMQCsS7zgjB/84Iwj/OCMI/zciB/9FMRj/zsnC//////////////////// - ////////8fDu/1tKM/82IAX/NSAF/4+DdP///////////////////////////////////////////9vX - 0v9GMhn/NB4D/zUgBP81IAT/NSAE/zciB/82IAX/a1tH/9/c1//8+/v/////////////////zsnC/0Ux - GP83Igf/OCMI/zgjCP84Iwf/QCsR72tbR0yglooMUj8ouTgjCP84Iwj/OCMI/zgjCP81IAX/iX1t//7+ - /f//////////////////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////////////////////// - /////////////9vX0v9GMhn/NB4D/zUgBP81IAT/NSAE/zciB/82IAX/a1tH/9/c2P/8+/v///////// - ///+/f3/iX1t/zUgBf84Iwj/OCMI/zgjCP84Iwj/Uj8ouaGXiwwbAwAAZVVAYD0pD/o4Iwj/OCMI/zgj - CP82IQb/UD4m/+Dd2P//////////////////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////// - /////////////////////////////+Th3v91Z1T/aFhE/2lZRf9pWUX/ZlVB/0k2Hv83Igf/OyYL/0s4 - IP+glon//Pv7///////g3Nj/UD4m/zYhBv84Iwj/OCMI/zgjCP89KQ/6ZVVAYBsDAABtXUoAcWNPE0Et - FMY4Iwj/OCMI/zgjCP84Iwf/PSkP/8S+tv//////////////////////8fDu/1tKM/82IAX/NSAF/4+D - dP////////////////////////////////////////////7+/v/5+Pj/+fj3//n49//5+Pf/9/b1/9LO - yP9eTjj/NiEG/zUgBP9PPCT/4+Dc///////Evrb/PSkP/zgjCP84Iwj/OCMI/zgjCP9BLRTGcWJQE21d - SgCwp5wAxb63C1I/KL83Igf/OCMI/zgjCP83Igf/RzMa/9nW0P//////////////////////8fDu/1tK - M/82IAX/NSAF/4+DdP////////////////////////////////////////////////////////////// - //////////////////+qoZX/OCMI/zciB/9HNBv/2dXQ///////Z1dD/RjMa/zciB/84Iwj/OCMI/zci - B/9SQCi/xsG4C7CpngByYk8AdmdVFUUxGNU4Iwj/OCMI/zgjCP82IQb/WEYw/+vp5v////////////// - ////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////////////////////////////////////// - //////////////////////////////////++t6//PCcN/zciBv9HNBv/2dXQ///////r6eb/WEYv/zYh - Bv84Iwj/OCMI/zgjCP9FMhjVdmhVFXFjUAB+cF4Af3FgNkUxGPA3Igf/OCMI/zgjCP81IAT/b2BN//n5 - +P//////////////////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////////////////////// - //////////////////////////////////////////////////+/ubD/PCgN/zciBv9HNBv/2dXQ//// - ///5+fj/b2BN/zUgBP84Iwj/OCMI/zciB/9FMRjwf3JgNn5wXwBKNh4AXUw2Sj0oDvk4Iwj/OCMI/zgj - CP82IAX/bF1J/+7s6v//////////////////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////// - //////////////////////////////////////////////////////////////////+/uLD/PCgN/zci - Bv9HNBv/2dXQ///////v7ev/bV1J/zYgBf84Iwj/OCMI/zgjCP89KA75XUw2Sko3HgA6JgsAY1M+ejwn - Df84Iwj/OCMI/zgjCP84Iwj/OiUL/3lqWP/j4N3/////////////////8fDu/1tKM/82IAX/NSAF/4+D - dP////////////////////////////////////////////////////////////////////////////// - //+/uLD/PCgN/zciBv9HNBv/2tbR/+bk4P96bFr/OyYL/zgjCP84Iwj/OCMI/zgjCP88Jw3/ZFM+ejsm - CwAAAAAATz0ljDkkCf84Iwj/OCMI/zgjCP84Iwj/OCMI/zYhBv9hUDr/zsnC//7+/v//////8fDu/1tK - M/82IAX/NSAF/4+DdP////////////////////////////7+/v/+/f3//v79//7+/v////////////// - //////////////////+/uLD/PCgN/zciBv9JNR3/x8G5/21dSf82IAX/OCMI/zgjCP84Iwj/OCMI/zgj - CP85JAn/Tz0ljAAAAAAAAAAAcGFOREo2HtQ4Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP83Igf/emxa//Dv - 7f//////8fDu/1tKM/82IAX/NSAF/4+DdP///////////////////////////7ixp/93aFb/d2lX/4p+ - bv/x7+3///////////////////////////+/ubD/PCgN/zciBv9JNh3/urKo/047I/83IQb/OCMI/zgj - CP84Iwj/OCMI/zgjCP9JNh3Ubl9LRAAAAADU0MsAAAAAAGFRPCROOyO3OiYL/TgjB/84Iwj/OCMI/zci - B/9KNh7/x8G5/8zGv//X087/2tbR/1hHMP82IQX/NSAF/4+DdP///////////////////////////5SJ - e/8yHAH/Mx0C/089Jf/p5+T///////////////////////////+xqZ7/OSQK/zciBv9LNx//u7Oq/088 - JP83Igb/OCMI/zgjCP84Iwf/OiYL/U47I7dhUTwkAAAAANTQywAAAAAAxL61AEUxGQCAcmEUSzggiz8r - Efo4Iwj/OCMI/zciBv9PPSX/u7Oq/048JP9EMRf/UD4m/z4qEP84Iwj/NSAF/4+DdP////////////// - /////////////52Thv82IQb/NyIG/0s4IP+6s6n/y8W+/8rEvP/KxLz/ysS9/7uzqv9hUDv/NyEG/zYh - Bv9RPyj/vLWs/0g1HP83Igf/OCMI/zgjCP8/KxH6Sjcfi31vXRRDLhUAxL61AAAAAAAAAAAAAAAAAAAA - AACFeGgAnJOFCmRUP3w+KhDzOCMI/zciBv9PPCX/urKp/0k2Hf82IQb/NiEG/zgjCP84Iwj/NSAF/4+D - dP///////////////////////////7qyqf86JQr/OCMI/zkkCf8+KhD/PysR/z8rEf8/KxH/PysR/zsm - DP83Igb/OCMI/zUgBP9rXEj/tK2i/zwoDv84Iwj/OCMI/z4qEPNjUz58mY+BCoJ2ZQAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAjIBwAJyShAhJNh61OCMI/zciBv9PPSX/urKp/0c0G/80HgP/NSAF/zYh - Bv83Igf/NSAE/4+DdP///////////////////////////+Xi3/9ZSDH/NB8E/zgjCP84Iwj/OCIH/zgj - B/84Iwf/OCMH/zgjCP84Iwj/NiEG/zsmC/+tpZr/in5u/zYgBf84Iwj/OCMI/0o3HrWckoUIjIBwAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnJGEACgRAABUQituOyYL/zciB/9KNh7/x8G5/6GX - iv+BdGP/bV5K/1hHMP9KNx7/OycM/5CEdf/////////////////////////////////Fv7f/XUw2/zwo - Df83Igf/NyIH/zciB/83Igf/NyIH/zciB/85JAn/TDoh/5qPgf+2rqT/RzQb/zciB/84Iwj/OyYL/1RC - K24oEQAAnJKGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF1MNgBiUjwtQCsR6Dci - B/86JQr/ZVVA/4p+bv+imIz/same/722rf+/uK//uLGn/9XRy//5+Pj///////////////////////z8 - +//9/fz/7evo/8bAuP+so5j/p52S/6Sajv+imIz/opiM/6Sajv+wp5z/w721/6ifk/9OOyP/NiEG/zgj - CP84Iwj/QCsR6GJSPC1dTDYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHhq - VwCNgnIIRTIYszgjCP84Iwj/NiAF/zUgBP82IQb/OyYM/0MwFv9QPSb/ZFQ//3FiT/9+cF//4d3Z//f2 - 9f/c2NP/saie/31vXf98b13/rqWa/9vY0//5+Pf/4+Hd/4x/cP97blz/fG9d/3ttXP9wYU7/VUMs/zom - C/83Igb/OCMI/zgjCP84Iwj/RTIZs46Dcwh4a1gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAMG5sAAvGQAAXEo0ejsmDP83Igf/NyIH/zgjCP84Iwj/OCMI/zciB/83IQb/NSAF/zUg - BP82IQX/YE85/21eSv9JNh3/OSQJ/zUgBP81IAT/OCMI/0g0HP9rW0f/YE86/zYgBf81HwT/NSAE/zUg - BP81IAT/NiEG/zgjCP84Iwj/NyIH/zciB/87Jgz/XEs1ei4YAADEvrQAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABYRjAAbl5LLFNBKrZEMRfaQi4U9DwnDfw5JQr/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/NiEF/zUgBf83Igf/OCMI/zgjCP84Iwj/OCMI/zciB/81IAX/NiEF/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zkkCf87Jgz8QCwS9EIuFdpRPie2bl9MLFhHMQAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARTEYALGpngp6bFoaa1xIO2JS - PWRRPyiMTjsjvzolC/A4Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP86JQrwTjsjv088JIxdSzVkZFM+O3FiTxqil4sKNCAGAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAPb19AD///8BnpOGCEIuFWY7JgzwOCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zsmC/BBLRRmnZOGCP///wH29fQAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkMrAG5dSQJGMxlmPCcN7zgjCP84Iwj/OCMI/zgj - CP84Iwf/OCMI/zsmC/87Jgv/OCMI/zciB/84Iwj/OCMI/zgjCP84Iwj/PCcM70QwF2ZmVkMCUT8oAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFlHMABxYU4CSTYdZT0o - Du84Igf/OCMJ/zwnDP1AKxHmSTYdvV1MNoleTTiJTDkhvUEtE+Y8KA39OCQJ/zciB/88KA3vSDUcZWlb - RgJUQywAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AABjUj0Af3FiA007InRFMhjvTjsjw11NN3xlVUAyi39vC////wH///8BlIl7C21dSTJjUz58UT8ow0cz - Gu9MOCB0eGpWA19ONwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAD///gf//8AAP//8A///wAA//AAAA//AAD/4AAAB/8AAP/gAAAH/wAA/8AAAAP/ - AAD/gAAAAf8AAP8AAAAA/wAA/AAAAAA/AADwAAAAAA8AAOAAAAAABwAA4AAAAAAHAADgAAAAAAcAAOAA - AAAABwAA4AAAAAAHAADgAAAAAAcAAOAAAAAABwAAwAAAAAADAACAAAAAAAEAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAQAAgAAAAAABAACAAAAAAAEAAIAA - AAAAAQAAgAAAAAABAACAAAAAAAEAAIAAAAAAAQAAgAAAAAABAACAAAAAAAEAAMAAAAAAAwAA4AAAAAAH - AADwAAAAAA8AAPgAAAAAHwAA/AAAAAA/AAD8AAAAAD8AAPwAAAAAPwAA/gAAAAB/AAD+AAAAAH8AAP8A - AAAA/wAA//AAAA//AAD//AAAP/8AAP/+AAB//wAA//8AAP//AAAoAAAAoAAAAEABAAABACAAAAAAAACQ - AQATCwAAEwsAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AHNjUf9zY1H/////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8Am5CD/z4pD/84Iwj/OCMI/zsnDP+NgXL/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wDCvLT/UD0m/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/0w4IP+2raP/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wBvXkv/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/2dX - RP////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AI2Bcv87Jgv/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OSQJ/4FzYv////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8Atq2j/0k1Hf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/RjMZ/6eek/////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AY1Q//zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/X085/9DMxv////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wCNgXL/OSQJ/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/gXNi/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AKee - k/9DLxf/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9AKxL/m5CD//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8ApJuQ/3ttW/+Ie2r/kIN0/5eMfv+il4z/q6OW/7Ssov+9tq3/yMK6/9DMxv////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8AysS+/1hHMP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9YRzD/ycS9/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wDQzMb/yMK6/722rf+0rKL/q6OW/6KXjP+XjH7/kIN0/4h7 - av97bVv/pJuQ/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ADolCv84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9CLhT/Sjcf/1NA - KP9fTzn/Xk02/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9cSzX/X085/1NAKP9KNx//Qi4U/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zolCv////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AGJRPf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/X085/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AKmg - lP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP+poJT/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wBDLxf/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Qi4U//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wB6alj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP96bFr/////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDHwrn/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/8O9tf////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8AU0Ao/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9QPSb/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8AlYp9/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/5eMfv////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ADwoDf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP87Jgv/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AGpbRv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/2ZWQv////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ALKq - oP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/sqqg//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wBINRz/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0YzGf////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wCDd2X/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OSQJ/1dGLv/Iwrr/yMK6/1dGLv84Iwj/OSQJ/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/gnRk/////wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDOyML/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/QCsS/5uQg/////////////// - ////////p56T/0IuFP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP/MxsD/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8AWEcw/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/emxa////////////////////////////////////////////iHtq/zkk - Cf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/VUQt/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8An5aI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/2ZW - Qv96bFr/emxa/3psWv96bFr/emxa/3psWv96bFr/emxa/3psWv96bFr/emxa/3psWv96bFr/dGRT/0Yz - Gf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9eTTb/0MzG//// - ///////////////////////////////////////////////////QzMb/Xk02/zgjCP84Iwj/Qi4U/1xL - Nf9pWUX/dGRT/3psWv96bFr/emxa/3psWv96bFr/emxa/3psWv96bFr/emxa/3psWv96bFr/emxa/3ps - Wv96bFr/emxa/3psWv9uYEz/ZlVB/048JP86JQr/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+floj/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Av7ev/z8qEP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/6adkf////////////////////////////// - ////////////////////////////////////////////////////////RjMZ/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/0YzGf+tpJj///////////////////////////////////////// - ///////////////////////////////////KxL7///////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////8/Lxf+hl4n/Y1M+/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/PyoQ/7+3r/////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wC0rKL/V0Yu/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP////////////////////////////////////////////////////////////// - /////////////////////////////3xuXf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OiUK/4l+ - bv////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////iX5u/zsnDP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/V0Yu/7Ss - ov////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AKeek/9OPCT/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj///////// - ////////opeM/6KXjP+il4z/opeM/6KXjP+il4z/opeM/6KXjP+il4z/opeM/6KXjP/JxL3///////// - //+Ie2r/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/ZlZC//////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////8O9 - tf+2raP/qJ2S/6KXjP+il4z/opeM/6KXjP+il4z/opeM/6KXjP+il4z/opeM/6KXjP+il4z/opeM/6KX - jP+il4z/opeM/6Sajv+yqZ//wLmx/87Iwv//////////////////////////////////////altG/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Tjwk/6eek/////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8Am5CD/0c0G/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy///////////////////////Szsj/ycS9/722 - rf+2raP/wryz//////////////////////////////////////////////////////////////////// - /////////////////////////////7Cnnf92Z1X/RjMZ/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/1VDLP+Qg3T///////////////////////////+ajoH/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/RzQb/52RhP////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wCQg3T/QS0T/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////pp2R/0Yz - Gf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9tX0r//////////////////////6uhlf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/QS0T/5CEdf////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AI2Bcv8+KQ//OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj///////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////ZlVB/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zsnDP+kmo7///////// - ////////lIh5/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/PCgN/4FzYv////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AgXNi/zol - Cv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////VUMs/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/42Bcv////////////////9mVUH/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OSQJ/3Nl - Uv////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AMnEvf9sXEj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////ZlZC/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/qJ2S/////////////////zolCv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/21dSf/KxL7/////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Awry0/2NT - Pv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj///////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////pp2R/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/z0p - Dv////////////////9/cmD/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/1xLNf+2raP/////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8AubOq/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////0c0 - G/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/dGZU/////////////////zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/7mxqP////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AKmglP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - /////////////////////////////////////////////6uhlf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP////////////////9eTTb/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP+poJT/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wCZkIL/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj///////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////9lVUD/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/k4d3////////////m5CD/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/m5CD/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8AjIBx/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - ////////////////////////////////////////////////////////////////////////PCgN/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/1VELf///////////9HM - x/85JAn/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/4yAcf////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AHtuXP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////0MzG/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/////////////////OSQJ/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9/cmD/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wBvXkv/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj///////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - /////////////6adkf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/PCgN/21d - Sf9tXUn/bV1J/21dSf9tXUn/bV1J/21dSf9tXUn/bV1J/21dSf9tXUn/bV1J/21dSf9tXUn/bV1J/21d - Sf9tXUn/YlI8/0MvF/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/8S+t////////////1dGLv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/b15L/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8AX085/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/SDUc/////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////+PgnT/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0g1HP////////////////////////////// - ////////////////////////////////////////////////////////////////////////wbux/1E+ - J/85JAn/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+onZL///////// - //9wYU3/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/19POf////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AFA+Jv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9QPSb/q6OW//////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////gnRk/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9INRz///////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////U0Ao/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/mo6B////////////hXdn/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9QPib/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wBCLhT/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/1pIMf+2raP///////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - /////////////3ZnVf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/SDUc//// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////8jCuv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv///////////5CEdf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Qi4U/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8AOCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/X043/7ato////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////9pWkX/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0g1HP////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////UT4n/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+Bc2L///////// - //+YjYD/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////ADgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9wYU3/zsjC//////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////aFdD/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9INRz///////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////3doVf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV////////////nZGE/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wA4Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OiUK/3xuXf////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - /////////////2hXQ/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/SDUc//// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+DeGb/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf///////////7KqoP86JQr/OSQJ/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wDPycL/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/RjMZ//////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////9oV0P/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0g1HP////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////0UxGP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP/PycL/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8AwLmx/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/1tKNf////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////aFdD/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9INRz///////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////9bSTT/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/wLmx/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ALKpn/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9qW0b///////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf//////////////////////aVpF/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/7KqoP////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wCimY3/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/empY//////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////3doVf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+kmo7/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8AlIh5/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/4d7a/////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////+GeWj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/lIp7/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AIV3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP+XjH7///////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf//////////////////////lot8/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/4Z5aP////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wB1aFT/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/pp2R//////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////6SbkP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8AZ1dE/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/7Ssov////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////+yqqD/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/Z1dE/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AEYzGf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP/CvLT///////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf//////////////////////wry0/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0YzGf////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AG9eS/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/0czH//////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////9DMxv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/altG/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AJuQ - g/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI//////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV////////////////////////////OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP+bkIP/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AMzGwP86JQr/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf///////////////////////////zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OSQJ/8jC - uv////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wBMOCD/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP85JAn///////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - //////////////////85JAn/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9JNR3/////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wBsXEj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/RTEY//////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV////////////////////////////QzAW/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/2lZRf////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wCXjH7/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/1RC - Kv////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf///////////////////////////1NAKP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/mI2A/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wDGwLj/OSQJ/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+elIb///////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - //////////////////+elIb/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zkkCf/GwLj/////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8ARzQb/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zkkCf9sXEj///////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV/////////////////////////////////25g - TP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/RjMZ/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8AZlZC/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9KNx////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf//////////////////////////////////////TToi/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9iUT3/////AP///wD///8A////AP///wD///8A////AP///wD///8Ak4d3/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP87Jwz/zsjC//////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////g3dl/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////////////////////9POyP88KA3/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/5KH - eP////8A////AP///wD///8A////AP///wD///8Aw762/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/opmN//////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////3doVf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////////////////// - ////////opeM/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/wLmx/////wD///8A////AP// - /wD///8A////AEYzGf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/cWJP//////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////9iUjz/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf////////////////////////////////////////////////92Z1X/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/0UxGP////8A////AP///wD///8A////AGNUP/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/Tjwk//////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////PCgN/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////////////////////////////////////1E+J/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/YVA7/////wD///8A////AI2Bcv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OycM/9DM - xv////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////ubGo/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////////////////// - ////////////////////////PCgN/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+PgnT/////AMK8 - tP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/6adkf////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////1pIMf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3tuXP///////////////////////////////////////////////////////////6ad - kf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/7+3r/9TQCj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////4l8 - bP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+QhHX///////// - ////////////////////////////////////////////////////////d2hV/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9TQCj/lYp9/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/1A9Jv////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////3tuXP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/wryz//////////////////////////////////// - //////////////////////////////////9TQCj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/l4x+//// - /wA7Jwz/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //+5s6r/hnlo/0ArEv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/UT4n//////////////////////////////////////////////////////////////////// - /////////////zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OyYL/////wD///8Aemxa/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/w721//////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////8O9tf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OiUK/8nEvf////////////// - /////////////////////////////////////////////////////////////8K8tP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3ttW/////8A////AM7Iwv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/29eS/////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ///////////////////DvbX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/QCsS/7u1rP////////////////////////////////////////////// - //////////////////////////////////9tX0r/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP/Py8X/////AP// - /wD///8AYU86/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj///////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////w721/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9HNBv/hnlo//// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9eTTb/////AP///wD///8A////ALSsov84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/4p+cP////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////8O9tf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/XEs1/722rf////////////////////////////////////////////// - ////////////////////////////////////////////////////////iX5u/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/tKyi/////wD///8A////AP///wD///8ASTYe/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP8/KhD///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ///////////////////DvbX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/3xu - Xf////////////////////////////////////////////////////////////////////////////// - /////////////////////////////0EtE/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/SDUc/////wD///8A////AP// - /wD///8A////AJiNgP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/6adkf////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////w721/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP98bl3///////////////////////// - /////////////////////////////////////////////////////////////////////////////6Sb - kP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/5qOgf////8A////AP///wD///8A////AP///wD///8AOycM/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9QPib///////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////8O9tf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/fG5d//////////////////////////////////////////////////// - //////////////////////////////////////////////////9TQCj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zwo - Df////8A////AP///wD///8A////AP///wD///8A////AHxuXf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/8G7 - sf////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ///////////////////DvbX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/3xu - Xf////////////////////////////////////////////////////////////////////////////// - ///////////////////AubH/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP98bl3/////AP///wD///8A////AP// - /wD///8A////AP///wDSzsj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9sXEj///////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////w721/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9cSzX/vbat//////////////////// - ////////////////////////////////////////////////////////////////////////aVpF/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/087I/////wD///8A////AP///wD///8A////AP///wD///8A////AGJS - PP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI//////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////8O9tf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/SDUc/4d7a/////////////////////////////// - /////////////////////////////////////////////zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/X085//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wC2r6T/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP+He2v///////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ///////////////////DvbX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/QS0T/722rf////////////////////////////////////////////// - /////////////4Z6af84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/7avpP////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AEo3H/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/QCsS//////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////w721/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP87Jwz/ysS+//////////////////////////////////////////////////////9AKxL/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/0k2Hv////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wCbkIP/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+kmo7///////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////ubGo/4J0ZP8/KhD/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/1NAKP////////////// - //////////////////////////////////+il4z/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+bkIP/////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ADwoDf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/bmBM//////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////3RmVP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/wry0//////////////////////////////////// - ////////bmBM/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP88KA3/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wB/cmD/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/42Bcv////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////g3hm/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/5GGdv///////////////////////////////////////////4yAcf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/f3Fh/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////ADgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+vp5z///////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //9XRi7/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP97blz///////// - //////////////////////////////////+tpJj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wA4Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/0MzG//////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////ubOq/zkkCf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////////////////// - ////////zsjC/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wDMxsD/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OSQJ//////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////89KQ7/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf////////////////////////////////////////////////84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/zcjB/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8Aq6GV/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/0w4IP////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////Y1Q//zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - ////////////////////////////////////////Sjcf/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/6ujlv////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AIl8bP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9tX0r///////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////3doVf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////////////////// - /////////////2xcSP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+Jfm7/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wBoV0P/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/joRz//////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+DeGb/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf////////////////////////////////////////////////+NgXL/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/Z1dE/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8ARzQb/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/7Cnnf////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - ////////////////////////////////////////r6ec/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0YzGf////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ADgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP/RzMf///////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////////////////// - /////////////9DMxv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AMrEvv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj///////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf//////////////////////////////////////////////////////OSQJ/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/8rGv/////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wCpoJT/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/TToi//////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////////////////////////////////////0w4IP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+poJT/////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AiHtq/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/29e - S/////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////////////////// - //////////////////9tX0r/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/h3tr/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AGZVQf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+Qg3T///////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf//////////////////////////////////////////////////////joRz/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/2ZWQv////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wBGMxn/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/qJ2S//////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////////////////////////////////////6idkv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9GMxn/////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AOCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/z0p - Dv+2r6T///////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////////////////// - /////////////722rf9AKxL/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8AycS9/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/5SKe/////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf///////////////////////////////////////////6KXjP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP/KxL7/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AKee - k/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/cGFN//////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////////////////////3VoVP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/qaCU//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wCGemn/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9UQir///////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV/////////////////////////////////1hH - MP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/4h7av////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8AZVVA/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0Iu - FP/CvLP///////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf//////////////////////x8C4/0UxGP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9mVUH/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AEUx - GP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OiUK/6KXjP////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - ////////p56T/zolCv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/QzAW//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wA4Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/fnFf//////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV////////////q6OW/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP////8A////AP///wD///8A////AP// - /wD///8A////AP///wDIwrr/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9fTjf///////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf///////////52RhP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/yMK6/////wD///8A////AP///wD///8A////AP///wD///8A////AHxu - Xf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0k2 - Hv/Py8X//////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - //+dkYT/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/fG5d//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Ap56T/zolCv84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Tjwk//////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////Z1dE/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9INRz///////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV////////////nZGE/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/m5CD/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wDEvrf/Qy8X/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9RPif/hnlo/3xuXf////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - /////////////2VVQP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/SDUc//// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf///////////52RhP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9DLxf/xL63/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wBVRC3/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9QPSb///////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////9nV0T/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0g1HP////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////g3hm/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - //+dkYT/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9VRC3/////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AHNj - Uf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////ZVVA/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9INRz///////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////3doVf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV////////////nZGE/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9zY1H/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Alot8/zgjCP85JAn/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/52RhP///////////8G7sf+7s6v///////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - /////////////2dXRP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/SDUc//// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////9QPSb/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/4FzYv///////////5iNgP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+Wi3z/////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wC7s6v/PyoQ/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+dkYT///////// - //93aFX/OCMI/zgjCP84Iwj/RjMZ/2JSPP98bl3/mI2A/7Ssov/OyML//////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////9pWUX/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0g1HP////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ///JxL3/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //+QhHX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/z4pD/+2raP/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wBPPCX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/nZGE////////////d2hV/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////dGZU/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9INRz///////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////U0Ao/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/mo6B////////////hXdn/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/088 - Jf////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AGpb - Rv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/52RhP///////////3doVf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - /////////////4J0ZP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/SDUc//// - //////////////////////////////////////////////////////////////////////////////// - ///////////////////CvLT/U0Ao/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/6idkv///////////3BhTf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/2ZVQf////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AiX5u/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+dkYT///////// - //93aFX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////+OhHP/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zwoDf9tXUn/bV1J/21dSf9tXUn/bV1J/21d - Sf9tXUn/bV1J/21dSf9tXUn/bV1J/21dSf9tXUn/bV1J/21dSf9tXUn/bV1J/2JRPf9FMRj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP/Dvrb///////// - //9XRi7/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/4l+bv////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wCvp5z/PCgN/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/nZGE////////////d2hV/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////raSY/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/////////////////OyYL/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OyYL/6mglP////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AM/Lxf9JNh7/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/52RhP///////////3doVf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////85JAn/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/VUMs////////////0MzG/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/STYe/8/Lxf////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AFVE - Lf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+dkYT///////// - //93aFX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - ////////////////////////////////////////////////////////////////////////Qi4U/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/5GGdv///////////5uQ - g/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/VUQt//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wCpoJT/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/nZGE////////////d2hV/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - /////////////////////////////////////////////39yYP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP////////////////9fTzn/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/6mglP////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////ADgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/52RhP///////////3doVf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - ///////////////////OyML/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9zZVL/////////////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wBiUT3/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+dkYT///////// - //93aFX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////3Bh - Tf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP89KQ7/////////////////gXNi/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9jVD//////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8ApJqO/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/nZGE////////////d2hV/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////Qy8X/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/pp2R/////////////////zolCv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/pJuQ/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wA4Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/52RhP///////////3doVf84Iwj/OSQJ/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////8rEvv8+KQ//OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//// - /////////////2ZWQv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8AX043/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+Sh3j///////// - ////////wry0/6adkf+JfGz/bV1J/1A9Jv85JAn/OCMI/zgjCP84Iwj/OCMI/zgjCP85JAn/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////z8nC/0k2Hv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP87Jgv/opeM/////////////////5aLfP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/19ON/////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AJ+WiP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/ZVVA//////////////////////////////////// - ///////////////////AubH/pJqO/4Z6af9qW0b/TToi/zgjCP84Iwj/OCMI/zkkCf84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////hnlo/zsm - C/84Iwj/OCMI/zkkCf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9qW0b//////////////////////6ujlv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+floj/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8AOCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP+PgnT///////////////////////////////////////////////////////// - /////////////////////////////722rf+gl4v/g3hm/2hXQ/9JNh7/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////npSG/2lZRf8/KhD/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/VUMs/4+CdP///////////////////////////5uQ - g/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AFpIMf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP8+KQ//W0k0/3doVf+Uinv/sKme/87Iwv////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////AubH/sqqg/6adkf+il4z/opeM/6KX - jP+il4z/opeM/6KXjP+il4z/opeM/6KXjP+il4z/opeM/6KXjP+il4z/opeM/6KXjP+imY3/sKme/7+3 - r//OyML//////////////////////////////////////2xcSP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Wkgx/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wCZkIL/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/0EtE/9eTTb/emxa/5eMfv+0rKL/0czH//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////jIBx/zwoDf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/5mQgv////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////ADgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/Qy8X/2FPOv98bl3/mZCC/7ewpv////////////////////////////// - //+2r6T///////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////0MzG/6KZjf9lVUD/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wBUQir/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9GMxn/Y1M+/3VoVP9INRz/OCMI/6ujlv////////////// - /////////////////////////////////////////////////////////////62kmP9xYk//PyoQ/z8q - EP9xYk//raSY//////////////////////////////////////////////////////////////////// - ////////sKme/3psWv96bFr/emxa/3psWv96bFr/emxa/3psWv96bFr/emxa/3psWv96bFr/bmBM/2ZV - Qf9OPCT/OyYL/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9UQir/////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AlIh5/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/qJ2S//////////////////////////////////// - //////////////////+il4z/ZlVB/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/WEcw/5SK - e//Py8X/////////////////////////////////////////////////raSY/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/lot8/////wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wA5JAn/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP+elIb////////////////////////////CvLT/iHtq/006Iv84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0c0G/96bFr/tq2j//// - ////////////////////////pJqO/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8ATzwl/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/52R - hP//////r6ec/3NlUv89KQ7/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP87Jgv/ZlVB/6KXjP//////opmN/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/048JP////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AI+C - dP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP+Qg3T/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wDOyML/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/0MzG//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AEw4IP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Sjcf/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wCKfnD/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/4yAcf////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDCvLT/m5CD/3Nl - Uv9JNR3/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Qy8X/2ZVQf+NgXL/tq2j/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDCvLT/m5CD/3Nl - Uv9JNh7/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9DLxf/ZlVB/42B - cv+2raP/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDCvLT/m5CD/3Nl - Uv9JNh7/OCMI/zgjCP84Iwj/OCMI/zkkCf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP85JAn/OCMI/zgj - CP84Iwj/OCMI/0MwFv9mVUH/jYFy/7ato/////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDAubH/mI2A/3Bh - Tf9CLhT/OSQJ/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Qi4U/3BhTf+YjYD/wLmx/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AEw4IP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/STYe/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8ATToi/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Sjcf/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wBRPif/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/Tjwk/////wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AFNA - KP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Tzwl//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AVEIq/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/UD0m/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wBYRzD/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/VEIq/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AFpIMf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/VUQt/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AW0k0/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/V0Yu/////wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wBcTDb/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/QCsS/3ZnVf+wqZ7/sKme/3ZnVf9AKxL/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/XEw2//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AGFQO/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0k2Hv+Bc2L/vbat/////wD///8A////AP// - /wD///8A////AMnEvf+NgXL/Tzwl/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/XEs1/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8AYlE9/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zkkCf84Iwj/OCMI/zkk - Cf9fTzn/m5CD/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8Ap56T/21dSf87Jgv/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/X043/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wBjVD//OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP8/KhD/c2VS/6+nnP////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AL22 - rf+Bc2L/Qy8X/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/X085/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AGpbRv84Iwj/OCMI/zgjCP84Iwj/UD4m/4h7 - av/CvLT/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDPy8X/lIp7/1dG - Lv84Iwj/OCMI/zgjCP84Iwj/ZlVB/////wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8AbV9K/19POf+bkIP/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AKeek/9tXUn/bFtH//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - //////////5/////////////////////////+B/////////////////////////gB/////////////// - /////////8AD////////////////////////AAD///////////////////////wAAD////////////// - ////////+AAAD//////////////////////gAAAH/////////////////////4AAAAH///////////// - ////wAf+AAAAAH/gA//////////////AAAAAAAAAAAAD/////////////4AAAAAAAAAAAAH///////// - ////AAAAAAAAAAAAAP////////////8AAAAAAAAAAAAA/////////////gAAAAAAAAAAAAB///////// - ///8AAAAAAAAAAAAAD////////////wAAAAAAAAAAAAAP///////////+AAAAAAAAAAAAAAf//////// - ///4AAAAAAAAAAAAAB////////////AAAAAAAAAAAAAAD///////////4AAAAAAAAAAAAAAH//////// - ///gAAAAAAAAAAAAAAf//////////8AAAAAAAAAAAAAAA///////////gAAAAAAAAAAAAAAB//////// - //+AAAAAAAAAAAAAAAH//////////wAAAAAAAAAAAAAAAP/////////+AAAAAAAAAAAAAAAAf/////// - //gAAAAAAAAAAAAAAAAf////////4AAAAAAAAAAAAAAAAAf///////+AAAAAAAAAAAAAAAAAAf////// - /gAAAAAAAAAAAAAAAAAAf//////4AAAAAAAAAAAAAAAAAAAf/////+AAAAAAAAAAAAAAAAAAAAf///// - AAAAAAAAAAAAAAAAAAAAAP////wAAAAAAAAAAAAAAAAAAAAAP///+AAAAAAAAAAAAAAAAAAAAAAf///4 - AAAAAAAAAAAAAAAAAAAAAB////gAAAAAAAAAAAAAAAAAAAAAH///+AAAAAAAAAAAAAAAAAAAAAAf///4 - AAAAAAAAAAAAAAAAAAAAAB////gAAAAAAAAAAAAAAAAAAAAAH///+AAAAAAAAAAAAAAAAAAAAAAf///4 - AAAAAAAAAAAAAAAAAAAAAB////gAAAAAAAAAAAAAAAAAAAAAH///+AAAAAAAAAAAAAAAAAAAAAAf///4 - AAAAAAAAAAAAAAAAAAAAAB////gAAAAAAAAAAAAAAAAAAAAAH///8AAAAAAAAAAAAAAAAAAAAAAP///w - AAAAAAAAAAAAAAAAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAD///8AAAAAAAAAAAAAAAAAAAAAAP///w - AAAAAAAAAAAAAAAAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAD///8AAAAAAAAAAAAAAAAAAAAAAP///w - AAAAAAAAAAAAAAAAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAD///4AAAAAAAAAAAAAAAAAAAAAAH///A - AAAAAAAAAAAAAAAAAAAAAAP//4AAAAAAAAAAAAAAAAAAAAAAAf//gAAAAAAAAAAAAAAAAAAAAAAB//8A - AAAAAAAAAAAAAAAAAAAAAAD//gAAAAAAAAAAAAAAAAAAAAAAAH/8AAAAAAAAAAAAAAAAAAAAAAAAP/wA - AAAAAAAAAAAAAAAAAAAAAAA/+AAAAAAAAAAAAAAAAAAAAAAAAB/wAAAAAAAAAAAAAAAAAAAAAAAAD+AA - AAAAAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAAAAAAAfAAAAAAAAAAAAAAAAAAAAAAAAAA4AA - AAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAYAA - AAAAAAAAAAAAAAAAAAAAAAABwAAAAAAAAAAAAAAAAAAAAAAAAAPAAAAAAAAAAAAAAAAAAAAAAAAAA+AA - AAAAAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAD/AA - AAAAAAAAAAAAAAAAAAAAAAAP8AAAAAAAAAAAAAAAAAAAAAAAAA/4AAAAAAAAAAAAAAAAAAAAAAAAH/gA - AAAAAAAAAAAAAAAAAAAAAAAf/AAAAAAAAAAAAAAAAAAAAAAAAD/8AAAAAAAAAAAAAAAAAAAAAAAAP/4A - AAAAAAAAAAAAAAAAAAAAAAB//gAAAAAAAAAAAAAAAAAAAAAAAH//AAAAAAAAAAAAAAAAAAAAAAAA//8A - AAAAAAAAAAAAAAAAAAAAAAD//gAAAAAAAAAAAAAAAAAAAAAAAH/+AAAAAAAAAAAAAAAAAAAAAAAAf/4A - AAAAAAAAAAAAAAAAAAAAAAB//gAAAAAAAAAAAAAAAAAAAAAAAH/+AAAAAAAAAAAAAAAAAAAAAAAAf/4A - AAAAAAAAAAAAAAAAAAAAAAB//AAAAAAAAAAAAAAAAAAAAAAAAD/8AAAAAAAAAAAAAAAAAAAAAAAAP/wA - AAAAAAAAAAAAAAAAAAAAAAA//AAAAAAAAAAAAAAAAAAAAAAAAD/8AAAAAAAAAAAAAAAAAAAAAAAAP/wA - AAAAAAAAAAAAAAAAAAAAAAA/+AAAAAAAAAAAAAAAAAAAAAAAAB/4AAAAAAAAAAAAAAAAAAAAAAAAH/gA - AAAAAAAAAAAAAAAAAAAAAAAf+AAAAAAAAAAAAAAAAAAAAAAAAB/4AAAAAAAAAAAAAAAAAAAAAAAAH/gA - AAAAAAAAAAAAAAAAAAAAAAAf8AAAAAAAAAAAAAAAAAAAAAAAAA/4AAAAAAAAAAAAAAAAAAAAAAAAH/wA - AAAAAAAAAAAAAAAAAAAAAAA//gAAAAAAAAAAAAAAAAAAAAAAAH//gAAAAAAAAAAAAAAAAAAAAAAB///A - AAAAAAAAAAAAAAAAAAAAAAP//+AAAAAAAAAAAAAAAAAAAAAAB///8AAAAAAAAAAAAAAAAAAAAAAP///8 - AAAAAAAAAAAAAAAAAAAAAD////4AAAAAAAAAAAAAAAAAAAAAf////wAAAAAAAAAAAAAAAAAAAAD///// - gAAAAAAAAAAAAAAAAAAAAf/////AAAAAAAAAAAAAAAAAAAAD//////AAAAAAAAAAAAAAAAAAAA////// - 8AAAAAAAAAAAAAAAAAAAD//////4AAAAAAAAAAAAAAAAAAAf//////gAAAAAAAAAAAAAAAAAAB////// - +AAAAAAAAAAAAAAAAAAAH//////8AAAAAAAAAAAAAAAAAAA///////wAAAAAAAAAAAAAAAAAAD////// - /AAAAAAAAAAAAAAAAAAAP//////+AAAAAAAAAAAAAAAAAAB///////4AAAAAAAAAAAAAAAAAAH////// - /gAAAAAAAAAAAAAAAAAAf///////AAAAAAAAAAAAAAAAAAD///////8AAAAAAAAAAAAAAAAAAP////// - /wAAAAAAAAAAAAAAAAAA////////gAAAAAAAAAAAAAAAAAH///////+AAAAAAAAAAAAAAAAAAf////// - /4AAAAAAAAAAAAAAAAAB////////gAAAAAAAAAAAAAAAAAH////////AAAAAAAAAAAAAAAAAA/////// - /8AAAAAAAAAAAAAAAAAD////////8AAAAAAAAAAAAAAAAA//////////gAAAAAAAAAAAAAAB//////// - ///8AAAAAAAAAAAAAD/////////////gAAAAAAAAAAAH//////////////4AAAAAAAAAAH////////// - /////wAAAAAAAAAA////////////////gAAAAAAAAAH////////////////AAAAAAAAAA/////////// - /////+AAAAAAAAAH////////////////8AAAAAAAAA/////////////////4AAAAAAAAH/////////// - //////wAAAAAAAA//////////////////gAAAAAAAH//////////////////AAAH4AAA//////////// - //////+AAH/+AAH//////////////////8AD///AA///////////////////4B////gH//////////// - ///////x/////4////////// - - - \ No newline at end of file diff --git a/TGS.ControlPanel/Login.Designer.cs b/TGS.ControlPanel/Login.Designer.cs deleted file mode 100644 index e4f9a30693..0000000000 --- a/TGS.ControlPanel/Login.Designer.cs +++ /dev/null @@ -1,245 +0,0 @@ -namespace TGS.ControlPanel -{ - partial class Login - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Login)); - this.LocalLoginButton = new System.Windows.Forms.Button(); - this.CurrentRevisionTitle = new System.Windows.Forms.Label(); - this.UsernameTextBox = new System.Windows.Forms.TextBox(); - this.PasswordTextBox = new System.Windows.Forms.TextBox(); - this.RemoteLoginButton = new System.Windows.Forms.Button(); - this.AddressLabel = new System.Windows.Forms.Label(); - this.label2 = new System.Windows.Forms.Label(); - this.label3 = new System.Windows.Forms.Label(); - this.label4 = new System.Windows.Forms.Label(); - this.PortSelector = new System.Windows.Forms.NumericUpDown(); - this.SavePasswordCheckBox = new System.Windows.Forms.CheckBox(); - this.IPComboBox = new System.Windows.Forms.ComboBox(); - this.DeleteLoginButton = new System.Windows.Forms.Button(); - ((System.ComponentModel.ISupportInitialize)(this.PortSelector)).BeginInit(); - this.SuspendLayout(); - // - // LocalLoginButton - // - this.LocalLoginButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.LocalLoginButton.Location = new System.Drawing.Point(102, 12); - this.LocalLoginButton.Name = "LocalLoginButton"; - this.LocalLoginButton.Size = new System.Drawing.Size(157, 25); - this.LocalLoginButton.TabIndex = 1; - this.LocalLoginButton.Text = "Connect to Local Service"; - this.LocalLoginButton.UseVisualStyleBackColor = true; - this.LocalLoginButton.Click += new System.EventHandler(this.LocalLoginButton_Click); - // - // CurrentRevisionTitle - // - this.CurrentRevisionTitle.AutoSize = true; - this.CurrentRevisionTitle.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.CurrentRevisionTitle.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.CurrentRevisionTitle.Location = new System.Drawing.Point(116, 69); - this.CurrentRevisionTitle.Name = "CurrentRevisionTitle"; - this.CurrentRevisionTitle.Size = new System.Drawing.Size(128, 18); - this.CurrentRevisionTitle.TabIndex = 14; - this.CurrentRevisionTitle.Text = "Remote Login:"; - this.CurrentRevisionTitle.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // UsernameTextBox - // - this.UsernameTextBox.Location = new System.Drawing.Point(129, 132); - this.UsernameTextBox.Name = "UsernameTextBox"; - this.UsernameTextBox.Size = new System.Drawing.Size(233, 20); - this.UsernameTextBox.TabIndex = 16; - // - // PasswordTextBox - // - this.PasswordTextBox.Location = new System.Drawing.Point(129, 164); - this.PasswordTextBox.Name = "PasswordTextBox"; - this.PasswordTextBox.Size = new System.Drawing.Size(233, 20); - this.PasswordTextBox.TabIndex = 17; - this.PasswordTextBox.UseSystemPasswordChar = true; - // - // RemoteLoginButton - // - this.RemoteLoginButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.RemoteLoginButton.Location = new System.Drawing.Point(102, 200); - this.RemoteLoginButton.Name = "RemoteLoginButton"; - this.RemoteLoginButton.Size = new System.Drawing.Size(157, 25); - this.RemoteLoginButton.TabIndex = 18; - this.RemoteLoginButton.Text = "Connect to Remote Service"; - this.RemoteLoginButton.UseVisualStyleBackColor = true; - this.RemoteLoginButton.Click += new System.EventHandler(this.RemoteLoginButton_Click); - // - // AddressLabel - // - this.AddressLabel.AutoSize = true; - this.AddressLabel.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.AddressLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.AddressLabel.Location = new System.Drawing.Point(9, 99); - this.AddressLabel.Name = "AddressLabel"; - this.AddressLabel.Size = new System.Drawing.Size(80, 18); - this.AddressLabel.TabIndex = 19; - this.AddressLabel.Text = "Address:"; - this.AddressLabel.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // label2 - // - this.label2.AutoSize = true; - this.label2.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label2.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.label2.Location = new System.Drawing.Point(9, 132); - this.label2.Name = "label2"; - this.label2.Size = new System.Drawing.Size(97, 18); - this.label2.TabIndex = 20; - this.label2.Text = "Username:"; - this.label2.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // label3 - // - this.label3.AutoSize = true; - this.label3.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label3.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.label3.Location = new System.Drawing.Point(9, 164); - this.label3.Name = "label3"; - this.label3.Size = new System.Drawing.Size(92, 18); - this.label3.TabIndex = 21; - this.label3.Text = "Password:"; - this.label3.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // label4 - // - this.label4.AutoSize = true; - this.label4.Font = new System.Drawing.Font("Verdana", 12F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.label4.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.label4.Location = new System.Drawing.Point(-19, 40); - this.label4.Name = "label4"; - this.label4.Size = new System.Drawing.Size(468, 18); - this.label4.TabIndex = 22; - this.label4.Text = "______________________________________________"; - this.label4.TextAlign = System.Drawing.ContentAlignment.MiddleCenter; - // - // PortSelector - // - this.PortSelector.Location = new System.Drawing.Point(282, 99); - this.PortSelector.Maximum = new decimal(new int[] { - 65535, - 0, - 0, - 0}); - this.PortSelector.Minimum = new decimal(new int[] { - 1, - 0, - 0, - 0}); - this.PortSelector.Name = "PortSelector"; - this.PortSelector.Size = new System.Drawing.Size(80, 20); - this.PortSelector.TabIndex = 16; - this.PortSelector.Value = new decimal(new int[] { - 38607, - 0, - 0, - 0}); - // - // SavePasswordCheckBox - // - this.SavePasswordCheckBox.AutoSize = true; - this.SavePasswordCheckBox.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.SavePasswordCheckBox.Location = new System.Drawing.Point(265, 205); - this.SavePasswordCheckBox.Name = "SavePasswordCheckBox"; - this.SavePasswordCheckBox.Size = new System.Drawing.Size(100, 17); - this.SavePasswordCheckBox.TabIndex = 23; - this.SavePasswordCheckBox.Text = "Save Password"; - this.SavePasswordCheckBox.UseVisualStyleBackColor = true; - this.SavePasswordCheckBox.CheckedChanged += new System.EventHandler(this.SavePasswordCheckBox_CheckedChanged); - // - // IPComboBox - // - this.IPComboBox.FormattingEnabled = true; - this.IPComboBox.Location = new System.Drawing.Point(130, 98); - this.IPComboBox.Name = "IPComboBox"; - this.IPComboBox.Size = new System.Drawing.Size(146, 21); - this.IPComboBox.TabIndex = 24; - // - // DeleteLoginButton - // - this.DeleteLoginButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Top | System.Windows.Forms.AnchorStyles.Right))); - this.DeleteLoginButton.Location = new System.Drawing.Point(105, 98); - this.DeleteLoginButton.Name = "DeleteLoginButton"; - this.DeleteLoginButton.Size = new System.Drawing.Size(19, 19); - this.DeleteLoginButton.TabIndex = 25; - this.DeleteLoginButton.Text = "x"; - this.DeleteLoginButton.UseVisualStyleBackColor = true; - this.DeleteLoginButton.Click += new System.EventHandler(this.DeleteLoginButton_Click); - // - // Login - // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(39)))), ((int)(((byte)(40)))), ((int)(((byte)(34))))); - this.ClientSize = new System.Drawing.Size(374, 237); - this.Controls.Add(this.DeleteLoginButton); - this.Controls.Add(this.IPComboBox); - this.Controls.Add(this.SavePasswordCheckBox); - this.Controls.Add(this.PortSelector); - this.Controls.Add(this.label4); - this.Controls.Add(this.label3); - this.Controls.Add(this.label2); - this.Controls.Add(this.AddressLabel); - this.Controls.Add(this.RemoteLoginButton); - this.Controls.Add(this.PasswordTextBox); - this.Controls.Add(this.UsernameTextBox); - this.Controls.Add(this.CurrentRevisionTitle); - this.Controls.Add(this.LocalLoginButton); - this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedSingle; - this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); - this.MaximizeBox = false; - this.Name = "Login"; - this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; - this.Text = "Login"; - ((System.ComponentModel.ISupportInitialize)(this.PortSelector)).EndInit(); - this.ResumeLayout(false); - this.PerformLayout(); - - } - - #endregion - - private System.Windows.Forms.Button LocalLoginButton; - private System.Windows.Forms.Label CurrentRevisionTitle; - private System.Windows.Forms.TextBox UsernameTextBox; - private System.Windows.Forms.TextBox PasswordTextBox; - private System.Windows.Forms.Button RemoteLoginButton; - private System.Windows.Forms.Label AddressLabel; - private System.Windows.Forms.Label label2; - private System.Windows.Forms.Label label3; - private System.Windows.Forms.Label label4; - private System.Windows.Forms.NumericUpDown PortSelector; - private System.Windows.Forms.CheckBox SavePasswordCheckBox; - private System.Windows.Forms.ComboBox IPComboBox; - private System.Windows.Forms.Button DeleteLoginButton; - } -} \ No newline at end of file diff --git a/TGS.ControlPanel/Login.cs b/TGS.ControlPanel/Login.cs deleted file mode 100644 index 40da36cd8c..0000000000 --- a/TGS.ControlPanel/Login.cs +++ /dev/null @@ -1,190 +0,0 @@ -using System; -using System.Collections.Specialized; -using System.Windows.Forms; -using TGS.Interface; - -namespace TGS.ControlPanel -{ - sealed partial class Login : Form - { - /// - /// Currently selected saved - /// - RemoteLoginInfo currentLoginInfo; - - bool updatingFields; - - /// - /// Construct a - /// - public Login() - { - InitializeComponent(); - var Config = Properties.Settings.Default; - AcceptButton = RemoteLoginButton; - if(Config.RemoteDefault) - RemoteLoginButton.TabIndex = 0; //make this the first thing selected when loading - - IPComboBox.SelectedIndexChanged += IPComboBox_SelectedIndexChanged; - var loginInfo = Config.RemoteLoginInfo; - if (loginInfo != null) - { - foreach(var I in loginInfo) - IPComboBox.Items.Add(new RemoteLoginInfo(I)); - if (IPComboBox.Items.Count > 0) - IPComboBox.SelectedIndex = 0; - } - - IPComboBox.TextChanged += (a, b) => ClearFields(2); - PortSelector.ValueChanged += (a, b) => ClearFields(3); - UsernameTextBox.TextChanged += (a, b) => ClearFields(4); - - } - - void ClearFields(int start = 1) - { - if (updatingFields || currentLoginInfo == null) - return; - updatingFields = true; - switch (start) { - case 1: - IPComboBox.Text = ""; - goto case 2; //reason number #3 why you shouldn't use c# - case 2: - PortSelector.Value = 38607; - goto case 3; - case 3: - UsernameTextBox.Text = ""; - goto case 4; - case 4: - if (currentLoginInfo?.HasPassword ?? false) //reason number #749 why you shouldn't use c# - PasswordTextBox.Text = ""; - break; - } - currentLoginInfo = null; - updatingFields = false; - } - - void IPComboBox_SelectedIndexChanged(object sender, EventArgs e) - { - RemoteLoginInfo newLoginInfo = IPComboBox.SelectedItem as RemoteLoginInfo; - //new thing - if (newLoginInfo == null) - { - ClearFields(); - currentLoginInfo = newLoginInfo; - return; - } - updatingFields = true; - currentLoginInfo = newLoginInfo; - IPComboBox.Text = currentLoginInfo.IP; - PortSelector.Value = currentLoginInfo.Port; - UsernameTextBox.Text = currentLoginInfo.Username; - SavePasswordCheckBox.Checked = currentLoginInfo.HasPassword; - if (currentLoginInfo.HasPassword) - PasswordTextBox.Text = "************"; - else - PasswordTextBox.Text = ""; - updatingFields = false; - } - - void RemoteLoginButton_Click(object sender, EventArgs e) - { - if (String.IsNullOrWhiteSpace(PasswordTextBox.Text) || String.IsNullOrWhiteSpace(UsernameTextBox.Text) || String.IsNullOrWhiteSpace(IPComboBox.Text) || PortSelector.Value == 0) - return; - - RemoteLoginInfo loginInfo; - if (currentLoginInfo == null) - { - loginInfo = new RemoteLoginInfo(IPComboBox.Text, (ushort)PortSelector.Value, UsernameTextBox.Text.Trim(), PasswordTextBox.Text); - } - else - { - loginInfo = (RemoteLoginInfo)IPComboBox.SelectedItem; - if (!loginInfo.HasPassword) - loginInfo.Password = PasswordTextBox.Text; - } - - var I = new Client(loginInfo); - var Config = Properties.Settings.Default; - //This needs to be read here because V&C Closing us will corrupt the data - var savePassword = SavePasswordCheckBox.Checked; - if (VerifyAndConnect(I)) - { - Config.RemoteDefault = true; - - if (!savePassword) - loginInfo.Password = null; - - Config.RemoteLoginInfo = new StringCollection { loginInfo.ToJSON() }; - - foreach (RemoteLoginInfo info in IPComboBox.Items) - if (!info.Equals(loginInfo)) - Config.RemoteLoginInfo.Add(info.ToJSON()); - } - } - - void LocalLoginButton_Click(object sender, EventArgs e) - { - Properties.Settings.Default.RemoteDefault = false; - VerifyAndConnect(new Client()); - } - - /// - /// Attempts a connection on a given - /// - /// The to attempt a connection on - /// if the connection was made and authenticated, otherwise - bool VerifyAndConnect(IClient I) - { - try - { - var res = I.ConnectionStatus(out string error); - if (!res.HasFlag(ConnectivityLevel.Connected)) - { - MessageBox.Show("Unable to connect to service! Error: " + error); - return false; - } - if (!res.HasFlag(ConnectivityLevel.Authenticated)) - { - MessageBox.Show("Authentication error: Username/password/windows identity is not authorized! Ensure you are a system administrator or in the correct Windows group on the service machine."); - return false; - } - - if (I.VersionMismatch(out error) && MessageBox.Show(error, "Warning", MessageBoxButtons.OKCancel) == DialogResult.Cancel) - return true; - - Close(); - Program.ServerInterface = I; - return true; - } - catch - { - I.Dispose(); - throw; - } - } - - void SavePasswordCheckBox_CheckedChanged(object sender, EventArgs e) - { - if (!updatingFields && !SavePasswordCheckBox.Checked && currentLoginInfo != null) - { - currentLoginInfo = null; - PasswordTextBox.Text = ""; - } - } - - /// - /// Removes the selected item from - /// - /// The sender of the event - /// The - void DeleteLoginButton_Click(object sender, EventArgs e) - { - //make sure we're trying to delete a real item - if (IPComboBox.SelectedItem as RemoteLoginInfo == null) - return; - IPComboBox.Items.RemoveAt(IPComboBox.SelectedIndex); - } - } -} diff --git a/TGS.ControlPanel/Login.resx b/TGS.ControlPanel/Login.resx deleted file mode 100644 index a83f34c04b..0000000000 --- a/TGS.ControlPanel/Login.resx +++ /dev/null @@ -1,2179 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - - - AAABAAUAEBAAAAEAIABoBAAAVgAAABgYAAABACAAiAkAAL4EAAAgIAAAAQAgAKgQAABGDgAAMDAAAAEA - IACoJQAA7h4AAKCgAAABACAAqJwBAJZEAAAoAAAAEAAAACAAAAABACAAAAAAAAAEAAATCwAAEwsAAAAA - AAAAAAAAAAAAAAAAAABeTTgAWkgyA1pIMilOOyMoRjMaRz4qEMM+KhDESDUcSU07IyhaSDIpWUgxA15N - NwAAAAAAAAAAAAAAAADSzsgAPyoQAEk2HTk9KQ7ZOSUK5DciB+43Igf/OCQJ/zkkCe46JQrkPSgO2UYz - Gjk8JwwA0s3HAAAAAABVQywAbV1JAk47IytALBK6XEo0/2xcSP9lVUD/mo+C/5CEdf9jUz7/ZFQ//2BP - Ov9LOCC6SzgfK2hYRAJSPygA////AUg1HHA7JgzkOSQJ/29gTP9wYU7/6+nm/+rn5f9rXEj/U0Ap/1NB - Kv9YRzD/Z1ZC/zsmC+RINBtw////AYFzYgU/KhCxOiUK/4V4aP+hl4r/ZFQ///b19P/a1tH/c2RR/87J - w//X083/Y1M+/3BhTv8+Kg//PyoQsYFzYgVYRi8cPSkOz0MvFf/a1tH/vreu/2RTPv/08/H//Pz7//Py - 8P/+/f3/+vn5/3JjUP+Hemr/RjMa/z0pDs9XRS8cQi4VnDciB/xsXEn/9PPx/7y1rP9kUz7/9PLx//// - //////////////f29f9qW0f/oJaJ/3BhTf83Igf8Qi4VnD8qEN45JAn/saie//////+7tKv/ZFM+//Ty - 8f//////7u3q/5SJev95a1n/Y1M9/9rX0v+zq6D/OSQJ/z8qEN5EMBeFNiEF/oV4aP//////vLSr/2RT - Pv/08vH//////+7t6v+UiXr/eWtZ/2NTPv/X0s3/h3pq/zYhBf5EMBeFRzMaZTYhBfuAc2L//////7y0 - q/9kUz7/9PLx//////////////////f29f9qWkX/rqaa/4R3Zv82IQX7RzMaZUUxGIc3Igf/X085/97b - 1/+9tqz/ZFM+//Tz8f/8/Pv/8/Lw//79/f/6+fn/cWJP/5KHeP9kUz7/NyIH/0UxGIdMOSE0PikPvzkk - Cf+Lf2//in5u/2JSPf/29fT/2tbR/3JkUf/PycP/19PN/2NTPv9oWEP/OycM/z0pD79LOCA0RDEYAE89 - JSU/KxHWcGBN/1NAKf9wYU3/9vX0/+rn5P9pWkX/VEIq/1NBKv9YRzD/ZlZB/zwnDdZPPSUlRTEYAGdY - QwAAAAAAQCwSkkw5If9cSjT/bF1J/6ujl/+lm4//j4N0/4R3Z/9lVUD/YE86/0IuFP8/KxGSAAAAAGdY - QwAAAAAAQy8WAFA+JiFDLxZmPCcMnzciBvM5JAn/NyIH/zgjCP87Jgz/OCMI8zwnDZ9DLxVmTzwlIUEs - EwAAAAAAAAAAAAAAAAAAAAAAQi4VAEYyGQc+KQ+GPSgO6EEtE61CLhStPSkP6D0pDoZEMBcHQS0TAAAA - AAAAAAAAAAAAAOAHAADgBwAAgAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAB - AADAAwAAwAMAAPAPAAAoAAAAGAAAADAAAAABACAAAAAAAAAJAAATCwAAEwsAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAP///wD///8A////AP///wBVQy0AemxaA047I0NCLhTGQS0TxlE+J0aFd2cEYlI9AP// - /wD///8A////AP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF5NNwBhUDsMTToifk47 - I50+KQ+BQy8WmzwnDeg4Iwj/OCMI/z0oDupDLxacPikPgU47I51NOiF+YFA6DF1NNwAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAj4R1AB8HAABLOCBgOiUK+DYhBf82IQX/OCMI/zciB/82IQX/NiEF/zUg - Bf82IQX/NiEF/zYhBf86JQr4SzgfYB8HAACOgnMAAAAAAAAAAAAAAAAAAAAAAAAAAACMgXIACAAAAGBP - OS0/KxHVRzMa/2BPOf9eTTf/QSwT/0UyGf+Lf2//joJy/15NN/9fTjn/X085/11MNv9KNx//PysR1V9O - OC0GAAAAjIFyAAAAAAAAAAAAnpWIANrY0wFaSTIhRTIZdUAsEtc2IQb/d2lW/35xX/+Lf2//vLWs/8/K - xP/7+vr/wLqx/3NkUf9qWkb/alpG/25fS/+GeWj/ZFM+/z8qENdFMRh1WEYvIdTOyQGZjX8AbF1KAG1e - ShxFMhjMOSQJ/jciB/8zHgL/dmhV/1E/KP9oWET/+Pf2///////h3tr/UD4m/zsnDP9EMBb/QzAW/zsm - C/9BLRT/hnlp/0EtFP84Iwj+RTEYzG1dShxsXUkATDkgAE06Iig7JgzoNyIH/z4pD/9wYU3/ubKo/1RB - Kv9qWkb/+Pf3///////Hwbn/OyYL/4p+bv/c2NT/3NjT/6iek/87Jwz/e21b/1E+J/82IQb/OyYM6E06 - IihMOSAAWUcwAF1MNjs9KQ/0NSAE/2pbRv/z8vD/7evo/1JAKf9qWkb/+Pf3///////p5+T/tq6l/9vX - 0v///////////9XRy/9BLRP/in1t/25fS/81HwT/PSkP9F5NNztZRzEAAAAAAEo3H3A6JQr9NB8D/3xu - XP//////7Oro/1JAKf9qWkb/+Pf3/////////////////////////////////9XRy/9BLBP/kIR1/35w - Xv80HwP/OiUK/Uo3HnAAAAAAUkApRj4pD+M3Igf/OiUK/6iekv//////7Oro/1JAKf9qWkb/+Pf3//// - /////////////////////////////9XQy/9ALBL/k4d5/6qhlf86JQr/NyIH/z4pD+NSPyhGQy8V1jgj - CP82IQb/emxa//Tz8f//////7Oro/1JAKf9qWkb/+Pf3//////////////////f29f/x7+3/8vDu/6yj - mP83Igf/mI6A//b29P97bVv/NiEG/zgjCP9DLxXWRTEYxDgjCP82IQb/m5CD////////////7Oro/1JA - Kf9qWkb/+Pf3/////////////////6ackP9XRS7/WEYw/0IuFf91Z1T/4d7Z//////+bkIP/NiEG/zgj - CP9FMRjEUT4nXTsmDPU2IAX/XUw2/+3r6f//////7Oro/1JAKf9qWkb/+Pf3/////////////////6ac - kP9XRS7/WEYv/0IuFP92Z1T/4t/b/+7s6v9dTDb/NiAF/zsmDPVRPiddYlE7Gz8qENQ3Igb/SDUc/93a - 1f//////7Oro/1JAKf9qWkb/+Pf3//////////////////f29f/x7+3/8vDu/6yjmP83Igf/mo+B/9/c - 2P9INRz/NyIG/z8qENRhUTwbX044Jj8qEOA2IAX/WUcx/+3r6f//////7Oro/1JAKf9qWkb/+Pf3//// - /////////////////////////////9XQy/9ALBL/kod4/+/t6/9ZRzD/NiAF/z8qEOBfTjgmVEIqRjwn - DfI3IQb/TDkg/8bAuf//////7Oro/1JAKf9qWkb/+Pf3/////////////////////////////////9XR - y/9BLBP/kYV2/8nDvP9MOSH/NiEG/zwnDfJUQitGVUMsQj4pD+Q4Iwj/NiEG/047I//PysT/7uzq/1JA - Kf9qWkb/+Pf3///////p5+T/tq6l/9vX0v///////////9XRy/9CLhT/fG5c/1tKNP82IQX/OCMI/z4p - D+RVQyxCwry1Ak07I0Y/KhDUNyIH/0s4IP+akIL/jIBw/0UxGP9rW0f/+Pf3///////Fv7j/OiYL/4p+ - bv/c2NT/3NjT/6iek/87Jwz/eWpY/006Iv83Igf/PioQ1E06Ika8ta0CY1M+AEIvFQBYRjA1PCcN3lA9 - Jf96a1n/NyIH/zQeA/9qW0b/+Pf2///////e29b/TToi/zsmC/9DLxb/QzAW/zsmC/9BLRT/hnlp/0Et - FP89KA7eV0YvNUMwFgBhUDwAAAAAAG5fSwD5+PcBQCwSm0QwF/+FeGj/gnVk/3lrWf+Vinz/9fPy//// - ///39vX/ubGo/4F0Y/9yYk//alpG/25fS/+GeWj/Y1M9/zciB/9BLhSb+fr5AW5gTAAAAAAAAAAAAAAA - AABBLRMASjcfVjsmDPs7Jgv/Qy8W/047I/9aSDL/hHdm/6CWif9xYk7/cWJP/52Thv+DdWX/Xk04/11M - Nv9KNx//OCMH/zsmDPtKNx9WQS0TAAAAAAAAAAAAAAAAAAAAAABcSzUAY1I9E006ImxFMRieQCsSxzgj - CPU2IAX/NiEG/zYhBv81IAT/NSAE/zYhBv82IQb/NSAF/zciB/U+KhDHQy8Wnko3H2xiUTwTXEo0AAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAKGXiwDJxL0BalpGDT4qEHM5JQrzOCMI/zgjCP86Jgv7OyYL+zgj - CP84Iwj/OSQK8z4pD3NnV0MNt66lAZKGeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAASzggAFNBKgVBLRSBPysR60QwFrBNOyNpTzwlaUYyGbBALBLrQS0TgVA+JgVJNh0AAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAA/4H/APgAHwD4AB8A8AAPAIAAAQCAAAEAgAABAIAAAQCAAAEAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAADAMAAAwDgAAcA4AAHAPgAHwD+AH8AKAAAACAA - AABAAAAAAQAgAAAAAAAAEAAAEwsAABMLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAHlrWQCGeWkEVkQtRkUyGclEMBfJU0EpR52UhwZ7blwAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAalpHAAAAAABlVUAmcGBNQG5fTDE8Jw0dXEs1L0g1HIw9KQ7qOCMI/zgjCP8+KQ/sTToikltK - My88Jw0dbl9MMXBgTUBlVUAmAAAAAGdXQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAABaSDIAY1I9IEQwF8pBLRP5PysR7TkkCeA+KQ/rOSUK/jgjCP84Iwj/OCMI/zgj - CP85JAr+PSkP6zkkCeA/KxHtQS0T+UQwF8piUTwgWUgxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAeGpXALy2rQJKNx+MOSQK/zchBv83Igb/NyIH/zgiB/84Iwj/NyIH/zUg - Bf81IAX/NyIH/zciB/83Igf/NyIH/zciBv83Igf/OSQK/0o3Hoy6s6oCdmhWAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAANnUzwA0HwMAXk44Rz4pD+05JQr/RTEY/0g1HP9GMhn/OSQJ/zUf - BP9AKxH/e21b/3xuXP9HMxr/RjIZ/0g0HP9INBv/RzQb/0EtFP83Igf/PSgO7V5NN0czHgIA2dTPAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ2ThgAWAAAAYlE8ElRCK2BFMRfVNiEG/1xLNf+pn5T/lIl7/6ee - kv+DdmX/bl9L/763rv/5+Pf/8vHv/66mm/+Wi33/lop8/5aKfP+Wi33/mo+C/4l9bf9HNBv/Qy8W1VRC - K2BhUDsSJxAAAI+CcwAAAAAAAAAAAAAAAAByY1AAg3ZlFVE/J2lALBLFOyYM+jgjCP81IAT/a1tH/4Z5 - af83Igf/e21b//X08//39vX///////v6+f+aj4L/STYd/z4pD/89KA7/PSgO/z0pDv9DLxb/dGZT/5WK - e/8+KQ//OyYL+j8rEcVQPSVpgHJhFW5eSwAAAAAAAAAAAEw6IgBgTzpiPioQ/jgjCP84Iwj/OCMI/zMe - Av9pWUT/g3Zl/zEcAP93aVb//Pv7////////////1tLN/0UyGf83Igf/Qi4U/0MvFv9DLxb/QCwS/zci - B/83IQb/kIR1/15NN/82IAX/OCMI/z4qEP5gTzliTDkhAAAAAAAAAAAAPCgNAEMvFWk5JAn/OCMI/zgj - CP86JQr/V0Uv/66lmv+Ie2v/MRwA/3dpVv/8+/v///////////+5sqn/NSAF/0k2Hv/DvLT/1tHM/9XR - y//Mx8D/Z1dD/zMdAv92aFX/cWJP/zUgBP84Iwj/OSQJ/0MvFmk8KA0AAAAAAAAAAAAAAAAAU0AphDkk - Cf84Iwj/OCIH/4x/cP/q5+T//////4p+bv8xHAD/d2lW//z7+////////////8fBuv9oWUT/fW9d/+7s - 6v////////////////+Wi3z/Mh0B/3VmU/+YjX//NyIH/zgjCP85JAn/U0EphAAAAAAAAAAAemlYAAAA - AABPPSWYOSQJ/zgjCP87Jgv/vbat////////////in1u/zEcAP93aVb//Pv7/////////////Pz8//j3 - 9v/5+Pj//v7+/////////////////5iMf/8yHQH/d2hW/7ixp/87Jgv/OCMI/zkkCf9PPSWYAAAAAHRm - UgBcSzUAZVQ/J0EtE9M4Iwj/OCMI/z0pDv/JxL3///////////+KfW7/MRwA/3dpVv/8+/v///////// - ////////////////////////////////////////mIx+/zIdAf93aFb/xb+3/z0pD/84Iwj/OCMI/0Et - E9NjUz4nW0kzAHlrWBBJNh2oOSQJ/zgjCP82IQb/U0Ap/+He2v///////////4p9bv8xHAD/d2lW//z7 - +/////////////////////////////////////////////////+YjH7/Mh0B/3VnVP/d2dX/U0Ep/zYh - Bv84Iwj/OSQJ/0g1HKh3aVcQTjsjjDsmC/k4Iwj/NyIH/0AsEv+zq6H/////////////////in1u/zEc - AP93aVb//Pv7/////////////////////////////////////////////////4t/b/8yHAD/c2RR//v7 - +v+0raP/QCwT/zciB/84Iwj/OyYL+U47I4xFMRjoOCMH/zgjCP82IAX/hHdm//r6+f////////////// - //+KfW7/MRwA/3dpVv/8+/v///////////////////////f29f/Szsj/zcjC/87Jw/+wqJ7/Tzwk/zUf - BP+MgHD///7+//v6+v+Ed2f/NiAF/zgjCP84Iwf/RTEY6E48JJs5JAn/OCMI/zUgBf97bVv/+vn5//// - /////////////4p9bv8xHAD/d2lW//z7+///////////////////////4d7Z/1A+Jv89KQ//PioQ/zkk - Cv89KQ//nJGE/+zq5///////+vn5/3ttW/81IAX/OCMI/zkkCf9OPCSbYlI9O0AsEuk4Iwf/NyIH/0Yy - Gf/Tzsj/////////////////in1u/zEcAP93aVb//Pv7///////////////////////h3tn/UD4m/z0p - D/8+KhD/OSQJ/z0pD/+ckYT/7Oro///////Tzsj/RjIZ/zciB/84Iwf/QCwS6WJSPTuooJQFRTEYoDgj - CP84Iwj/NiEG/6KYi/////////////////+KfW7/MRwA/3dpVv/8+/v///////////////////////f2 - 9f/Szsj/zcjC/87Jw/+wqJ3/Tzwk/zUfBP+MgHD//////6KYi/82IQX/OCMI/zgjCP9FMRigqJ+TBQAA - AABOOyOLOSQJ/zgjCP84Iwj/sKid/////////////////4p9bv8xHAD/d2lW//z7+/////////////// - //////////////////////////////////+Lf2//MhwA/3NkUf/+/v7/sKid/zgjCP84Iwj/OSQJ/048 - I4sAAAAAycO7BUk1Ha04Iwj/OCIH/0ArEf/MxsD/////////////////in1u/zEcAP93aVb//Pv7//// - /////////////////////////////////////////////5iMf/8yHQH/dGVS//79/f/Mx8D/QCsR/zgi - B/84Iwj/STYdrcjEvAV8b10UQzAWzDgjCP84Iwf/PikP/62kmf/9/Pz///////////+KfW7/MRwA/3dp - Vv/8+/v/////////////////////////////////////////////////mIx+/zIdAf90ZVL/+fj3/66m - m/8+KQ//OCMH/zgjCP9EMBbMfW9eFGVVQCZBLBPhNyIH/zgjCP83Igf/Qy8V/5yShf/18/L//////4p9 - bv8xHAD/d2lW//z7+/////////////z8/P/49/b/+fj4//7+/v////////////////+YjH//Mh0B/3Rl - Uv+nnZH/Qy8W/zciB/84Iwj/NyIH/0EsE+FlVUAmhHZmDE48JIE8Jw3zOCMI/zgjCP81IAX/VkQt/9bS - zf/+/v7/in1t/zEcAP93aVb//Pv7////////////x8G6/2hZRP99b13/7uzq/////////////////5aL - fP8yHQH/cWJO/3RlUv80HwP/OCMI/zgjCP88Jw3zTTsjgYF0YwxjUj0AfG5dBE88JV4/KxHkOCMI/zUg - BP91Z1T/kIV2/3NlUv9XRS//Mx4C/3dpVv/8+/v///////////+3sKb/NSAE/0k2Hv/DvLT/1tHM/9XR - y//Mx8D/Z1dD/zMdAv92aFX/cmNP/zUgBP84Iwj/PyoQ5E48JF58blwEY1I9AAAAAAByZVEA////AF5N - OEk+KhDmNR8E/3ZnVP9tXUn/LxkA/zUfBP80HgP/d2hW//z7+////////////9LNx/9CLhX/OCMI/0Iu - FP9DLxb/Qy8W/0AsEv83Igf/NyEG/5CEdf9eTTf/NiAF/z4qEOZdTTdJ////AHBhTgAAAAAAAAAAAAAA - AACBc2MAvreuA0UxGKM1IAX/cWJP/5qQgv9pWUX/Wkgy/0g1HP9/cWD//Pv7////////////+fj4/5SJ - ev9GMhn/OyYM/zwnDf89KA7/PSkO/0MvFv90ZlP/lYp8/z4qD/84Iwj/RTEYo764sAOBdGQAAAAAAAAA - AAAAAAAAAAAAAAAAAAA6JQoASzggXDkkCfxDLxb/bF1J/4ByYf+NgXL/lop8/62kmf/g3dn///////j3 - 9v/f29f/2NTP/8/KxP/CvLT/pZyQ/5WKfP+Wi33/mo+C/4l9bf9HNBv/NyIG/zolCvxLOCBcOiUKAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAF1MNgBdTDYkQCwS4jciB/81HwT/NR8E/zciB/88Jw3/QS0T/1pI - Mv+UiHr/cGBN/0s4H/9KNx//cGBN/5aLff9fTzn/RzMa/0c0G/9BLRT/NyIH/zciBv84Iwj/QCwS4l1M - NiRdTDYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAi39wAI2AcgVWRC1lSDUcqEMvFs0/KxHqOiUK/Dgj - CP84Igf/NyEG/zUgBf81IAT/NyIH/zciB/81IAX/NSAF/zYhBv83Igf/NyIH/zolCvw+KhDqQS0UzUYy - GahUQiplkIR1BYyAcQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJuRhACzq6IDeGpYEWNT - PipCLhV5OiUK8jgjCP84Iwj/OCMI/zgjCP84Igf/OCIH/zgjCP84Iwj/OCMI/zgjCP85JAryQi4UeWFQ - OypvX0wRpJqOA4x/cQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAASzgfAFVDKwRCLhRtOiYL8jgjCP84Iwj/OiUL+0EsE+dBLRPnOyYL+zgjCP84Iwj/OiUL8kEt - E21RPicESTUdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAU0EqAGBPOgRFMhl9QS0T7Ek2HblNOiJpZFQ/MGhYQzBRPidpSzkguUIu - FOxEMRd9XEw2BFE/KAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/+B///wAA//4A - AH/8AAA//AAAP/AAAA/AAAADwAAAA8AAAAPAAAADwAAAA4AAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AACAAAABAAAAAAAAAAAAAAAAAAAAAIAAAAHgAAAH4AAAB/AAAA/wAAAP8AAAD/wAAD//gAH//8AD/ygA - AAAwAAAAYAAAAAEAIAAAAAAAACQAABMLAAATCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGxc - SACShXcEaVlFT0o3HstINBzLZFM+T4R3ZwRkUz4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AACXjH4AAAAAAGdXQyNRPiaTQCsR6zkkCf85JAn/PyoQ6048JJR+cF8v////AIl8bAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1c/OAGtcSAB0ZVMib2BMU4F0 - Y1OMgHBJSTYdIT4pDx1rW0cmXUw2dEIuFNY6JQr+OCMH/zgjCP84Iwj/OCMI/zolC/9HMxrgW0kzdWtb - RyY+KQ8dSTYdIYyAcEmBdGNTb2BMU3RlUyJrXEgAysjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfW9eAIR3 - Zw1MOSGsQS0T/0UxGP5FMRj4OSQK4zolC+FBLRPmPSkO/DgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP83Igf/PSgO/EEtE+Y6JQvhOSQK40UxGPhFMRj+QS0T/0s5IKyEd2YNfW9dAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AACgl4sAAAAAAGFQOmw9KA76NyIH/zciB/83Igf/OCMI/zgjCP84Iwf/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjB/84Iwj/OCMI/zciB/83Igf/NyIH/z0oDvpgTzpsAAAAAKKY - jAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAABXRS4Aa1tHJEQxF9U4Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zYhBf82IQX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9EMBfValpGJFZELQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAKCWiQDEvbUHVEIrmjolCv84Iwj/NiEF/zUgBP81IAT/NSAE/zUg - BP83Igf/OCMI/zgjCP82IQb/OSQJ/2BPOf9gUDr/OSQJ/zUgBP81IAT/NSAE/zUgBP81IAT/NSAE/zUg - BP81IAT/NiEG/zgjCP86JQr/VEEqmsG6sweek4cAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAl41+AODd2ARtXUleQi4U9DciB/85JAn/YE86/31v - Xf98b13/fG9d/3lrWf9KNx//NB8D/zYgBf9PPSX/n5WI/+zq5//t6+j/oZeK/21dSv94aVf/fG9d/3xv - Xf98b13/fG9d/3ttXP9wYU7/VUMs/zomC/82IQb/Qi4U9GxdSV7h3dkEl41+AAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AGtaRgCNgHAFY1I9OlNBKp9EMBbvOCMI/zgj - CP8+Kg//wbuy/7Wto/+il4v/oZeL/8nEvf+so5j/alpG/4l9bf/b19L//v79////////////7Orn/763 - rv+onpP/opiM/6OYjP+jmIz/opiM/6Sajv+wp53/w721/6ifk/9OOyP/NiEG/0QwFu9TQiqfY1M+Oo2A - cQVrW0cA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wANAAAAqKCUDlxKND9JNh2fQCwS7Tkk - Cf83Igf/OCMI/zgjCP8/KxH/wLqx/11MNv81IAT/NR8E/4+DdP/+/v7/+Pf2//v7+/////////////// - ///Qy8T/Y1I9/z0pD/84Iwj/NyIH/zciB/83Igf/NyIH/zciB/85JAn/TToh/5qPgv+2rqT/RzQb/zYh - Bv85JAn/QCwS7Uc0G59ZRzE/p52RDh4IAAD08e8AAAAAAAAAAAAAAAAAAAAAAJ+WiQCjmo0YX085sj8r - EfM4Iwj/OCMH/zgjCP84Iwj/OCMI/zgjCP8/KxH/wLqx/15NN/82IAX/NSAF/4+DdP////////////// - /////////////+zq5/9jUj3/NB8D/zgjCP84Iwj/OCIH/zgjB/84Iwf/OCMH/zgjCP84Iwj/NiEG/zsm - C/+upZr/in5u/zYgBf84Iwj/OCMH/zgjCP8/KxHzXkw3sqGXihiflIcAAAAAAAAAAAAAAAAAAAAAAIF0 - YwB/cmEtSDUc7TchBv84Iwj/OCMI/zgjCP84Iwj/OCMI/zciB/89KQ7/wLmw/15NN/82IAX/NSAF/4+D - dP///////////////////////////8C6sf88Jw3/OCMI/zkkCf8+KhD/PysR/z8rEf8/KxH/PysR/zsm - DP83Igb/OCMI/zUgBP9sXEj/tKyi/zwoDv84Iwj/OCMI/zgjCP83IQb/SDUc7YByYS2CdGQAAAAAAAAA - AAAAAAAAAAAAAFZFLgBWRC0tPysR7TgjCP84Iwj/OCMI/zgjCP83Igf/NSAE/zwoDf9rXEj/1NDK/11M - Nv82IAX/NSAF/4+DdP///////////////////////////6KYi/82IQb/NyIG/0s4IP+6s6n/y8W+/8rE - vP/KxLz/ysS9/7uzqv9hUDv/NyEG/zYhBv9RPyj/vLSr/0g0HP83Igf/OCMI/zgjCP84Iwj/PysR7VZE - LS1XRS4AAAAAAAAAAAAAAAAAAAAAAEEtEwBINBsxOSUK7zgjCP84Iwj/OCMI/zgjB/9ALBL/cGFO/7ix - p//y8O7/8fDu/1tKM/82IAX/NSAF/4+DdP///////////////////////////5aKfP8yHAH/Mx0C/089 - Jf/p5+T///////////////////////////+xqZ7/OSQK/zciBv9KNx//vreu/1NBKv83IQb/OCMI/zgj - CP84Iwj/OSUK70c0GzFBLRMAAAAAAAAAAAAAAAAAAAAAAGVUPwB+cF5eQCwS/jgiB/84Iwj/OCMI/zYg - Bf+GeWj/9/b1////////////8fDu/1tKM/82IAX/NSAF/4+DdP///////////////////////////7ix - p/93aFb/d2lX/4p+bv/x7+3///////////////////////////+/ubD/PCgN/zciBv9INBv/1tHM/4p9 - bf81IAX/OCMI/zgjCP84Igf/QCwS/n5wXl5lVD8AAAAAAAAAAAAAAAAAAAAAAFpJMwBsXEhpPysR/zgj - CP84Iwj/OCMI/zYhBv+glYj/////////////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////// - //////////////7+/v/+/f3//v79//7+/v////////////////////////////////+/uLD/PCgN/zci - Bv9HNBv/3NjT/6GYi/82IQb/OCMI/zgjCP84Iwf/PysR/2xdSWlbSTMAAAAAAAAAAAAAAAAAn5WIAAAA - AABPPSV9OiYL/zgjCP84Iwj/OCMI/zgjCP+tpZr/////////////////8fDu/1tKM/82IAX/NSAF/4+D - dP////////////////////////////////////////////////////////////////////////////// - //+/uLD/PCgN/zciBv9HNBv/3NjT/6+nnP84Iwj/OCMI/zgjCP84Iwj/OiYL/089JX0AAAAAn5SIAAAA - AAD///8AY1M+AG5fS0BFMRjnNyIH/zgjCP84Iwj/OCMI/zkkCf+8taz/////////////////8fDu/1tK - M/82IAX/NSAF/4+DdP////////////////////////////////////////////////////////////// - //////////////////+/uLD/PCgN/zciBv9HNBv/3dnU/764r/85JAn/OCMI/zgjCP84Iwj/NyIH/0Ux - GOdsXUlAYlE8AP///wB7bVsAgHJhH0s4IMY4Iwj/OCMI/zgjCP84Iwj/NyIH/0MvFf/OycP///////// - ////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////////////////////////////////////// - //////////////////////////////////+/ubD/PCgN/zciBv9HNBv/3NjT/9HMxf9DLxX/NyIH/zgj - CP84Iwj/OCMI/zgjCP9LOCDGf3JgH3ttWwCimYwNUkAomjolC/84Iwj/OCMI/zgjCP84Iwj/OCMI/5CE - df/6+vn/////////////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////////////////////// - //////////////////////////////////////////////////++t67/PCcN/zciBv9HNBv/2tbQ//38 - /P+RhXb/OCMI/zgjCP84Iwj/OCMI/zgjCP86JQv/UT8omqCWiQ1ZSDJ6PioQ9jgjB/84Iwj/OCMI/zgj - CP82IAX/bl9L/+3r6f//////////////////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////// - //////////////////////////////////////////////////////////////////+qoZX/OCMI/zci - B/9HNBv/2dXQ///////u7Or/cWJO/zYgBf84Iwj/OCMI/zgjCP84Iwf/PioP9llHMXpKNx/tNyIH/zgj - CP84Iwj/OCMI/zYhBv9UQiv/19LN////////////////////////////8fDu/1tKM/82IAX/NSAF/4+D - dP////////////////////////////////////////////7+/v/5+Pj/+fj3//n49//5+Pf/9/b1/9PO - yP9fTjj/NiEG/zUgBP9PPCT/4t/c////////////2NTP/1RCK/82IQb/OCMI/zgjCP84Iwj/NyIH/0s3 - H+1XRS+1OiUK/zgjCP84Iwj/OCMI/zUgBP9uX0z/9/b1////////////////////////////8fDu/1tK - M/82IAX/NSAF/4+DdP///////////////////////////////////////////+Th3v91Z1T/aFhE/2lZ - Rf9pWUX/ZlVB/0o3Hv83Igf/OyYL/0s4IP+flYj//Pv7////////////9/b1/25fTP81IAT/OCMI/zgj - CP84Iwj/OiUK/1dGL7VrW0hMQCsS7zgjB/84Iwj/OCMI/zciB/9FMRj/zsnC//////////////////// - ////////8fDu/1tKM/82IAX/NSAF/4+DdP///////////////////////////////////////////9vX - 0v9GMhn/NB4D/zUgBP81IAT/NSAE/zciB/82IAX/a1tH/9/c1//8+/v/////////////////zsnC/0Ux - GP83Igf/OCMI/zgjCP84Iwf/QCsR72tbR0yglooMUj8ouTgjCP84Iwj/OCMI/zgjCP81IAX/iX1t//7+ - /f//////////////////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////////////////////// - /////////////9vX0v9GMhn/NB4D/zUgBP81IAT/NSAE/zciB/82IAX/a1tH/9/c2P/8+/v///////// - ///+/f3/iX1t/zUgBf84Iwj/OCMI/zgjCP84Iwj/Uj8ouaGXiwwbAwAAZVVAYD0pD/o4Iwj/OCMI/zgj - CP82IQb/UD4m/+Dd2P//////////////////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////// - /////////////////////////////+Th3v91Z1T/aFhE/2lZRf9pWUX/ZlVB/0k2Hv83Igf/OyYL/0s4 - IP+glon//Pv7///////g3Nj/UD4m/zYhBv84Iwj/OCMI/zgjCP89KQ/6ZVVAYBsDAABtXUoAcWNPE0Et - FMY4Iwj/OCMI/zgjCP84Iwf/PSkP/8S+tv//////////////////////8fDu/1tKM/82IAX/NSAF/4+D - dP////////////////////////////////////////////7+/v/5+Pj/+fj3//n49//5+Pf/9/b1/9LO - yP9eTjj/NiEG/zUgBP9PPCT/4+Dc///////Evrb/PSkP/zgjCP84Iwj/OCMI/zgjCP9BLRTGcWJQE21d - SgCwp5wAxb63C1I/KL83Igf/OCMI/zgjCP83Igf/RzMa/9nW0P//////////////////////8fDu/1tK - M/82IAX/NSAF/4+DdP////////////////////////////////////////////////////////////// - //////////////////+qoZX/OCMI/zciB/9HNBv/2dXQ///////Z1dD/RjMa/zciB/84Iwj/OCMI/zci - B/9SQCi/xsG4C7CpngByYk8AdmdVFUUxGNU4Iwj/OCMI/zgjCP82IQb/WEYw/+vp5v////////////// - ////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////////////////////////////////////// - //////////////////////////////////++t6//PCcN/zciBv9HNBv/2dXQ///////r6eb/WEYv/zYh - Bv84Iwj/OCMI/zgjCP9FMhjVdmhVFXFjUAB+cF4Af3FgNkUxGPA3Igf/OCMI/zgjCP81IAT/b2BN//n5 - +P//////////////////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////////////////////// - //////////////////////////////////////////////////+/ubD/PCgN/zciBv9HNBv/2dXQ//// - ///5+fj/b2BN/zUgBP84Iwj/OCMI/zciB/9FMRjwf3JgNn5wXwBKNh4AXUw2Sj0oDvk4Iwj/OCMI/zgj - CP82IAX/bF1J/+7s6v//////////////////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////// - //////////////////////////////////////////////////////////////////+/uLD/PCgN/zci - Bv9HNBv/2dXQ///////v7ev/bV1J/zYgBf84Iwj/OCMI/zgjCP89KA75XUw2Sko3HgA6JgsAY1M+ejwn - Df84Iwj/OCMI/zgjCP84Iwj/OiUL/3lqWP/j4N3/////////////////8fDu/1tKM/82IAX/NSAF/4+D - dP////////////////////////////////////////////////////////////////////////////// - //+/uLD/PCgN/zciBv9HNBv/2tbR/+bk4P96bFr/OyYL/zgjCP84Iwj/OCMI/zgjCP88Jw3/ZFM+ejsm - CwAAAAAATz0ljDkkCf84Iwj/OCMI/zgjCP84Iwj/OCMI/zYhBv9hUDr/zsnC//7+/v//////8fDu/1tK - M/82IAX/NSAF/4+DdP////////////////////////////7+/v/+/f3//v79//7+/v////////////// - //////////////////+/uLD/PCgN/zciBv9JNR3/x8G5/21dSf82IAX/OCMI/zgjCP84Iwj/OCMI/zgj - CP85JAn/Tz0ljAAAAAAAAAAAcGFOREo2HtQ4Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP83Igf/emxa//Dv - 7f//////8fDu/1tKM/82IAX/NSAF/4+DdP///////////////////////////7ixp/93aFb/d2lX/4p+ - bv/x7+3///////////////////////////+/ubD/PCgN/zciBv9JNh3/urKo/047I/83IQb/OCMI/zgj - CP84Iwj/OCMI/zgjCP9JNh3Ubl9LRAAAAADU0MsAAAAAAGFRPCROOyO3OiYL/TgjB/84Iwj/OCMI/zci - B/9KNh7/x8G5/8zGv//X087/2tbR/1hHMP82IQX/NSAF/4+DdP///////////////////////////5SJ - e/8yHAH/Mx0C/089Jf/p5+T///////////////////////////+xqZ7/OSQK/zciBv9LNx//u7Oq/088 - JP83Igb/OCMI/zgjCP84Iwf/OiYL/U47I7dhUTwkAAAAANTQywAAAAAAxL61AEUxGQCAcmEUSzggiz8r - Efo4Iwj/OCMI/zciBv9PPSX/u7Oq/048JP9EMRf/UD4m/z4qEP84Iwj/NSAF/4+DdP////////////// - /////////////52Thv82IQb/NyIG/0s4IP+6s6n/y8W+/8rEvP/KxLz/ysS9/7uzqv9hUDv/NyEG/zYh - Bv9RPyj/vLWs/0g1HP83Igf/OCMI/zgjCP8/KxH6Sjcfi31vXRRDLhUAxL61AAAAAAAAAAAAAAAAAAAA - AACFeGgAnJOFCmRUP3w+KhDzOCMI/zciBv9PPCX/urKp/0k2Hf82IQb/NiEG/zgjCP84Iwj/NSAF/4+D - dP///////////////////////////7qyqf86JQr/OCMI/zkkCf8+KhD/PysR/z8rEf8/KxH/PysR/zsm - DP83Igb/OCMI/zUgBP9rXEj/tK2i/zwoDv84Iwj/OCMI/z4qEPNjUz58mY+BCoJ2ZQAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAjIBwAJyShAhJNh61OCMI/zciBv9PPSX/urKp/0c0G/80HgP/NSAF/zYh - Bv83Igf/NSAE/4+DdP///////////////////////////+Xi3/9ZSDH/NB8E/zgjCP84Iwj/OCIH/zgj - B/84Iwf/OCMH/zgjCP84Iwj/NiEG/zsmC/+tpZr/in5u/zYgBf84Iwj/OCMI/0o3HrWckoUIjIBwAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnJGEACgRAABUQituOyYL/zciB/9KNh7/x8G5/6GX - iv+BdGP/bV5K/1hHMP9KNx7/OycM/5CEdf/////////////////////////////////Fv7f/XUw2/zwo - Df83Igf/NyIH/zciB/83Igf/NyIH/zciB/85JAn/TDoh/5qPgf+2rqT/RzQb/zciB/84Iwj/OyYL/1RC - K24oEQAAnJKGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF1MNgBiUjwtQCsR6Dci - B/86JQr/ZVVA/4p+bv+imIz/same/722rf+/uK//uLGn/9XRy//5+Pj///////////////////////z8 - +//9/fz/7evo/8bAuP+so5j/p52S/6Sajv+imIz/opiM/6Sajv+wp5z/w721/6ifk/9OOyP/NiEG/zgj - CP84Iwj/QCsR6GJSPC1dTDYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHhq - VwCNgnIIRTIYszgjCP84Iwj/NiAF/zUgBP82IQb/OyYM/0MwFv9QPSb/ZFQ//3FiT/9+cF//4d3Z//f2 - 9f/c2NP/saie/31vXf98b13/rqWa/9vY0//5+Pf/4+Hd/4x/cP97blz/fG9d/3ttXP9wYU7/VUMs/zom - C/83Igb/OCMI/zgjCP84Iwj/RTIZs46Dcwh4a1gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAMG5sAAvGQAAXEo0ejsmDP83Igf/NyIH/zgjCP84Iwj/OCMI/zciB/83IQb/NSAF/zUg - BP82IQX/YE85/21eSv9JNh3/OSQJ/zUgBP81IAT/OCMI/0g0HP9rW0f/YE86/zYgBf81HwT/NSAE/zUg - BP81IAT/NiEG/zgjCP84Iwj/NyIH/zciB/87Jgz/XEs1ei4YAADEvrQAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABYRjAAbl5LLFNBKrZEMRfaQi4U9DwnDfw5JQr/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/NiEF/zUgBf83Igf/OCMI/zgjCP84Iwj/OCMI/zciB/81IAX/NiEF/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zkkCf87Jgz8QCwS9EIuFdpRPie2bl9MLFhHMQAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARTEYALGpngp6bFoaa1xIO2JS - PWRRPyiMTjsjvzolC/A4Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP86JQrwTjsjv088JIxdSzVkZFM+O3FiTxqil4sKNCAGAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAPb19AD///8BnpOGCEIuFWY7JgzwOCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zsmC/BBLRRmnZOGCP///wH29fQAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkMrAG5dSQJGMxlmPCcN7zgjCP84Iwj/OCMI/zgj - CP84Iwf/OCMI/zsmC/87Jgv/OCMI/zciB/84Iwj/OCMI/zgjCP84Iwj/PCcM70QwF2ZmVkMCUT8oAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFlHMABxYU4CSTYdZT0o - Du84Igf/OCMJ/zwnDP1AKxHmSTYdvV1MNoleTTiJTDkhvUEtE+Y8KA39OCQJ/zciB/88KA3vSDUcZWlb - RgJUQywAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AABjUj0Af3FiA007InRFMhjvTjsjw11NN3xlVUAyi39vC////wH///8BlIl7C21dSTJjUz58UT8ow0cz - Gu9MOCB0eGpWA19ONwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAD///gf//8AAP//8A///wAA//AAAA//AAD/4AAAB/8AAP/gAAAH/wAA/8AAAAP/ - AAD/gAAAAf8AAP8AAAAA/wAA/AAAAAA/AADwAAAAAA8AAOAAAAAABwAA4AAAAAAHAADgAAAAAAcAAOAA - AAAABwAA4AAAAAAHAADgAAAAAAcAAOAAAAAABwAAwAAAAAADAACAAAAAAAEAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAQAAgAAAAAABAACAAAAAAAEAAIAA - AAAAAQAAgAAAAAABAACAAAAAAAEAAIAAAAAAAQAAgAAAAAABAACAAAAAAAEAAMAAAAAAAwAA4AAAAAAH - AADwAAAAAA8AAPgAAAAAHwAA/AAAAAA/AAD8AAAAAD8AAPwAAAAAPwAA/gAAAAB/AAD+AAAAAH8AAP8A - AAAA/wAA//AAAA//AAD//AAAP/8AAP/+AAB//wAA//8AAP//AAAoAAAAoAAAAEABAAABACAAAAAAAACQ - AQATCwAAEwsAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AHNjUf9zY1H/////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8Am5CD/z4pD/84Iwj/OCMI/zsnDP+NgXL/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wDCvLT/UD0m/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/0w4IP+2raP/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wBvXkv/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/2dX - RP////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AI2Bcv87Jgv/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OSQJ/4FzYv////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8Atq2j/0k1Hf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/RjMZ/6eek/////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AY1Q//zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/X085/9DMxv////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wCNgXL/OSQJ/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/gXNi/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AKee - k/9DLxf/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9AKxL/m5CD//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8ApJuQ/3ttW/+Ie2r/kIN0/5eMfv+il4z/q6OW/7Ssov+9tq3/yMK6/9DMxv////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8AysS+/1hHMP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9YRzD/ycS9/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wDQzMb/yMK6/722rf+0rKL/q6OW/6KXjP+XjH7/kIN0/4h7 - av97bVv/pJuQ/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ADolCv84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9CLhT/Sjcf/1NA - KP9fTzn/Xk02/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9cSzX/X085/1NAKP9KNx//Qi4U/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zolCv////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AGJRPf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/X085/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AKmg - lP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP+poJT/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wBDLxf/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Qi4U//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wB6alj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP96bFr/////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDHwrn/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/8O9tf////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8AU0Ao/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9QPSb/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8AlYp9/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/5eMfv////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ADwoDf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP87Jgv/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AGpbRv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/2ZWQv////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ALKq - oP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/sqqg//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wBINRz/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0YzGf////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wCDd2X/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OSQJ/1dGLv/Iwrr/yMK6/1dGLv84Iwj/OSQJ/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/gnRk/////wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDOyML/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/QCsS/5uQg/////////////// - ////////p56T/0IuFP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP/MxsD/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8AWEcw/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/emxa////////////////////////////////////////////iHtq/zkk - Cf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/VUQt/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8An5aI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/2ZW - Qv96bFr/emxa/3psWv96bFr/emxa/3psWv96bFr/emxa/3psWv96bFr/emxa/3psWv96bFr/dGRT/0Yz - Gf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9eTTb/0MzG//// - ///////////////////////////////////////////////////QzMb/Xk02/zgjCP84Iwj/Qi4U/1xL - Nf9pWUX/dGRT/3psWv96bFr/emxa/3psWv96bFr/emxa/3psWv96bFr/emxa/3psWv96bFr/emxa/3ps - Wv96bFr/emxa/3psWv9uYEz/ZlVB/048JP86JQr/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+floj/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Av7ev/z8qEP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/6adkf////////////////////////////// - ////////////////////////////////////////////////////////RjMZ/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/0YzGf+tpJj///////////////////////////////////////// - ///////////////////////////////////KxL7///////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////8/Lxf+hl4n/Y1M+/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/PyoQ/7+3r/////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wC0rKL/V0Yu/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP////////////////////////////////////////////////////////////// - /////////////////////////////3xuXf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OiUK/4l+ - bv////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////iX5u/zsnDP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/V0Yu/7Ss - ov////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AKeek/9OPCT/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj///////// - ////////opeM/6KXjP+il4z/opeM/6KXjP+il4z/opeM/6KXjP+il4z/opeM/6KXjP/JxL3///////// - //+Ie2r/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/ZlZC//////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////8O9 - tf+2raP/qJ2S/6KXjP+il4z/opeM/6KXjP+il4z/opeM/6KXjP+il4z/opeM/6KXjP+il4z/opeM/6KX - jP+il4z/opeM/6Sajv+yqZ//wLmx/87Iwv//////////////////////////////////////altG/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Tjwk/6eek/////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8Am5CD/0c0G/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy///////////////////////Szsj/ycS9/722 - rf+2raP/wryz//////////////////////////////////////////////////////////////////// - /////////////////////////////7Cnnf92Z1X/RjMZ/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/1VDLP+Qg3T///////////////////////////+ajoH/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/RzQb/52RhP////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wCQg3T/QS0T/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////pp2R/0Yz - Gf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9tX0r//////////////////////6uhlf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/QS0T/5CEdf////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AI2Bcv8+KQ//OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj///////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////ZlVB/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zsnDP+kmo7///////// - ////////lIh5/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/PCgN/4FzYv////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AgXNi/zol - Cv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////VUMs/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/42Bcv////////////////9mVUH/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OSQJ/3Nl - Uv////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AMnEvf9sXEj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////ZlZC/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/qJ2S/////////////////zolCv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/21dSf/KxL7/////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Awry0/2NT - Pv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj///////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////pp2R/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/z0p - Dv////////////////9/cmD/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/1xLNf+2raP/////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8AubOq/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////0c0 - G/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/dGZU/////////////////zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/7mxqP////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AKmglP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - /////////////////////////////////////////////6uhlf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP////////////////9eTTb/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP+poJT/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wCZkIL/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj///////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////9lVUD/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/k4d3////////////m5CD/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/m5CD/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8AjIBx/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - ////////////////////////////////////////////////////////////////////////PCgN/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/1VELf///////////9HM - x/85JAn/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/4yAcf////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AHtuXP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////0MzG/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/////////////////OSQJ/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9/cmD/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wBvXkv/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj///////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - /////////////6adkf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/PCgN/21d - Sf9tXUn/bV1J/21dSf9tXUn/bV1J/21dSf9tXUn/bV1J/21dSf9tXUn/bV1J/21dSf9tXUn/bV1J/21d - Sf9tXUn/YlI8/0MvF/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/8S+t////////////1dGLv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/b15L/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8AX085/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/SDUc/////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////+PgnT/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0g1HP////////////////////////////// - ////////////////////////////////////////////////////////////////////////wbux/1E+ - J/85JAn/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+onZL///////// - //9wYU3/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/19POf////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AFA+Jv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9QPSb/q6OW//////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////gnRk/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9INRz///////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////U0Ao/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/mo6B////////////hXdn/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9QPib/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wBCLhT/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/1pIMf+2raP///////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - /////////////3ZnVf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/SDUc//// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////8jCuv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv///////////5CEdf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Qi4U/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8AOCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/X043/7ato////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////9pWkX/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0g1HP////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////UT4n/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+Bc2L///////// - //+YjYD/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////ADgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9wYU3/zsjC//////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////aFdD/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9INRz///////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////3doVf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV////////////nZGE/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wA4Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OiUK/3xuXf////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - /////////////2hXQ/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/SDUc//// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+DeGb/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf///////////7KqoP86JQr/OSQJ/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wDPycL/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/RjMZ//////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////9oV0P/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0g1HP////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////0UxGP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP/PycL/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8AwLmx/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/1tKNf////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////aFdD/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9INRz///////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////9bSTT/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/wLmx/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ALKpn/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9qW0b///////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf//////////////////////aVpF/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/7KqoP////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wCimY3/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/empY//////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////3doVf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+kmo7/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8AlIh5/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/4d7a/////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////+GeWj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/lIp7/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AIV3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP+XjH7///////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf//////////////////////lot8/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/4Z5aP////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wB1aFT/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/pp2R//////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////6SbkP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8AZ1dE/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/7Ssov////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////+yqqD/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/Z1dE/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AEYzGf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP/CvLT///////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf//////////////////////wry0/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0YzGf////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AG9eS/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/0czH//////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////9DMxv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/altG/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AJuQ - g/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI//////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV////////////////////////////OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP+bkIP/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AMzGwP86JQr/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf///////////////////////////zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OSQJ/8jC - uv////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wBMOCD/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP85JAn///////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - //////////////////85JAn/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9JNR3/////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wBsXEj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/RTEY//////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV////////////////////////////QzAW/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/2lZRf////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wCXjH7/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/1RC - Kv////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf///////////////////////////1NAKP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/mI2A/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wDGwLj/OSQJ/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+elIb///////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - //////////////////+elIb/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zkkCf/GwLj/////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8ARzQb/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zkkCf9sXEj///////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV/////////////////////////////////25g - TP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/RjMZ/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8AZlZC/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9KNx////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf//////////////////////////////////////TToi/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9iUT3/////AP///wD///8A////AP///wD///8A////AP///wD///8Ak4d3/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP87Jwz/zsjC//////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////g3dl/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////////////////////9POyP88KA3/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/5KH - eP////8A////AP///wD///8A////AP///wD///8Aw762/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/opmN//////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////3doVf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////////////////// - ////////opeM/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/wLmx/////wD///8A////AP// - /wD///8A////AEYzGf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/cWJP//////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////9iUjz/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf////////////////////////////////////////////////92Z1X/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/0UxGP////8A////AP///wD///8A////AGNUP/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/Tjwk//////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////PCgN/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////////////////////////////////////1E+J/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/YVA7/////wD///8A////AI2Bcv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OycM/9DM - xv////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////ubGo/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////////////////// - ////////////////////////PCgN/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+PgnT/////AMK8 - tP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/6adkf////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////1pIMf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3tuXP///////////////////////////////////////////////////////////6ad - kf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/7+3r/9TQCj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////4l8 - bP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+QhHX///////// - ////////////////////////////////////////////////////////d2hV/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9TQCj/lYp9/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/1A9Jv////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////3tuXP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/wryz//////////////////////////////////// - //////////////////////////////////9TQCj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/l4x+//// - /wA7Jwz/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //+5s6r/hnlo/0ArEv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/UT4n//////////////////////////////////////////////////////////////////// - /////////////zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OyYL/////wD///8Aemxa/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/w721//////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////8O9tf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OiUK/8nEvf////////////// - /////////////////////////////////////////////////////////////8K8tP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3ttW/////8A////AM7Iwv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/29eS/////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ///////////////////DvbX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/QCsS/7u1rP////////////////////////////////////////////// - //////////////////////////////////9tX0r/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP/Py8X/////AP// - /wD///8AYU86/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj///////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////w721/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9HNBv/hnlo//// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9eTTb/////AP///wD///8A////ALSsov84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/4p+cP////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////8O9tf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/XEs1/722rf////////////////////////////////////////////// - ////////////////////////////////////////////////////////iX5u/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/tKyi/////wD///8A////AP///wD///8ASTYe/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP8/KhD///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ///////////////////DvbX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/3xu - Xf////////////////////////////////////////////////////////////////////////////// - /////////////////////////////0EtE/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/SDUc/////wD///8A////AP// - /wD///8A////AJiNgP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/6adkf////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////w721/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP98bl3///////////////////////// - /////////////////////////////////////////////////////////////////////////////6Sb - kP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/5qOgf////8A////AP///wD///8A////AP///wD///8AOycM/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9QPib///////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////8O9tf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/fG5d//////////////////////////////////////////////////// - //////////////////////////////////////////////////9TQCj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zwo - Df////8A////AP///wD///8A////AP///wD///8A////AHxuXf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/8G7 - sf////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ///////////////////DvbX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/3xu - Xf////////////////////////////////////////////////////////////////////////////// - ///////////////////AubH/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP98bl3/////AP///wD///8A////AP// - /wD///8A////AP///wDSzsj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9sXEj///////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////w721/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9cSzX/vbat//////////////////// - ////////////////////////////////////////////////////////////////////////aVpF/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/087I/////wD///8A////AP///wD///8A////AP///wD///8A////AGJS - PP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI//////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////8O9tf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/SDUc/4d7a/////////////////////////////// - /////////////////////////////////////////////zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/X085//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wC2r6T/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP+He2v///////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ///////////////////DvbX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/QS0T/722rf////////////////////////////////////////////// - /////////////4Z6af84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/7avpP////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AEo3H/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/QCsS//////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////w721/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP87Jwz/ysS+//////////////////////////////////////////////////////9AKxL/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/0k2Hv////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wCbkIP/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+kmo7///////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////ubGo/4J0ZP8/KhD/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/1NAKP////////////// - //////////////////////////////////+il4z/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+bkIP/////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ADwoDf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/bmBM//////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////3RmVP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/wry0//////////////////////////////////// - ////////bmBM/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP88KA3/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wB/cmD/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/42Bcv////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////g3hm/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/5GGdv///////////////////////////////////////////4yAcf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/f3Fh/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////ADgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+vp5z///////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //9XRi7/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP97blz///////// - //////////////////////////////////+tpJj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wA4Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/0MzG//////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////ubOq/zkkCf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////////////////// - ////////zsjC/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wDMxsD/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OSQJ//////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////89KQ7/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf////////////////////////////////////////////////84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/zcjB/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8Aq6GV/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/0w4IP////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////Y1Q//zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - ////////////////////////////////////////Sjcf/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/6ujlv////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AIl8bP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9tX0r///////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////3doVf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////////////////// - /////////////2xcSP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+Jfm7/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wBoV0P/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/joRz//////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+DeGb/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf////////////////////////////////////////////////+NgXL/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/Z1dE/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8ARzQb/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/7Cnnf////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - ////////////////////////////////////////r6ec/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0YzGf////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ADgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP/RzMf///////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////////////////// - /////////////9DMxv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AMrEvv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj///////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf//////////////////////////////////////////////////////OSQJ/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/8rGv/////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wCpoJT/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/TToi//////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////////////////////////////////////0w4IP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+poJT/////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AiHtq/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/29e - S/////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////////////////// - //////////////////9tX0r/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/h3tr/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AGZVQf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+Qg3T///////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf//////////////////////////////////////////////////////joRz/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/2ZWQv////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wBGMxn/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/qJ2S//////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////////////////////////////////////6idkv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9GMxn/////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AOCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/z0p - Dv+2r6T///////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////////////////// - /////////////722rf9AKxL/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8AycS9/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/5SKe/////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf///////////////////////////////////////////6KXjP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP/KxL7/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AKee - k/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/cGFN//////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////////////////////3VoVP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/qaCU//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wCGemn/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9UQir///////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV/////////////////////////////////1hH - MP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/4h7av////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8AZVVA/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0Iu - FP/CvLP///////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf//////////////////////x8C4/0UxGP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9mVUH/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AEUx - GP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OiUK/6KXjP////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - ////////p56T/zolCv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/QzAW//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wA4Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/fnFf//////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV////////////q6OW/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP////8A////AP///wD///8A////AP// - /wD///8A////AP///wDIwrr/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9fTjf///////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf///////////52RhP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/yMK6/////wD///8A////AP///wD///8A////AP///wD///8A////AHxu - Xf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0k2 - Hv/Py8X//////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - //+dkYT/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/fG5d//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Ap56T/zolCv84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Tjwk//////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////Z1dE/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9INRz///////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV////////////nZGE/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/m5CD/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wDEvrf/Qy8X/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9RPif/hnlo/3xuXf////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - /////////////2VVQP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/SDUc//// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf///////////52RhP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9DLxf/xL63/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wBVRC3/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9QPSb///////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////9nV0T/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0g1HP////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////g3hm/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - //+dkYT/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9VRC3/////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AHNj - Uf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////ZVVA/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9INRz///////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////3doVf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV////////////nZGE/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9zY1H/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Alot8/zgjCP85JAn/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/52RhP///////////8G7sf+7s6v///////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - /////////////2dXRP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/SDUc//// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////9QPSb/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/4FzYv///////////5iNgP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+Wi3z/////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wC7s6v/PyoQ/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+dkYT///////// - //93aFX/OCMI/zgjCP84Iwj/RjMZ/2JSPP98bl3/mI2A/7Ssov/OyML//////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////9pWUX/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0g1HP////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ///JxL3/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //+QhHX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/z4pD/+2raP/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wBPPCX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/nZGE////////////d2hV/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////dGZU/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9INRz///////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////U0Ao/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/mo6B////////////hXdn/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/088 - Jf////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AGpb - Rv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/52RhP///////////3doVf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - /////////////4J0ZP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/SDUc//// - //////////////////////////////////////////////////////////////////////////////// - ///////////////////CvLT/U0Ao/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/6idkv///////////3BhTf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/2ZVQf////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AiX5u/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+dkYT///////// - //93aFX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////+OhHP/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zwoDf9tXUn/bV1J/21dSf9tXUn/bV1J/21d - Sf9tXUn/bV1J/21dSf9tXUn/bV1J/21dSf9tXUn/bV1J/21dSf9tXUn/bV1J/2JRPf9FMRj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP/Dvrb///////// - //9XRi7/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/4l+bv////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wCvp5z/PCgN/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/nZGE////////////d2hV/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////raSY/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/////////////////OyYL/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OyYL/6mglP////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AM/Lxf9JNh7/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/52RhP///////////3doVf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////85JAn/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/VUMs////////////0MzG/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/STYe/8/Lxf////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AFVE - Lf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+dkYT///////// - //93aFX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - ////////////////////////////////////////////////////////////////////////Qi4U/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/5GGdv///////////5uQ - g/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/VUQt//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wCpoJT/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/nZGE////////////d2hV/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - /////////////////////////////////////////////39yYP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP////////////////9fTzn/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/6mglP////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////ADgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/52RhP///////////3doVf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - ///////////////////OyML/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9zZVL/////////////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wBiUT3/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+dkYT///////// - //93aFX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////3Bh - Tf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP89KQ7/////////////////gXNi/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9jVD//////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8ApJqO/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/nZGE////////////d2hV/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////Qy8X/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/pp2R/////////////////zolCv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/pJuQ/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wA4Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/52RhP///////////3doVf84Iwj/OSQJ/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////8rEvv8+KQ//OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//// - /////////////2ZWQv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8AX043/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+Sh3j///////// - ////////wry0/6adkf+JfGz/bV1J/1A9Jv85JAn/OCMI/zgjCP84Iwj/OCMI/zgjCP85JAn/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////z8nC/0k2Hv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP87Jgv/opeM/////////////////5aLfP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/19ON/////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AJ+WiP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/ZVVA//////////////////////////////////// - ///////////////////AubH/pJqO/4Z6af9qW0b/TToi/zgjCP84Iwj/OCMI/zkkCf84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////hnlo/zsm - C/84Iwj/OCMI/zkkCf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9qW0b//////////////////////6ujlv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+floj/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8AOCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP+PgnT///////////////////////////////////////////////////////// - /////////////////////////////722rf+gl4v/g3hm/2hXQ/9JNh7/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////npSG/2lZRf8/KhD/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/VUMs/4+CdP///////////////////////////5uQ - g/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AFpIMf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP8+KQ//W0k0/3doVf+Uinv/sKme/87Iwv////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////AubH/sqqg/6adkf+il4z/opeM/6KX - jP+il4z/opeM/6KXjP+il4z/opeM/6KXjP+il4z/opeM/6KXjP+il4z/opeM/6KXjP+imY3/sKme/7+3 - r//OyML//////////////////////////////////////2xcSP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Wkgx/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wCZkIL/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/0EtE/9eTTb/emxa/5eMfv+0rKL/0czH//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////jIBx/zwoDf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/5mQgv////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////ADgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/Qy8X/2FPOv98bl3/mZCC/7ewpv////////////////////////////// - //+2r6T///////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////0MzG/6KZjf9lVUD/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wBUQir/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9GMxn/Y1M+/3VoVP9INRz/OCMI/6ujlv////////////// - /////////////////////////////////////////////////////////////62kmP9xYk//PyoQ/z8q - EP9xYk//raSY//////////////////////////////////////////////////////////////////// - ////////sKme/3psWv96bFr/emxa/3psWv96bFr/emxa/3psWv96bFr/emxa/3psWv96bFr/bmBM/2ZV - Qf9OPCT/OyYL/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9UQir/////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AlIh5/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/qJ2S//////////////////////////////////// - //////////////////+il4z/ZlVB/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/WEcw/5SK - e//Py8X/////////////////////////////////////////////////raSY/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/lot8/////wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wA5JAn/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP+elIb////////////////////////////CvLT/iHtq/006Iv84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0c0G/96bFr/tq2j//// - ////////////////////////pJqO/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8ATzwl/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/52R - hP//////r6ec/3NlUv89KQ7/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP87Jgv/ZlVB/6KXjP//////opmN/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/048JP////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AI+C - dP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP+Qg3T/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wDOyML/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/0MzG//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AEw4IP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Sjcf/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wCKfnD/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/4yAcf////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDCvLT/m5CD/3Nl - Uv9JNR3/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Qy8X/2ZVQf+NgXL/tq2j/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDCvLT/m5CD/3Nl - Uv9JNh7/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9DLxf/ZlVB/42B - cv+2raP/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDCvLT/m5CD/3Nl - Uv9JNh7/OCMI/zgjCP84Iwj/OCMI/zkkCf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP85JAn/OCMI/zgj - CP84Iwj/OCMI/0MwFv9mVUH/jYFy/7ato/////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDAubH/mI2A/3Bh - Tf9CLhT/OSQJ/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Qi4U/3BhTf+YjYD/wLmx/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AEw4IP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/STYe/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8ATToi/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Sjcf/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wBRPif/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/Tjwk/////wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AFNA - KP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Tzwl//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AVEIq/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/UD0m/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wBYRzD/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/VEIq/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AFpIMf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/VUQt/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AW0k0/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/V0Yu/////wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wBcTDb/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/QCsS/3ZnVf+wqZ7/sKme/3ZnVf9AKxL/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/XEw2//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AGFQO/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0k2Hv+Bc2L/vbat/////wD///8A////AP// - /wD///8A////AMnEvf+NgXL/Tzwl/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/XEs1/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8AYlE9/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zkkCf84Iwj/OCMI/zkk - Cf9fTzn/m5CD/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8Ap56T/21dSf87Jgv/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/X043/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wBjVD//OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP8/KhD/c2VS/6+nnP////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AL22 - rf+Bc2L/Qy8X/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/X085/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AGpbRv84Iwj/OCMI/zgjCP84Iwj/UD4m/4h7 - av/CvLT/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDPy8X/lIp7/1dG - Lv84Iwj/OCMI/zgjCP84Iwj/ZlVB/////wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8AbV9K/19POf+bkIP/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AKeek/9tXUn/bFtH//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - //////////5/////////////////////////+B/////////////////////////gB/////////////// - /////////8AD////////////////////////AAD///////////////////////wAAD////////////// - ////////+AAAD//////////////////////gAAAH/////////////////////4AAAAH///////////// - ////wAf+AAAAAH/gA//////////////AAAAAAAAAAAAD/////////////4AAAAAAAAAAAAH///////// - ////AAAAAAAAAAAAAP////////////8AAAAAAAAAAAAA/////////////gAAAAAAAAAAAAB///////// - ///8AAAAAAAAAAAAAD////////////wAAAAAAAAAAAAAP///////////+AAAAAAAAAAAAAAf//////// - ///4AAAAAAAAAAAAAB////////////AAAAAAAAAAAAAAD///////////4AAAAAAAAAAAAAAH//////// - ///gAAAAAAAAAAAAAAf//////////8AAAAAAAAAAAAAAA///////////gAAAAAAAAAAAAAAB//////// - //+AAAAAAAAAAAAAAAH//////////wAAAAAAAAAAAAAAAP/////////+AAAAAAAAAAAAAAAAf/////// - //gAAAAAAAAAAAAAAAAf////////4AAAAAAAAAAAAAAAAAf///////+AAAAAAAAAAAAAAAAAAf////// - /gAAAAAAAAAAAAAAAAAAf//////4AAAAAAAAAAAAAAAAAAAf/////+AAAAAAAAAAAAAAAAAAAAf///// - AAAAAAAAAAAAAAAAAAAAAP////wAAAAAAAAAAAAAAAAAAAAAP///+AAAAAAAAAAAAAAAAAAAAAAf///4 - AAAAAAAAAAAAAAAAAAAAAB////gAAAAAAAAAAAAAAAAAAAAAH///+AAAAAAAAAAAAAAAAAAAAAAf///4 - AAAAAAAAAAAAAAAAAAAAAB////gAAAAAAAAAAAAAAAAAAAAAH///+AAAAAAAAAAAAAAAAAAAAAAf///4 - AAAAAAAAAAAAAAAAAAAAAB////gAAAAAAAAAAAAAAAAAAAAAH///+AAAAAAAAAAAAAAAAAAAAAAf///4 - AAAAAAAAAAAAAAAAAAAAAB////gAAAAAAAAAAAAAAAAAAAAAH///8AAAAAAAAAAAAAAAAAAAAAAP///w - AAAAAAAAAAAAAAAAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAD///8AAAAAAAAAAAAAAAAAAAAAAP///w - AAAAAAAAAAAAAAAAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAD///8AAAAAAAAAAAAAAAAAAAAAAP///w - AAAAAAAAAAAAAAAAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAD///4AAAAAAAAAAAAAAAAAAAAAAH///A - AAAAAAAAAAAAAAAAAAAAAAP//4AAAAAAAAAAAAAAAAAAAAAAAf//gAAAAAAAAAAAAAAAAAAAAAAB//8A - AAAAAAAAAAAAAAAAAAAAAAD//gAAAAAAAAAAAAAAAAAAAAAAAH/8AAAAAAAAAAAAAAAAAAAAAAAAP/wA - AAAAAAAAAAAAAAAAAAAAAAA/+AAAAAAAAAAAAAAAAAAAAAAAAB/wAAAAAAAAAAAAAAAAAAAAAAAAD+AA - AAAAAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAAAAAAAfAAAAAAAAAAAAAAAAAAAAAAAAAA4AA - AAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAYAA - AAAAAAAAAAAAAAAAAAAAAAABwAAAAAAAAAAAAAAAAAAAAAAAAAPAAAAAAAAAAAAAAAAAAAAAAAAAA+AA - AAAAAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAD/AA - AAAAAAAAAAAAAAAAAAAAAAAP8AAAAAAAAAAAAAAAAAAAAAAAAA/4AAAAAAAAAAAAAAAAAAAAAAAAH/gA - AAAAAAAAAAAAAAAAAAAAAAAf/AAAAAAAAAAAAAAAAAAAAAAAAD/8AAAAAAAAAAAAAAAAAAAAAAAAP/4A - AAAAAAAAAAAAAAAAAAAAAAB//gAAAAAAAAAAAAAAAAAAAAAAAH//AAAAAAAAAAAAAAAAAAAAAAAA//8A - AAAAAAAAAAAAAAAAAAAAAAD//gAAAAAAAAAAAAAAAAAAAAAAAH/+AAAAAAAAAAAAAAAAAAAAAAAAf/4A - AAAAAAAAAAAAAAAAAAAAAAB//gAAAAAAAAAAAAAAAAAAAAAAAH/+AAAAAAAAAAAAAAAAAAAAAAAAf/4A - AAAAAAAAAAAAAAAAAAAAAAB//AAAAAAAAAAAAAAAAAAAAAAAAD/8AAAAAAAAAAAAAAAAAAAAAAAAP/wA - AAAAAAAAAAAAAAAAAAAAAAA//AAAAAAAAAAAAAAAAAAAAAAAAD/8AAAAAAAAAAAAAAAAAAAAAAAAP/wA - AAAAAAAAAAAAAAAAAAAAAAA/+AAAAAAAAAAAAAAAAAAAAAAAAB/4AAAAAAAAAAAAAAAAAAAAAAAAH/gA - AAAAAAAAAAAAAAAAAAAAAAAf+AAAAAAAAAAAAAAAAAAAAAAAAB/4AAAAAAAAAAAAAAAAAAAAAAAAH/gA - AAAAAAAAAAAAAAAAAAAAAAAf8AAAAAAAAAAAAAAAAAAAAAAAAA/4AAAAAAAAAAAAAAAAAAAAAAAAH/wA - AAAAAAAAAAAAAAAAAAAAAAA//gAAAAAAAAAAAAAAAAAAAAAAAH//gAAAAAAAAAAAAAAAAAAAAAAB///A - AAAAAAAAAAAAAAAAAAAAAAP//+AAAAAAAAAAAAAAAAAAAAAAB///8AAAAAAAAAAAAAAAAAAAAAAP///8 - AAAAAAAAAAAAAAAAAAAAAD////4AAAAAAAAAAAAAAAAAAAAAf////wAAAAAAAAAAAAAAAAAAAAD///// - gAAAAAAAAAAAAAAAAAAAAf/////AAAAAAAAAAAAAAAAAAAAD//////AAAAAAAAAAAAAAAAAAAA////// - 8AAAAAAAAAAAAAAAAAAAD//////4AAAAAAAAAAAAAAAAAAAf//////gAAAAAAAAAAAAAAAAAAB////// - +AAAAAAAAAAAAAAAAAAAH//////8AAAAAAAAAAAAAAAAAAA///////wAAAAAAAAAAAAAAAAAAD////// - /AAAAAAAAAAAAAAAAAAAP//////+AAAAAAAAAAAAAAAAAAB///////4AAAAAAAAAAAAAAAAAAH////// - /gAAAAAAAAAAAAAAAAAAf///////AAAAAAAAAAAAAAAAAAD///////8AAAAAAAAAAAAAAAAAAP////// - /wAAAAAAAAAAAAAAAAAA////////gAAAAAAAAAAAAAAAAAH///////+AAAAAAAAAAAAAAAAAAf////// - /4AAAAAAAAAAAAAAAAAB////////gAAAAAAAAAAAAAAAAAH////////AAAAAAAAAAAAAAAAAA/////// - /8AAAAAAAAAAAAAAAAAD////////8AAAAAAAAAAAAAAAAA//////////gAAAAAAAAAAAAAAB//////// - ///8AAAAAAAAAAAAAD/////////////gAAAAAAAAAAAH//////////////4AAAAAAAAAAH////////// - /////wAAAAAAAAAA////////////////gAAAAAAAAAH////////////////AAAAAAAAAA/////////// - /////+AAAAAAAAAH////////////////8AAAAAAAAA/////////////////4AAAAAAAAH/////////// - //////wAAAAAAAA//////////////////gAAAAAAAH//////////////////AAAH4AAA//////////// - //////+AAH/+AAH//////////////////8AD///AA///////////////////4B////gH//////////// - ///////x/////4////////// - - - \ No newline at end of file diff --git a/TGS.ControlPanel/Program.cs b/TGS.ControlPanel/Program.cs deleted file mode 100644 index 50768d847a..0000000000 --- a/TGS.ControlPanel/Program.cs +++ /dev/null @@ -1,135 +0,0 @@ -using System; -using System.Windows.Forms; -using TGS.Interface; -using TGS.Interface.Components; - -namespace TGS.ControlPanel -{ - static class Program - { - public static IClient ServerInterface; - [STAThread] - static void Main(string[] args) - { - try - { - if (Properties.Settings.Default.UpgradeRequired) - { - Properties.Settings.Default.Upgrade(); - Properties.Settings.Default.UpgradeRequired = false; - Properties.Settings.Default.Save(); - } - Application.EnableVisualStyles(); - Application.SetCompatibleTextRenderingDefault(false); - Interface.Client.SetBadCertificateHandler(BadCertificateHandler); - Application.Run(new Login()); - if(ServerInterface != null) - { - new InstanceSelector(ServerInterface.Server).Show(); - Application.Run(); - } - } - catch (Exception e) - { - if (ServerInterface != null) - ServerInterface.Dispose(); - ServiceDisconnectException(e); - } - finally - { - Properties.Settings.Default.Save(); - } - } - static bool SSLErrorPromptResult = false; - static bool BadCertificateHandler(string message) - { - if (!SSLErrorPromptResult) - { - var result = MessageBox.Show(message + " IT IS HIGHLY RECCOMENDED YOU DO NOT PROCEED! Continue?", "SSL Error", MessageBoxButtons.YesNo) == DialogResult.Yes; - SSLErrorPromptResult = result; - return result; - } - return true; - } - - public static void ServiceDisconnectException(Exception e) - { - MessageBox.Show("An unhandled exception occurred. This usually means we lost connection to the service. Error" + e.ToString()); - } - - public static string TextPrompt(string caption, string text) - { - Form prompt = new Form() - { - Width = 500, - Height = 150, - FormBorderStyle = FormBorderStyle.FixedDialog, - Text = caption, - StartPosition = FormStartPosition.CenterScreen, - MaximizeBox = false, - MinimizeBox = false, - }; - Label textLabel = new Label() { Left = 50, Top = 20, Text = text, AutoSize = true }; - TextBox textBox = new TextBox() { Left = 50, Top = 50, Width = 400 }; - Button confirmation = new Button() { Text = "Ok", Left = 350, Width = 100, Top = 70, DialogResult = DialogResult.OK }; - confirmation.Click += (sender, e) => { prompt.Close(); }; - prompt.Controls.Add(textBox); - prompt.Controls.Add(confirmation); - prompt.Controls.Add(textLabel); - prompt.AcceptButton = confirmation; - - return prompt.ShowDialog() == DialogResult.OK ? textBox.Text : null; - } - - /// - /// Prompts the user to open the explaining the API rate limit - /// - /// The to use - /// if the ran and returned a , otherwise - public static bool RateLimitPrompt(Octokit.GitHubClient client) - { - if (MessageBox.Show("You seem to have hit the rate limit of 60 requests per hour of the GitHub API for anonymous requests. Would you like to enter credentials to bypass this?", "Rate limited", MessageBoxButtons.YesNo) != DialogResult.Yes) - return false; - using (var D = new GitHubLoginPrompt(client)) - return D.ShowDialog() == DialogResult.OK; - } - - /// - /// Gets the and of a given 's remote. Shows an error if the target remote isn't GitHub - /// - /// The to get the remote of - /// The owner of the remote - /// The remote - /// if 's remote was a valid GitHub , otherwise and the user was prompted - public static bool GetRepositoryRemote(ITGRepository repo, out string owner, out string name) - { - string remote = null; - remote = repo.GetRemote(out string error); - if (remote == null) - { - MessageBox.Show(String.Format("Error retrieving remote repository: {0}", error)); - owner = null; - name = null; - return false; - } - if (!remote.Contains("github.com")) - { - MessageBox.Show("Pull request support is only available for GitHub based repositories!", "Error"); - owner = null; - name = null; - return false; - } - - //Assume standard gh format: [(git)|(https)]://github.com/owner/repo(.git)[0-1] - //Yes use .git twice in case it was weird - var toRemove = new string[] { ".git", "/", ".git" }; - foreach (string item in toRemove) - if (remote.EndsWith(item)) - remote = remote.Substring(0, remote.LastIndexOf(item)); - var splits = remote.Split('/'); - name = splits[splits.Length - 1]; - owner = splits[splits.Length - 2].Split('.')[0]; - return true; - } - } -} diff --git a/TGS.ControlPanel/Properties/AssemblyInfo.cs b/TGS.ControlPanel/Properties/AssemblyInfo.cs deleted file mode 100644 index 1f05bee96e..0000000000 --- a/TGS.ControlPanel/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("TGStation Server Control Panel")] -[assembly: AssemblyDescription("Control panel for the TG Station Server Service")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("394e7643-6b8c-416f-ab18-95ac12648cdc")] diff --git a/TGS.ControlPanel/Properties/Settings.Designer.cs b/TGS.ControlPanel/Properties/Settings.Designer.cs deleted file mode 100644 index 391f1eca5b..0000000000 --- a/TGS.ControlPanel/Properties/Settings.Designer.cs +++ /dev/null @@ -1,121 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace TGS.ControlPanel.Properties { - - - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "15.3.0.0")] - internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase { - - private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); - - public static Settings Default { - get { - return defaultInstance; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("0")] - public int LastPageIndex { - get { - return ((int)(this["LastPageIndex"])); - } - set { - this["LastPageIndex"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("0")] - public int LastConfigPageIndex { - get { - return ((int)(this["LastConfigPageIndex"])); - } - set { - this["LastConfigPageIndex"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("True")] - public bool UpgradeRequired { - get { - return ((bool)(this["UpgradeRequired"])); - } - set { - this["UpgradeRequired"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("0")] - public int LastChatProvider { - get { - return ((int)(this["LastChatProvider"])); - } - set { - this["LastChatProvider"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - public global::System.Collections.Specialized.StringCollection RemoteLoginInfo { - get { - return ((global::System.Collections.Specialized.StringCollection)(this["RemoteLoginInfo"])); - } - set { - this["RemoteLoginInfo"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("False")] - public bool RemoteDefault { - get { - return ((bool)(this["RemoteDefault"])); - } - set { - this["RemoteDefault"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("")] - public string GitHubAPIKey { - get { - return ((string)(this["GitHubAPIKey"])); - } - set { - this["GitHubAPIKey"] = value; - } - } - - [global::System.Configuration.UserScopedSettingAttribute()] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Configuration.DefaultSettingValueAttribute("")] - public string GitHubAPIKeyEntropy { - get { - return ((string)(this["GitHubAPIKeyEntropy"])); - } - set { - this["GitHubAPIKeyEntropy"] = value; - } - } - } -} diff --git a/TGS.ControlPanel/Properties/Settings.settings b/TGS.ControlPanel/Properties/Settings.settings deleted file mode 100644 index f2be0bac77..0000000000 --- a/TGS.ControlPanel/Properties/Settings.settings +++ /dev/null @@ -1,30 +0,0 @@ - - - - - - 0 - - - 0 - - - True - - - 0 - - - - - - False - - - - - - - - - \ No newline at end of file diff --git a/TGS.ControlPanel/ServerOpForm.cs b/TGS.ControlPanel/ServerOpForm.cs deleted file mode 100644 index 5188cd2b31..0000000000 --- a/TGS.ControlPanel/ServerOpForm.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System; -using System.Threading.Tasks; -using System.Windows.Forms; - -namespace TGS.ControlPanel -{ - /// - /// Used to provide an ATP function for calls into an - /// -#if !DEBUG - abstract -#endif - class ServerOpForm : Form - { - /// - /// Used to wrap calls in a non-blocking fashion while disabling the and enabling the wait cursor - /// - /// The operation to wrap - /// A wrapping - protected Task WrapServerOp(Action action) - { - Enabled = false; - UseWaitCursor = true; - try - { - return Task.Run(action); - } - finally - { - Enabled = true; - UseWaitCursor = false; - } - } - } -} diff --git a/TGS.ControlPanel/TGS.ControlPanel.csproj b/TGS.ControlPanel/TGS.ControlPanel.csproj deleted file mode 100644 index 3f5b25b9e5..0000000000 --- a/TGS.ControlPanel/TGS.ControlPanel.csproj +++ /dev/null @@ -1,151 +0,0 @@ - - - - - Debug - AnyCPU - {394E7643-6B8C-416F-AB18-95AC12648CDC} - WinExe - TGS.ControlPanel - TGControlPanel - v4.5.2 - 512 - true - - - - - tgs.ico - - - true - bin\Debug\ - DEBUG;TRACE - full - AnyCPU - prompt - MinimumRecommendedRules.ruleset - true - - - bin\Release\ - TRACE - bin\x86\Release\TGS.ControlPanel.xml - true - true - pdbonly - AnyCPU - prompt - MinimumRecommendedRules.ruleset - true - - - false - - - - ..\packages\Octokit.0.29.0\lib\net45\Octokit.dll - - - - - - C:\Program Files (x86)\Reference Assemblies\Microsoft\Framework\.NETFramework\v4.5.2\System.Windows.Forms.dll - - - - - - Form - - - Form - - - Form - - - Form - - - GitHubLoginPrompt.cs - - - Form - - - InstanceSelector.cs - - - Form - - - Login.cs - - - Form - - - ControlPanel.cs - - - - - True - True - Settings.settings - - - Form - - - Form - - - - Form - - - Form - - - TestMergeManager.cs - - - Form - - - GitHubLoginPrompt.cs - - - InstanceSelector.cs - - - Login.cs - - - ControlPanel.cs - - - TestMergeManager.cs - - - - - - - SettingsSingleFileGenerator - Settings.Designer.cs - - - - - {ac4e7e8b-f83a-481c-a8b0-8fa4e8ae59ab} - TGS.Interface - - - - - - - diff --git a/TGS.ControlPanel/TestMergeManager.Designer.cs b/TGS.ControlPanel/TestMergeManager.Designer.cs deleted file mode 100644 index e73ba3944e..0000000000 --- a/TGS.ControlPanel/TestMergeManager.Designer.cs +++ /dev/null @@ -1,222 +0,0 @@ -namespace TGS.ControlPanel -{ - partial class TestMergeManager - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(TestMergeManager)); - this.PullRequestListBox = new System.Windows.Forms.CheckedListBox(); - this.ApplyButton = new System.Windows.Forms.Button(); - this.UpdateToRemoteRadioButton = new System.Windows.Forms.RadioButton(); - this.UpdateToOriginRadioButton = new System.Windows.Forms.RadioButton(); - this.NoUpdateRadioButton = new System.Windows.Forms.RadioButton(); - this.RefreshButton = new System.Windows.Forms.Button(); - this.ApplyingPullRequestsLabel = new System.Windows.Forms.Label(); - this.ApplyingPullRequestsProgressBar = new System.Windows.Forms.ProgressBar(); - this.AddPRButton = new System.Windows.Forms.Button(); - this.AddPRNumericUpDown = new System.Windows.Forms.NumericUpDown(); - ((System.ComponentModel.ISupportInitialize)(this.AddPRNumericUpDown)).BeginInit(); - this.SuspendLayout(); - // - // PullRequestListBox - // - this.PullRequestListBox.CheckOnClick = true; - this.PullRequestListBox.FormattingEnabled = true; - this.PullRequestListBox.Location = new System.Drawing.Point(13, 13); - this.PullRequestListBox.Name = "PullRequestListBox"; - this.PullRequestListBox.Size = new System.Drawing.Size(588, 199); - this.PullRequestListBox.TabIndex = 0; - this.PullRequestListBox.ThreeDCheckBoxes = true; - this.PullRequestListBox.UseWaitCursor = true; - // - // ApplyButton - // - this.ApplyButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.ApplyButton.Location = new System.Drawing.Point(544, 226); - this.ApplyButton.Name = "ApplyButton"; - this.ApplyButton.Size = new System.Drawing.Size(57, 23); - this.ApplyButton.TabIndex = 2; - this.ApplyButton.Text = "Apply"; - this.ApplyButton.UseVisualStyleBackColor = true; - this.ApplyButton.UseWaitCursor = true; - this.ApplyButton.Click += new System.EventHandler(this.ApplyButton_Click); - // - // UpdateToRemoteRadioButton - // - this.UpdateToRemoteRadioButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.UpdateToRemoteRadioButton.AutoSize = true; - this.UpdateToRemoteRadioButton.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.UpdateToRemoteRadioButton.Location = new System.Drawing.Point(13, 229); - this.UpdateToRemoteRadioButton.Name = "UpdateToRemoteRadioButton"; - this.UpdateToRemoteRadioButton.Size = new System.Drawing.Size(116, 17); - this.UpdateToRemoteRadioButton.TabIndex = 3; - this.UpdateToRemoteRadioButton.TabStop = true; - this.UpdateToRemoteRadioButton.Text = "Update To Remote"; - this.UpdateToRemoteRadioButton.UseVisualStyleBackColor = true; - this.UpdateToRemoteRadioButton.UseWaitCursor = true; - // - // UpdateToOriginRadioButton - // - this.UpdateToOriginRadioButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.UpdateToOriginRadioButton.AutoSize = true; - this.UpdateToOriginRadioButton.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.UpdateToOriginRadioButton.Location = new System.Drawing.Point(135, 229); - this.UpdateToOriginRadioButton.Name = "UpdateToOriginRadioButton"; - this.UpdateToOriginRadioButton.Size = new System.Drawing.Size(106, 17); - this.UpdateToOriginRadioButton.TabIndex = 4; - this.UpdateToOriginRadioButton.TabStop = true; - this.UpdateToOriginRadioButton.Text = "Update To Origin"; - this.UpdateToOriginRadioButton.UseVisualStyleBackColor = true; - this.UpdateToOriginRadioButton.UseWaitCursor = true; - // - // NoUpdateRadioButton - // - this.NoUpdateRadioButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.NoUpdateRadioButton.AutoSize = true; - this.NoUpdateRadioButton.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.NoUpdateRadioButton.Location = new System.Drawing.Point(247, 229); - this.NoUpdateRadioButton.Name = "NoUpdateRadioButton"; - this.NoUpdateRadioButton.Size = new System.Drawing.Size(77, 17); - this.NoUpdateRadioButton.TabIndex = 5; - this.NoUpdateRadioButton.TabStop = true; - this.NoUpdateRadioButton.Text = "No Update"; - this.NoUpdateRadioButton.UseVisualStyleBackColor = true; - this.NoUpdateRadioButton.UseWaitCursor = true; - // - // RefreshButton - // - this.RefreshButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.RefreshButton.Location = new System.Drawing.Point(481, 226); - this.RefreshButton.Name = "RefreshButton"; - this.RefreshButton.Size = new System.Drawing.Size(57, 23); - this.RefreshButton.TabIndex = 6; - this.RefreshButton.Text = "Refresh"; - this.RefreshButton.UseVisualStyleBackColor = true; - this.RefreshButton.UseWaitCursor = true; - this.RefreshButton.Click += new System.EventHandler(this.RefreshButton_Click); - // - // ApplyingPullRequestsLabel - // - this.ApplyingPullRequestsLabel.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Left))); - this.ApplyingPullRequestsLabel.AutoSize = true; - this.ApplyingPullRequestsLabel.Font = new System.Drawing.Font("Microsoft Sans Serif", 9.75F, System.Drawing.FontStyle.Regular, System.Drawing.GraphicsUnit.Point, ((byte)(0))); - this.ApplyingPullRequestsLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.ApplyingPullRequestsLabel.Location = new System.Drawing.Point(224, 92); - this.ApplyingPullRequestsLabel.Name = "ApplyingPullRequestsLabel"; - this.ApplyingPullRequestsLabel.Size = new System.Drawing.Size(156, 16); - this.ApplyingPullRequestsLabel.TabIndex = 8; - this.ApplyingPullRequestsLabel.Text = "Applying Pull Requests..."; - this.ApplyingPullRequestsLabel.UseWaitCursor = true; - this.ApplyingPullRequestsLabel.Visible = false; - // - // ApplyingPullRequestsProgressBar - // - this.ApplyingPullRequestsProgressBar.Location = new System.Drawing.Point(48, 112); - this.ApplyingPullRequestsProgressBar.Name = "ApplyingPullRequestsProgressBar"; - this.ApplyingPullRequestsProgressBar.Size = new System.Drawing.Size(513, 23); - this.ApplyingPullRequestsProgressBar.Style = System.Windows.Forms.ProgressBarStyle.Marquee; - this.ApplyingPullRequestsProgressBar.TabIndex = 9; - this.ApplyingPullRequestsProgressBar.UseWaitCursor = true; - this.ApplyingPullRequestsProgressBar.Visible = false; - // - // AddPRButton - // - this.AddPRButton.Anchor = ((System.Windows.Forms.AnchorStyles)((System.Windows.Forms.AnchorStyles.Bottom | System.Windows.Forms.AnchorStyles.Right))); - this.AddPRButton.Location = new System.Drawing.Point(418, 226); - this.AddPRButton.Name = "AddPRButton"; - this.AddPRButton.Size = new System.Drawing.Size(57, 23); - this.AddPRButton.TabIndex = 10; - this.AddPRButton.Text = "Add PR"; - this.AddPRButton.UseVisualStyleBackColor = true; - this.AddPRButton.UseWaitCursor = true; - this.AddPRButton.Click += new System.EventHandler(this.AddPRButton_Click); - // - // AddPRNumericUpDown - // - this.AddPRNumericUpDown.Location = new System.Drawing.Point(331, 229); - this.AddPRNumericUpDown.Maximum = new decimal(new int[] { - 10000000, - 0, - 0, - 0}); - this.AddPRNumericUpDown.Minimum = new decimal(new int[] { - 1, - 0, - 0, - 0}); - this.AddPRNumericUpDown.Name = "AddPRNumericUpDown"; - this.AddPRNumericUpDown.Size = new System.Drawing.Size(81, 20); - this.AddPRNumericUpDown.TabIndex = 11; - this.AddPRNumericUpDown.UseWaitCursor = true; - this.AddPRNumericUpDown.Value = new decimal(new int[] { - 1, - 0, - 0, - 0}); - // - // TestMergeManager - // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(39)))), ((int)(((byte)(40)))), ((int)(((byte)(34))))); - this.ClientSize = new System.Drawing.Size(613, 261); - this.Controls.Add(this.AddPRNumericUpDown); - this.Controls.Add(this.AddPRButton); - this.Controls.Add(this.ApplyingPullRequestsProgressBar); - this.Controls.Add(this.ApplyingPullRequestsLabel); - this.Controls.Add(this.RefreshButton); - this.Controls.Add(this.NoUpdateRadioButton); - this.Controls.Add(this.UpdateToOriginRadioButton); - this.Controls.Add(this.UpdateToRemoteRadioButton); - this.Controls.Add(this.ApplyButton); - this.Controls.Add(this.PullRequestListBox); - this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); - this.MaximizeBox = false; - this.Name = "TestMergeManager"; - this.StartPosition = System.Windows.Forms.FormStartPosition.CenterScreen; - this.Text = "Test Merge Manager"; - this.UseWaitCursor = true; - ((System.ComponentModel.ISupportInitialize)(this.AddPRNumericUpDown)).EndInit(); - this.ResumeLayout(false); - this.PerformLayout(); - - } - - #endregion - - private System.Windows.Forms.CheckedListBox PullRequestListBox; - private System.Windows.Forms.Button ApplyButton; - private System.Windows.Forms.RadioButton UpdateToRemoteRadioButton; - private System.Windows.Forms.RadioButton UpdateToOriginRadioButton; - private System.Windows.Forms.RadioButton NoUpdateRadioButton; - private System.Windows.Forms.Button RefreshButton; - private System.Windows.Forms.Label ApplyingPullRequestsLabel; - private System.Windows.Forms.ProgressBar ApplyingPullRequestsProgressBar; - private System.Windows.Forms.Button AddPRButton; - private System.Windows.Forms.NumericUpDown AddPRNumericUpDown; - } -} \ No newline at end of file diff --git a/TGS.ControlPanel/TestMergeManager.cs b/TGS.ControlPanel/TestMergeManager.cs deleted file mode 100644 index b463c444dc..0000000000 --- a/TGS.ControlPanel/TestMergeManager.cs +++ /dev/null @@ -1,384 +0,0 @@ -using Octokit; -using System; -using System.Collections.Generic; -using System.Linq; -using System.Threading.Tasks; -using System.Windows.Forms; -using TGS.Interface; -using TGS.Interface.Components; - -namespace TGS.ControlPanel -{ - sealed partial class TestMergeManager : ServerOpForm - { - /// - /// Error message format used when fails - /// - const string MergedPullsError = "Error retrieving currently merged pull requests: {0}"; - - /// - /// The to handle pull requests for - /// - readonly IInstance currentInterface; - - /// - /// The to use to read PR lists - /// - readonly GitHubClient client; - - /// - /// The owner of the target - /// - string repoOwner; - /// - /// The name of the target - /// - string repoName; - - /// - /// Construct a - /// - /// The to manage pull requests for - /// The to use for getting pull request information - public TestMergeManager(IInstance interfaceToUse, GitHubClient clientToUse) - { - InitializeComponent(); - DialogResult = DialogResult.Cancel; - UpdateToRemoteRadioButton.Checked = true; - currentInterface = interfaceToUse; - client = clientToUse; - Load += PullRequestManager_Load; - PullRequestListBox.ItemCheck += PullRequestListBox_ItemCheck; - } - - /// - /// Called when an item in is checked or unchecked. Unchecks opposing PRs - /// - /// The sender of the event - /// The - void PullRequestListBox_ItemCheck(object sender, ItemCheckEventArgs e) - { - if (e.NewValue != CheckState.Checked) - return; - //let's uncheck the opposing PR# if this is one of the outdated/updated testmerge - var item = (string)PullRequestListBox.Items[e.Index]; - var prefix = item.Split(' ')[0]; - - for (var I = 0; I < PullRequestListBox.Items.Count; ++I) - { - var S = (string)PullRequestListBox.Items[I]; - if (S.Split(' ')[0] == prefix && S != item) - { - PullRequestListBox.SetItemChecked(I, false); - break; - } - } - } - - /// - /// Populate with from github and check off ones that are currently merged. Prompts the user to login to GitHub if they hit the rate limit - /// - async void LoadPullRequests() - { - try - { - Enabled = false; - UseWaitCursor = true; - try - { - PullRequestListBox.Items.Clear(); - - var repo = currentInterface.Repository; - string error = null; - List pulls = null; - - //get started on this while we're processing here - var pullsRequest = Task.Run(() => pulls = repo.MergedPullRequests(out error)); - - //Search for open PRs - Enabled = false; - UseWaitCursor = true; - SearchIssuesResult result; - try - { - result = await client.Search.SearchIssues(new SearchIssuesRequest - { - Repos = new RepositoryCollection { { repoOwner, repoName } }, - State = ItemState.Open, - Type = IssueTypeQualifier.PullRequest - }); - } - finally - { - Enabled = true; - UseWaitCursor = false; - } - - //now we need to know what's merged - await pullsRequest; - if (pulls == null) - MessageBox.Show(String.Format(MergedPullsError, error)); - - //insert the open pull requests, checking already merged once - foreach (var I in result.Items) - if (!pulls.Any(x => x.Number == I.Number)) - InsertPullRequest(I, false, CheckState.Unchecked); - - //insert remaining merged pulls - foreach (var I in pulls) - { - var pr = await client.PullRequest.Get(repoOwner, repoName, I.Number); - var outdated = pr.Head.Sha != I.Sha; - var mergedOrOutdated = pr.Merged || outdated; - InsertPullRequest(await client.Issue.Get(repoOwner, repoName, I.Number), true, mergedOrOutdated ? CheckState.Indeterminate : CheckState.Checked, String.Format("{1}{0}", mergedOrOutdated ? String.Format(" - {0}", I.Sha) : String.Empty, pr.Merged ? " - MERGED ON REMOTE: " : (outdated ? " - OUTDATED: " : String.Empty))); - } - } - finally - { - Enabled = true; - UseWaitCursor = false; - } - } - catch (ForbiddenException) - { - if (client.Credentials.AuthenticationType == AuthenticationType.Anonymous) //assume request limit hit - { - if(Program.RateLimitPrompt(client)) - LoadPullRequests(); - } - else - throw; - } - } - - /// - /// Format an entry for and insert it into - /// - /// The to format, must contain a - /// If this or is mpt , will be inserted at the top of as opposed to the bottom - /// The of the item - /// An optional to append to the entry before it's inserted - void InsertPullRequest(Issue issue, bool prioritize, CheckState checkState, string append = null) - { - bool needsTesting = issue.Labels.Any(x => x.Name.ToLower().Contains("test")); - prioritize |= needsTesting; - var itemString = String.Format("#{0} - {1}{2}{3}", issue.Number, issue.Title, needsTesting ? " - TESTING REQUESTED" : String.Empty, append ?? String.Empty); - InsertItem(itemString, prioritize, checkState); - } - - /// - /// Insert an into - /// - /// The to insert - /// If this or is not , will be inserted at the top of as opposed to the bottom - /// The of the item - void InsertItem(string itemString, bool prioritize, CheckState checkState) - { - prioritize = prioritize || checkState != CheckState.Unchecked; - if (prioritize) - { - PullRequestListBox.Items.Insert(0, itemString); - PullRequestListBox.SetItemCheckState(0, checkState); - } - else - PullRequestListBox.Items.Add(itemString, checkState); - } - - /// - /// Called when the is loaded. Sets and - /// - /// The sender of the event - /// The - void PullRequestManager_Load(object sender, EventArgs e) - { - Enabled = false; - var repo = currentInterface.Repository; - if(!Program.GetRepositoryRemote(repo, out repoOwner, out repoName)) - { - Close(); - return; - } - LoadPullRequests(); - } - - /// - /// Calls and shows the user an error prompt if it fails - /// - /// The to call on - async void GenerateChangelog(ITGRepository repo) - { - string error = null; - await WrapServerOp(() => repo.GenerateChangelog(out error)); - if (error != null) - MessageBox.Show(String.Format("Error generating changelog: {0}", error)); - } - - /// - /// Called when the is clicked. Calls if necessary, merge pull requests, and call . Closes the if appropriate - /// - /// The sender of the event - /// The - async void ApplyButton_Click(object sender, EventArgs e) - { - Enabled = false; - UseWaitCursor = true; - ApplyingPullRequestsLabel.Visible = true; - ApplyingPullRequestsProgressBar.Visible = true; - PullRequestListBox.Visible = false; - try - { - //so first collect a list of pulls that are checked - var pulls = new List(); - foreach (int I in PullRequestListBox.CheckedIndices) - { - var S = (string)PullRequestListBox.Items[I]; - string mergedSha = null; - var splits = S.Split(' '); - var mergedOnRemote = S.Contains(" - MERGED ON REMOTE: "); - if (mergedOnRemote && UpdateToRemoteRadioButton.Checked) - continue; - if ((S.Contains(" - OUTDATED: " ) || mergedOnRemote) && PullRequestListBox.GetItemCheckState(I) == CheckState.Indeterminate) - mergedSha = splits[splits.Length - 1]; - var key = Convert.ToInt32((splits[0].Substring(1))); - try - { - pulls.Add(new PullRequestInfo(key, mergedSha)); - } - catch - { - MessageBox.Show(String.Format("Checked both keep and update option for #{0}", key), "Error"); - return; - } - } - - var repo = currentInterface.Repository; - - string error = null; - //Do standard repo updates - if (UpdateToRemoteRadioButton.Checked) - await WrapServerOp(() => error = repo.Update(true)); - else if (UpdateToOriginRadioButton.Checked) - await WrapServerOp(() => error = repo.Reset(true)); - - if (error != null) - { - MessageBox.Show(String.Format("Error updating repository: {0}", error)); - return; - } - - if (UpdateToRemoteRadioButton.Checked) - { - GenerateChangelog(repo); - error = await Task.Run(() => repo.SynchronizePush()); - } - - //Merge the PRs, collect errors - var errors = await Task.Run(() => repo.MergePullRequests(pulls, false)); - - if (errors != null) - { - //Show any errors - for (var I = 0; I < errors.Count(); ++I) - { - var err = errors.ElementAt(I); - if (err != null) - MessageBox.Show(err, String.Format("Error merging PR #{0}", pulls[I].Number)); - } - return; - } - - if (pulls.Count > 0) - //regen the changelog - GenerateChangelog(repo); - - //Start the compile - var compileStarted = await Task.Run(() => currentInterface.Compiler.Compile(pulls.Count == 1)); - - if (error != null) - MessageBox.Show(String.Format("Error sychronizing repo: {0}", error)); - - if (!compileStarted) - MessageBox.Show("Could not start compilation!"); - else - MessageBox.Show("Test merges updated and compilation started!"); - } - finally - { - Enabled = true; - UseWaitCursor = false; - ApplyingPullRequestsLabel.Visible = false; - ApplyingPullRequestsProgressBar.Visible = false; - PullRequestListBox.Visible = true; - } - } - - /// - /// Called when the is clicked - /// - /// The sender of the event - /// The - void RefreshButton_Click(object sender, EventArgs e) - { - LoadPullRequests(); - } - - /// - /// Called when the is clicked. Adds the PR with the number in to or shows an error prompt if it doesn't/already exists - /// - /// The sender of the event - /// The - async void AddPRButton_Click(object sender, EventArgs e) - { - - Enabled = false; - UseWaitCursor = true; - try - { - IList pulls = null; - string error = null; - var mergedPullsTask = WrapServerOp(() => pulls = currentInterface.Repository.MergedPullRequests(out error)); - - int PRNumber; - try - { - PRNumber = Convert.ToInt32(AddPRNumericUpDown.Value); - } - catch - { - MessageBox.Show("Invalid PR number!"); - return; - } - var found = false; - var asString = PRNumber.ToString(); - foreach (var I in PullRequestListBox.Items) - if (((string)I).Split(' ')[0].Substring(1) == asString) - { - found = true; - break; - } - if (found) - { - MessageBox.Show("That PR is already in the list!"); - return; - } - - await mergedPullsTask; - if (pulls == null) - MessageBox.Show(String.Format(MergedPullsError, error)); - //get the PR in question - var PR = await client.Issue.Get(repoName, repoOwner, PRNumber); - if(PR == null ||PR.PullRequest == null) - { - MessageBox.Show("That doesn't seem to be a valid PR!"); - return; - } - InsertPullRequest(PR, true, CheckState.Unchecked); - } - finally - { - UseWaitCursor = false; - Enabled = true; - } - } - } -} diff --git a/TGS.ControlPanel/TestMergeManager.resx b/TGS.ControlPanel/TestMergeManager.resx deleted file mode 100644 index a83f34c04b..0000000000 --- a/TGS.ControlPanel/TestMergeManager.resx +++ /dev/null @@ -1,2179 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - - - AAABAAUAEBAAAAEAIABoBAAAVgAAABgYAAABACAAiAkAAL4EAAAgIAAAAQAgAKgQAABGDgAAMDAAAAEA - IACoJQAA7h4AAKCgAAABACAAqJwBAJZEAAAoAAAAEAAAACAAAAABACAAAAAAAAAEAAATCwAAEwsAAAAA - AAAAAAAAAAAAAAAAAABeTTgAWkgyA1pIMilOOyMoRjMaRz4qEMM+KhDESDUcSU07IyhaSDIpWUgxA15N - NwAAAAAAAAAAAAAAAADSzsgAPyoQAEk2HTk9KQ7ZOSUK5DciB+43Igf/OCQJ/zkkCe46JQrkPSgO2UYz - Gjk8JwwA0s3HAAAAAABVQywAbV1JAk47IytALBK6XEo0/2xcSP9lVUD/mo+C/5CEdf9jUz7/ZFQ//2BP - Ov9LOCC6SzgfK2hYRAJSPygA////AUg1HHA7JgzkOSQJ/29gTP9wYU7/6+nm/+rn5f9rXEj/U0Ap/1NB - Kv9YRzD/Z1ZC/zsmC+RINBtw////AYFzYgU/KhCxOiUK/4V4aP+hl4r/ZFQ///b19P/a1tH/c2RR/87J - w//X083/Y1M+/3BhTv8+Kg//PyoQsYFzYgVYRi8cPSkOz0MvFf/a1tH/vreu/2RTPv/08/H//Pz7//Py - 8P/+/f3/+vn5/3JjUP+Hemr/RjMa/z0pDs9XRS8cQi4VnDciB/xsXEn/9PPx/7y1rP9kUz7/9PLx//// - //////////////f29f9qW0f/oJaJ/3BhTf83Igf8Qi4VnD8qEN45JAn/saie//////+7tKv/ZFM+//Ty - 8f//////7u3q/5SJev95a1n/Y1M9/9rX0v+zq6D/OSQJ/z8qEN5EMBeFNiEF/oV4aP//////vLSr/2RT - Pv/08vH//////+7t6v+UiXr/eWtZ/2NTPv/X0s3/h3pq/zYhBf5EMBeFRzMaZTYhBfuAc2L//////7y0 - q/9kUz7/9PLx//////////////////f29f9qWkX/rqaa/4R3Zv82IQX7RzMaZUUxGIc3Igf/X085/97b - 1/+9tqz/ZFM+//Tz8f/8/Pv/8/Lw//79/f/6+fn/cWJP/5KHeP9kUz7/NyIH/0UxGIdMOSE0PikPvzkk - Cf+Lf2//in5u/2JSPf/29fT/2tbR/3JkUf/PycP/19PN/2NTPv9oWEP/OycM/z0pD79LOCA0RDEYAE89 - JSU/KxHWcGBN/1NAKf9wYU3/9vX0/+rn5P9pWkX/VEIq/1NBKv9YRzD/ZlZB/zwnDdZPPSUlRTEYAGdY - QwAAAAAAQCwSkkw5If9cSjT/bF1J/6ujl/+lm4//j4N0/4R3Z/9lVUD/YE86/0IuFP8/KxGSAAAAAGdY - QwAAAAAAQy8WAFA+JiFDLxZmPCcMnzciBvM5JAn/NyIH/zgjCP87Jgz/OCMI8zwnDZ9DLxVmTzwlIUEs - EwAAAAAAAAAAAAAAAAAAAAAAQi4VAEYyGQc+KQ+GPSgO6EEtE61CLhStPSkP6D0pDoZEMBcHQS0TAAAA - AAAAAAAAAAAAAOAHAADgBwAAgAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAB - AADAAwAAwAMAAPAPAAAoAAAAGAAAADAAAAABACAAAAAAAAAJAAATCwAAEwsAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAP///wD///8A////AP///wBVQy0AemxaA047I0NCLhTGQS0TxlE+J0aFd2cEYlI9AP// - /wD///8A////AP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF5NNwBhUDsMTToifk47 - I50+KQ+BQy8WmzwnDeg4Iwj/OCMI/z0oDupDLxacPikPgU47I51NOiF+YFA6DF1NNwAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAj4R1AB8HAABLOCBgOiUK+DYhBf82IQX/OCMI/zciB/82IQX/NiEF/zUg - Bf82IQX/NiEF/zYhBf86JQr4SzgfYB8HAACOgnMAAAAAAAAAAAAAAAAAAAAAAAAAAACMgXIACAAAAGBP - OS0/KxHVRzMa/2BPOf9eTTf/QSwT/0UyGf+Lf2//joJy/15NN/9fTjn/X085/11MNv9KNx//PysR1V9O - OC0GAAAAjIFyAAAAAAAAAAAAnpWIANrY0wFaSTIhRTIZdUAsEtc2IQb/d2lW/35xX/+Lf2//vLWs/8/K - xP/7+vr/wLqx/3NkUf9qWkb/alpG/25fS/+GeWj/ZFM+/z8qENdFMRh1WEYvIdTOyQGZjX8AbF1KAG1e - ShxFMhjMOSQJ/jciB/8zHgL/dmhV/1E/KP9oWET/+Pf2///////h3tr/UD4m/zsnDP9EMBb/QzAW/zsm - C/9BLRT/hnlp/0EtFP84Iwj+RTEYzG1dShxsXUkATDkgAE06Iig7JgzoNyIH/z4pD/9wYU3/ubKo/1RB - Kv9qWkb/+Pf3///////Hwbn/OyYL/4p+bv/c2NT/3NjT/6iek/87Jwz/e21b/1E+J/82IQb/OyYM6E06 - IihMOSAAWUcwAF1MNjs9KQ/0NSAE/2pbRv/z8vD/7evo/1JAKf9qWkb/+Pf3///////p5+T/tq6l/9vX - 0v///////////9XRy/9BLRP/in1t/25fS/81HwT/PSkP9F5NNztZRzEAAAAAAEo3H3A6JQr9NB8D/3xu - XP//////7Oro/1JAKf9qWkb/+Pf3/////////////////////////////////9XRy/9BLBP/kIR1/35w - Xv80HwP/OiUK/Uo3HnAAAAAAUkApRj4pD+M3Igf/OiUK/6iekv//////7Oro/1JAKf9qWkb/+Pf3//// - /////////////////////////////9XQy/9ALBL/k4d5/6qhlf86JQr/NyIH/z4pD+NSPyhGQy8V1jgj - CP82IQb/emxa//Tz8f//////7Oro/1JAKf9qWkb/+Pf3//////////////////f29f/x7+3/8vDu/6yj - mP83Igf/mI6A//b29P97bVv/NiEG/zgjCP9DLxXWRTEYxDgjCP82IQb/m5CD////////////7Oro/1JA - Kf9qWkb/+Pf3/////////////////6ackP9XRS7/WEYw/0IuFf91Z1T/4d7Z//////+bkIP/NiEG/zgj - CP9FMRjEUT4nXTsmDPU2IAX/XUw2/+3r6f//////7Oro/1JAKf9qWkb/+Pf3/////////////////6ac - kP9XRS7/WEYv/0IuFP92Z1T/4t/b/+7s6v9dTDb/NiAF/zsmDPVRPiddYlE7Gz8qENQ3Igb/SDUc/93a - 1f//////7Oro/1JAKf9qWkb/+Pf3//////////////////f29f/x7+3/8vDu/6yjmP83Igf/mo+B/9/c - 2P9INRz/NyIG/z8qENRhUTwbX044Jj8qEOA2IAX/WUcx/+3r6f//////7Oro/1JAKf9qWkb/+Pf3//// - /////////////////////////////9XQy/9ALBL/kod4/+/t6/9ZRzD/NiAF/z8qEOBfTjgmVEIqRjwn - DfI3IQb/TDkg/8bAuf//////7Oro/1JAKf9qWkb/+Pf3/////////////////////////////////9XR - y/9BLBP/kYV2/8nDvP9MOSH/NiEG/zwnDfJUQitGVUMsQj4pD+Q4Iwj/NiEG/047I//PysT/7uzq/1JA - Kf9qWkb/+Pf3///////p5+T/tq6l/9vX0v///////////9XRy/9CLhT/fG5c/1tKNP82IQX/OCMI/z4p - D+RVQyxCwry1Ak07I0Y/KhDUNyIH/0s4IP+akIL/jIBw/0UxGP9rW0f/+Pf3///////Fv7j/OiYL/4p+ - bv/c2NT/3NjT/6iek/87Jwz/eWpY/006Iv83Igf/PioQ1E06Ika8ta0CY1M+AEIvFQBYRjA1PCcN3lA9 - Jf96a1n/NyIH/zQeA/9qW0b/+Pf2///////e29b/TToi/zsmC/9DLxb/QzAW/zsmC/9BLRT/hnlp/0Et - FP89KA7eV0YvNUMwFgBhUDwAAAAAAG5fSwD5+PcBQCwSm0QwF/+FeGj/gnVk/3lrWf+Vinz/9fPy//// - ///39vX/ubGo/4F0Y/9yYk//alpG/25fS/+GeWj/Y1M9/zciB/9BLhSb+fr5AW5gTAAAAAAAAAAAAAAA - AABBLRMASjcfVjsmDPs7Jgv/Qy8W/047I/9aSDL/hHdm/6CWif9xYk7/cWJP/52Thv+DdWX/Xk04/11M - Nv9KNx//OCMH/zsmDPtKNx9WQS0TAAAAAAAAAAAAAAAAAAAAAABcSzUAY1I9E006ImxFMRieQCsSxzgj - CPU2IAX/NiEG/zYhBv81IAT/NSAE/zYhBv82IQb/NSAF/zciB/U+KhDHQy8Wnko3H2xiUTwTXEo0AAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAKGXiwDJxL0BalpGDT4qEHM5JQrzOCMI/zgjCP86Jgv7OyYL+zgj - CP84Iwj/OSQK8z4pD3NnV0MNt66lAZKGeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAASzggAFNBKgVBLRSBPysR60QwFrBNOyNpTzwlaUYyGbBALBLrQS0TgVA+JgVJNh0AAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAA/4H/APgAHwD4AB8A8AAPAIAAAQCAAAEAgAABAIAAAQCAAAEAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAADAMAAAwDgAAcA4AAHAPgAHwD+AH8AKAAAACAA - AABAAAAAAQAgAAAAAAAAEAAAEwsAABMLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAHlrWQCGeWkEVkQtRkUyGclEMBfJU0EpR52UhwZ7blwAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAalpHAAAAAABlVUAmcGBNQG5fTDE8Jw0dXEs1L0g1HIw9KQ7qOCMI/zgjCP8+KQ/sTToikltK - My88Jw0dbl9MMXBgTUBlVUAmAAAAAGdXQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAABaSDIAY1I9IEQwF8pBLRP5PysR7TkkCeA+KQ/rOSUK/jgjCP84Iwj/OCMI/zgj - CP85JAr+PSkP6zkkCeA/KxHtQS0T+UQwF8piUTwgWUgxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAeGpXALy2rQJKNx+MOSQK/zchBv83Igb/NyIH/zgiB/84Iwj/NyIH/zUg - Bf81IAX/NyIH/zciB/83Igf/NyIH/zciBv83Igf/OSQK/0o3Hoy6s6oCdmhWAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAANnUzwA0HwMAXk44Rz4pD+05JQr/RTEY/0g1HP9GMhn/OSQJ/zUf - BP9AKxH/e21b/3xuXP9HMxr/RjIZ/0g0HP9INBv/RzQb/0EtFP83Igf/PSgO7V5NN0czHgIA2dTPAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ2ThgAWAAAAYlE8ElRCK2BFMRfVNiEG/1xLNf+pn5T/lIl7/6ee - kv+DdmX/bl9L/763rv/5+Pf/8vHv/66mm/+Wi33/lop8/5aKfP+Wi33/mo+C/4l9bf9HNBv/Qy8W1VRC - K2BhUDsSJxAAAI+CcwAAAAAAAAAAAAAAAAByY1AAg3ZlFVE/J2lALBLFOyYM+jgjCP81IAT/a1tH/4Z5 - af83Igf/e21b//X08//39vX///////v6+f+aj4L/STYd/z4pD/89KA7/PSgO/z0pDv9DLxb/dGZT/5WK - e/8+KQ//OyYL+j8rEcVQPSVpgHJhFW5eSwAAAAAAAAAAAEw6IgBgTzpiPioQ/jgjCP84Iwj/OCMI/zMe - Av9pWUT/g3Zl/zEcAP93aVb//Pv7////////////1tLN/0UyGf83Igf/Qi4U/0MvFv9DLxb/QCwS/zci - B/83IQb/kIR1/15NN/82IAX/OCMI/z4qEP5gTzliTDkhAAAAAAAAAAAAPCgNAEMvFWk5JAn/OCMI/zgj - CP86JQr/V0Uv/66lmv+Ie2v/MRwA/3dpVv/8+/v///////////+5sqn/NSAF/0k2Hv/DvLT/1tHM/9XR - y//Mx8D/Z1dD/zMdAv92aFX/cWJP/zUgBP84Iwj/OSQJ/0MvFmk8KA0AAAAAAAAAAAAAAAAAU0AphDkk - Cf84Iwj/OCIH/4x/cP/q5+T//////4p+bv8xHAD/d2lW//z7+////////////8fBuv9oWUT/fW9d/+7s - 6v////////////////+Wi3z/Mh0B/3VmU/+YjX//NyIH/zgjCP85JAn/U0EphAAAAAAAAAAAemlYAAAA - AABPPSWYOSQJ/zgjCP87Jgv/vbat////////////in1u/zEcAP93aVb//Pv7/////////////Pz8//j3 - 9v/5+Pj//v7+/////////////////5iMf/8yHQH/d2hW/7ixp/87Jgv/OCMI/zkkCf9PPSWYAAAAAHRm - UgBcSzUAZVQ/J0EtE9M4Iwj/OCMI/z0pDv/JxL3///////////+KfW7/MRwA/3dpVv/8+/v///////// - ////////////////////////////////////////mIx+/zIdAf93aFb/xb+3/z0pD/84Iwj/OCMI/0Et - E9NjUz4nW0kzAHlrWBBJNh2oOSQJ/zgjCP82IQb/U0Ap/+He2v///////////4p9bv8xHAD/d2lW//z7 - +/////////////////////////////////////////////////+YjH7/Mh0B/3VnVP/d2dX/U0Ep/zYh - Bv84Iwj/OSQJ/0g1HKh3aVcQTjsjjDsmC/k4Iwj/NyIH/0AsEv+zq6H/////////////////in1u/zEc - AP93aVb//Pv7/////////////////////////////////////////////////4t/b/8yHAD/c2RR//v7 - +v+0raP/QCwT/zciB/84Iwj/OyYL+U47I4xFMRjoOCMH/zgjCP82IAX/hHdm//r6+f////////////// - //+KfW7/MRwA/3dpVv/8+/v///////////////////////f29f/Szsj/zcjC/87Jw/+wqJ7/Tzwk/zUf - BP+MgHD///7+//v6+v+Ed2f/NiAF/zgjCP84Iwf/RTEY6E48JJs5JAn/OCMI/zUgBf97bVv/+vn5//// - /////////////4p9bv8xHAD/d2lW//z7+///////////////////////4d7Z/1A+Jv89KQ//PioQ/zkk - Cv89KQ//nJGE/+zq5///////+vn5/3ttW/81IAX/OCMI/zkkCf9OPCSbYlI9O0AsEuk4Iwf/NyIH/0Yy - Gf/Tzsj/////////////////in1u/zEcAP93aVb//Pv7///////////////////////h3tn/UD4m/z0p - D/8+KhD/OSQJ/z0pD/+ckYT/7Oro///////Tzsj/RjIZ/zciB/84Iwf/QCwS6WJSPTuooJQFRTEYoDgj - CP84Iwj/NiEG/6KYi/////////////////+KfW7/MRwA/3dpVv/8+/v///////////////////////f2 - 9f/Szsj/zcjC/87Jw/+wqJ3/Tzwk/zUfBP+MgHD//////6KYi/82IQX/OCMI/zgjCP9FMRigqJ+TBQAA - AABOOyOLOSQJ/zgjCP84Iwj/sKid/////////////////4p9bv8xHAD/d2lW//z7+/////////////// - //////////////////////////////////+Lf2//MhwA/3NkUf/+/v7/sKid/zgjCP84Iwj/OSQJ/048 - I4sAAAAAycO7BUk1Ha04Iwj/OCIH/0ArEf/MxsD/////////////////in1u/zEcAP93aVb//Pv7//// - /////////////////////////////////////////////5iMf/8yHQH/dGVS//79/f/Mx8D/QCsR/zgi - B/84Iwj/STYdrcjEvAV8b10UQzAWzDgjCP84Iwf/PikP/62kmf/9/Pz///////////+KfW7/MRwA/3dp - Vv/8+/v/////////////////////////////////////////////////mIx+/zIdAf90ZVL/+fj3/66m - m/8+KQ//OCMH/zgjCP9EMBbMfW9eFGVVQCZBLBPhNyIH/zgjCP83Igf/Qy8V/5yShf/18/L//////4p9 - bv8xHAD/d2lW//z7+/////////////z8/P/49/b/+fj4//7+/v////////////////+YjH//Mh0B/3Rl - Uv+nnZH/Qy8W/zciB/84Iwj/NyIH/0EsE+FlVUAmhHZmDE48JIE8Jw3zOCMI/zgjCP81IAX/VkQt/9bS - zf/+/v7/in1t/zEcAP93aVb//Pv7////////////x8G6/2hZRP99b13/7uzq/////////////////5aL - fP8yHQH/cWJO/3RlUv80HwP/OCMI/zgjCP88Jw3zTTsjgYF0YwxjUj0AfG5dBE88JV4/KxHkOCMI/zUg - BP91Z1T/kIV2/3NlUv9XRS//Mx4C/3dpVv/8+/v///////////+3sKb/NSAE/0k2Hv/DvLT/1tHM/9XR - y//Mx8D/Z1dD/zMdAv92aFX/cmNP/zUgBP84Iwj/PyoQ5E48JF58blwEY1I9AAAAAAByZVEA////AF5N - OEk+KhDmNR8E/3ZnVP9tXUn/LxkA/zUfBP80HgP/d2hW//z7+////////////9LNx/9CLhX/OCMI/0Iu - FP9DLxb/Qy8W/0AsEv83Igf/NyEG/5CEdf9eTTf/NiAF/z4qEOZdTTdJ////AHBhTgAAAAAAAAAAAAAA - AACBc2MAvreuA0UxGKM1IAX/cWJP/5qQgv9pWUX/Wkgy/0g1HP9/cWD//Pv7////////////+fj4/5SJ - ev9GMhn/OyYM/zwnDf89KA7/PSkO/0MvFv90ZlP/lYp8/z4qD/84Iwj/RTEYo764sAOBdGQAAAAAAAAA - AAAAAAAAAAAAAAAAAAA6JQoASzggXDkkCfxDLxb/bF1J/4ByYf+NgXL/lop8/62kmf/g3dn///////j3 - 9v/f29f/2NTP/8/KxP/CvLT/pZyQ/5WKfP+Wi33/mo+C/4l9bf9HNBv/NyIG/zolCvxLOCBcOiUKAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAF1MNgBdTDYkQCwS4jciB/81HwT/NR8E/zciB/88Jw3/QS0T/1pI - Mv+UiHr/cGBN/0s4H/9KNx//cGBN/5aLff9fTzn/RzMa/0c0G/9BLRT/NyIH/zciBv84Iwj/QCwS4l1M - NiRdTDYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAi39wAI2AcgVWRC1lSDUcqEMvFs0/KxHqOiUK/Dgj - CP84Igf/NyEG/zUgBf81IAT/NyIH/zciB/81IAX/NSAF/zYhBv83Igf/NyIH/zolCvw+KhDqQS0UzUYy - GahUQiplkIR1BYyAcQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJuRhACzq6IDeGpYEWNT - PipCLhV5OiUK8jgjCP84Iwj/OCMI/zgjCP84Igf/OCIH/zgjCP84Iwj/OCMI/zgjCP85JAryQi4UeWFQ - OypvX0wRpJqOA4x/cQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAASzgfAFVDKwRCLhRtOiYL8jgjCP84Iwj/OiUL+0EsE+dBLRPnOyYL+zgjCP84Iwj/OiUL8kEt - E21RPicESTUdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAU0EqAGBPOgRFMhl9QS0T7Ek2HblNOiJpZFQ/MGhYQzBRPidpSzkguUIu - FOxEMRd9XEw2BFE/KAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/+B///wAA//4A - AH/8AAA//AAAP/AAAA/AAAADwAAAA8AAAAPAAAADwAAAA4AAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AACAAAABAAAAAAAAAAAAAAAAAAAAAIAAAAHgAAAH4AAAB/AAAA/wAAAP8AAAD/wAAD//gAH//8AD/ygA - AAAwAAAAYAAAAAEAIAAAAAAAACQAABMLAAATCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGxc - SACShXcEaVlFT0o3HstINBzLZFM+T4R3ZwRkUz4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AACXjH4AAAAAAGdXQyNRPiaTQCsR6zkkCf85JAn/PyoQ6048JJR+cF8v////AIl8bAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1c/OAGtcSAB0ZVMib2BMU4F0 - Y1OMgHBJSTYdIT4pDx1rW0cmXUw2dEIuFNY6JQr+OCMH/zgjCP84Iwj/OCMI/zolC/9HMxrgW0kzdWtb - RyY+KQ8dSTYdIYyAcEmBdGNTb2BMU3RlUyJrXEgAysjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfW9eAIR3 - Zw1MOSGsQS0T/0UxGP5FMRj4OSQK4zolC+FBLRPmPSkO/DgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP83Igf/PSgO/EEtE+Y6JQvhOSQK40UxGPhFMRj+QS0T/0s5IKyEd2YNfW9dAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AACgl4sAAAAAAGFQOmw9KA76NyIH/zciB/83Igf/OCMI/zgjCP84Iwf/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjB/84Iwj/OCMI/zciB/83Igf/NyIH/z0oDvpgTzpsAAAAAKKY - jAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAABXRS4Aa1tHJEQxF9U4Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zYhBf82IQX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9EMBfValpGJFZELQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAKCWiQDEvbUHVEIrmjolCv84Iwj/NiEF/zUgBP81IAT/NSAE/zUg - BP83Igf/OCMI/zgjCP82IQb/OSQJ/2BPOf9gUDr/OSQJ/zUgBP81IAT/NSAE/zUgBP81IAT/NSAE/zUg - BP81IAT/NiEG/zgjCP86JQr/VEEqmsG6sweek4cAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAl41+AODd2ARtXUleQi4U9DciB/85JAn/YE86/31v - Xf98b13/fG9d/3lrWf9KNx//NB8D/zYgBf9PPSX/n5WI/+zq5//t6+j/oZeK/21dSv94aVf/fG9d/3xv - Xf98b13/fG9d/3ttXP9wYU7/VUMs/zomC/82IQb/Qi4U9GxdSV7h3dkEl41+AAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AGtaRgCNgHAFY1I9OlNBKp9EMBbvOCMI/zgj - CP8+Kg//wbuy/7Wto/+il4v/oZeL/8nEvf+so5j/alpG/4l9bf/b19L//v79////////////7Orn/763 - rv+onpP/opiM/6OYjP+jmIz/opiM/6Sajv+wp53/w721/6ifk/9OOyP/NiEG/0QwFu9TQiqfY1M+Oo2A - cQVrW0cA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wANAAAAqKCUDlxKND9JNh2fQCwS7Tkk - Cf83Igf/OCMI/zgjCP8/KxH/wLqx/11MNv81IAT/NR8E/4+DdP/+/v7/+Pf2//v7+/////////////// - ///Qy8T/Y1I9/z0pD/84Iwj/NyIH/zciB/83Igf/NyIH/zciB/85JAn/TToh/5qPgv+2rqT/RzQb/zYh - Bv85JAn/QCwS7Uc0G59ZRzE/p52RDh4IAAD08e8AAAAAAAAAAAAAAAAAAAAAAJ+WiQCjmo0YX085sj8r - EfM4Iwj/OCMH/zgjCP84Iwj/OCMI/zgjCP8/KxH/wLqx/15NN/82IAX/NSAF/4+DdP////////////// - /////////////+zq5/9jUj3/NB8D/zgjCP84Iwj/OCIH/zgjB/84Iwf/OCMH/zgjCP84Iwj/NiEG/zsm - C/+upZr/in5u/zYgBf84Iwj/OCMH/zgjCP8/KxHzXkw3sqGXihiflIcAAAAAAAAAAAAAAAAAAAAAAIF0 - YwB/cmEtSDUc7TchBv84Iwj/OCMI/zgjCP84Iwj/OCMI/zciB/89KQ7/wLmw/15NN/82IAX/NSAF/4+D - dP///////////////////////////8C6sf88Jw3/OCMI/zkkCf8+KhD/PysR/z8rEf8/KxH/PysR/zsm - DP83Igb/OCMI/zUgBP9sXEj/tKyi/zwoDv84Iwj/OCMI/zgjCP83IQb/SDUc7YByYS2CdGQAAAAAAAAA - AAAAAAAAAAAAAFZFLgBWRC0tPysR7TgjCP84Iwj/OCMI/zgjCP83Igf/NSAE/zwoDf9rXEj/1NDK/11M - Nv82IAX/NSAF/4+DdP///////////////////////////6KYi/82IQb/NyIG/0s4IP+6s6n/y8W+/8rE - vP/KxLz/ysS9/7uzqv9hUDv/NyEG/zYhBv9RPyj/vLSr/0g0HP83Igf/OCMI/zgjCP84Iwj/PysR7VZE - LS1XRS4AAAAAAAAAAAAAAAAAAAAAAEEtEwBINBsxOSUK7zgjCP84Iwj/OCMI/zgjB/9ALBL/cGFO/7ix - p//y8O7/8fDu/1tKM/82IAX/NSAF/4+DdP///////////////////////////5aKfP8yHAH/Mx0C/089 - Jf/p5+T///////////////////////////+xqZ7/OSQK/zciBv9KNx//vreu/1NBKv83IQb/OCMI/zgj - CP84Iwj/OSUK70c0GzFBLRMAAAAAAAAAAAAAAAAAAAAAAGVUPwB+cF5eQCwS/jgiB/84Iwj/OCMI/zYg - Bf+GeWj/9/b1////////////8fDu/1tKM/82IAX/NSAF/4+DdP///////////////////////////7ix - p/93aFb/d2lX/4p+bv/x7+3///////////////////////////+/ubD/PCgN/zciBv9INBv/1tHM/4p9 - bf81IAX/OCMI/zgjCP84Igf/QCwS/n5wXl5lVD8AAAAAAAAAAAAAAAAAAAAAAFpJMwBsXEhpPysR/zgj - CP84Iwj/OCMI/zYhBv+glYj/////////////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////// - //////////////7+/v/+/f3//v79//7+/v////////////////////////////////+/uLD/PCgN/zci - Bv9HNBv/3NjT/6GYi/82IQb/OCMI/zgjCP84Iwf/PysR/2xdSWlbSTMAAAAAAAAAAAAAAAAAn5WIAAAA - AABPPSV9OiYL/zgjCP84Iwj/OCMI/zgjCP+tpZr/////////////////8fDu/1tKM/82IAX/NSAF/4+D - dP////////////////////////////////////////////////////////////////////////////// - //+/uLD/PCgN/zciBv9HNBv/3NjT/6+nnP84Iwj/OCMI/zgjCP84Iwj/OiYL/089JX0AAAAAn5SIAAAA - AAD///8AY1M+AG5fS0BFMRjnNyIH/zgjCP84Iwj/OCMI/zkkCf+8taz/////////////////8fDu/1tK - M/82IAX/NSAF/4+DdP////////////////////////////////////////////////////////////// - //////////////////+/uLD/PCgN/zciBv9HNBv/3dnU/764r/85JAn/OCMI/zgjCP84Iwj/NyIH/0Ux - GOdsXUlAYlE8AP///wB7bVsAgHJhH0s4IMY4Iwj/OCMI/zgjCP84Iwj/NyIH/0MvFf/OycP///////// - ////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////////////////////////////////////// - //////////////////////////////////+/ubD/PCgN/zciBv9HNBv/3NjT/9HMxf9DLxX/NyIH/zgj - CP84Iwj/OCMI/zgjCP9LOCDGf3JgH3ttWwCimYwNUkAomjolC/84Iwj/OCMI/zgjCP84Iwj/OCMI/5CE - df/6+vn/////////////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////////////////////// - //////////////////////////////////////////////////++t67/PCcN/zciBv9HNBv/2tbQ//38 - /P+RhXb/OCMI/zgjCP84Iwj/OCMI/zgjCP86JQv/UT8omqCWiQ1ZSDJ6PioQ9jgjB/84Iwj/OCMI/zgj - CP82IAX/bl9L/+3r6f//////////////////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////// - //////////////////////////////////////////////////////////////////+qoZX/OCMI/zci - B/9HNBv/2dXQ///////u7Or/cWJO/zYgBf84Iwj/OCMI/zgjCP84Iwf/PioP9llHMXpKNx/tNyIH/zgj - CP84Iwj/OCMI/zYhBv9UQiv/19LN////////////////////////////8fDu/1tKM/82IAX/NSAF/4+D - dP////////////////////////////////////////////7+/v/5+Pj/+fj3//n49//5+Pf/9/b1/9PO - yP9fTjj/NiEG/zUgBP9PPCT/4t/c////////////2NTP/1RCK/82IQb/OCMI/zgjCP84Iwj/NyIH/0s3 - H+1XRS+1OiUK/zgjCP84Iwj/OCMI/zUgBP9uX0z/9/b1////////////////////////////8fDu/1tK - M/82IAX/NSAF/4+DdP///////////////////////////////////////////+Th3v91Z1T/aFhE/2lZ - Rf9pWUX/ZlVB/0o3Hv83Igf/OyYL/0s4IP+flYj//Pv7////////////9/b1/25fTP81IAT/OCMI/zgj - CP84Iwj/OiUK/1dGL7VrW0hMQCsS7zgjB/84Iwj/OCMI/zciB/9FMRj/zsnC//////////////////// - ////////8fDu/1tKM/82IAX/NSAF/4+DdP///////////////////////////////////////////9vX - 0v9GMhn/NB4D/zUgBP81IAT/NSAE/zciB/82IAX/a1tH/9/c1//8+/v/////////////////zsnC/0Ux - GP83Igf/OCMI/zgjCP84Iwf/QCsR72tbR0yglooMUj8ouTgjCP84Iwj/OCMI/zgjCP81IAX/iX1t//7+ - /f//////////////////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////////////////////// - /////////////9vX0v9GMhn/NB4D/zUgBP81IAT/NSAE/zciB/82IAX/a1tH/9/c2P/8+/v///////// - ///+/f3/iX1t/zUgBf84Iwj/OCMI/zgjCP84Iwj/Uj8ouaGXiwwbAwAAZVVAYD0pD/o4Iwj/OCMI/zgj - CP82IQb/UD4m/+Dd2P//////////////////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////// - /////////////////////////////+Th3v91Z1T/aFhE/2lZRf9pWUX/ZlVB/0k2Hv83Igf/OyYL/0s4 - IP+glon//Pv7///////g3Nj/UD4m/zYhBv84Iwj/OCMI/zgjCP89KQ/6ZVVAYBsDAABtXUoAcWNPE0Et - FMY4Iwj/OCMI/zgjCP84Iwf/PSkP/8S+tv//////////////////////8fDu/1tKM/82IAX/NSAF/4+D - dP////////////////////////////////////////////7+/v/5+Pj/+fj3//n49//5+Pf/9/b1/9LO - yP9eTjj/NiEG/zUgBP9PPCT/4+Dc///////Evrb/PSkP/zgjCP84Iwj/OCMI/zgjCP9BLRTGcWJQE21d - SgCwp5wAxb63C1I/KL83Igf/OCMI/zgjCP83Igf/RzMa/9nW0P//////////////////////8fDu/1tK - M/82IAX/NSAF/4+DdP////////////////////////////////////////////////////////////// - //////////////////+qoZX/OCMI/zciB/9HNBv/2dXQ///////Z1dD/RjMa/zciB/84Iwj/OCMI/zci - B/9SQCi/xsG4C7CpngByYk8AdmdVFUUxGNU4Iwj/OCMI/zgjCP82IQb/WEYw/+vp5v////////////// - ////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////////////////////////////////////// - //////////////////////////////////++t6//PCcN/zciBv9HNBv/2dXQ///////r6eb/WEYv/zYh - Bv84Iwj/OCMI/zgjCP9FMhjVdmhVFXFjUAB+cF4Af3FgNkUxGPA3Igf/OCMI/zgjCP81IAT/b2BN//n5 - +P//////////////////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////////////////////// - //////////////////////////////////////////////////+/ubD/PCgN/zciBv9HNBv/2dXQ//// - ///5+fj/b2BN/zUgBP84Iwj/OCMI/zciB/9FMRjwf3JgNn5wXwBKNh4AXUw2Sj0oDvk4Iwj/OCMI/zgj - CP82IAX/bF1J/+7s6v//////////////////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////// - //////////////////////////////////////////////////////////////////+/uLD/PCgN/zci - Bv9HNBv/2dXQ///////v7ev/bV1J/zYgBf84Iwj/OCMI/zgjCP89KA75XUw2Sko3HgA6JgsAY1M+ejwn - Df84Iwj/OCMI/zgjCP84Iwj/OiUL/3lqWP/j4N3/////////////////8fDu/1tKM/82IAX/NSAF/4+D - dP////////////////////////////////////////////////////////////////////////////// - //+/uLD/PCgN/zciBv9HNBv/2tbR/+bk4P96bFr/OyYL/zgjCP84Iwj/OCMI/zgjCP88Jw3/ZFM+ejsm - CwAAAAAATz0ljDkkCf84Iwj/OCMI/zgjCP84Iwj/OCMI/zYhBv9hUDr/zsnC//7+/v//////8fDu/1tK - M/82IAX/NSAF/4+DdP////////////////////////////7+/v/+/f3//v79//7+/v////////////// - //////////////////+/uLD/PCgN/zciBv9JNR3/x8G5/21dSf82IAX/OCMI/zgjCP84Iwj/OCMI/zgj - CP85JAn/Tz0ljAAAAAAAAAAAcGFOREo2HtQ4Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP83Igf/emxa//Dv - 7f//////8fDu/1tKM/82IAX/NSAF/4+DdP///////////////////////////7ixp/93aFb/d2lX/4p+ - bv/x7+3///////////////////////////+/ubD/PCgN/zciBv9JNh3/urKo/047I/83IQb/OCMI/zgj - CP84Iwj/OCMI/zgjCP9JNh3Ubl9LRAAAAADU0MsAAAAAAGFRPCROOyO3OiYL/TgjB/84Iwj/OCMI/zci - B/9KNh7/x8G5/8zGv//X087/2tbR/1hHMP82IQX/NSAF/4+DdP///////////////////////////5SJ - e/8yHAH/Mx0C/089Jf/p5+T///////////////////////////+xqZ7/OSQK/zciBv9LNx//u7Oq/088 - JP83Igb/OCMI/zgjCP84Iwf/OiYL/U47I7dhUTwkAAAAANTQywAAAAAAxL61AEUxGQCAcmEUSzggiz8r - Efo4Iwj/OCMI/zciBv9PPSX/u7Oq/048JP9EMRf/UD4m/z4qEP84Iwj/NSAF/4+DdP////////////// - /////////////52Thv82IQb/NyIG/0s4IP+6s6n/y8W+/8rEvP/KxLz/ysS9/7uzqv9hUDv/NyEG/zYh - Bv9RPyj/vLWs/0g1HP83Igf/OCMI/zgjCP8/KxH6Sjcfi31vXRRDLhUAxL61AAAAAAAAAAAAAAAAAAAA - AACFeGgAnJOFCmRUP3w+KhDzOCMI/zciBv9PPCX/urKp/0k2Hf82IQb/NiEG/zgjCP84Iwj/NSAF/4+D - dP///////////////////////////7qyqf86JQr/OCMI/zkkCf8+KhD/PysR/z8rEf8/KxH/PysR/zsm - DP83Igb/OCMI/zUgBP9rXEj/tK2i/zwoDv84Iwj/OCMI/z4qEPNjUz58mY+BCoJ2ZQAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAjIBwAJyShAhJNh61OCMI/zciBv9PPSX/urKp/0c0G/80HgP/NSAF/zYh - Bv83Igf/NSAE/4+DdP///////////////////////////+Xi3/9ZSDH/NB8E/zgjCP84Iwj/OCIH/zgj - B/84Iwf/OCMH/zgjCP84Iwj/NiEG/zsmC/+tpZr/in5u/zYgBf84Iwj/OCMI/0o3HrWckoUIjIBwAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnJGEACgRAABUQituOyYL/zciB/9KNh7/x8G5/6GX - iv+BdGP/bV5K/1hHMP9KNx7/OycM/5CEdf/////////////////////////////////Fv7f/XUw2/zwo - Df83Igf/NyIH/zciB/83Igf/NyIH/zciB/85JAn/TDoh/5qPgf+2rqT/RzQb/zciB/84Iwj/OyYL/1RC - K24oEQAAnJKGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF1MNgBiUjwtQCsR6Dci - B/86JQr/ZVVA/4p+bv+imIz/same/722rf+/uK//uLGn/9XRy//5+Pj///////////////////////z8 - +//9/fz/7evo/8bAuP+so5j/p52S/6Sajv+imIz/opiM/6Sajv+wp5z/w721/6ifk/9OOyP/NiEG/zgj - CP84Iwj/QCsR6GJSPC1dTDYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHhq - VwCNgnIIRTIYszgjCP84Iwj/NiAF/zUgBP82IQb/OyYM/0MwFv9QPSb/ZFQ//3FiT/9+cF//4d3Z//f2 - 9f/c2NP/saie/31vXf98b13/rqWa/9vY0//5+Pf/4+Hd/4x/cP97blz/fG9d/3ttXP9wYU7/VUMs/zom - C/83Igb/OCMI/zgjCP84Iwj/RTIZs46Dcwh4a1gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAMG5sAAvGQAAXEo0ejsmDP83Igf/NyIH/zgjCP84Iwj/OCMI/zciB/83IQb/NSAF/zUg - BP82IQX/YE85/21eSv9JNh3/OSQJ/zUgBP81IAT/OCMI/0g0HP9rW0f/YE86/zYgBf81HwT/NSAE/zUg - BP81IAT/NiEG/zgjCP84Iwj/NyIH/zciB/87Jgz/XEs1ei4YAADEvrQAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABYRjAAbl5LLFNBKrZEMRfaQi4U9DwnDfw5JQr/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/NiEF/zUgBf83Igf/OCMI/zgjCP84Iwj/OCMI/zciB/81IAX/NiEF/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zkkCf87Jgz8QCwS9EIuFdpRPie2bl9MLFhHMQAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARTEYALGpngp6bFoaa1xIO2JS - PWRRPyiMTjsjvzolC/A4Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP86JQrwTjsjv088JIxdSzVkZFM+O3FiTxqil4sKNCAGAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAPb19AD///8BnpOGCEIuFWY7JgzwOCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zsmC/BBLRRmnZOGCP///wH29fQAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkMrAG5dSQJGMxlmPCcN7zgjCP84Iwj/OCMI/zgj - CP84Iwf/OCMI/zsmC/87Jgv/OCMI/zciB/84Iwj/OCMI/zgjCP84Iwj/PCcM70QwF2ZmVkMCUT8oAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFlHMABxYU4CSTYdZT0o - Du84Igf/OCMJ/zwnDP1AKxHmSTYdvV1MNoleTTiJTDkhvUEtE+Y8KA39OCQJ/zciB/88KA3vSDUcZWlb - RgJUQywAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AABjUj0Af3FiA007InRFMhjvTjsjw11NN3xlVUAyi39vC////wH///8BlIl7C21dSTJjUz58UT8ow0cz - Gu9MOCB0eGpWA19ONwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAD///gf//8AAP//8A///wAA//AAAA//AAD/4AAAB/8AAP/gAAAH/wAA/8AAAAP/ - AAD/gAAAAf8AAP8AAAAA/wAA/AAAAAA/AADwAAAAAA8AAOAAAAAABwAA4AAAAAAHAADgAAAAAAcAAOAA - AAAABwAA4AAAAAAHAADgAAAAAAcAAOAAAAAABwAAwAAAAAADAACAAAAAAAEAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAQAAgAAAAAABAACAAAAAAAEAAIAA - AAAAAQAAgAAAAAABAACAAAAAAAEAAIAAAAAAAQAAgAAAAAABAACAAAAAAAEAAMAAAAAAAwAA4AAAAAAH - AADwAAAAAA8AAPgAAAAAHwAA/AAAAAA/AAD8AAAAAD8AAPwAAAAAPwAA/gAAAAB/AAD+AAAAAH8AAP8A - AAAA/wAA//AAAA//AAD//AAAP/8AAP/+AAB//wAA//8AAP//AAAoAAAAoAAAAEABAAABACAAAAAAAACQ - AQATCwAAEwsAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AHNjUf9zY1H/////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8Am5CD/z4pD/84Iwj/OCMI/zsnDP+NgXL/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wDCvLT/UD0m/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/0w4IP+2raP/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wBvXkv/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/2dX - RP////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AI2Bcv87Jgv/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OSQJ/4FzYv////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8Atq2j/0k1Hf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/RjMZ/6eek/////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AY1Q//zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/X085/9DMxv////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wCNgXL/OSQJ/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/gXNi/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AKee - k/9DLxf/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9AKxL/m5CD//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8ApJuQ/3ttW/+Ie2r/kIN0/5eMfv+il4z/q6OW/7Ssov+9tq3/yMK6/9DMxv////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8AysS+/1hHMP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9YRzD/ycS9/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wDQzMb/yMK6/722rf+0rKL/q6OW/6KXjP+XjH7/kIN0/4h7 - av97bVv/pJuQ/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ADolCv84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9CLhT/Sjcf/1NA - KP9fTzn/Xk02/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9cSzX/X085/1NAKP9KNx//Qi4U/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zolCv////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AGJRPf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/X085/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AKmg - lP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP+poJT/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wBDLxf/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Qi4U//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wB6alj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP96bFr/////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDHwrn/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/8O9tf////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8AU0Ao/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9QPSb/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8AlYp9/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/5eMfv////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ADwoDf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP87Jgv/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AGpbRv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/2ZWQv////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ALKq - oP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/sqqg//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wBINRz/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0YzGf////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wCDd2X/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OSQJ/1dGLv/Iwrr/yMK6/1dGLv84Iwj/OSQJ/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/gnRk/////wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDOyML/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/QCsS/5uQg/////////////// - ////////p56T/0IuFP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP/MxsD/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8AWEcw/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/emxa////////////////////////////////////////////iHtq/zkk - Cf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/VUQt/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8An5aI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/2ZW - Qv96bFr/emxa/3psWv96bFr/emxa/3psWv96bFr/emxa/3psWv96bFr/emxa/3psWv96bFr/dGRT/0Yz - Gf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9eTTb/0MzG//// - ///////////////////////////////////////////////////QzMb/Xk02/zgjCP84Iwj/Qi4U/1xL - Nf9pWUX/dGRT/3psWv96bFr/emxa/3psWv96bFr/emxa/3psWv96bFr/emxa/3psWv96bFr/emxa/3ps - Wv96bFr/emxa/3psWv9uYEz/ZlVB/048JP86JQr/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+floj/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Av7ev/z8qEP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/6adkf////////////////////////////// - ////////////////////////////////////////////////////////RjMZ/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/0YzGf+tpJj///////////////////////////////////////// - ///////////////////////////////////KxL7///////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////8/Lxf+hl4n/Y1M+/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/PyoQ/7+3r/////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wC0rKL/V0Yu/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP////////////////////////////////////////////////////////////// - /////////////////////////////3xuXf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OiUK/4l+ - bv////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////iX5u/zsnDP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/V0Yu/7Ss - ov////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AKeek/9OPCT/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj///////// - ////////opeM/6KXjP+il4z/opeM/6KXjP+il4z/opeM/6KXjP+il4z/opeM/6KXjP/JxL3///////// - //+Ie2r/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/ZlZC//////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////8O9 - tf+2raP/qJ2S/6KXjP+il4z/opeM/6KXjP+il4z/opeM/6KXjP+il4z/opeM/6KXjP+il4z/opeM/6KX - jP+il4z/opeM/6Sajv+yqZ//wLmx/87Iwv//////////////////////////////////////altG/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Tjwk/6eek/////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8Am5CD/0c0G/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy///////////////////////Szsj/ycS9/722 - rf+2raP/wryz//////////////////////////////////////////////////////////////////// - /////////////////////////////7Cnnf92Z1X/RjMZ/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/1VDLP+Qg3T///////////////////////////+ajoH/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/RzQb/52RhP////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wCQg3T/QS0T/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////pp2R/0Yz - Gf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9tX0r//////////////////////6uhlf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/QS0T/5CEdf////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AI2Bcv8+KQ//OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj///////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////ZlVB/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zsnDP+kmo7///////// - ////////lIh5/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/PCgN/4FzYv////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AgXNi/zol - Cv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////VUMs/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/42Bcv////////////////9mVUH/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OSQJ/3Nl - Uv////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AMnEvf9sXEj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////ZlZC/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/qJ2S/////////////////zolCv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/21dSf/KxL7/////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Awry0/2NT - Pv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj///////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////pp2R/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/z0p - Dv////////////////9/cmD/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/1xLNf+2raP/////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8AubOq/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////0c0 - G/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/dGZU/////////////////zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/7mxqP////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AKmglP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - /////////////////////////////////////////////6uhlf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP////////////////9eTTb/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP+poJT/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wCZkIL/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj///////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////9lVUD/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/k4d3////////////m5CD/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/m5CD/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8AjIBx/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - ////////////////////////////////////////////////////////////////////////PCgN/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/1VELf///////////9HM - x/85JAn/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/4yAcf////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AHtuXP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////0MzG/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/////////////////OSQJ/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9/cmD/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wBvXkv/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj///////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - /////////////6adkf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/PCgN/21d - Sf9tXUn/bV1J/21dSf9tXUn/bV1J/21dSf9tXUn/bV1J/21dSf9tXUn/bV1J/21dSf9tXUn/bV1J/21d - Sf9tXUn/YlI8/0MvF/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/8S+t////////////1dGLv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/b15L/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8AX085/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/SDUc/////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////+PgnT/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0g1HP////////////////////////////// - ////////////////////////////////////////////////////////////////////////wbux/1E+ - J/85JAn/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+onZL///////// - //9wYU3/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/19POf////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AFA+Jv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9QPSb/q6OW//////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////gnRk/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9INRz///////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////U0Ao/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/mo6B////////////hXdn/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9QPib/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wBCLhT/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/1pIMf+2raP///////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - /////////////3ZnVf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/SDUc//// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////8jCuv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv///////////5CEdf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Qi4U/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8AOCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/X043/7ato////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////9pWkX/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0g1HP////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////UT4n/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+Bc2L///////// - //+YjYD/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////ADgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9wYU3/zsjC//////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////aFdD/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9INRz///////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////3doVf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV////////////nZGE/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wA4Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OiUK/3xuXf////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - /////////////2hXQ/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/SDUc//// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+DeGb/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf///////////7KqoP86JQr/OSQJ/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wDPycL/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/RjMZ//////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////9oV0P/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0g1HP////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////0UxGP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP/PycL/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8AwLmx/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/1tKNf////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////aFdD/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9INRz///////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////9bSTT/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/wLmx/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ALKpn/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9qW0b///////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf//////////////////////aVpF/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/7KqoP////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wCimY3/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/empY//////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////3doVf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+kmo7/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8AlIh5/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/4d7a/////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////+GeWj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/lIp7/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AIV3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP+XjH7///////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf//////////////////////lot8/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/4Z5aP////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wB1aFT/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/pp2R//////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////6SbkP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8AZ1dE/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/7Ssov////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////+yqqD/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/Z1dE/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AEYzGf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP/CvLT///////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf//////////////////////wry0/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0YzGf////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AG9eS/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/0czH//////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////9DMxv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/altG/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AJuQ - g/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI//////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV////////////////////////////OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP+bkIP/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AMzGwP86JQr/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf///////////////////////////zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OSQJ/8jC - uv////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wBMOCD/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP85JAn///////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - //////////////////85JAn/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9JNR3/////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wBsXEj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/RTEY//////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV////////////////////////////QzAW/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/2lZRf////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wCXjH7/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/1RC - Kv////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf///////////////////////////1NAKP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/mI2A/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wDGwLj/OSQJ/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+elIb///////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - //////////////////+elIb/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zkkCf/GwLj/////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8ARzQb/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zkkCf9sXEj///////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV/////////////////////////////////25g - TP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/RjMZ/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8AZlZC/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9KNx////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf//////////////////////////////////////TToi/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9iUT3/////AP///wD///8A////AP///wD///8A////AP///wD///8Ak4d3/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP87Jwz/zsjC//////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////g3dl/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////////////////////9POyP88KA3/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/5KH - eP////8A////AP///wD///8A////AP///wD///8Aw762/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/opmN//////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////3doVf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////////////////// - ////////opeM/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/wLmx/////wD///8A////AP// - /wD///8A////AEYzGf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/cWJP//////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////9iUjz/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf////////////////////////////////////////////////92Z1X/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/0UxGP////8A////AP///wD///8A////AGNUP/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/Tjwk//////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////PCgN/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////////////////////////////////////1E+J/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/YVA7/////wD///8A////AI2Bcv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OycM/9DM - xv////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////ubGo/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////////////////// - ////////////////////////PCgN/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+PgnT/////AMK8 - tP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/6adkf////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////1pIMf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3tuXP///////////////////////////////////////////////////////////6ad - kf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/7+3r/9TQCj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////4l8 - bP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+QhHX///////// - ////////////////////////////////////////////////////////d2hV/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9TQCj/lYp9/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/1A9Jv////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////3tuXP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/wryz//////////////////////////////////// - //////////////////////////////////9TQCj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/l4x+//// - /wA7Jwz/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //+5s6r/hnlo/0ArEv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/UT4n//////////////////////////////////////////////////////////////////// - /////////////zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OyYL/////wD///8Aemxa/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/w721//////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////8O9tf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OiUK/8nEvf////////////// - /////////////////////////////////////////////////////////////8K8tP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3ttW/////8A////AM7Iwv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/29eS/////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ///////////////////DvbX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/QCsS/7u1rP////////////////////////////////////////////// - //////////////////////////////////9tX0r/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP/Py8X/////AP// - /wD///8AYU86/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj///////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////w721/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9HNBv/hnlo//// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9eTTb/////AP///wD///8A////ALSsov84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/4p+cP////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////8O9tf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/XEs1/722rf////////////////////////////////////////////// - ////////////////////////////////////////////////////////iX5u/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/tKyi/////wD///8A////AP///wD///8ASTYe/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP8/KhD///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ///////////////////DvbX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/3xu - Xf////////////////////////////////////////////////////////////////////////////// - /////////////////////////////0EtE/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/SDUc/////wD///8A////AP// - /wD///8A////AJiNgP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/6adkf////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////w721/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP98bl3///////////////////////// - /////////////////////////////////////////////////////////////////////////////6Sb - kP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/5qOgf////8A////AP///wD///8A////AP///wD///8AOycM/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9QPib///////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////8O9tf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/fG5d//////////////////////////////////////////////////// - //////////////////////////////////////////////////9TQCj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zwo - Df////8A////AP///wD///8A////AP///wD///8A////AHxuXf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/8G7 - sf////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ///////////////////DvbX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/3xu - Xf////////////////////////////////////////////////////////////////////////////// - ///////////////////AubH/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP98bl3/////AP///wD///8A////AP// - /wD///8A////AP///wDSzsj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9sXEj///////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////w721/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9cSzX/vbat//////////////////// - ////////////////////////////////////////////////////////////////////////aVpF/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/087I/////wD///8A////AP///wD///8A////AP///wD///8A////AGJS - PP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI//////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////8O9tf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/SDUc/4d7a/////////////////////////////// - /////////////////////////////////////////////zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/X085//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wC2r6T/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP+He2v///////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ///////////////////DvbX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/QS0T/722rf////////////////////////////////////////////// - /////////////4Z6af84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/7avpP////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AEo3H/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/QCsS//////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////w721/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP87Jwz/ysS+//////////////////////////////////////////////////////9AKxL/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/0k2Hv////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wCbkIP/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+kmo7///////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////ubGo/4J0ZP8/KhD/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/1NAKP////////////// - //////////////////////////////////+il4z/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+bkIP/////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ADwoDf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/bmBM//////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////3RmVP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/wry0//////////////////////////////////// - ////////bmBM/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP88KA3/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wB/cmD/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/42Bcv////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////g3hm/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/5GGdv///////////////////////////////////////////4yAcf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/f3Fh/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////ADgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+vp5z///////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //9XRi7/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP97blz///////// - //////////////////////////////////+tpJj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wA4Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/0MzG//////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////ubOq/zkkCf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////////////////// - ////////zsjC/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wDMxsD/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OSQJ//////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////89KQ7/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf////////////////////////////////////////////////84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/zcjB/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8Aq6GV/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/0w4IP////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////Y1Q//zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - ////////////////////////////////////////Sjcf/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/6ujlv////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AIl8bP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9tX0r///////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////3doVf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////////////////// - /////////////2xcSP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+Jfm7/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wBoV0P/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/joRz//////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+DeGb/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf////////////////////////////////////////////////+NgXL/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/Z1dE/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8ARzQb/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/7Cnnf////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - ////////////////////////////////////////r6ec/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0YzGf////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ADgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP/RzMf///////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////////////////// - /////////////9DMxv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AMrEvv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj///////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf//////////////////////////////////////////////////////OSQJ/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/8rGv/////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wCpoJT/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/TToi//////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////////////////////////////////////0w4IP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+poJT/////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AiHtq/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/29e - S/////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////////////////// - //////////////////9tX0r/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/h3tr/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AGZVQf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+Qg3T///////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf//////////////////////////////////////////////////////joRz/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/2ZWQv////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wBGMxn/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/qJ2S//////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////////////////////////////////////6idkv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9GMxn/////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AOCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/z0p - Dv+2r6T///////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////////////////// - /////////////722rf9AKxL/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8AycS9/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/5SKe/////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf///////////////////////////////////////////6KXjP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP/KxL7/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AKee - k/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/cGFN//////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////////////////////3VoVP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/qaCU//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wCGemn/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9UQir///////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV/////////////////////////////////1hH - MP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/4h7av////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8AZVVA/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0Iu - FP/CvLP///////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf//////////////////////x8C4/0UxGP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9mVUH/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AEUx - GP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OiUK/6KXjP////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - ////////p56T/zolCv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/QzAW//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wA4Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/fnFf//////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV////////////q6OW/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP////8A////AP///wD///8A////AP// - /wD///8A////AP///wDIwrr/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9fTjf///////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf///////////52RhP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/yMK6/////wD///8A////AP///wD///8A////AP///wD///8A////AHxu - Xf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0k2 - Hv/Py8X//////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - //+dkYT/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/fG5d//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Ap56T/zolCv84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Tjwk//////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////Z1dE/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9INRz///////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV////////////nZGE/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/m5CD/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wDEvrf/Qy8X/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9RPif/hnlo/3xuXf////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - /////////////2VVQP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/SDUc//// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf///////////52RhP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9DLxf/xL63/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wBVRC3/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9QPSb///////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////9nV0T/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0g1HP////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////g3hm/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - //+dkYT/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9VRC3/////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AHNj - Uf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////ZVVA/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9INRz///////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////3doVf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV////////////nZGE/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9zY1H/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Alot8/zgjCP85JAn/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/52RhP///////////8G7sf+7s6v///////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - /////////////2dXRP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/SDUc//// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////9QPSb/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/4FzYv///////////5iNgP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+Wi3z/////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wC7s6v/PyoQ/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+dkYT///////// - //93aFX/OCMI/zgjCP84Iwj/RjMZ/2JSPP98bl3/mI2A/7Ssov/OyML//////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////9pWUX/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0g1HP////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ///JxL3/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //+QhHX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/z4pD/+2raP/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wBPPCX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/nZGE////////////d2hV/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////dGZU/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9INRz///////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////U0Ao/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/mo6B////////////hXdn/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/088 - Jf////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AGpb - Rv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/52RhP///////////3doVf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - /////////////4J0ZP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/SDUc//// - //////////////////////////////////////////////////////////////////////////////// - ///////////////////CvLT/U0Ao/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/6idkv///////////3BhTf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/2ZVQf////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AiX5u/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+dkYT///////// - //93aFX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////+OhHP/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zwoDf9tXUn/bV1J/21dSf9tXUn/bV1J/21d - Sf9tXUn/bV1J/21dSf9tXUn/bV1J/21dSf9tXUn/bV1J/21dSf9tXUn/bV1J/2JRPf9FMRj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP/Dvrb///////// - //9XRi7/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/4l+bv////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wCvp5z/PCgN/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/nZGE////////////d2hV/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////raSY/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/////////////////OyYL/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OyYL/6mglP////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AM/Lxf9JNh7/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/52RhP///////////3doVf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////85JAn/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/VUMs////////////0MzG/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/STYe/8/Lxf////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AFVE - Lf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+dkYT///////// - //93aFX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - ////////////////////////////////////////////////////////////////////////Qi4U/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/5GGdv///////////5uQ - g/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/VUQt//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wCpoJT/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/nZGE////////////d2hV/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - /////////////////////////////////////////////39yYP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP////////////////9fTzn/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/6mglP////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////ADgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/52RhP///////////3doVf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - ///////////////////OyML/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9zZVL/////////////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wBiUT3/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+dkYT///////// - //93aFX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////3Bh - Tf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP89KQ7/////////////////gXNi/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9jVD//////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8ApJqO/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/nZGE////////////d2hV/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////Qy8X/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/pp2R/////////////////zolCv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/pJuQ/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wA4Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/52RhP///////////3doVf84Iwj/OSQJ/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////8rEvv8+KQ//OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//// - /////////////2ZWQv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8AX043/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+Sh3j///////// - ////////wry0/6adkf+JfGz/bV1J/1A9Jv85JAn/OCMI/zgjCP84Iwj/OCMI/zgjCP85JAn/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////z8nC/0k2Hv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP87Jgv/opeM/////////////////5aLfP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/19ON/////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AJ+WiP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/ZVVA//////////////////////////////////// - ///////////////////AubH/pJqO/4Z6af9qW0b/TToi/zgjCP84Iwj/OCMI/zkkCf84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////hnlo/zsm - C/84Iwj/OCMI/zkkCf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9qW0b//////////////////////6ujlv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+floj/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8AOCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP+PgnT///////////////////////////////////////////////////////// - /////////////////////////////722rf+gl4v/g3hm/2hXQ/9JNh7/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////npSG/2lZRf8/KhD/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/VUMs/4+CdP///////////////////////////5uQ - g/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AFpIMf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP8+KQ//W0k0/3doVf+Uinv/sKme/87Iwv////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////AubH/sqqg/6adkf+il4z/opeM/6KX - jP+il4z/opeM/6KXjP+il4z/opeM/6KXjP+il4z/opeM/6KXjP+il4z/opeM/6KXjP+imY3/sKme/7+3 - r//OyML//////////////////////////////////////2xcSP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Wkgx/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wCZkIL/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/0EtE/9eTTb/emxa/5eMfv+0rKL/0czH//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////jIBx/zwoDf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/5mQgv////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////ADgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/Qy8X/2FPOv98bl3/mZCC/7ewpv////////////////////////////// - //+2r6T///////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////0MzG/6KZjf9lVUD/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wBUQir/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9GMxn/Y1M+/3VoVP9INRz/OCMI/6ujlv////////////// - /////////////////////////////////////////////////////////////62kmP9xYk//PyoQ/z8q - EP9xYk//raSY//////////////////////////////////////////////////////////////////// - ////////sKme/3psWv96bFr/emxa/3psWv96bFr/emxa/3psWv96bFr/emxa/3psWv96bFr/bmBM/2ZV - Qf9OPCT/OyYL/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9UQir/////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AlIh5/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/qJ2S//////////////////////////////////// - //////////////////+il4z/ZlVB/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/WEcw/5SK - e//Py8X/////////////////////////////////////////////////raSY/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/lot8/////wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wA5JAn/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP+elIb////////////////////////////CvLT/iHtq/006Iv84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0c0G/96bFr/tq2j//// - ////////////////////////pJqO/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8ATzwl/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/52R - hP//////r6ec/3NlUv89KQ7/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP87Jgv/ZlVB/6KXjP//////opmN/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/048JP////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AI+C - dP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP+Qg3T/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wDOyML/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/0MzG//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AEw4IP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Sjcf/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wCKfnD/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/4yAcf////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDCvLT/m5CD/3Nl - Uv9JNR3/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Qy8X/2ZVQf+NgXL/tq2j/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDCvLT/m5CD/3Nl - Uv9JNh7/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9DLxf/ZlVB/42B - cv+2raP/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDCvLT/m5CD/3Nl - Uv9JNh7/OCMI/zgjCP84Iwj/OCMI/zkkCf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP85JAn/OCMI/zgj - CP84Iwj/OCMI/0MwFv9mVUH/jYFy/7ato/////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDAubH/mI2A/3Bh - Tf9CLhT/OSQJ/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Qi4U/3BhTf+YjYD/wLmx/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AEw4IP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/STYe/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8ATToi/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Sjcf/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wBRPif/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/Tjwk/////wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AFNA - KP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Tzwl//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AVEIq/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/UD0m/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wBYRzD/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/VEIq/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AFpIMf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/VUQt/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AW0k0/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/V0Yu/////wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wBcTDb/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/QCsS/3ZnVf+wqZ7/sKme/3ZnVf9AKxL/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/XEw2//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AGFQO/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0k2Hv+Bc2L/vbat/////wD///8A////AP// - /wD///8A////AMnEvf+NgXL/Tzwl/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/XEs1/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8AYlE9/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zkkCf84Iwj/OCMI/zkk - Cf9fTzn/m5CD/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8Ap56T/21dSf87Jgv/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/X043/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wBjVD//OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP8/KhD/c2VS/6+nnP////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AL22 - rf+Bc2L/Qy8X/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/X085/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AGpbRv84Iwj/OCMI/zgjCP84Iwj/UD4m/4h7 - av/CvLT/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDPy8X/lIp7/1dG - Lv84Iwj/OCMI/zgjCP84Iwj/ZlVB/////wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8AbV9K/19POf+bkIP/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AKeek/9tXUn/bFtH//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - //////////5/////////////////////////+B/////////////////////////gB/////////////// - /////////8AD////////////////////////AAD///////////////////////wAAD////////////// - ////////+AAAD//////////////////////gAAAH/////////////////////4AAAAH///////////// - ////wAf+AAAAAH/gA//////////////AAAAAAAAAAAAD/////////////4AAAAAAAAAAAAH///////// - ////AAAAAAAAAAAAAP////////////8AAAAAAAAAAAAA/////////////gAAAAAAAAAAAAB///////// - ///8AAAAAAAAAAAAAD////////////wAAAAAAAAAAAAAP///////////+AAAAAAAAAAAAAAf//////// - ///4AAAAAAAAAAAAAB////////////AAAAAAAAAAAAAAD///////////4AAAAAAAAAAAAAAH//////// - ///gAAAAAAAAAAAAAAf//////////8AAAAAAAAAAAAAAA///////////gAAAAAAAAAAAAAAB//////// - //+AAAAAAAAAAAAAAAH//////////wAAAAAAAAAAAAAAAP/////////+AAAAAAAAAAAAAAAAf/////// - //gAAAAAAAAAAAAAAAAf////////4AAAAAAAAAAAAAAAAAf///////+AAAAAAAAAAAAAAAAAAf////// - /gAAAAAAAAAAAAAAAAAAf//////4AAAAAAAAAAAAAAAAAAAf/////+AAAAAAAAAAAAAAAAAAAAf///// - AAAAAAAAAAAAAAAAAAAAAP////wAAAAAAAAAAAAAAAAAAAAAP///+AAAAAAAAAAAAAAAAAAAAAAf///4 - AAAAAAAAAAAAAAAAAAAAAB////gAAAAAAAAAAAAAAAAAAAAAH///+AAAAAAAAAAAAAAAAAAAAAAf///4 - AAAAAAAAAAAAAAAAAAAAAB////gAAAAAAAAAAAAAAAAAAAAAH///+AAAAAAAAAAAAAAAAAAAAAAf///4 - AAAAAAAAAAAAAAAAAAAAAB////gAAAAAAAAAAAAAAAAAAAAAH///+AAAAAAAAAAAAAAAAAAAAAAf///4 - AAAAAAAAAAAAAAAAAAAAAB////gAAAAAAAAAAAAAAAAAAAAAH///8AAAAAAAAAAAAAAAAAAAAAAP///w - AAAAAAAAAAAAAAAAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAD///8AAAAAAAAAAAAAAAAAAAAAAP///w - AAAAAAAAAAAAAAAAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAD///8AAAAAAAAAAAAAAAAAAAAAAP///w - AAAAAAAAAAAAAAAAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAD///4AAAAAAAAAAAAAAAAAAAAAAH///A - AAAAAAAAAAAAAAAAAAAAAAP//4AAAAAAAAAAAAAAAAAAAAAAAf//gAAAAAAAAAAAAAAAAAAAAAAB//8A - AAAAAAAAAAAAAAAAAAAAAAD//gAAAAAAAAAAAAAAAAAAAAAAAH/8AAAAAAAAAAAAAAAAAAAAAAAAP/wA - AAAAAAAAAAAAAAAAAAAAAAA/+AAAAAAAAAAAAAAAAAAAAAAAAB/wAAAAAAAAAAAAAAAAAAAAAAAAD+AA - AAAAAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAAAAAAAfAAAAAAAAAAAAAAAAAAAAAAAAAA4AA - AAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAYAA - AAAAAAAAAAAAAAAAAAAAAAABwAAAAAAAAAAAAAAAAAAAAAAAAAPAAAAAAAAAAAAAAAAAAAAAAAAAA+AA - AAAAAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAD/AA - AAAAAAAAAAAAAAAAAAAAAAAP8AAAAAAAAAAAAAAAAAAAAAAAAA/4AAAAAAAAAAAAAAAAAAAAAAAAH/gA - AAAAAAAAAAAAAAAAAAAAAAAf/AAAAAAAAAAAAAAAAAAAAAAAAD/8AAAAAAAAAAAAAAAAAAAAAAAAP/4A - AAAAAAAAAAAAAAAAAAAAAAB//gAAAAAAAAAAAAAAAAAAAAAAAH//AAAAAAAAAAAAAAAAAAAAAAAA//8A - AAAAAAAAAAAAAAAAAAAAAAD//gAAAAAAAAAAAAAAAAAAAAAAAH/+AAAAAAAAAAAAAAAAAAAAAAAAf/4A - AAAAAAAAAAAAAAAAAAAAAAB//gAAAAAAAAAAAAAAAAAAAAAAAH/+AAAAAAAAAAAAAAAAAAAAAAAAf/4A - AAAAAAAAAAAAAAAAAAAAAAB//AAAAAAAAAAAAAAAAAAAAAAAAD/8AAAAAAAAAAAAAAAAAAAAAAAAP/wA - AAAAAAAAAAAAAAAAAAAAAAA//AAAAAAAAAAAAAAAAAAAAAAAAD/8AAAAAAAAAAAAAAAAAAAAAAAAP/wA - AAAAAAAAAAAAAAAAAAAAAAA/+AAAAAAAAAAAAAAAAAAAAAAAAB/4AAAAAAAAAAAAAAAAAAAAAAAAH/gA - AAAAAAAAAAAAAAAAAAAAAAAf+AAAAAAAAAAAAAAAAAAAAAAAAB/4AAAAAAAAAAAAAAAAAAAAAAAAH/gA - AAAAAAAAAAAAAAAAAAAAAAAf8AAAAAAAAAAAAAAAAAAAAAAAAA/4AAAAAAAAAAAAAAAAAAAAAAAAH/wA - AAAAAAAAAAAAAAAAAAAAAAA//gAAAAAAAAAAAAAAAAAAAAAAAH//gAAAAAAAAAAAAAAAAAAAAAAB///A - AAAAAAAAAAAAAAAAAAAAAAP//+AAAAAAAAAAAAAAAAAAAAAAB///8AAAAAAAAAAAAAAAAAAAAAAP///8 - AAAAAAAAAAAAAAAAAAAAAD////4AAAAAAAAAAAAAAAAAAAAAf////wAAAAAAAAAAAAAAAAAAAAD///// - gAAAAAAAAAAAAAAAAAAAAf/////AAAAAAAAAAAAAAAAAAAAD//////AAAAAAAAAAAAAAAAAAAA////// - 8AAAAAAAAAAAAAAAAAAAD//////4AAAAAAAAAAAAAAAAAAAf//////gAAAAAAAAAAAAAAAAAAB////// - +AAAAAAAAAAAAAAAAAAAH//////8AAAAAAAAAAAAAAAAAAA///////wAAAAAAAAAAAAAAAAAAD////// - /AAAAAAAAAAAAAAAAAAAP//////+AAAAAAAAAAAAAAAAAAB///////4AAAAAAAAAAAAAAAAAAH////// - /gAAAAAAAAAAAAAAAAAAf///////AAAAAAAAAAAAAAAAAAD///////8AAAAAAAAAAAAAAAAAAP////// - /wAAAAAAAAAAAAAAAAAA////////gAAAAAAAAAAAAAAAAAH///////+AAAAAAAAAAAAAAAAAAf////// - /4AAAAAAAAAAAAAAAAAB////////gAAAAAAAAAAAAAAAAAH////////AAAAAAAAAAAAAAAAAA/////// - /8AAAAAAAAAAAAAAAAAD////////8AAAAAAAAAAAAAAAAA//////////gAAAAAAAAAAAAAAB//////// - ///8AAAAAAAAAAAAAD/////////////gAAAAAAAAAAAH//////////////4AAAAAAAAAAH////////// - /////wAAAAAAAAAA////////////////gAAAAAAAAAH////////////////AAAAAAAAAA/////////// - /////+AAAAAAAAAH////////////////8AAAAAAAAA/////////////////4AAAAAAAAH/////////// - //////wAAAAAAAA//////////////////gAAAAAAAH//////////////////AAAH4AAA//////////// - //////+AAH/+AAH//////////////////8AD///AA///////////////////4B////gH//////////// - ///////x/////4////////// - - - \ No newline at end of file diff --git a/TGS.ControlPanel/packages.config b/TGS.ControlPanel/packages.config deleted file mode 100644 index 346d66fcc6..0000000000 --- a/TGS.ControlPanel/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/TGS.ControlPanel/tgs.ico b/TGS.ControlPanel/tgs.ico deleted file mode 100644 index 6ed69fadfc..0000000000 Binary files a/TGS.ControlPanel/tgs.ico and /dev/null differ diff --git a/TGS.Installer.UI/FodyWeavers.xml b/TGS.Installer.UI/FodyWeavers.xml deleted file mode 100644 index c6e1b7c8af..0000000000 --- a/TGS.Installer.UI/FodyWeavers.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/TGS.Installer.UI/Main.Designer.cs b/TGS.Installer.UI/Main.Designer.cs deleted file mode 100644 index 0ed1cf9dec..0000000000 --- a/TGS.Installer.UI/Main.Designer.cs +++ /dev/null @@ -1,227 +0,0 @@ -using System; -using System.Diagnostics; -using System.Reflection; - -namespace TGS.Installer.UI -{ - partial class Main - { - /// - /// Required designer variable. - /// - private System.ComponentModel.IContainer components = null; - - /// - /// Clean up any resources being used. - /// - /// true if managed resources should be disposed; otherwise, false. - protected override void Dispose(bool disposing) - { - if (disposing && (components != null)) - { - components.Dispose(); - CleanTempDir(); - } - base.Dispose(disposing); - } - - #region Windows Form Designer generated code - - /// - /// Required method for Designer support - do not modify - /// the contents of this method with the code editor. - /// - private void InitializeComponent() - { - System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(Main)); - this.ProgressBar = new System.Windows.Forms.ProgressBar(); - this.DeskShortcutsCheckbox = new System.Windows.Forms.CheckBox(); - this.StartShortcutsCheckbox = new System.Windows.Forms.CheckBox(); - this.InstallingLabel = new System.Windows.Forms.Label(); - this.PathTextBox = new System.Windows.Forms.TextBox(); - this.SelectPathButton = new System.Windows.Forms.Button(); - this.label1 = new System.Windows.Forms.Label(); - this.VersionLabel = new System.Windows.Forms.Label(); - this.InstallButton = new System.Windows.Forms.Button(); - this.ShowLogCheckbox = new System.Windows.Forms.CheckBox(); - this.TargetVersionLabel = new System.Windows.Forms.Label(); - this.InstallCancelButton = new System.Windows.Forms.Button(); - this.SuspendLayout(); - // - // ProgressBar - // - this.ProgressBar.Location = new System.Drawing.Point(11, 119); - this.ProgressBar.Name = "ProgressBar"; - this.ProgressBar.Size = new System.Drawing.Size(386, 22); - this.ProgressBar.TabIndex = 0; - // - // DeskShortcutsCheckbox - // - this.DeskShortcutsCheckbox.AutoSize = true; - this.DeskShortcutsCheckbox.Checked = true; - this.DeskShortcutsCheckbox.CheckState = System.Windows.Forms.CheckState.Checked; - this.DeskShortcutsCheckbox.Font = new System.Drawing.Font("Verdana", 12F); - this.DeskShortcutsCheckbox.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.DeskShortcutsCheckbox.Location = new System.Drawing.Point(12, 61); - this.DeskShortcutsCheckbox.Name = "DeskShortcutsCheckbox"; - this.DeskShortcutsCheckbox.Size = new System.Drawing.Size(214, 22); - this.DeskShortcutsCheckbox.TabIndex = 2; - this.DeskShortcutsCheckbox.Text = "Add Desktop Shortcuts"; - this.DeskShortcutsCheckbox.UseVisualStyleBackColor = true; - // - // StartShortcutsCheckbox - // - this.StartShortcutsCheckbox.AutoSize = true; - this.StartShortcutsCheckbox.Checked = true; - this.StartShortcutsCheckbox.CheckState = System.Windows.Forms.CheckState.Checked; - this.StartShortcutsCheckbox.Font = new System.Drawing.Font("Verdana", 12F); - this.StartShortcutsCheckbox.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.StartShortcutsCheckbox.Location = new System.Drawing.Point(289, 61); - this.StartShortcutsCheckbox.Name = "StartShortcutsCheckbox"; - this.StartShortcutsCheckbox.Size = new System.Drawing.Size(236, 22); - this.StartShortcutsCheckbox.TabIndex = 3; - this.StartShortcutsCheckbox.Text = "Add Start Menu Shortcuts"; - this.StartShortcutsCheckbox.UseVisualStyleBackColor = true; - // - // InstallingLabel - // - this.InstallingLabel.AutoSize = true; - this.InstallingLabel.Font = new System.Drawing.Font("Verdana", 12F); - this.InstallingLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.InstallingLabel.Location = new System.Drawing.Point(8, 37); - this.InstallingLabel.Name = "InstallingLabel"; - this.InstallingLabel.Size = new System.Drawing.Size(52, 18); - this.InstallingLabel.TabIndex = 1; - this.InstallingLabel.Text = "Path:"; - // - // PathTextBox - // - this.PathTextBox.Location = new System.Drawing.Point(66, 35); - this.PathTextBox.Name = "PathTextBox"; - this.PathTextBox.ReadOnly = true; - this.PathTextBox.Size = new System.Drawing.Size(413, 20); - this.PathTextBox.TabIndex = 4; - // - // SelectPathButton - // - this.SelectPathButton.Location = new System.Drawing.Point(485, 35); - this.SelectPathButton.Name = "SelectPathButton"; - this.SelectPathButton.Size = new System.Drawing.Size(40, 20); - this.SelectPathButton.TabIndex = 6; - this.SelectPathButton.Text = "..."; - this.SelectPathButton.UseVisualStyleBackColor = true; - this.SelectPathButton.Click += new System.EventHandler(this.SelectPathButton_Click); - // - // label1 - // - this.label1.AutoSize = true; - this.label1.Font = new System.Drawing.Font("Verdana", 12F); - this.label1.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.label1.Location = new System.Drawing.Point(8, 9); - this.label1.Name = "label1"; - this.label1.Size = new System.Drawing.Size(155, 18); - this.label1.TabIndex = 7; - this.label1.Text = "Detected Version:"; - // - // VersionLabel - // - this.VersionLabel.AutoSize = true; - this.VersionLabel.Font = new System.Drawing.Font("Verdana", 12F); - this.VersionLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.VersionLabel.Location = new System.Drawing.Point(169, 9); - this.VersionLabel.Name = "VersionLabel"; - this.VersionLabel.Size = new System.Drawing.Size(228, 18); - this.VersionLabel.TabIndex = 8; - this.VersionLabel.Text = "Unknown (No service running)"; - // - // InstallButton - // - this.InstallButton.Location = new System.Drawing.Point(400, 89); - this.InstallButton.Name = "InstallButton"; - this.InstallButton.Size = new System.Drawing.Size(125, 22); - this.InstallButton.TabIndex = 9; - this.InstallButton.Text = "Install"; - this.InstallButton.UseVisualStyleBackColor = true; - this.InstallButton.Click += new System.EventHandler(this.InstallButton_Click); - // - // ShowLogCheckbox - // - this.ShowLogCheckbox.AutoSize = true; - this.ShowLogCheckbox.Font = new System.Drawing.Font("Verdana", 12F); - this.ShowLogCheckbox.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.ShowLogCheckbox.Location = new System.Drawing.Point(289, 90); - this.ShowLogCheckbox.Name = "ShowLogCheckbox"; - this.ShowLogCheckbox.Size = new System.Drawing.Size(105, 22); - this.ShowLogCheckbox.TabIndex = 10; - this.ShowLogCheckbox.Text = "Show Log"; - this.ShowLogCheckbox.UseVisualStyleBackColor = true; - // - // TargetVersionLabel - // - this.TargetVersionLabel.AutoSize = true; - this.TargetVersionLabel.Font = new System.Drawing.Font("Verdana", 12F); - this.TargetVersionLabel.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(248)))), ((int)(((byte)(248)))), ((int)(((byte)(242))))); - this.TargetVersionLabel.Location = new System.Drawing.Point(8, 90); - this.TargetVersionLabel.Name = "TargetVersionLabel"; - this.TargetVersionLabel.Size = new System.Drawing.Size(132, 18); - this.TargetVersionLabel.TabIndex = 11; - this.TargetVersionLabel.Text = "Target Version: v" + FileVersionInfo.GetVersionInfo(Assembly.GetExecutingAssembly().Location).FileVersion; - // - // InstallCancelButton - // - this.InstallCancelButton.Enabled = false; - this.InstallCancelButton.Location = new System.Drawing.Point(400, 119); - this.InstallCancelButton.Name = "InstallCancelButton"; - this.InstallCancelButton.Size = new System.Drawing.Size(125, 22); - this.InstallCancelButton.TabIndex = 12; - this.InstallCancelButton.Text = "Cancel"; - this.InstallCancelButton.UseVisualStyleBackColor = true; - this.InstallCancelButton.Click += new System.EventHandler(this.CancelButton_Click); - // - // Main - // - this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); - this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; - this.BackColor = System.Drawing.Color.FromArgb(((int)(((byte)(39)))), ((int)(((byte)(40)))), ((int)(((byte)(34))))); - this.ClientSize = new System.Drawing.Size(537, 153); - this.FormClosing += Main_FormClosing; - this.Controls.Add(this.InstallCancelButton); - this.Controls.Add(this.TargetVersionLabel); - this.Controls.Add(this.ShowLogCheckbox); - this.Controls.Add(this.InstallButton); - this.Controls.Add(this.VersionLabel); - this.Controls.Add(this.label1); - this.Controls.Add(this.SelectPathButton); - this.Controls.Add(this.PathTextBox); - this.Controls.Add(this.StartShortcutsCheckbox); - this.Controls.Add(this.DeskShortcutsCheckbox); - this.Controls.Add(this.InstallingLabel); - this.Controls.Add(this.ProgressBar); - this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; - this.Icon = ((System.Drawing.Icon)(resources.GetObject("$this.Icon"))); - this.MaximizeBox = false; - this.MinimizeBox = false; - this.Name = "Main"; - this.Text = "/tg/station Server Installer"; - this.ResumeLayout(false); - this.PerformLayout(); - - } - - #endregion - - private System.Windows.Forms.ProgressBar ProgressBar; - private System.Windows.Forms.CheckBox DeskShortcutsCheckbox; - private System.Windows.Forms.CheckBox StartShortcutsCheckbox; - private System.Windows.Forms.Label InstallingLabel; - private System.Windows.Forms.TextBox PathTextBox; - private System.Windows.Forms.Button SelectPathButton; - private System.Windows.Forms.Label label1; - private System.Windows.Forms.Label VersionLabel; - private System.Windows.Forms.Button InstallButton; - private System.Windows.Forms.CheckBox ShowLogCheckbox; - private System.Windows.Forms.Label TargetVersionLabel; - private System.Windows.Forms.Button InstallCancelButton; - } -} - diff --git a/TGS.Installer.UI/Main.cs b/TGS.Installer.UI/Main.cs deleted file mode 100644 index 955283ed10..0000000000 --- a/TGS.Installer.UI/Main.cs +++ /dev/null @@ -1,461 +0,0 @@ -using Microsoft.Deployment.WindowsInstaller; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.ServiceProcess; -using System.Threading; -using System.Threading.Tasks; -using System.Windows.Forms; -using TGS.Interface; -using TGS.Interface.Components; -using TGS.Server; - -namespace TGS.Installer.UI -{ - partial class Main : Form - { - const string DefaultInstallDir = "TG Station Server"; //keep this in sync with the msi installer - - string tempDir; - bool installing = false; - bool cancelled = false; - bool pathIsDefault = true; - /// - /// If we should attempt to make a for the new install - /// - bool attemptNetSettingsMigration = false; - - IClient Interface; - - /// - /// Construct an installer form - /// - public Main() - { - InitializeComponent(); - SetupTempDir(); - CheckForExistingVersion(); - PathTextBox.Text = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFilesX86), DefaultInstallDir); - } - - void SetupTempDir() - { - tempDir = Path.Combine(Path.GetTempPath(), "TGS3InstallerTempDir"); - try - { - if (File.Exists(tempDir)) - File.Delete(tempDir); - else if (Directory.Exists(tempDir)) - Directory.Delete(tempDir, true); - } - catch { } - if (File.Exists(tempDir) || Directory.Exists(tempDir)) - { - tempDir = Path.GetTempFileName(); - File.Delete(tempDir); //we want a dir not a file - } - try - { - Directory.CreateDirectory(tempDir); - } - catch - { - tempDir = null; - } - } - - void CleanTempDir() { - if(tempDir != null) - try - { - Directory.Delete(tempDir, true); - } - catch { } - } - - void CheckForExistingVersion() { - Interface = new Client(); - var verifiedConnection = Interface.ConnectionStatus().HasFlag(ConnectivityLevel.Administrator); - try - { - var realVersion = Interface.Server.Version; - var isV0 = realVersion < new Version(3, 1, 0, 0); - if (isV0) //OH GOD!!!! - MessageBox.Show("Upgrading from version 3.0 may trigger a bug that can delete /config and /data. IT IS STRONGLY RECCOMMENDED THAT YOU BACKUP THESE FOLDERS BEFORE UPDATING!", "Warning"); - var isUnderV2 = isV0 || realVersion < new Version(3, 2, 0, 0); - if (isUnderV2) - //Friendly reminger - MessageBox.Show("Upgrading to service version 3.2 will break the 3.1 DMAPI. It is recommended you update your game to the 3.2 API before updating the servive to avoid having to trigger hard restarts.", "Note"); - attemptNetSettingsMigration = isUnderV2; - } - catch - { - if (verifiedConnection) - VersionLabel.Text = "< v3.0.85.0 (Missing ITGService.Version())"; - } - } - - bool ConfirmDangerousUpgrade() - { - return MessageBox.Show("Unable connect to service! Existing DreamDaemon instances will be terminated. Continue?", "Warning", MessageBoxButtons.YesNo) == DialogResult.Yes; - } - - bool TellServiceWereComingForThem() - { - var connectionVerified = Interface.ConnectionStatus().HasFlag(ConnectivityLevel.Administrator); - try - { - Interface.Server.Management.PrepareForUpdate(); - Thread.Sleep(3000); //chat messages - return true; - } - catch - { - return ConfirmDangerousUpgrade(); - } - } - - private void Main_FormClosing(object sender, FormClosingEventArgs e) - { - e.Cancel = installing; //not allowed, nununu - } - - enum PKillType - { - Killed, - Aborted, - NoneFound, - } - - PKillType PromptKillProcesses(string name) - { - var processes = new List(Process.GetProcessesByName(name)); - processes.RemoveAll(x => x.HasExited); - if(processes.Count > 0) - { - if (MessageBox.Show(String.Format("Found {1} running instance{2} of {0}.exe! Shall I terminate {3}?", name, processes.Count, processes.Count > 1 ? "s" : "", processes.Count > 1 ? "them" : "it"), "Warning", MessageBoxButtons.YesNo) != DialogResult.Yes) - return PKillType.Aborted; - foreach (var P in processes) - { - if (!P.HasExited) - { - P.Kill(); - P.WaitForExit(); - } - P.Dispose(); - } - return PKillType.Killed; - } - return PKillType.NoneFound; - } - static string TextPrompt(string caption, string text) - { - Form prompt = new Form() - { - Width = 500, - Height = 150, - FormBorderStyle = FormBorderStyle.FixedDialog, - Text = caption, - StartPosition = FormStartPosition.CenterScreen, - MaximizeBox = false, - MinimizeBox = false, - }; - Label textLabel = new Label() { Left = 50, Top = 20, Text = text, AutoSize = true }; - TextBox textBox = new TextBox() { Left = 50, Top = 50, Width = 400 }; - Button confirmation = new Button() { Text = "Ok", Left = 350, Width = 100, Top = 70, DialogResult = DialogResult.OK }; - confirmation.Click += (sender, e) => { prompt.Close(); }; - prompt.Controls.Add(textBox); - prompt.Controls.Add(confirmation); - prompt.Controls.Add(textLabel); - prompt.AcceptButton = confirmation; - - return prompt.ShowDialog() == DialogResult.OK ? textBox.Text : null; - } - - bool HandleNoConfigMigration(ServerConfig sc) - { - var path = @"C:\TGSTATION-SERVER-3"; - if (Directory.Exists(path)) { - sc.InstancePaths.Add(path); - new InstanceConfig(path).Save(); - return MessageBox.Show(String.Format("Unable to migrate settings, default config created at {0}. You will need to reconfigure your server instance. Continue with installation?", path), "Migration Error", MessageBoxButtons.YesNo) == DialogResult.Yes; - } - return MessageBox.Show("All config migrations have failed. You will need to fully recreate your server. Continue with installation?", "Migration Failure", MessageBoxButtons.YesNo) == DialogResult.Yes; - } - - /// - /// Migrate to the new since the won't know about it until it's upgraded - /// - bool AttemptMigrationOfNetSettings() - { - if (!attemptNetSettingsMigration) - return true; - var sc = new ServerConfig(); - try - { - sc.PythonPath = Interface.Server.Management.PythonPath(); - } - catch { } - try - { - sc.RemoteAccessPort = Interface.Server.Management.RemoteAccessPort(); - } - catch { } - try - { - foreach (var I in Interface.Server.Instances) - sc.InstancePaths.Add(I.Metadata.Path); - } - catch { } - - try - { - if (sc.InstancePaths.Count > 0) //nice suprise, this is uneeded - return true; - - //stopping the service here is MANDATORY to get the correct reattach values - using (var controller = new ServiceController("TG Station Server")) - { - controller.Stop(); - controller.WaitForStatus(ServiceControllerStatus.Stopped); - //we need to find the old user.config - //check wow64 first - var path = @"C:\Windows\SysWOW64\config\systemprofile\AppData\Local\TGServerService"; - if (!Directory.Exists(path)) - { - //ok... check the System32 path - path = @"C:\Windows\System32\config\systemprofile\AppData\Local\TGServerService"; - if (!Directory.Exists(path)) - { - //well, i'm out of ideas, just use the default location - var res = HandleNoConfigMigration(sc); - if(!res) - { - controller.Start(); - return false; - } - } - } - - //now who knows wtf windows calls the damn folder - //take our best guess based on last modified time - DirectoryInfo lastModified = null; - foreach (var D in new DirectoryInfo(path).GetDirectories()) - if (lastModified == null || D.LastWriteTime > lastModified.LastWriteTime) - lastModified = D; - - if (lastModified == null) - { - //well, i'm out of ideas, just use the default location - var res = HandleNoConfigMigration(sc); - if (!res) - { - controller.Start(); - return false; - } - } - - var next = lastModified; - lastModified = null; - foreach (var D in next.GetDirectories()) - if (lastModified == null || D.LastWriteTime > lastModified.LastWriteTime) - lastModified = D; - - if (lastModified == null) - { - //well, i'm out of ideas, just use the default location - var res = HandleNoConfigMigration(sc); - if (!res) - { - controller.Start(); - return false; - } - } - - path = Path.Combine(tempDir, "user.config"); - var netConfigPath = Path.Combine(lastModified.FullName, "user.config"); - File.Copy(netConfigPath, path, true); - new InstanceConfig(tempDir).Save(); - - var instanceConfigPath = Path.Combine(tempDir, "Instance.json"); - - if (MessageBox.Show(String.Format("The 3.2 settings migration is a manual process, please open \"{0}\" with your favorite text editor and copy the values under TGServerService.Properties.Settings to the relevent fields at \"{1}\". If something in the original config appears wrong to you, correct it in the new config, but do not modify the \"Version\", \"Enabled\", or \"Name\" fields at all. See field mappings here: https://github.com/tgstation/tgstation-server/blob/a372b22fd3367dd60ee0cbebd9210f4b072c952d/TGServerService/DeprecatedInstanceConfig.cs#L23-L39", path, instanceConfigPath), "Manual Migration Required", MessageBoxButtons.OKCancel) != DialogResult.OK) - { - controller.Start(); - return false; - } - - var name = TextPrompt("Set Instance Directory", String.Format("Please enter the ServerDirectory entry from the original config here.{0}Use backslashes and uppercase letters. Leave this blank if it is not present.", Environment.NewLine)); - if (name == null) - { - controller.Start(); - return false; - } - - if (String.IsNullOrWhiteSpace(name)) - name = @"C:\TGSTATION-SERVER-3"; - - sc.InstancePaths.Add(name); - - if (MessageBox.Show(String.Format("Please confirm you have finished copying settings from \"{0}\" to \"{1}\"!", path, instanceConfigPath), "Last Confirmation", MessageBoxButtons.OKCancel) != DialogResult.OK) - { - controller.Start(); - return false; - } - //validate it for good measure - try - { - InstanceConfig.Load(tempDir); - } - catch (Exception e) - { - MessageBox.Show(String.Format("JSON Validation Error: {0}", e.Message)); - controller.Start(); - return false; - } - File.Copy(instanceConfigPath, Path.Combine(name, "Instance.json"), true); - return true; - } - } - finally - { - Directory.CreateDirectory(Server.Server.MigrationConfigDirectory); - sc.Save(Server.Server.MigrationConfigDirectory); - } - } - - async void DoInstall() - { - string logfile = null; - try - { - while (true) - { - var res = PromptKillProcesses("TGCommandLine"); - if (res == PKillType.Aborted) - return; - else if (res == PKillType.Killed) - continue; - res = PromptKillProcesses("TGControlPanel"); - if (res == PKillType.Aborted) - return; - else if (res == PKillType.Killed) - continue; - break; - } - - if (!TellServiceWereComingForThem()) - return; - - if (!AttemptMigrationOfNetSettings()) - return; - - var args = new List(); - if (!pathIsDefault) - args.Add(String.Format("INSTALLFOLDER=\"{0}\"", PathTextBox.Text)); - if (StartShortcutsCheckbox.Checked) - args.Add("INSTALLSHORTCUTSTART=1"); - if (DeskShortcutsCheckbox.Checked) - args.Add("INSTALLSHORTCUTDESK=1"); - - args.Add("REBOOT=R"); - - SelectPathButton.Enabled = false; - PathTextBox.Enabled = false; - DeskShortcutsCheckbox.Enabled = false; - StartShortcutsCheckbox.Enabled = false; - InstallButton.Enabled = false; - ShowLogCheckbox.Enabled = false; - InstallButton.Text = "Installing..."; - - var msipath = Path.Combine(tempDir, "TGS.Installer.msi"); - File.WriteAllBytes(msipath, Properties.Resources.TGSInstaller); - File.WriteAllBytes(Path.Combine(tempDir, "cab1.cab"), Properties.Resources.cab1); - - ProgressBar.Style = ProgressBarStyle.Marquee; - InstallCancelButton.Enabled = true; - - if (ShowLogCheckbox.Checked) - { - logfile = Path.Combine(tempDir, "tgsinstall.log"); - Microsoft.Deployment.WindowsInstaller.Installer.EnableLog(InstallLogModes.Verbose | InstallLogModes.PropertyDump, logfile); - } - var cl = String.Join(" ", args); - - // TODO: Uncomment this when OnUIUpdate is more robust - // Microsoft.Deployment.WindowsInstaller.Installer.SetInternalUI(InstallUIOptions.Silent); - Microsoft.Deployment.WindowsInstaller.Installer.SetExternalUI(OnUIUpdate, InstallLogModes.Progress); - - await Task.Run(() => Microsoft.Deployment.WindowsInstaller.Installer.InstallProduct(msipath, cl)); - - if (cancelled) - { - MessageBox.Show("Operation cancelled!"); - return; - } - } - catch (Exception ex) - { - MessageBox.Show("Error: " + ex.ToString()); - return; - } - finally - { - InstallCancelButton.Enabled = false; - ShowLogCheckbox.Enabled = true; - SelectPathButton.Enabled = true; - PathTextBox.Enabled = true; - DeskShortcutsCheckbox.Enabled = true; - StartShortcutsCheckbox.Enabled = true; - ProgressBar.Style = ProgressBarStyle.Blocks; - InstallButton.Enabled = true; - InstallButton.Text = "Install"; - installing = false; - cancelled = false; - if (ShowLogCheckbox.Checked && logfile != null) - try - { - Process.Start(logfile).WaitForInputIdle(); - } - catch { } - } - MessageBox.Show("Success!"); - Application.Exit(); - } - - MessageResult OnUIUpdate(InstallMessage messageType, string message, MessageButtons buttons, MessageIcon icon, MessageDefaultButton defaultButton) - { - if (cancelled && installing) - { - installing = false; - return MessageResult.Cancel; - } - return MessageResult.OK; - } - - private void InstallButton_Click(object sender, EventArgs e) - { - DoInstall(); - } - - private void SelectPathButton_Click(object sender, EventArgs e) - { - var fbd = new FolderBrowserDialog() - { - Description = "Select where you would like to install the service executables. This isn't where an actual server instance is created.", - ShowNewFolderButton = true - }; - if (fbd.ShowDialog() != DialogResult.OK) - return; - pathIsDefault = false; - PathTextBox.Text = fbd.SelectedPath + Path.DirectorySeparatorChar + DefaultInstallDir; - } - - private void CancelButton_Click(object sender, EventArgs e) - { - cancelled = true; - InstallCancelButton.Enabled = false; - } - } -} diff --git a/TGS.Installer.UI/Main.resx b/TGS.Installer.UI/Main.resx deleted file mode 100644 index a83f34c04b..0000000000 --- a/TGS.Installer.UI/Main.resx +++ /dev/null @@ -1,2179 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - text/microsoft-resx - - - 2.0 - - - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - - - AAABAAUAEBAAAAEAIABoBAAAVgAAABgYAAABACAAiAkAAL4EAAAgIAAAAQAgAKgQAABGDgAAMDAAAAEA - IACoJQAA7h4AAKCgAAABACAAqJwBAJZEAAAoAAAAEAAAACAAAAABACAAAAAAAAAEAAATCwAAEwsAAAAA - AAAAAAAAAAAAAAAAAABeTTgAWkgyA1pIMilOOyMoRjMaRz4qEMM+KhDESDUcSU07IyhaSDIpWUgxA15N - NwAAAAAAAAAAAAAAAADSzsgAPyoQAEk2HTk9KQ7ZOSUK5DciB+43Igf/OCQJ/zkkCe46JQrkPSgO2UYz - Gjk8JwwA0s3HAAAAAABVQywAbV1JAk47IytALBK6XEo0/2xcSP9lVUD/mo+C/5CEdf9jUz7/ZFQ//2BP - Ov9LOCC6SzgfK2hYRAJSPygA////AUg1HHA7JgzkOSQJ/29gTP9wYU7/6+nm/+rn5f9rXEj/U0Ap/1NB - Kv9YRzD/Z1ZC/zsmC+RINBtw////AYFzYgU/KhCxOiUK/4V4aP+hl4r/ZFQ///b19P/a1tH/c2RR/87J - w//X083/Y1M+/3BhTv8+Kg//PyoQsYFzYgVYRi8cPSkOz0MvFf/a1tH/vreu/2RTPv/08/H//Pz7//Py - 8P/+/f3/+vn5/3JjUP+Hemr/RjMa/z0pDs9XRS8cQi4VnDciB/xsXEn/9PPx/7y1rP9kUz7/9PLx//// - //////////////f29f9qW0f/oJaJ/3BhTf83Igf8Qi4VnD8qEN45JAn/saie//////+7tKv/ZFM+//Ty - 8f//////7u3q/5SJev95a1n/Y1M9/9rX0v+zq6D/OSQJ/z8qEN5EMBeFNiEF/oV4aP//////vLSr/2RT - Pv/08vH//////+7t6v+UiXr/eWtZ/2NTPv/X0s3/h3pq/zYhBf5EMBeFRzMaZTYhBfuAc2L//////7y0 - q/9kUz7/9PLx//////////////////f29f9qWkX/rqaa/4R3Zv82IQX7RzMaZUUxGIc3Igf/X085/97b - 1/+9tqz/ZFM+//Tz8f/8/Pv/8/Lw//79/f/6+fn/cWJP/5KHeP9kUz7/NyIH/0UxGIdMOSE0PikPvzkk - Cf+Lf2//in5u/2JSPf/29fT/2tbR/3JkUf/PycP/19PN/2NTPv9oWEP/OycM/z0pD79LOCA0RDEYAE89 - JSU/KxHWcGBN/1NAKf9wYU3/9vX0/+rn5P9pWkX/VEIq/1NBKv9YRzD/ZlZB/zwnDdZPPSUlRTEYAGdY - QwAAAAAAQCwSkkw5If9cSjT/bF1J/6ujl/+lm4//j4N0/4R3Z/9lVUD/YE86/0IuFP8/KxGSAAAAAGdY - QwAAAAAAQy8WAFA+JiFDLxZmPCcMnzciBvM5JAn/NyIH/zgjCP87Jgz/OCMI8zwnDZ9DLxVmTzwlIUEs - EwAAAAAAAAAAAAAAAAAAAAAAQi4VAEYyGQc+KQ+GPSgO6EEtE61CLhStPSkP6D0pDoZEMBcHQS0TAAAA - AAAAAAAAAAAAAOAHAADgBwAAgAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAB - AADAAwAAwAMAAPAPAAAoAAAAGAAAADAAAAABACAAAAAAAAAJAAATCwAAEwsAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAP///wD///8A////AP///wBVQy0AemxaA047I0NCLhTGQS0TxlE+J0aFd2cEYlI9AP// - /wD///8A////AP///wAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF5NNwBhUDsMTToifk47 - I50+KQ+BQy8WmzwnDeg4Iwj/OCMI/z0oDupDLxacPikPgU47I51NOiF+YFA6DF1NNwAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAj4R1AB8HAABLOCBgOiUK+DYhBf82IQX/OCMI/zciB/82IQX/NiEF/zUg - Bf82IQX/NiEF/zYhBf86JQr4SzgfYB8HAACOgnMAAAAAAAAAAAAAAAAAAAAAAAAAAACMgXIACAAAAGBP - OS0/KxHVRzMa/2BPOf9eTTf/QSwT/0UyGf+Lf2//joJy/15NN/9fTjn/X085/11MNv9KNx//PysR1V9O - OC0GAAAAjIFyAAAAAAAAAAAAnpWIANrY0wFaSTIhRTIZdUAsEtc2IQb/d2lW/35xX/+Lf2//vLWs/8/K - xP/7+vr/wLqx/3NkUf9qWkb/alpG/25fS/+GeWj/ZFM+/z8qENdFMRh1WEYvIdTOyQGZjX8AbF1KAG1e - ShxFMhjMOSQJ/jciB/8zHgL/dmhV/1E/KP9oWET/+Pf2///////h3tr/UD4m/zsnDP9EMBb/QzAW/zsm - C/9BLRT/hnlp/0EtFP84Iwj+RTEYzG1dShxsXUkATDkgAE06Iig7JgzoNyIH/z4pD/9wYU3/ubKo/1RB - Kv9qWkb/+Pf3///////Hwbn/OyYL/4p+bv/c2NT/3NjT/6iek/87Jwz/e21b/1E+J/82IQb/OyYM6E06 - IihMOSAAWUcwAF1MNjs9KQ/0NSAE/2pbRv/z8vD/7evo/1JAKf9qWkb/+Pf3///////p5+T/tq6l/9vX - 0v///////////9XRy/9BLRP/in1t/25fS/81HwT/PSkP9F5NNztZRzEAAAAAAEo3H3A6JQr9NB8D/3xu - XP//////7Oro/1JAKf9qWkb/+Pf3/////////////////////////////////9XRy/9BLBP/kIR1/35w - Xv80HwP/OiUK/Uo3HnAAAAAAUkApRj4pD+M3Igf/OiUK/6iekv//////7Oro/1JAKf9qWkb/+Pf3//// - /////////////////////////////9XQy/9ALBL/k4d5/6qhlf86JQr/NyIH/z4pD+NSPyhGQy8V1jgj - CP82IQb/emxa//Tz8f//////7Oro/1JAKf9qWkb/+Pf3//////////////////f29f/x7+3/8vDu/6yj - mP83Igf/mI6A//b29P97bVv/NiEG/zgjCP9DLxXWRTEYxDgjCP82IQb/m5CD////////////7Oro/1JA - Kf9qWkb/+Pf3/////////////////6ackP9XRS7/WEYw/0IuFf91Z1T/4d7Z//////+bkIP/NiEG/zgj - CP9FMRjEUT4nXTsmDPU2IAX/XUw2/+3r6f//////7Oro/1JAKf9qWkb/+Pf3/////////////////6ac - kP9XRS7/WEYv/0IuFP92Z1T/4t/b/+7s6v9dTDb/NiAF/zsmDPVRPiddYlE7Gz8qENQ3Igb/SDUc/93a - 1f//////7Oro/1JAKf9qWkb/+Pf3//////////////////f29f/x7+3/8vDu/6yjmP83Igf/mo+B/9/c - 2P9INRz/NyIG/z8qENRhUTwbX044Jj8qEOA2IAX/WUcx/+3r6f//////7Oro/1JAKf9qWkb/+Pf3//// - /////////////////////////////9XQy/9ALBL/kod4/+/t6/9ZRzD/NiAF/z8qEOBfTjgmVEIqRjwn - DfI3IQb/TDkg/8bAuf//////7Oro/1JAKf9qWkb/+Pf3/////////////////////////////////9XR - y/9BLBP/kYV2/8nDvP9MOSH/NiEG/zwnDfJUQitGVUMsQj4pD+Q4Iwj/NiEG/047I//PysT/7uzq/1JA - Kf9qWkb/+Pf3///////p5+T/tq6l/9vX0v///////////9XRy/9CLhT/fG5c/1tKNP82IQX/OCMI/z4p - D+RVQyxCwry1Ak07I0Y/KhDUNyIH/0s4IP+akIL/jIBw/0UxGP9rW0f/+Pf3///////Fv7j/OiYL/4p+ - bv/c2NT/3NjT/6iek/87Jwz/eWpY/006Iv83Igf/PioQ1E06Ika8ta0CY1M+AEIvFQBYRjA1PCcN3lA9 - Jf96a1n/NyIH/zQeA/9qW0b/+Pf2///////e29b/TToi/zsmC/9DLxb/QzAW/zsmC/9BLRT/hnlp/0Et - FP89KA7eV0YvNUMwFgBhUDwAAAAAAG5fSwD5+PcBQCwSm0QwF/+FeGj/gnVk/3lrWf+Vinz/9fPy//// - ///39vX/ubGo/4F0Y/9yYk//alpG/25fS/+GeWj/Y1M9/zciB/9BLhSb+fr5AW5gTAAAAAAAAAAAAAAA - AABBLRMASjcfVjsmDPs7Jgv/Qy8W/047I/9aSDL/hHdm/6CWif9xYk7/cWJP/52Thv+DdWX/Xk04/11M - Nv9KNx//OCMH/zsmDPtKNx9WQS0TAAAAAAAAAAAAAAAAAAAAAABcSzUAY1I9E006ImxFMRieQCsSxzgj - CPU2IAX/NiEG/zYhBv81IAT/NSAE/zYhBv82IQb/NSAF/zciB/U+KhDHQy8Wnko3H2xiUTwTXEo0AAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAKGXiwDJxL0BalpGDT4qEHM5JQrzOCMI/zgjCP86Jgv7OyYL+zgj - CP84Iwj/OSQK8z4pD3NnV0MNt66lAZKGeAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAASzggAFNBKgVBLRSBPysR60QwFrBNOyNpTzwlaUYyGbBALBLrQS0TgVA+JgVJNh0AAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAA/4H/APgAHwD4AB8A8AAPAIAAAQCAAAEAgAABAIAAAQCAAAEAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwAADAMAAAwDgAAcA4AAHAPgAHwD+AH8AKAAAACAA - AABAAAAAAQAgAAAAAAAAEAAAEwsAABMLAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAHlrWQCGeWkEVkQtRkUyGclEMBfJU0EpR52UhwZ7blwAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAalpHAAAAAABlVUAmcGBNQG5fTDE8Jw0dXEs1L0g1HIw9KQ7qOCMI/zgjCP8+KQ/sTToikltK - My88Jw0dbl9MMXBgTUBlVUAmAAAAAGdXQgAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAABaSDIAY1I9IEQwF8pBLRP5PysR7TkkCeA+KQ/rOSUK/jgjCP84Iwj/OCMI/zgj - CP85JAr+PSkP6zkkCeA/KxHtQS0T+UQwF8piUTwgWUgxAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAeGpXALy2rQJKNx+MOSQK/zchBv83Igb/NyIH/zgiB/84Iwj/NyIH/zUg - Bf81IAX/NyIH/zciB/83Igf/NyIH/zciBv83Igf/OSQK/0o3Hoy6s6oCdmhWAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAANnUzwA0HwMAXk44Rz4pD+05JQr/RTEY/0g1HP9GMhn/OSQJ/zUf - BP9AKxH/e21b/3xuXP9HMxr/RjIZ/0g0HP9INBv/RzQb/0EtFP83Igf/PSgO7V5NN0czHgIA2dTPAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAJ2ThgAWAAAAYlE8ElRCK2BFMRfVNiEG/1xLNf+pn5T/lIl7/6ee - kv+DdmX/bl9L/763rv/5+Pf/8vHv/66mm/+Wi33/lop8/5aKfP+Wi33/mo+C/4l9bf9HNBv/Qy8W1VRC - K2BhUDsSJxAAAI+CcwAAAAAAAAAAAAAAAAByY1AAg3ZlFVE/J2lALBLFOyYM+jgjCP81IAT/a1tH/4Z5 - af83Igf/e21b//X08//39vX///////v6+f+aj4L/STYd/z4pD/89KA7/PSgO/z0pDv9DLxb/dGZT/5WK - e/8+KQ//OyYL+j8rEcVQPSVpgHJhFW5eSwAAAAAAAAAAAEw6IgBgTzpiPioQ/jgjCP84Iwj/OCMI/zMe - Av9pWUT/g3Zl/zEcAP93aVb//Pv7////////////1tLN/0UyGf83Igf/Qi4U/0MvFv9DLxb/QCwS/zci - B/83IQb/kIR1/15NN/82IAX/OCMI/z4qEP5gTzliTDkhAAAAAAAAAAAAPCgNAEMvFWk5JAn/OCMI/zgj - CP86JQr/V0Uv/66lmv+Ie2v/MRwA/3dpVv/8+/v///////////+5sqn/NSAF/0k2Hv/DvLT/1tHM/9XR - y//Mx8D/Z1dD/zMdAv92aFX/cWJP/zUgBP84Iwj/OSQJ/0MvFmk8KA0AAAAAAAAAAAAAAAAAU0AphDkk - Cf84Iwj/OCIH/4x/cP/q5+T//////4p+bv8xHAD/d2lW//z7+////////////8fBuv9oWUT/fW9d/+7s - 6v////////////////+Wi3z/Mh0B/3VmU/+YjX//NyIH/zgjCP85JAn/U0EphAAAAAAAAAAAemlYAAAA - AABPPSWYOSQJ/zgjCP87Jgv/vbat////////////in1u/zEcAP93aVb//Pv7/////////////Pz8//j3 - 9v/5+Pj//v7+/////////////////5iMf/8yHQH/d2hW/7ixp/87Jgv/OCMI/zkkCf9PPSWYAAAAAHRm - UgBcSzUAZVQ/J0EtE9M4Iwj/OCMI/z0pDv/JxL3///////////+KfW7/MRwA/3dpVv/8+/v///////// - ////////////////////////////////////////mIx+/zIdAf93aFb/xb+3/z0pD/84Iwj/OCMI/0Et - E9NjUz4nW0kzAHlrWBBJNh2oOSQJ/zgjCP82IQb/U0Ap/+He2v///////////4p9bv8xHAD/d2lW//z7 - +/////////////////////////////////////////////////+YjH7/Mh0B/3VnVP/d2dX/U0Ep/zYh - Bv84Iwj/OSQJ/0g1HKh3aVcQTjsjjDsmC/k4Iwj/NyIH/0AsEv+zq6H/////////////////in1u/zEc - AP93aVb//Pv7/////////////////////////////////////////////////4t/b/8yHAD/c2RR//v7 - +v+0raP/QCwT/zciB/84Iwj/OyYL+U47I4xFMRjoOCMH/zgjCP82IAX/hHdm//r6+f////////////// - //+KfW7/MRwA/3dpVv/8+/v///////////////////////f29f/Szsj/zcjC/87Jw/+wqJ7/Tzwk/zUf - BP+MgHD///7+//v6+v+Ed2f/NiAF/zgjCP84Iwf/RTEY6E48JJs5JAn/OCMI/zUgBf97bVv/+vn5//// - /////////////4p9bv8xHAD/d2lW//z7+///////////////////////4d7Z/1A+Jv89KQ//PioQ/zkk - Cv89KQ//nJGE/+zq5///////+vn5/3ttW/81IAX/OCMI/zkkCf9OPCSbYlI9O0AsEuk4Iwf/NyIH/0Yy - Gf/Tzsj/////////////////in1u/zEcAP93aVb//Pv7///////////////////////h3tn/UD4m/z0p - D/8+KhD/OSQJ/z0pD/+ckYT/7Oro///////Tzsj/RjIZ/zciB/84Iwf/QCwS6WJSPTuooJQFRTEYoDgj - CP84Iwj/NiEG/6KYi/////////////////+KfW7/MRwA/3dpVv/8+/v///////////////////////f2 - 9f/Szsj/zcjC/87Jw/+wqJ3/Tzwk/zUfBP+MgHD//////6KYi/82IQX/OCMI/zgjCP9FMRigqJ+TBQAA - AABOOyOLOSQJ/zgjCP84Iwj/sKid/////////////////4p9bv8xHAD/d2lW//z7+/////////////// - //////////////////////////////////+Lf2//MhwA/3NkUf/+/v7/sKid/zgjCP84Iwj/OSQJ/048 - I4sAAAAAycO7BUk1Ha04Iwj/OCIH/0ArEf/MxsD/////////////////in1u/zEcAP93aVb//Pv7//// - /////////////////////////////////////////////5iMf/8yHQH/dGVS//79/f/Mx8D/QCsR/zgi - B/84Iwj/STYdrcjEvAV8b10UQzAWzDgjCP84Iwf/PikP/62kmf/9/Pz///////////+KfW7/MRwA/3dp - Vv/8+/v/////////////////////////////////////////////////mIx+/zIdAf90ZVL/+fj3/66m - m/8+KQ//OCMH/zgjCP9EMBbMfW9eFGVVQCZBLBPhNyIH/zgjCP83Igf/Qy8V/5yShf/18/L//////4p9 - bv8xHAD/d2lW//z7+/////////////z8/P/49/b/+fj4//7+/v////////////////+YjH//Mh0B/3Rl - Uv+nnZH/Qy8W/zciB/84Iwj/NyIH/0EsE+FlVUAmhHZmDE48JIE8Jw3zOCMI/zgjCP81IAX/VkQt/9bS - zf/+/v7/in1t/zEcAP93aVb//Pv7////////////x8G6/2hZRP99b13/7uzq/////////////////5aL - fP8yHQH/cWJO/3RlUv80HwP/OCMI/zgjCP88Jw3zTTsjgYF0YwxjUj0AfG5dBE88JV4/KxHkOCMI/zUg - BP91Z1T/kIV2/3NlUv9XRS//Mx4C/3dpVv/8+/v///////////+3sKb/NSAE/0k2Hv/DvLT/1tHM/9XR - y//Mx8D/Z1dD/zMdAv92aFX/cmNP/zUgBP84Iwj/PyoQ5E48JF58blwEY1I9AAAAAAByZVEA////AF5N - OEk+KhDmNR8E/3ZnVP9tXUn/LxkA/zUfBP80HgP/d2hW//z7+////////////9LNx/9CLhX/OCMI/0Iu - FP9DLxb/Qy8W/0AsEv83Igf/NyEG/5CEdf9eTTf/NiAF/z4qEOZdTTdJ////AHBhTgAAAAAAAAAAAAAA - AACBc2MAvreuA0UxGKM1IAX/cWJP/5qQgv9pWUX/Wkgy/0g1HP9/cWD//Pv7////////////+fj4/5SJ - ev9GMhn/OyYM/zwnDf89KA7/PSkO/0MvFv90ZlP/lYp8/z4qD/84Iwj/RTEYo764sAOBdGQAAAAAAAAA - AAAAAAAAAAAAAAAAAAA6JQoASzggXDkkCfxDLxb/bF1J/4ByYf+NgXL/lop8/62kmf/g3dn///////j3 - 9v/f29f/2NTP/8/KxP/CvLT/pZyQ/5WKfP+Wi33/mo+C/4l9bf9HNBv/NyIG/zolCvxLOCBcOiUKAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAF1MNgBdTDYkQCwS4jciB/81HwT/NR8E/zciB/88Jw3/QS0T/1pI - Mv+UiHr/cGBN/0s4H/9KNx//cGBN/5aLff9fTzn/RzMa/0c0G/9BLRT/NyIH/zciBv84Iwj/QCwS4l1M - NiRdTDYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAi39wAI2AcgVWRC1lSDUcqEMvFs0/KxHqOiUK/Dgj - CP84Igf/NyEG/zUgBf81IAT/NyIH/zciB/81IAX/NSAF/zYhBv83Igf/NyIH/zolCvw+KhDqQS0UzUYy - GahUQiplkIR1BYyAcQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAJuRhACzq6IDeGpYEWNT - PipCLhV5OiUK8jgjCP84Iwj/OCMI/zgjCP84Igf/OCIH/zgjCP84Iwj/OCMI/zgjCP85JAryQi4UeWFQ - OypvX0wRpJqOA4x/cQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAASzgfAFVDKwRCLhRtOiYL8jgjCP84Iwj/OiUL+0EsE+dBLRPnOyYL+zgjCP84Iwj/OiUL8kEt - E21RPicESTUdAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAU0EqAGBPOgRFMhl9QS0T7Ek2HblNOiJpZFQ/MGhYQzBRPidpSzkguUIu - FOxEMRd9XEw2BFE/KAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAD/+B///wAA//4A - AH/8AAA//AAAP/AAAA/AAAADwAAAA8AAAAPAAAADwAAAA4AAAAEAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AACAAAABAAAAAAAAAAAAAAAAAAAAAIAAAAHgAAAH4AAAB/AAAA/wAAAP8AAAD/wAAD//gAH//8AD/ygA - AAAwAAAAYAAAAAEAIAAAAAAAACQAABMLAAATCwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAGxc - SACShXcEaVlFT0o3HstINBzLZFM+T4R3ZwRkUz4AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AACXjH4AAAAAAGdXQyNRPiaTQCsR6zkkCf85JAn/PyoQ6048JJR+cF8v////AIl8bAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA1c/OAGtcSAB0ZVMib2BMU4F0 - Y1OMgHBJSTYdIT4pDx1rW0cmXUw2dEIuFNY6JQr+OCMH/zgjCP84Iwj/OCMI/zolC/9HMxrgW0kzdWtb - RyY+KQ8dSTYdIYyAcEmBdGNTb2BMU3RlUyJrXEgAysjAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAfW9eAIR3 - Zw1MOSGsQS0T/0UxGP5FMRj4OSQK4zolC+FBLRPmPSkO/DgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP83Igf/PSgO/EEtE+Y6JQvhOSQK40UxGPhFMRj+QS0T/0s5IKyEd2YNfW9dAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AACgl4sAAAAAAGFQOmw9KA76NyIH/zciB/83Igf/OCMI/zgjCP84Iwf/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjB/84Iwj/OCMI/zciB/83Igf/NyIH/z0oDvpgTzpsAAAAAKKY - jAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAABXRS4Aa1tHJEQxF9U4Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zYhBf82IQX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9EMBfValpGJFZELQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAKCWiQDEvbUHVEIrmjolCv84Iwj/NiEF/zUgBP81IAT/NSAE/zUg - BP83Igf/OCMI/zgjCP82IQb/OSQJ/2BPOf9gUDr/OSQJ/zUgBP81IAT/NSAE/zUgBP81IAT/NSAE/zUg - BP81IAT/NiEG/zgjCP86JQr/VEEqmsG6sweek4cAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAl41+AODd2ARtXUleQi4U9DciB/85JAn/YE86/31v - Xf98b13/fG9d/3lrWf9KNx//NB8D/zYgBf9PPSX/n5WI/+zq5//t6+j/oZeK/21dSv94aVf/fG9d/3xv - Xf98b13/fG9d/3ttXP9wYU7/VUMs/zomC/82IQb/Qi4U9GxdSV7h3dkEl41+AAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA////AGtaRgCNgHAFY1I9OlNBKp9EMBbvOCMI/zgj - CP8+Kg//wbuy/7Wto/+il4v/oZeL/8nEvf+so5j/alpG/4l9bf/b19L//v79////////////7Orn/763 - rv+onpP/opiM/6OYjP+jmIz/opiM/6Sajv+wp53/w721/6ifk/9OOyP/NiEG/0QwFu9TQiqfY1M+Oo2A - cQVrW0cA////AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAP///wANAAAAqKCUDlxKND9JNh2fQCwS7Tkk - Cf83Igf/OCMI/zgjCP8/KxH/wLqx/11MNv81IAT/NR8E/4+DdP/+/v7/+Pf2//v7+/////////////// - ///Qy8T/Y1I9/z0pD/84Iwj/NyIH/zciB/83Igf/NyIH/zciB/85JAn/TToh/5qPgv+2rqT/RzQb/zYh - Bv85JAn/QCwS7Uc0G59ZRzE/p52RDh4IAAD08e8AAAAAAAAAAAAAAAAAAAAAAJ+WiQCjmo0YX085sj8r - EfM4Iwj/OCMH/zgjCP84Iwj/OCMI/zgjCP8/KxH/wLqx/15NN/82IAX/NSAF/4+DdP////////////// - /////////////+zq5/9jUj3/NB8D/zgjCP84Iwj/OCIH/zgjB/84Iwf/OCMH/zgjCP84Iwj/NiEG/zsm - C/+upZr/in5u/zYgBf84Iwj/OCMH/zgjCP8/KxHzXkw3sqGXihiflIcAAAAAAAAAAAAAAAAAAAAAAIF0 - YwB/cmEtSDUc7TchBv84Iwj/OCMI/zgjCP84Iwj/OCMI/zciB/89KQ7/wLmw/15NN/82IAX/NSAF/4+D - dP///////////////////////////8C6sf88Jw3/OCMI/zkkCf8+KhD/PysR/z8rEf8/KxH/PysR/zsm - DP83Igb/OCMI/zUgBP9sXEj/tKyi/zwoDv84Iwj/OCMI/zgjCP83IQb/SDUc7YByYS2CdGQAAAAAAAAA - AAAAAAAAAAAAAFZFLgBWRC0tPysR7TgjCP84Iwj/OCMI/zgjCP83Igf/NSAE/zwoDf9rXEj/1NDK/11M - Nv82IAX/NSAF/4+DdP///////////////////////////6KYi/82IQb/NyIG/0s4IP+6s6n/y8W+/8rE - vP/KxLz/ysS9/7uzqv9hUDv/NyEG/zYhBv9RPyj/vLSr/0g0HP83Igf/OCMI/zgjCP84Iwj/PysR7VZE - LS1XRS4AAAAAAAAAAAAAAAAAAAAAAEEtEwBINBsxOSUK7zgjCP84Iwj/OCMI/zgjB/9ALBL/cGFO/7ix - p//y8O7/8fDu/1tKM/82IAX/NSAF/4+DdP///////////////////////////5aKfP8yHAH/Mx0C/089 - Jf/p5+T///////////////////////////+xqZ7/OSQK/zciBv9KNx//vreu/1NBKv83IQb/OCMI/zgj - CP84Iwj/OSUK70c0GzFBLRMAAAAAAAAAAAAAAAAAAAAAAGVUPwB+cF5eQCwS/jgiB/84Iwj/OCMI/zYg - Bf+GeWj/9/b1////////////8fDu/1tKM/82IAX/NSAF/4+DdP///////////////////////////7ix - p/93aFb/d2lX/4p+bv/x7+3///////////////////////////+/ubD/PCgN/zciBv9INBv/1tHM/4p9 - bf81IAX/OCMI/zgjCP84Igf/QCwS/n5wXl5lVD8AAAAAAAAAAAAAAAAAAAAAAFpJMwBsXEhpPysR/zgj - CP84Iwj/OCMI/zYhBv+glYj/////////////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////// - //////////////7+/v/+/f3//v79//7+/v////////////////////////////////+/uLD/PCgN/zci - Bv9HNBv/3NjT/6GYi/82IQb/OCMI/zgjCP84Iwf/PysR/2xdSWlbSTMAAAAAAAAAAAAAAAAAn5WIAAAA - AABPPSV9OiYL/zgjCP84Iwj/OCMI/zgjCP+tpZr/////////////////8fDu/1tKM/82IAX/NSAF/4+D - dP////////////////////////////////////////////////////////////////////////////// - //+/uLD/PCgN/zciBv9HNBv/3NjT/6+nnP84Iwj/OCMI/zgjCP84Iwj/OiYL/089JX0AAAAAn5SIAAAA - AAD///8AY1M+AG5fS0BFMRjnNyIH/zgjCP84Iwj/OCMI/zkkCf+8taz/////////////////8fDu/1tK - M/82IAX/NSAF/4+DdP////////////////////////////////////////////////////////////// - //////////////////+/uLD/PCgN/zciBv9HNBv/3dnU/764r/85JAn/OCMI/zgjCP84Iwj/NyIH/0Ux - GOdsXUlAYlE8AP///wB7bVsAgHJhH0s4IMY4Iwj/OCMI/zgjCP84Iwj/NyIH/0MvFf/OycP///////// - ////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////////////////////////////////////// - //////////////////////////////////+/ubD/PCgN/zciBv9HNBv/3NjT/9HMxf9DLxX/NyIH/zgj - CP84Iwj/OCMI/zgjCP9LOCDGf3JgH3ttWwCimYwNUkAomjolC/84Iwj/OCMI/zgjCP84Iwj/OCMI/5CE - df/6+vn/////////////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////////////////////// - //////////////////////////////////////////////////++t67/PCcN/zciBv9HNBv/2tbQ//38 - /P+RhXb/OCMI/zgjCP84Iwj/OCMI/zgjCP86JQv/UT8omqCWiQ1ZSDJ6PioQ9jgjB/84Iwj/OCMI/zgj - CP82IAX/bl9L/+3r6f//////////////////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////// - //////////////////////////////////////////////////////////////////+qoZX/OCMI/zci - B/9HNBv/2dXQ///////u7Or/cWJO/zYgBf84Iwj/OCMI/zgjCP84Iwf/PioP9llHMXpKNx/tNyIH/zgj - CP84Iwj/OCMI/zYhBv9UQiv/19LN////////////////////////////8fDu/1tKM/82IAX/NSAF/4+D - dP////////////////////////////////////////////7+/v/5+Pj/+fj3//n49//5+Pf/9/b1/9PO - yP9fTjj/NiEG/zUgBP9PPCT/4t/c////////////2NTP/1RCK/82IQb/OCMI/zgjCP84Iwj/NyIH/0s3 - H+1XRS+1OiUK/zgjCP84Iwj/OCMI/zUgBP9uX0z/9/b1////////////////////////////8fDu/1tK - M/82IAX/NSAF/4+DdP///////////////////////////////////////////+Th3v91Z1T/aFhE/2lZ - Rf9pWUX/ZlVB/0o3Hv83Igf/OyYL/0s4IP+flYj//Pv7////////////9/b1/25fTP81IAT/OCMI/zgj - CP84Iwj/OiUK/1dGL7VrW0hMQCsS7zgjB/84Iwj/OCMI/zciB/9FMRj/zsnC//////////////////// - ////////8fDu/1tKM/82IAX/NSAF/4+DdP///////////////////////////////////////////9vX - 0v9GMhn/NB4D/zUgBP81IAT/NSAE/zciB/82IAX/a1tH/9/c1//8+/v/////////////////zsnC/0Ux - GP83Igf/OCMI/zgjCP84Iwf/QCsR72tbR0yglooMUj8ouTgjCP84Iwj/OCMI/zgjCP81IAX/iX1t//7+ - /f//////////////////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////////////////////// - /////////////9vX0v9GMhn/NB4D/zUgBP81IAT/NSAE/zciB/82IAX/a1tH/9/c2P/8+/v///////// - ///+/f3/iX1t/zUgBf84Iwj/OCMI/zgjCP84Iwj/Uj8ouaGXiwwbAwAAZVVAYD0pD/o4Iwj/OCMI/zgj - CP82IQb/UD4m/+Dd2P//////////////////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////// - /////////////////////////////+Th3v91Z1T/aFhE/2lZRf9pWUX/ZlVB/0k2Hv83Igf/OyYL/0s4 - IP+glon//Pv7///////g3Nj/UD4m/zYhBv84Iwj/OCMI/zgjCP89KQ/6ZVVAYBsDAABtXUoAcWNPE0Et - FMY4Iwj/OCMI/zgjCP84Iwf/PSkP/8S+tv//////////////////////8fDu/1tKM/82IAX/NSAF/4+D - dP////////////////////////////////////////////7+/v/5+Pj/+fj3//n49//5+Pf/9/b1/9LO - yP9eTjj/NiEG/zUgBP9PPCT/4+Dc///////Evrb/PSkP/zgjCP84Iwj/OCMI/zgjCP9BLRTGcWJQE21d - SgCwp5wAxb63C1I/KL83Igf/OCMI/zgjCP83Igf/RzMa/9nW0P//////////////////////8fDu/1tK - M/82IAX/NSAF/4+DdP////////////////////////////////////////////////////////////// - //////////////////+qoZX/OCMI/zciB/9HNBv/2dXQ///////Z1dD/RjMa/zciB/84Iwj/OCMI/zci - B/9SQCi/xsG4C7CpngByYk8AdmdVFUUxGNU4Iwj/OCMI/zgjCP82IQb/WEYw/+vp5v////////////// - ////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////////////////////////////////////// - //////////////////////////////////++t6//PCcN/zciBv9HNBv/2dXQ///////r6eb/WEYv/zYh - Bv84Iwj/OCMI/zgjCP9FMhjVdmhVFXFjUAB+cF4Af3FgNkUxGPA3Igf/OCMI/zgjCP81IAT/b2BN//n5 - +P//////////////////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////////////////////// - //////////////////////////////////////////////////+/ubD/PCgN/zciBv9HNBv/2dXQ//// - ///5+fj/b2BN/zUgBP84Iwj/OCMI/zciB/9FMRjwf3JgNn5wXwBKNh4AXUw2Sj0oDvk4Iwj/OCMI/zgj - CP82IAX/bF1J/+7s6v//////////////////////8fDu/1tKM/82IAX/NSAF/4+DdP////////////// - //////////////////////////////////////////////////////////////////+/uLD/PCgN/zci - Bv9HNBv/2dXQ///////v7ev/bV1J/zYgBf84Iwj/OCMI/zgjCP89KA75XUw2Sko3HgA6JgsAY1M+ejwn - Df84Iwj/OCMI/zgjCP84Iwj/OiUL/3lqWP/j4N3/////////////////8fDu/1tKM/82IAX/NSAF/4+D - dP////////////////////////////////////////////////////////////////////////////// - //+/uLD/PCgN/zciBv9HNBv/2tbR/+bk4P96bFr/OyYL/zgjCP84Iwj/OCMI/zgjCP88Jw3/ZFM+ejsm - CwAAAAAATz0ljDkkCf84Iwj/OCMI/zgjCP84Iwj/OCMI/zYhBv9hUDr/zsnC//7+/v//////8fDu/1tK - M/82IAX/NSAF/4+DdP////////////////////////////7+/v/+/f3//v79//7+/v////////////// - //////////////////+/uLD/PCgN/zciBv9JNR3/x8G5/21dSf82IAX/OCMI/zgjCP84Iwj/OCMI/zgj - CP85JAn/Tz0ljAAAAAAAAAAAcGFOREo2HtQ4Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP83Igf/emxa//Dv - 7f//////8fDu/1tKM/82IAX/NSAF/4+DdP///////////////////////////7ixp/93aFb/d2lX/4p+ - bv/x7+3///////////////////////////+/ubD/PCgN/zciBv9JNh3/urKo/047I/83IQb/OCMI/zgj - CP84Iwj/OCMI/zgjCP9JNh3Ubl9LRAAAAADU0MsAAAAAAGFRPCROOyO3OiYL/TgjB/84Iwj/OCMI/zci - B/9KNh7/x8G5/8zGv//X087/2tbR/1hHMP82IQX/NSAF/4+DdP///////////////////////////5SJ - e/8yHAH/Mx0C/089Jf/p5+T///////////////////////////+xqZ7/OSQK/zciBv9LNx//u7Oq/088 - JP83Igb/OCMI/zgjCP84Iwf/OiYL/U47I7dhUTwkAAAAANTQywAAAAAAxL61AEUxGQCAcmEUSzggiz8r - Efo4Iwj/OCMI/zciBv9PPSX/u7Oq/048JP9EMRf/UD4m/z4qEP84Iwj/NSAF/4+DdP////////////// - /////////////52Thv82IQb/NyIG/0s4IP+6s6n/y8W+/8rEvP/KxLz/ysS9/7uzqv9hUDv/NyEG/zYh - Bv9RPyj/vLWs/0g1HP83Igf/OCMI/zgjCP8/KxH6Sjcfi31vXRRDLhUAxL61AAAAAAAAAAAAAAAAAAAA - AACFeGgAnJOFCmRUP3w+KhDzOCMI/zciBv9PPCX/urKp/0k2Hf82IQb/NiEG/zgjCP84Iwj/NSAF/4+D - dP///////////////////////////7qyqf86JQr/OCMI/zkkCf8+KhD/PysR/z8rEf8/KxH/PysR/zsm - DP83Igb/OCMI/zUgBP9rXEj/tK2i/zwoDv84Iwj/OCMI/z4qEPNjUz58mY+BCoJ2ZQAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAjIBwAJyShAhJNh61OCMI/zciBv9PPSX/urKp/0c0G/80HgP/NSAF/zYh - Bv83Igf/NSAE/4+DdP///////////////////////////+Xi3/9ZSDH/NB8E/zgjCP84Iwj/OCIH/zgj - B/84Iwf/OCMH/zgjCP84Iwj/NiEG/zsmC/+tpZr/in5u/zYgBf84Iwj/OCMI/0o3HrWckoUIjIBwAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAnJGEACgRAABUQituOyYL/zciB/9KNh7/x8G5/6GX - iv+BdGP/bV5K/1hHMP9KNx7/OycM/5CEdf/////////////////////////////////Fv7f/XUw2/zwo - Df83Igf/NyIH/zciB/83Igf/NyIH/zciB/85JAn/TDoh/5qPgf+2rqT/RzQb/zciB/84Iwj/OyYL/1RC - K24oEQAAnJKGAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAF1MNgBiUjwtQCsR6Dci - B/86JQr/ZVVA/4p+bv+imIz/same/722rf+/uK//uLGn/9XRy//5+Pj///////////////////////z8 - +//9/fz/7evo/8bAuP+so5j/p52S/6Sajv+imIz/opiM/6Sajv+wp5z/w721/6ifk/9OOyP/NiEG/zgj - CP84Iwj/QCsR6GJSPC1dTDYAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAHhq - VwCNgnIIRTIYszgjCP84Iwj/NiAF/zUgBP82IQb/OyYM/0MwFv9QPSb/ZFQ//3FiT/9+cF//4d3Z//f2 - 9f/c2NP/saie/31vXf98b13/rqWa/9vY0//5+Pf/4+Hd/4x/cP97blz/fG9d/3ttXP9wYU7/VUMs/zom - C/83Igb/OCMI/zgjCP84Iwj/RTIZs46Dcwh4a1gAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAMG5sAAvGQAAXEo0ejsmDP83Igf/NyIH/zgjCP84Iwj/OCMI/zciB/83IQb/NSAF/zUg - BP82IQX/YE85/21eSv9JNh3/OSQJ/zUgBP81IAT/OCMI/0g0HP9rW0f/YE86/zYgBf81HwT/NSAE/zUg - BP81IAT/NiEG/zgjCP84Iwj/NyIH/zciB/87Jgz/XEs1ei4YAADEvrQAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABYRjAAbl5LLFNBKrZEMRfaQi4U9DwnDfw5JQr/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/NiEF/zUgBf83Igf/OCMI/zgjCP84Iwj/OCMI/zciB/81IAX/NiEF/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zkkCf87Jgz8QCwS9EIuFdpRPie2bl9MLFhHMQAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAARTEYALGpngp6bFoaa1xIO2JS - PWRRPyiMTjsjvzolC/A4Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP86JQrwTjsjv088JIxdSzVkZFM+O3FiTxqil4sKNCAGAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAPb19AD///8BnpOGCEIuFWY7JgzwOCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zsmC/BBLRRmnZOGCP///wH29fQAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAVkMrAG5dSQJGMxlmPCcN7zgjCP84Iwj/OCMI/zgj - CP84Iwf/OCMI/zsmC/87Jgv/OCMI/zciB/84Iwj/OCMI/zgjCP84Iwj/PCcM70QwF2ZmVkMCUT8oAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAFlHMABxYU4CSTYdZT0o - Du84Igf/OCMJ/zwnDP1AKxHmSTYdvV1MNoleTTiJTDkhvUEtE+Y8KA39OCQJ/zciB/88KA3vSDUcZWlb - RgJUQywAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AABjUj0Af3FiA007InRFMhjvTjsjw11NN3xlVUAyi39vC////wH///8BlIl7C21dSTJjUz58UT8ow0cz - Gu9MOCB0eGpWA19ONwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAD///gf//8AAP//8A///wAA//AAAA//AAD/4AAAB/8AAP/gAAAH/wAA/8AAAAP/ - AAD/gAAAAf8AAP8AAAAA/wAA/AAAAAA/AADwAAAAAA8AAOAAAAAABwAA4AAAAAAHAADgAAAAAAcAAOAA - AAAABwAA4AAAAAAHAADgAAAAAAcAAOAAAAAABwAAwAAAAAADAACAAAAAAAEAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIAAAAAAAQAAgAAAAAABAACAAAAAAAEAAIAA - AAAAAQAAgAAAAAABAACAAAAAAAEAAIAAAAAAAQAAgAAAAAABAACAAAAAAAEAAMAAAAAAAwAA4AAAAAAH - AADwAAAAAA8AAPgAAAAAHwAA/AAAAAA/AAD8AAAAAD8AAPwAAAAAPwAA/gAAAAB/AAD+AAAAAH8AAP8A - AAAA/wAA//AAAA//AAD//AAAP/8AAP/+AAB//wAA//8AAP//AAAoAAAAoAAAAEABAAABACAAAAAAAACQ - AQATCwAAEwsAAAAAAAAAAAAA////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AHNjUf9zY1H/////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8Am5CD/z4pD/84Iwj/OCMI/zsnDP+NgXL/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wDCvLT/UD0m/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/0w4IP+2raP/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wBvXkv/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/2dX - RP////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AI2Bcv87Jgv/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OSQJ/4FzYv////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8Atq2j/0k1Hf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/RjMZ/6eek/////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AY1Q//zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/X085/9DMxv////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wCNgXL/OSQJ/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/gXNi/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AKee - k/9DLxf/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9AKxL/m5CD//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8ApJuQ/3ttW/+Ie2r/kIN0/5eMfv+il4z/q6OW/7Ssov+9tq3/yMK6/9DMxv////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8AysS+/1hHMP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9YRzD/ycS9/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wDQzMb/yMK6/722rf+0rKL/q6OW/6KXjP+XjH7/kIN0/4h7 - av97bVv/pJuQ/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ADolCv84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9CLhT/Sjcf/1NA - KP9fTzn/Xk02/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9cSzX/X085/1NAKP9KNx//Qi4U/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zolCv////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AGJRPf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/X085/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AKmg - lP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP+poJT/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wBDLxf/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Qi4U//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wB6alj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP96bFr/////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDHwrn/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/8O9tf////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8AU0Ao/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9QPSb/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8AlYp9/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/5eMfv////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ADwoDf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP87Jgv/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AGpbRv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/2ZWQv////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ALKq - oP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/sqqg//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wBINRz/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0YzGf////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wCDd2X/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OSQJ/1dGLv/Iwrr/yMK6/1dGLv84Iwj/OSQJ/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/gnRk/////wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDOyML/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/QCsS/5uQg/////////////// - ////////p56T/0IuFP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP/MxsD/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8AWEcw/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/emxa////////////////////////////////////////////iHtq/zkk - Cf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/VUQt/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8An5aI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/2ZW - Qv96bFr/emxa/3psWv96bFr/emxa/3psWv96bFr/emxa/3psWv96bFr/emxa/3psWv96bFr/dGRT/0Yz - Gf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9eTTb/0MzG//// - ///////////////////////////////////////////////////QzMb/Xk02/zgjCP84Iwj/Qi4U/1xL - Nf9pWUX/dGRT/3psWv96bFr/emxa/3psWv96bFr/emxa/3psWv96bFr/emxa/3psWv96bFr/emxa/3ps - Wv96bFr/emxa/3psWv9uYEz/ZlVB/048JP86JQr/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+floj/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Av7ev/z8qEP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/6adkf////////////////////////////// - ////////////////////////////////////////////////////////RjMZ/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/0YzGf+tpJj///////////////////////////////////////// - ///////////////////////////////////KxL7///////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////8/Lxf+hl4n/Y1M+/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/PyoQ/7+3r/////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wC0rKL/V0Yu/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP////////////////////////////////////////////////////////////// - /////////////////////////////3xuXf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OiUK/4l+ - bv////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////iX5u/zsnDP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/V0Yu/7Ss - ov////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AKeek/9OPCT/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj///////// - ////////opeM/6KXjP+il4z/opeM/6KXjP+il4z/opeM/6KXjP+il4z/opeM/6KXjP/JxL3///////// - //+Ie2r/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/ZlZC//////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////8O9 - tf+2raP/qJ2S/6KXjP+il4z/opeM/6KXjP+il4z/opeM/6KXjP+il4z/opeM/6KXjP+il4z/opeM/6KX - jP+il4z/opeM/6Sajv+yqZ//wLmx/87Iwv//////////////////////////////////////altG/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Tjwk/6eek/////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8Am5CD/0c0G/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy///////////////////////Szsj/ycS9/722 - rf+2raP/wryz//////////////////////////////////////////////////////////////////// - /////////////////////////////7Cnnf92Z1X/RjMZ/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/1VDLP+Qg3T///////////////////////////+ajoH/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/RzQb/52RhP////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wCQg3T/QS0T/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////pp2R/0Yz - Gf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9tX0r//////////////////////6uhlf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/QS0T/5CEdf////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AI2Bcv8+KQ//OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj///////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////ZlVB/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zsnDP+kmo7///////// - ////////lIh5/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/PCgN/4FzYv////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AgXNi/zol - Cv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////VUMs/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/42Bcv////////////////9mVUH/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OSQJ/3Nl - Uv////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AMnEvf9sXEj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////ZlZC/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/qJ2S/////////////////zolCv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/21dSf/KxL7/////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Awry0/2NT - Pv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj///////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////pp2R/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/z0p - Dv////////////////9/cmD/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/1xLNf+2raP/////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8AubOq/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////0c0 - G/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/dGZU/////////////////zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/7mxqP////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AKmglP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - /////////////////////////////////////////////6uhlf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP////////////////9eTTb/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP+poJT/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wCZkIL/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj///////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////9lVUD/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/k4d3////////////m5CD/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/m5CD/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8AjIBx/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - ////////////////////////////////////////////////////////////////////////PCgN/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/1VELf///////////9HM - x/85JAn/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/4yAcf////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AHtuXP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////0MzG/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/////////////////OSQJ/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9/cmD/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wBvXkv/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj///////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - /////////////6adkf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/PCgN/21d - Sf9tXUn/bV1J/21dSf9tXUn/bV1J/21dSf9tXUn/bV1J/21dSf9tXUn/bV1J/21dSf9tXUn/bV1J/21d - Sf9tXUn/YlI8/0MvF/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/8S+t////////////1dGLv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/b15L/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8AX085/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/SDUc/////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////+PgnT/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0g1HP////////////////////////////// - ////////////////////////////////////////////////////////////////////////wbux/1E+ - J/85JAn/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+onZL///////// - //9wYU3/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/19POf////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AFA+Jv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9QPSb/q6OW//////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////gnRk/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9INRz///////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////U0Ao/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/mo6B////////////hXdn/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9QPib/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wBCLhT/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/1pIMf+2raP///////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - /////////////3ZnVf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/SDUc//// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////8jCuv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv///////////5CEdf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Qi4U/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8AOCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/X043/7ato////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////9pWkX/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0g1HP////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////UT4n/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+Bc2L///////// - //+YjYD/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////ADgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9wYU3/zsjC//////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////aFdD/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9INRz///////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////3doVf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV////////////nZGE/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wA4Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OiUK/3xuXf////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - /////////////2hXQ/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/SDUc//// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+DeGb/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf///////////7KqoP86JQr/OSQJ/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wDPycL/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/RjMZ//////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////9oV0P/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0g1HP////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////0UxGP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP/PycL/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8AwLmx/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/1tKNf////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////aFdD/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9INRz///////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////9bSTT/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/wLmx/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ALKpn/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9qW0b///////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf//////////////////////aVpF/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/7KqoP////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wCimY3/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/empY//////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////3doVf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+kmo7/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8AlIh5/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/4d7a/////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////+GeWj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/lIp7/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AIV3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP+XjH7///////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf//////////////////////lot8/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/4Z5aP////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wB1aFT/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/pp2R//////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////6SbkP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8AZ1dE/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/7Ssov////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////+yqqD/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/Z1dE/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AEYzGf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP/CvLT///////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf//////////////////////wry0/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0YzGf////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AG9eS/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/0czH//////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////9DMxv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/altG/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AJuQ - g/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI//////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV////////////////////////////OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP+bkIP/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AMzGwP86JQr/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf///////////////////////////zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OSQJ/8jC - uv////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wBMOCD/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP85JAn///////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - //////////////////85JAn/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9JNR3/////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wBsXEj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/RTEY//////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV////////////////////////////QzAW/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/2lZRf////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wCXjH7/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/1RC - Kv////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf///////////////////////////1NAKP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/mI2A/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wDGwLj/OSQJ/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+elIb///////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - //////////////////+elIb/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zkkCf/GwLj/////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8ARzQb/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zkkCf9sXEj///////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV/////////////////////////////////25g - TP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/RjMZ/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8AZlZC/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9KNx////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf//////////////////////////////////////TToi/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9iUT3/////AP///wD///8A////AP///wD///8A////AP///wD///8Ak4d3/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP87Jwz/zsjC//////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////g3dl/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////////////////////9POyP88KA3/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/5KH - eP////8A////AP///wD///8A////AP///wD///8Aw762/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/opmN//////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////3doVf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////////////////// - ////////opeM/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/wLmx/////wD///8A////AP// - /wD///8A////AEYzGf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/cWJP//////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////9iUjz/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf////////////////////////////////////////////////92Z1X/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/0UxGP////8A////AP///wD///8A////AGNUP/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/Tjwk//////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////PCgN/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////////////////////////////////////1E+J/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/YVA7/////wD///8A////AI2Bcv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OycM/9DM - xv////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////ubGo/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////////////////// - ////////////////////////PCgN/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+PgnT/////AMK8 - tP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/6adkf////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////1pIMf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3tuXP///////////////////////////////////////////////////////////6ad - kf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/7+3r/9TQCj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////4l8 - bP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+QhHX///////// - ////////////////////////////////////////////////////////d2hV/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9TQCj/lYp9/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/1A9Jv////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////3tuXP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/wryz//////////////////////////////////// - //////////////////////////////////9TQCj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/l4x+//// - /wA7Jwz/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //+5s6r/hnlo/0ArEv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/UT4n//////////////////////////////////////////////////////////////////// - /////////////zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OyYL/////wD///8Aemxa/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/w721//////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////8O9tf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OiUK/8nEvf////////////// - /////////////////////////////////////////////////////////////8K8tP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3ttW/////8A////AM7Iwv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/29eS/////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ///////////////////DvbX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/QCsS/7u1rP////////////////////////////////////////////// - //////////////////////////////////9tX0r/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP/Py8X/////AP// - /wD///8AYU86/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj///////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////w721/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9HNBv/hnlo//// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9eTTb/////AP///wD///8A////ALSsov84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/4p+cP////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////8O9tf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/XEs1/722rf////////////////////////////////////////////// - ////////////////////////////////////////////////////////iX5u/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/tKyi/////wD///8A////AP///wD///8ASTYe/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP8/KhD///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ///////////////////DvbX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/3xu - Xf////////////////////////////////////////////////////////////////////////////// - /////////////////////////////0EtE/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/SDUc/////wD///8A////AP// - /wD///8A////AJiNgP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/6adkf////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////w721/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP98bl3///////////////////////// - /////////////////////////////////////////////////////////////////////////////6Sb - kP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/5qOgf////8A////AP///wD///8A////AP///wD///8AOycM/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9QPib///////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////8O9tf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/fG5d//////////////////////////////////////////////////// - //////////////////////////////////////////////////9TQCj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zwo - Df////8A////AP///wD///8A////AP///wD///8A////AHxuXf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/8G7 - sf////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ///////////////////DvbX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/3xu - Xf////////////////////////////////////////////////////////////////////////////// - ///////////////////AubH/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP98bl3/////AP///wD///8A////AP// - /wD///8A////AP///wDSzsj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9sXEj///////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////w721/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9cSzX/vbat//////////////////// - ////////////////////////////////////////////////////////////////////////aVpF/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/087I/////wD///8A////AP///wD///8A////AP///wD///8A////AGJS - PP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI//////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////8O9tf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/SDUc/4d7a/////////////////////////////// - /////////////////////////////////////////////zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/X085//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wC2r6T/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP+He2v///////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ///////////////////DvbX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/QS0T/722rf////////////////////////////////////////////// - /////////////4Z6af84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/7avpP////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AEo3H/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/QCsS//////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////w721/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP87Jwz/ysS+//////////////////////////////////////////////////////9AKxL/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/0k2Hv////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wCbkIP/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+kmo7///////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////ubGo/4J0ZP8/KhD/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/1NAKP////////////// - //////////////////////////////////+il4z/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+bkIP/////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ADwoDf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/bmBM//////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////3RmVP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/wry0//////////////////////////////////// - ////////bmBM/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP88KA3/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wB/cmD/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/42Bcv////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////g3hm/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/5GGdv///////////////////////////////////////////4yAcf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/f3Fh/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////ADgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+vp5z///////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //9XRi7/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP97blz///////// - //////////////////////////////////+tpJj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wA4Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/0MzG//////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////ubOq/zkkCf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////////////////// - ////////zsjC/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wDMxsD/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OSQJ//////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////89KQ7/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf////////////////////////////////////////////////84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/zcjB/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8Aq6GV/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/0w4IP////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////Y1Q//zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - ////////////////////////////////////////Sjcf/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/6ujlv////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AIl8bP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9tX0r///////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////3doVf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////////////////// - /////////////2xcSP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+Jfm7/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wBoV0P/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/joRz//////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+DeGb/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf////////////////////////////////////////////////+NgXL/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/Z1dE/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8ARzQb/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/7Cnnf////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - ////////////////////////////////////////r6ec/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0YzGf////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////ADgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP/RzMf///////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////////////////// - /////////////9DMxv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AMrEvv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj///////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf//////////////////////////////////////////////////////OSQJ/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/8rGv/////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wCpoJT/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/TToi//////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////////////////////////////////////0w4IP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+poJT/////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AiHtq/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/29e - S/////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////////////////// - //////////////////9tX0r/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/h3tr/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AGZVQf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+Qg3T///////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf//////////////////////////////////////////////////////joRz/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/2ZWQv////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wBGMxn/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/qJ2S//////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////////////////////////////////////6idkv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9GMxn/////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AOCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/z0p - Dv+2r6T///////////////////////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV//////////////////////////////////// - /////////////722rf9AKxL/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8AycS9/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/5SKe/////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf///////////////////////////////////////////6KXjP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP/KxL7/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AKee - k/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/cGFN//////////////////////////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - /////////////////////////////3VoVP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/qaCU//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wCGemn/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9UQir///////////////////////////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV/////////////////////////////////1hH - MP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/4h7av////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8AZVVA/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0Iu - FP/CvLP///////////////////////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf//////////////////////x8C4/0UxGP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9mVUH/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AEUx - GP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OiUK/6KXjP////////////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - ////////p56T/zolCv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/QzAW//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wA4Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/fnFf//////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV////////////q6OW/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP////8A////AP///wD///8A////AP// - /wD///8A////AP///wDIwrr/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9fTjf///////////////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf///////////52RhP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/yMK6/////wD///8A////AP///wD///8A////AP///wD///8A////AHxu - Xf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0k2 - Hv/Py8X//////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////hXdn/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - //+dkYT/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/fG5d//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Ap56T/zolCv84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Tjwk//////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////Z1dE/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9INRz///////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////4V3Z/84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV////////////nZGE/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/m5CD/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wDEvrf/Qy8X/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9RPif/hnlo/3xuXf////////////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - /////////////2VVQP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/SDUc//// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////+Fd2f/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/3doVf///////////52RhP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9DLxf/xL63/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wBVRC3/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9QPSb///////// - /////////////////////////////////////////////////////////////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////9nV0T/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0g1HP////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////g3hm/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP93aFX///////// - //+dkYT/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9VRC3/////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AHNj - Uf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////ZVVA/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9INRz///////////////////////////////////////////////////////// - /////////////////////////////////////////////////////////////3doVf84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/d2hV////////////nZGE/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9zY1H/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8Alot8/zgjCP85JAn/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/52RhP///////////8G7sf+7s6v///////////////////////////////////////// - ////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - /////////////2dXRP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/SDUc//// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////9QPSb/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/4FzYv///////////5iNgP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+Wi3z/////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wC7s6v/PyoQ/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+dkYT///////// - //93aFX/OCMI/zgjCP84Iwj/RjMZ/2JSPP98bl3/mI2A/7Ssov/OyML//////zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////9pWUX/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0g1HP////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ///JxL3/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //+QhHX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/z4pD/+2raP/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wBPPCX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/nZGE////////////d2hV/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////dGZU/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP9INRz///////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////U0Ao/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/mo6B////////////hXdn/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/088 - Jf////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AGpb - Rv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/52RhP///////////3doVf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - /////////////4J0ZP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/SDUc//// - //////////////////////////////////////////////////////////////////////////////// - ///////////////////CvLT/U0Ao/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/6idkv///////////3BhTf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/2ZVQf////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AiX5u/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+dkYT///////// - //93aFX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////+OhHP/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zwoDf9tXUn/bV1J/21dSf9tXUn/bV1J/21d - Sf9tXUn/bV1J/21dSf9tXUn/bV1J/21dSf9tXUn/bV1J/21dSf9tXUn/bV1J/2JRPf9FMRj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP/Dvrb///////// - //9XRi7/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/4l+bv////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wCvp5z/PCgN/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/nZGE////////////d2hV/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////raSY/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/////////////////OyYL/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OyYL/6mglP////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AM/Lxf9JNh7/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/52RhP///////////3doVf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////85JAn/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/VUMs////////////0MzG/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/STYe/8/Lxf////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AFVE - Lf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+dkYT///////// - //93aFX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - ////////////////////////////////////////////////////////////////////////Qi4U/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/5GGdv///////////5uQ - g/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/VUQt//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wCpoJT/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/nZGE////////////d2hV/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - /////////////////////////////////////////////39yYP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP////////////////9fTzn/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/6mglP////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////ADgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/52RhP///////////3doVf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - ///////////////////OyML/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9zZVL/////////////////OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wBiUT3/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+dkYT///////// - //93aFX/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - /////////////////////////////////////////////////////////////////////////////3Bh - Tf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP89KQ7/////////////////gXNi/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9jVD//////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8ApJqO/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/nZGE////////////d2hV/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////Qy8X/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/pp2R/////////////////zolCv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/pJuQ/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wA4Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/52RhP///////////3doVf84Iwj/OSQJ/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - /////////////////////////////8rEvv8+KQ//OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//// - /////////////2ZWQv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8AX043/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+Sh3j///////// - ////////wry0/6adkf+JfGz/bV1J/1A9Jv85JAn/OCMI/zgjCP84Iwj/OCMI/zgjCP85JAn/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/jYFy//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////z8nC/0k2Hv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP87Jgv/opeM/////////////////5aLfP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/19ON/////8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AJ+WiP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/ZVVA//////////////////////////////////// - ///////////////////AubH/pJqO/4Z6af9qW0b/TToi/zgjCP84Iwj/OCMI/zkkCf84Iwj/OCMI/zgj - CP84Iwj/OCMI/42Bcv////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////////////////////hnlo/zsm - C/84Iwj/OCMI/zkkCf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP9qW0b//////////////////////6ujlv84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP+floj/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8AOCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP+PgnT///////////////////////////////////////////////////////// - /////////////////////////////722rf+gl4v/g3hm/2hXQ/9JNh7/OCMI/zgjCP+NgXL///////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////////////////////npSG/2lZRf8/KhD/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/VUMs/4+CdP///////////////////////////5uQ - g/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AFpIMf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP8+KQ//W0k0/3doVf+Uinv/sKme/87Iwv////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ///////////////////////////////////////////////////AubH/sqqg/6adkf+il4z/opeM/6KX - jP+il4z/opeM/6KXjP+il4z/opeM/6KXjP+il4z/opeM/6KXjP+il4z/opeM/6KXjP+imY3/sKme/7+3 - r//OyML//////////////////////////////////////2xcSP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Wkgx/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wCZkIL/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/0EtE/9eTTb/emxa/5eMfv+0rKL/0czH//////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////jIBx/zwoDf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/5mQgv////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////ADgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/Qy8X/2FPOv98bl3/mZCC/7ewpv////////////////////////////// - //+2r6T///////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - //////////////////////////////////////////////////////////////////////////////// - ////////////////////////////////////////0MzG/6KZjf9lVUD/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wBUQir/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9GMxn/Y1M+/3VoVP9INRz/OCMI/6ujlv////////////// - /////////////////////////////////////////////////////////////62kmP9xYk//PyoQ/z8q - EP9xYk//raSY//////////////////////////////////////////////////////////////////// - ////////sKme/3psWv96bFr/emxa/3psWv96bFr/emxa/3psWv96bFr/emxa/3psWv96bFr/bmBM/2ZV - Qf9OPCT/OyYL/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9UQir/////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AlIh5/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/qJ2S//////////////////////////////////// - //////////////////+il4z/ZlVB/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/WEcw/5SK - e//Py8X/////////////////////////////////////////////////raSY/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/lot8/////wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wA5JAn/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP+elIb////////////////////////////CvLT/iHtq/006Iv84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0c0G/96bFr/tq2j//// - ////////////////////////pJqO/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8ATzwl/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/52R - hP//////r6ec/3NlUv89KQ7/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP87Jgv/ZlVB/6KXjP//////opmN/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/048JP////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AI+C - dP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP+Qg3T/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wDOyML/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/0MzG//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AEw4IP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Sjcf/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wCKfnD/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/4yAcf////8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDCvLT/m5CD/3Nl - Uv9JNR3/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Qy8X/2ZVQf+NgXL/tq2j/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDCvLT/m5CD/3Nl - Uv9JNh7/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP9DLxf/ZlVB/42B - cv+2raP/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDCvLT/m5CD/3Nl - Uv9JNh7/OCMI/zgjCP84Iwj/OCMI/zkkCf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP85JAn/OCMI/zgj - CP84Iwj/OCMI/0MwFv9mVUH/jYFy/7ato/////8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDAubH/mI2A/3Bh - Tf9CLhT/OSQJ/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Qi4U/3BhTf+YjYD/wLmx/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AEw4IP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/STYe/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8ATToi/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Sjcf/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wBRPif/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/Tjwk/////wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AFNA - KP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/Tzwl//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AVEIq/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/UD0m/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wBYRzD/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/VEIq/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AFpIMf84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/VUQt/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8AW0k0/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/V0Yu/////wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wBcTDb/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/QCsS/3ZnVf+wqZ7/sKme/3ZnVf9AKxL/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/XEw2//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AGFQO/84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/0k2Hv+Bc2L/vbat/////wD///8A////AP// - /wD///8A////AMnEvf+NgXL/Tzwl/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/XEs1/////wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8AYlE9/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zkkCf84Iwj/OCMI/zkk - Cf9fTzn/m5CD/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8Ap56T/21dSf87Jgv/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgj - CP84Iwj/X043/////wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wBjVD//OCMI/zgj - CP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP8/KhD/c2VS/6+nnP////8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AL22 - rf+Bc2L/Qy8X/zgjCP84Iwj/OCMI/zgjCP84Iwj/OCMI/zgjCP84Iwj/X085/////wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AGpbRv84Iwj/OCMI/zgjCP84Iwj/UD4m/4h7 - av/CvLT/////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wDPy8X/lIp7/1dG - Lv84Iwj/OCMI/zgjCP84Iwj/ZlVB/////wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8AbV9K/19POf+bkIP/////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AKeek/9tXUn/bFtH//// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - /wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP///wD///8A////AP// - //////////5/////////////////////////+B/////////////////////////gB/////////////// - /////////8AD////////////////////////AAD///////////////////////wAAD////////////// - ////////+AAAD//////////////////////gAAAH/////////////////////4AAAAH///////////// - ////wAf+AAAAAH/gA//////////////AAAAAAAAAAAAD/////////////4AAAAAAAAAAAAH///////// - ////AAAAAAAAAAAAAP////////////8AAAAAAAAAAAAA/////////////gAAAAAAAAAAAAB///////// - ///8AAAAAAAAAAAAAD////////////wAAAAAAAAAAAAAP///////////+AAAAAAAAAAAAAAf//////// - ///4AAAAAAAAAAAAAB////////////AAAAAAAAAAAAAAD///////////4AAAAAAAAAAAAAAH//////// - ///gAAAAAAAAAAAAAAf//////////8AAAAAAAAAAAAAAA///////////gAAAAAAAAAAAAAAB//////// - //+AAAAAAAAAAAAAAAH//////////wAAAAAAAAAAAAAAAP/////////+AAAAAAAAAAAAAAAAf/////// - //gAAAAAAAAAAAAAAAAf////////4AAAAAAAAAAAAAAAAAf///////+AAAAAAAAAAAAAAAAAAf////// - /gAAAAAAAAAAAAAAAAAAf//////4AAAAAAAAAAAAAAAAAAAf/////+AAAAAAAAAAAAAAAAAAAAf///// - AAAAAAAAAAAAAAAAAAAAAP////wAAAAAAAAAAAAAAAAAAAAAP///+AAAAAAAAAAAAAAAAAAAAAAf///4 - AAAAAAAAAAAAAAAAAAAAAB////gAAAAAAAAAAAAAAAAAAAAAH///+AAAAAAAAAAAAAAAAAAAAAAf///4 - AAAAAAAAAAAAAAAAAAAAAB////gAAAAAAAAAAAAAAAAAAAAAH///+AAAAAAAAAAAAAAAAAAAAAAf///4 - AAAAAAAAAAAAAAAAAAAAAB////gAAAAAAAAAAAAAAAAAAAAAH///+AAAAAAAAAAAAAAAAAAAAAAf///4 - AAAAAAAAAAAAAAAAAAAAAB////gAAAAAAAAAAAAAAAAAAAAAH///8AAAAAAAAAAAAAAAAAAAAAAP///w - AAAAAAAAAAAAAAAAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAD///8AAAAAAAAAAAAAAAAAAAAAAP///w - AAAAAAAAAAAAAAAAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAD///8AAAAAAAAAAAAAAAAAAAAAAP///w - AAAAAAAAAAAAAAAAAAAAAA////AAAAAAAAAAAAAAAAAAAAAAD///4AAAAAAAAAAAAAAAAAAAAAAH///A - AAAAAAAAAAAAAAAAAAAAAAP//4AAAAAAAAAAAAAAAAAAAAAAAf//gAAAAAAAAAAAAAAAAAAAAAAB//8A - AAAAAAAAAAAAAAAAAAAAAAD//gAAAAAAAAAAAAAAAAAAAAAAAH/8AAAAAAAAAAAAAAAAAAAAAAAAP/wA - AAAAAAAAAAAAAAAAAAAAAAA/+AAAAAAAAAAAAAAAAAAAAAAAAB/wAAAAAAAAAAAAAAAAAAAAAAAAD+AA - AAAAAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAAAAAAAfAAAAAAAAAAAAAAAAAAAAAAAAAA4AA - AAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA - AAAAAAAAAAAAAAAAAAAAAAAAgAAAAAAAAAAAAAAAAAAAAAAAAAGAAAAAAAAAAAAAAAAAAAAAAAAAAYAA - AAAAAAAAAAAAAAAAAAAAAAABwAAAAAAAAAAAAAAAAAAAAAAAAAPAAAAAAAAAAAAAAAAAAAAAAAAAA+AA - AAAAAAAAAAAAAAAAAAAAAAAH4AAAAAAAAAAAAAAAAAAAAAAAAAfwAAAAAAAAAAAAAAAAAAAAAAAAD/AA - AAAAAAAAAAAAAAAAAAAAAAAP8AAAAAAAAAAAAAAAAAAAAAAAAA/4AAAAAAAAAAAAAAAAAAAAAAAAH/gA - AAAAAAAAAAAAAAAAAAAAAAAf/AAAAAAAAAAAAAAAAAAAAAAAAD/8AAAAAAAAAAAAAAAAAAAAAAAAP/4A - AAAAAAAAAAAAAAAAAAAAAAB//gAAAAAAAAAAAAAAAAAAAAAAAH//AAAAAAAAAAAAAAAAAAAAAAAA//8A - AAAAAAAAAAAAAAAAAAAAAAD//gAAAAAAAAAAAAAAAAAAAAAAAH/+AAAAAAAAAAAAAAAAAAAAAAAAf/4A - AAAAAAAAAAAAAAAAAAAAAAB//gAAAAAAAAAAAAAAAAAAAAAAAH/+AAAAAAAAAAAAAAAAAAAAAAAAf/4A - AAAAAAAAAAAAAAAAAAAAAAB//AAAAAAAAAAAAAAAAAAAAAAAAD/8AAAAAAAAAAAAAAAAAAAAAAAAP/wA - AAAAAAAAAAAAAAAAAAAAAAA//AAAAAAAAAAAAAAAAAAAAAAAAD/8AAAAAAAAAAAAAAAAAAAAAAAAP/wA - AAAAAAAAAAAAAAAAAAAAAAA/+AAAAAAAAAAAAAAAAAAAAAAAAB/4AAAAAAAAAAAAAAAAAAAAAAAAH/gA - AAAAAAAAAAAAAAAAAAAAAAAf+AAAAAAAAAAAAAAAAAAAAAAAAB/4AAAAAAAAAAAAAAAAAAAAAAAAH/gA - AAAAAAAAAAAAAAAAAAAAAAAf8AAAAAAAAAAAAAAAAAAAAAAAAA/4AAAAAAAAAAAAAAAAAAAAAAAAH/wA - AAAAAAAAAAAAAAAAAAAAAAA//gAAAAAAAAAAAAAAAAAAAAAAAH//gAAAAAAAAAAAAAAAAAAAAAAB///A - AAAAAAAAAAAAAAAAAAAAAAP//+AAAAAAAAAAAAAAAAAAAAAAB///8AAAAAAAAAAAAAAAAAAAAAAP///8 - AAAAAAAAAAAAAAAAAAAAAD////4AAAAAAAAAAAAAAAAAAAAAf////wAAAAAAAAAAAAAAAAAAAAD///// - gAAAAAAAAAAAAAAAAAAAAf/////AAAAAAAAAAAAAAAAAAAAD//////AAAAAAAAAAAAAAAAAAAA////// - 8AAAAAAAAAAAAAAAAAAAD//////4AAAAAAAAAAAAAAAAAAAf//////gAAAAAAAAAAAAAAAAAAB////// - +AAAAAAAAAAAAAAAAAAAH//////8AAAAAAAAAAAAAAAAAAA///////wAAAAAAAAAAAAAAAAAAD////// - /AAAAAAAAAAAAAAAAAAAP//////+AAAAAAAAAAAAAAAAAAB///////4AAAAAAAAAAAAAAAAAAH////// - /gAAAAAAAAAAAAAAAAAAf///////AAAAAAAAAAAAAAAAAAD///////8AAAAAAAAAAAAAAAAAAP////// - /wAAAAAAAAAAAAAAAAAA////////gAAAAAAAAAAAAAAAAAH///////+AAAAAAAAAAAAAAAAAAf////// - /4AAAAAAAAAAAAAAAAAB////////gAAAAAAAAAAAAAAAAAH////////AAAAAAAAAAAAAAAAAA/////// - /8AAAAAAAAAAAAAAAAAD////////8AAAAAAAAAAAAAAAAA//////////gAAAAAAAAAAAAAAB//////// - ///8AAAAAAAAAAAAAD/////////////gAAAAAAAAAAAH//////////////4AAAAAAAAAAH////////// - /////wAAAAAAAAAA////////////////gAAAAAAAAAH////////////////AAAAAAAAAA/////////// - /////+AAAAAAAAAH////////////////8AAAAAAAAA/////////////////4AAAAAAAAH/////////// - //////wAAAAAAAA//////////////////gAAAAAAAH//////////////////AAAH4AAA//////////// - //////+AAH/+AAH//////////////////8AD///AA///////////////////4B////gH//////////// - ///////x/////4////////// - - - \ No newline at end of file diff --git a/TGS.Installer.UI/Program.cs b/TGS.Installer.UI/Program.cs deleted file mode 100644 index 851a948e3b..0000000000 --- a/TGS.Installer.UI/Program.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System; -using System.Windows.Forms; - -namespace TGS.Installer.UI -{ - static class Program - { - /// - /// The main entry point for the application. - /// - [STAThread] - static void Main() - { - Application.EnableVisualStyles(); - Application.SetCompatibleTextRenderingDefault(false); - Application.Run(new Main()); - } - } -} diff --git a/TGS.Installer.UI/Properties/AssemblyInfo.cs b/TGS.Installer.UI/Properties/AssemblyInfo.cs deleted file mode 100644 index debafbb073..0000000000 --- a/TGS.Installer.UI/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("TGStation Server Installer")] -[assembly: AssemblyDescription("Installation manager for /tg/station Server")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("8956d4c3-bfb9-448e-bf5f-ee7e6f9996f9")] diff --git a/TGS.Installer.UI/Properties/Resources.Designer.cs b/TGS.Installer.UI/Properties/Resources.Designer.cs deleted file mode 100644 index 9a0670c601..0000000000 --- a/TGS.Installer.UI/Properties/Resources.Designer.cs +++ /dev/null @@ -1,83 +0,0 @@ -//------------------------------------------------------------------------------ -// -// This code was generated by a tool. -// Runtime Version:4.0.30319.42000 -// -// Changes to this file may cause incorrect behavior and will be lost if -// the code is regenerated. -// -//------------------------------------------------------------------------------ - -namespace TGS.Installer.UI.Properties { - using System; - - - /// - /// A strongly-typed resource class, for looking up localized strings, etc. - /// - // This class was auto-generated by the StronglyTypedResourceBuilder - // class via a tool like ResGen or Visual Studio. - // To add or remove a member, edit your .ResX file then rerun ResGen - // with the /str option, or rebuild your VS project. - [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "15.0.0.0")] - [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] - [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] - internal class Resources { - - private static global::System.Resources.ResourceManager resourceMan; - - private static global::System.Globalization.CultureInfo resourceCulture; - - [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] - internal Resources() { - } - - /// - /// Returns the cached ResourceManager instance used by this class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Resources.ResourceManager ResourceManager { - get { - if (object.ReferenceEquals(resourceMan, null)) { - global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("TGS.Installer.UI.Properties.Resources", typeof(Resources).Assembly); - resourceMan = temp; - } - return resourceMan; - } - } - - /// - /// Overrides the current thread's CurrentUICulture property for all - /// resource lookups using this strongly typed resource class. - /// - [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] - internal static global::System.Globalization.CultureInfo Culture { - get { - return resourceCulture; - } - set { - resourceCulture = value; - } - } - - /// - /// Looks up a localized resource of type System.Byte[]. - /// - internal static byte[] cab1 { - get { - object obj = ResourceManager.GetObject("cab1", resourceCulture); - return ((byte[])(obj)); - } - } - - /// - /// Looks up a localized resource of type System.Byte[]. - /// - internal static byte[] TGSInstaller { - get { - object obj = ResourceManager.GetObject("TGSInstaller", resourceCulture); - return ((byte[])(obj)); - } - } - } -} diff --git a/TGS.Installer.UI/TGS.Installer.UI.csproj b/TGS.Installer.UI/TGS.Installer.UI.csproj deleted file mode 100644 index 0db0a2fa49..0000000000 --- a/TGS.Installer.UI/TGS.Installer.UI.csproj +++ /dev/null @@ -1,116 +0,0 @@ - - - - - - Debug - AnyCPU - {8956D4C3-BFB9-448E-BF5F-EE7E6F9996F9} - WinExe - TGS.Installer.UI - TG Station Server Installer - v4.5.2 - 512 - true - - - - - app.manifest - - - tgs.ico - - - true - bin\Debug\ - DEBUG;TRACE - full - AnyCPU - prompt - MinimumRecommendedRules.ruleset - true - - - bin\Release\ - TRACE - bin\x86\Release\TG Station Server Installer.xml - true - true - pdbonly - AnyCPU - prompt - MinimumRecommendedRules.ruleset - true - - - false - - - - ..\packages\Costura.Fody.1.6.2\lib\dotnet\Costura.dll - False - - - ..\packages\WiX.3.11.0\tools\Microsoft.Deployment.WindowsInstaller.dll - - - - - - - - - Form - - - Main.cs - - - - - True - True - Resources.resx - - - - Main.cs - - - ResXFileCodeGenerator - Resources.Designer.cs - Designer - - - - - - - - - - - - - - {ac4e7e8b-f83a-481c-a8b0-8fa4e8ae59ab} - TGS.Interface - - - {f32eda25-0855-411c-af5e-f0d042917e2d} - TGS.Server - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - - - \ No newline at end of file diff --git a/TGS.Installer.UI/app.manifest b/TGS.Installer.UI/app.manifest deleted file mode 100644 index 467015914e..0000000000 --- a/TGS.Installer.UI/app.manifest +++ /dev/null @@ -1,76 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/TGS.Installer.UI/packages.config b/TGS.Installer.UI/packages.config deleted file mode 100644 index c77cd9eca7..0000000000 --- a/TGS.Installer.UI/packages.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/TGS.Installer.UI/tgs.ico b/TGS.Installer.UI/tgs.ico deleted file mode 100644 index 6ed69fadfc..0000000000 Binary files a/TGS.Installer.UI/tgs.ico and /dev/null differ diff --git a/TGS.Installer/Product.wxs b/TGS.Installer/Product.wxs deleted file mode 100644 index dfb6546fce..0000000000 --- a/TGS.Installer/Product.wxs +++ /dev/null @@ -1,155 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - Installed AND NOT UPGRADINGPRODUCTCODE - - - - - - - - - - - - - - - - - - - - - - - - - - INSTALLSHORTCUTSTART = 1 - - - - - - - - - - - INSTALLSHORTCUTDESK = 1 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/TGS.Installer/TGS.Installer.wixproj b/TGS.Installer/TGS.Installer.wixproj deleted file mode 100644 index fb6790d2e1..0000000000 --- a/TGS.Installer/TGS.Installer.wixproj +++ /dev/null @@ -1,110 +0,0 @@ - - - - - Debug - x86 - 3.10 - 154435f6-0890-42d4-9aec-b743d4fbc1cb - 2.0 - TGServiceInstaller - Package - - - - - bin\$(Configuration)\ - obj\$(Configuration)\ - Debug - - - bin\$(Configuration)\ - obj\$(Configuration)\ - True - True - - - - - - - TGS.ControlPanel - {394e7643-6b8c-416f-ab18-95ac12648cdc} - True - True - Binaries;Content;Satellites - INSTALLFOLDER - - - TGS.CommandLine - {89191f69-b18e-4b59-b72e-e12f9b6811a0} - True - True - Binaries;Content;Satellites - INSTALLFOLDER - - - TGS.Interface.Bridge - {9a01ef03-8eae-45cb-8b87-4a17bd904557} - True - True - Binaries;Content;Satellites - INSTALLFOLDER - - - TGS.Server.Service - {3f81e398-b223-4006-b40c-c2800714ce29} - True - True - Binaries;Content;Satellites - INSTALLFOLDER - - - TGS.Server - {f32eda25-0855-411c-af5e-f0d042917e2d} - True - True - Binaries;Content;Satellites - INSTALLFOLDER - - - TGS.Interface - {ac4e7e8b-f83a-481c-a8b0-8fa4e8ae59ab} - True - True - Binaries;Content;Satellites - INSTALLFOLDER - - - - - - - - - - - - - - - powershell -Command "& \"$(SolutionDir)Tools/SignBasics.ps1\"" - - - powershell -Command "& \"$(SolutionDir)Tools/SignMSI.ps1\"" - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - \ No newline at end of file diff --git a/TGS.Installer/packages.config b/TGS.Installer/packages.config deleted file mode 100644 index 8ab6137aee..0000000000 --- a/TGS.Installer/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/TGS.Interface.Bridge/DreamDaemonBridge.cs b/TGS.Interface.Bridge/DreamDaemonBridge.cs deleted file mode 100644 index 8465beb682..0000000000 --- a/TGS.Interface.Bridge/DreamDaemonBridge.cs +++ /dev/null @@ -1,35 +0,0 @@ -using RGiesecke.DllExport; -using System; -using System.Collections.Generic; -using System.Runtime.InteropServices; - -namespace TGS.Interface.Bridge -{ - /// - /// Holds the proc that DD calls to access - /// - public static class DreamDaemonBridge - { - /// - /// The proc that DD calls to access - /// - /// The number of arguments passed - /// The arguments passed - /// 0 - [DllExport("DDEntryPoint", CallingConvention = CallingConvention.Cdecl)] - public static int DDEntryPoint(int argc, [MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.LPStr, SizeParamIndex = 0)]string[] args) - { - try - { - var parsedArgs = new List(); - parsedArgs.AddRange(args); - var instance = parsedArgs[0]; - parsedArgs.RemoveAt(0); - using (var I = new Client()) - I.Server.GetInstance(instance).Interop.InteropMessage(String.Join(" ", parsedArgs)); - } - catch { } - return 0; - } - } -} diff --git a/TGS.Interface.Bridge/FodyWeavers.xml b/TGS.Interface.Bridge/FodyWeavers.xml deleted file mode 100644 index 43fc6a6308..0000000000 --- a/TGS.Interface.Bridge/FodyWeavers.xml +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/TGS.Interface.Bridge/Properties/AssemblyInfo.cs b/TGS.Interface.Bridge/Properties/AssemblyInfo.cs deleted file mode 100644 index 7195e9f298..0000000000 --- a/TGS.Interface.Bridge/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("TGStation Server Service DreamDaemon Bridge")] -[assembly: AssemblyDescription("Used by DreamDaemon to call into the TGStation Server Service")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("9a01ef03-8eae-45cb-8b87-4a17bd904557")] diff --git a/TGS.Interface.Bridge/TGS.Interface.Bridge.csproj b/TGS.Interface.Bridge/TGS.Interface.Bridge.csproj deleted file mode 100644 index d960e47808..0000000000 --- a/TGS.Interface.Bridge/TGS.Interface.Bridge.csproj +++ /dev/null @@ -1,78 +0,0 @@ - - - - - Debug - AnyCPU - {9A01EF03-8EAE-45CB-8B87-4A17BD904557} - Library - Properties - TGS.Interface.Bridge - TGDreamDaemonBridge - v4.5.2 - 512 - x86 - - - - - - true - bin\x86\Debug\ - DEBUG;TRACE - full - x86 - prompt - MinimumRecommendedRules.ruleset - - - bin\x86\Release\ - TRACE - true - pdbonly - x86 - prompt - MinimumRecommendedRules.ruleset - - - false - - - - - - - - - ..\packages\Costura.Fody.1.6.2\lib\dotnet\Costura.dll - False - - - ..\packages\UnmanagedExports.1.2.7\lib\net\RGiesecke.DllExport.Metadata.dll - False - - - - - - - - - - - {ac4e7e8b-f83a-481c-a8b0-8fa4e8ae59ab} - TGS.Interface - - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - - \ No newline at end of file diff --git a/TGS.Interface.Bridge/packages.config b/TGS.Interface.Bridge/packages.config deleted file mode 100644 index 3d97405485..0000000000 --- a/TGS.Interface.Bridge/packages.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - \ No newline at end of file diff --git a/TGS.Interface/AuthenticationHeaderApplicator.cs b/TGS.Interface/AuthenticationHeaderApplicator.cs deleted file mode 100644 index 6031f4eb68..0000000000 --- a/TGS.Interface/AuthenticationHeaderApplicator.cs +++ /dev/null @@ -1,97 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Reflection; -using System.ServiceModel; -using System.ServiceModel.Channels; -using System.ServiceModel.Dispatcher; -using System.ServiceModel.Description; - -namespace TGS.Interface -{ - /// - /// Used to attach windows credential headers to SOAP messages - /// - sealed class AuthenticationHeaderApplicator : IEndpointBehavior, IClientMessageInspector - { - /// - /// The credentials to attach - /// - readonly RemoteLoginInfo remoteLoginInfo; - - /// - /// Construct a - /// - /// The to use - public AuthenticationHeaderApplicator(RemoteLoginInfo loginInfo) - { - remoteLoginInfo = loginInfo; - } - - /// - /// Add to the message inspectors for the channel - /// - /// The - /// The - public void ApplyClientBehavior(ServiceEndpoint endpoint, ClientRuntime clientRuntime) - { - var t = Type.GetType("Mono.Runtime"); - ICollection inspectors; - if (t != null) - { - var prop = clientRuntime.GetType() - .GetTypeInfo() - .GetDeclaredProperty("MessageInspectors"); - inspectors = (ICollection)prop - .GetValue(clientRuntime); - } - else - inspectors = clientRuntime.ClientMessageInspectors; - inspectors.Add(this); - } - - /// - /// Attach and to the - /// - /// The outgoing request - /// The - /// - public object BeforeSendRequest(ref Message request, IClientChannel channel) - { - request.Headers.Add(MessageHeader.CreateHeader("Username", "http://tempuri.org", remoteLoginInfo.Username)); - request.Headers.Add(MessageHeader.CreateHeader("Password", "http://tempuri.org", remoteLoginInfo.Password)); - return null; - } - - /// - /// Unused implementation of - /// - public void AddBindingParameters(ServiceEndpoint endpoint, BindingParameterCollection bindingParameters) - { - //intentionally left blank - } - - /// - /// Unused implementation of - /// - public void AfterReceiveReply(ref Message reply, object correlationState) - { - //intentionally left blank - } - - /// - /// Unused implementation of - /// - public void ApplyDispatchBehavior(ServiceEndpoint endpoint, EndpointDispatcher endpointDispatcher) - { - //intentionally left blank - } - - /// - /// Unused implementation of - /// - public void Validate(ServiceEndpoint endpoint) - { - //intentionally left blank - } - } -} diff --git a/TGS.Interface/ChatSetupInfo.cs b/TGS.Interface/ChatSetupInfo.cs deleted file mode 100644 index 91cde83a75..0000000000 --- a/TGS.Interface/ChatSetupInfo.cs +++ /dev/null @@ -1,344 +0,0 @@ -using Newtonsoft.Json; -using System; -using System.Collections.Generic; -using System.Runtime.Serialization; - -namespace TGS.Interface -{ - /// - /// For setting up authentication no matter the chat provider - /// - [DataContract] - [KnownType(typeof(IRCSetupInfo))] - [KnownType(typeof(DiscordSetupInfo))] - public class ChatSetupInfo - { - const int AdminListIndex = 0; - const int AdminModeIndex = 1; - const int AdminChannelIndex = 2; - const int DevChannelIndex = 3; - const int WDChannelIndex = 4; - const int GameChannelIndex = 5; - const int ProviderIndex = 6; - const int EnabledIndex = 7; - /// - /// Starting index of which child classes should use to write their custom data to - /// - protected const int BaseIndex = 8; - /// - /// Set to if a child constructor should use the baseInfo parameter of to initialize it's property fields, otherwise - /// - protected readonly bool InitializeFields; - - /// - /// Raw access to the underlying data - /// - [DataMember] - public IList DataFields { get; protected set; } - - /// - /// Constructs a from optional - /// - /// The that this is for - /// Optional past data - /// The number of fields in this chat provider - protected internal ChatSetupInfo(ChatProvider provider, ChatSetupInfo baseInfo, int numFields) - { - numFields += BaseIndex; - InitializeFields = baseInfo == null || baseInfo.DataFields.Count != numFields; - - if (InitializeFields) - { - DataFields = new List(numFields); - for (var I = 0; I < numFields; ++I) - DataFields.Add(null); - - AdminList = new List(); - AdminChannels = new List(); - DevChannels = new List(); - GameChannels = new List(); - WatchdogChannels = new List(); - AdminsAreSpecial = false; - Enabled = false; - } - else - DataFields = baseInfo.DataFields; - Provider = provider; - Specialize(true); //to check we have a valid provider - } - - /// - /// Recreates as the correct child - /// - /// If , is returned provided is a valid - /// A new based on the type - ChatSetupInfo Specialize(bool checkOnly) - { - switch (Provider) - { - case ChatProvider.IRC: - if (!checkOnly) - return new IRCSetupInfo(this); - break; - case ChatProvider.Discord: - if (!checkOnly) - return new DiscordSetupInfo(this); - break; - default: - throw new Exception("Invalid provider!"); - } - return null; - } - - /// - /// Properly formats a name for the - /// - /// The to format - /// The formatted - protected virtual string SanitizeChannelName(string channel) - { - return Specialize(false).SanitizeChannelName(channel); - } - - /// - /// Sanitizes a list of - /// - /// A of strings - void SanitizeChannelNames(IList channelnames) - { - for (var I = 0; I < channelnames.Count; ++I) - - if (String.IsNullOrWhiteSpace(channelnames[I])) - { - channelnames.RemoveAt(I); - --I; - } - else - channelnames[I] = SanitizeChannelName(channelnames[I].Trim()); - } - - /// - /// Constructs a from a data list - /// - /// The data - public ChatSetupInfo(IList DeserializedData) - { - DataFields = DeserializedData; - Specialize(false); //ensure provider type is valid - } - /// - /// The list of admin entries - /// - public List AdminList - { - get { return JsonConvert.DeserializeObject>(DataFields[AdminListIndex]); } - set { DataFields[AdminListIndex] = JsonConvert.SerializeObject(value); } - } - /// - /// If AdminList corresponds to a Provider specific recognization method - /// - public bool AdminsAreSpecial - { - get { return Convert.ToBoolean(DataFields[AdminModeIndex]); } - set { DataFields[AdminModeIndex] = Convert.ToString(value); } - } - /// - /// The channels from which admin commands/messages can be sent/received - /// - public List AdminChannels - { - get { return JsonConvert.DeserializeObject>(DataFields[AdminChannelIndex]); } - set - { - SanitizeChannelNames(value); - DataFields[AdminChannelIndex] = JsonConvert.SerializeObject(value); - } - } - /// - /// The channels to which repo and compile messages are sent - /// - public List DevChannels - { - get { return JsonConvert.DeserializeObject>(DataFields[DevChannelIndex]); } - set - { - SanitizeChannelNames(value); - DataFields[DevChannelIndex] = JsonConvert.SerializeObject(value); - } - } - /// - /// The channels to which watchdog messages are sent - /// - public List WatchdogChannels - { - get { return JsonConvert.DeserializeObject>(DataFields[WDChannelIndex]); } - set - { - SanitizeChannelNames(value); - DataFields[WDChannelIndex] = JsonConvert.SerializeObject(value); - } - } - /// - /// The channels to which game messages are sent - /// - public List GameChannels - { - get { return JsonConvert.DeserializeObject>(DataFields[GameChannelIndex]); } - set - { - SanitizeChannelNames(value); - DataFields[GameChannelIndex] = JsonConvert.SerializeObject(value); - } - } - /// - /// If this chat provider is enabled - /// - public bool Enabled - { - get { return Convert.ToBoolean(DataFields[EnabledIndex]); } - set { DataFields[EnabledIndex] = Convert.ToString(value); } - } - - /// - /// The type of provider - /// - public ChatProvider Provider - { - get { return (ChatProvider)Convert.ToInt32(DataFields[ProviderIndex]); } - set { DataFields[ProviderIndex] = Convert.ToString((int)value); } - } - } - - /// - /// Chat provider for IRC. Admin entries should be user nicknames in normal mode or required channel flags in special mode - /// - [DataContract] - public sealed class IRCSetupInfo : ChatSetupInfo - { - const int URLIndex = 0; - const int PortIndex = 1; - const int NickIndex = 2; - const int AuthTargetIndex = 3; - const int AuthMessageIndex = 4; - const int AuthLevelIndex = 5; - const int FieldsLen = 6; - - /// - /// Construct IRC setup info from optional generic info. Defaults to TGS3 on rizons IRC server - /// - /// Optional generic info - public IRCSetupInfo(ChatSetupInfo baseInfo = null) : base(ChatProvider.IRC, baseInfo, FieldsLen) - { - if (!InitializeFields) - return; - - Nickname = "TGS3"; - URL = "irc.rizon.net"; - Port = 6667; - AuthTarget = ""; - AuthMessage = ""; - AdminsAreSpecial = true; - AuthLevel = IRCMode.Op; - } - - /// - protected override string SanitizeChannelName(string working) - { - if (working[0] != '#') - return "#" + working; - return working; - } - - /// - /// The port of the IRC server - /// - public ushort Port - { - get { return Convert.ToUInt16(DataFields[BaseIndex + PortIndex]); } - set { DataFields[BaseIndex + PortIndex] = value.ToString(); } - } - /// - /// The URL of the IRC server - /// - public string URL - { - get { return DataFields[BaseIndex + URLIndex]; } - set { DataFields[BaseIndex + URLIndex] = value; } - } - /// - /// The nickname of the IRC bot - /// - public string Nickname - { - get { return DataFields[BaseIndex + NickIndex]; } - set { DataFields[BaseIndex + NickIndex] = value; } - } - /// - /// The target for sending authentication messages - /// - public string AuthTarget - { - get { return DataFields[BaseIndex + AuthTargetIndex]; } - set { DataFields[BaseIndex + AuthTargetIndex] = value; } - } - /// - /// The authentication message - /// - public string AuthMessage - { - get { return DataFields[BaseIndex + AuthMessageIndex]; } - set { DataFields[BaseIndex + AuthMessageIndex] = value; } - } - /// - /// The minimum mode required to use admin bot commands when in special auth mode - /// - public IRCMode AuthLevel - { - get { return (IRCMode)Convert.ToInt32(DataFields[BaseIndex + AuthLevelIndex]); } - set { DataFields[BaseIndex + AuthLevelIndex] = Convert.ToString((int)value); } - } - } - - /// - /// Chat provider for Discord. Admin entires should be user ids in normal mode or group ids in special mode - /// - [DataContract] - public sealed class DiscordSetupInfo : ChatSetupInfo - { - const int BotTokenIndex = 0; - const int FieldsLen = 1; - /// - /// Construct Discord setup info from optional generic info. Default is not a valid discord bot tokent - /// - /// Optional generic info - public DiscordSetupInfo(ChatSetupInfo baseInfo = null) : base(ChatProvider.Discord, baseInfo, FieldsLen) - { - if (!InitializeFields) - return; - BotToken = "aaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa"; //needless to say, this is fake - } - /// - protected override string SanitizeChannelName(string working) - { - working = working.Replace("<", "").Replace(">", "").Replace("&", ""); //filter out some stuff that can come in the copypasta - try - { - Convert.ToUInt64(working); - } - catch - { - throw new Exception("Invalid Discord channel ID!"); - } - return working; - } - - /// - /// The Discord bot token to use. See https://discordapp.com/developers/applications/me for registering bot accounts - /// - public string BotToken - { - get { return DataFields[BaseIndex + BotTokenIndex]; } - set { DataFields[BaseIndex + BotTokenIndex] = value; } - } - } -} diff --git a/TGS.Interface/Client.cs b/TGS.Interface/Client.cs deleted file mode 100644 index ef367d62e3..0000000000 --- a/TGS.Interface/Client.cs +++ /dev/null @@ -1,318 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Collections.ObjectModel; -using System.Net; -using System.Net.Security; -using System.Reflection; -using System.Security.Principal; -using System.ServiceModel; -using System.ServiceModel.Description; -using TGS.Interface.Components; - -namespace TGS.Interface -{ - /// - sealed public class Client : IClient - { - /// - /// Version of the - /// - public static readonly Version Version = Assembly.GetExecutingAssembly().GetName().Version; - - /// - public IServer Server => server; - - /// - public Version ServerVersion { get - { - lock (this) - if (_serverVersion == null) - { - string rawVersion; - //check ITGSService first for compatiblity reasons - try - { - rawVersion = GetComponent(null).Version(); - } - catch - { - rawVersion = GetComponent(null).Version(); - } - var splits = rawVersion.Split(' '); - _serverVersion = new Version(splits[splits.Length - 1].Substring(1)); - } - return _serverVersion; - } } - - /// - public string InstanceName { get; private set; } - - /// - public RemoteLoginInfo LoginInfo { get { return _loginInfo; } } - - /// - /// Backing field for - /// - readonly IServer server; - - /// - /// Backing field for - /// - readonly RemoteLoginInfo _loginInfo; - - /// - /// The - /// - Version _serverVersion; - - /// - /// Associated list of open s keyed by type name. A in this list may close or fault at any time. must be locked before being accessed - /// - IDictionary ChannelFactoryCache; - - /// - /// Sets the function called when a remote login fails due to the server having an invalid SSL cert - /// - /// The to be called when a remote login is attempted while the server posesses a bad certificate. Passed a of error information about the and should return if it the connection should be made anyway - public static void SetBadCertificateHandler(Func handler) - { - ServicePointManager.ServerCertificateValidationCallback = (sender, cert, chain, error) => - { - string ErrorMessage; - switch (error) - { - case SslPolicyErrors.None: - return true; - case SslPolicyErrors.RemoteCertificateChainErrors: - ErrorMessage = "There are certificate chain errors."; - break; - case SslPolicyErrors.RemoteCertificateNameMismatch: - ErrorMessage = "The certificate name does not match."; - break; - case SslPolicyErrors.RemoteCertificateNotAvailable: - ErrorMessage = "The certificate doesn't exist in the trust store."; - break; - default: - ErrorMessage = "An unknown error occurred."; - break; - } - ErrorMessage = String.Format("The server's certificate failed to verify! Error: {0} Cert: {1}", ErrorMessage, cert.ToString()); - return handler(ErrorMessage); - }; - } - - /// - /// Construct an for a local connection - /// - public Client() - { - ChannelFactoryCache = new Dictionary(); - server = new Server(this); - } - - /// - /// Construct an for a remote connection - /// - /// The for a remote connection - public Client(RemoteLoginInfo loginInfo) : this() - { - if (!loginInfo.HasPassword) - throw new InvalidOperationException("password must be set on loginInfo!"); - _loginInfo = loginInfo; - } - - /// - public bool IsRemoteConnection { get { return LoginInfo != null; } } - - /// - /// Closes all s stored in and it - /// - public void Dispose() - { - lock (this) - { - if (ChannelFactoryCache == null) - return; - foreach (var I in ChannelFactoryCache) - { - var cf = I.Value; - try - { - cf.Close(); - } - catch - { - cf.Abort(); - } - } - ChannelFactoryCache = null; - } - } - - /// - public bool VersionMismatch(out string errorMessage) - { - if (ServerVersion.Major != Version.Major || ServerVersion.Minor != Version.Minor || ServerVersion.Build != Version.Build) //don't care about the patch level - { - errorMessage = String.Format("Version mismatch between interface version ({0}) and service version ({1}). Some functionality may crash this program.", Version, ServerVersion); - return true; - } - errorMessage = null; - return false; - } - - /// - /// Returns the requested component for the instance . This does not guarantee a successful connection. s created this way are recycled for minimum latency and bandwidth usage - /// - /// The component to retrieve - /// The name of the to use - /// The correct component - internal T GetComponent(string instanceName) - { - var actualToT = typeof(T); - var tot = actualToT.Name; - if (instanceName != null) - tot = instanceName + tot; - ChannelFactory cf; - - lock (this) - { - if (ChannelFactoryCache == null) - throw new ObjectDisposedException(GetType().Name); - if (ChannelFactoryCache.ContainsKey(tot)) - try - { - cf = ((ChannelFactory)ChannelFactoryCache[tot]); - if (cf.State != CommunicationState.Opened) - throw new Exception(); - return cf.CreateChannel(); - } - catch - { - ChannelFactoryCache[tot].Abort(); - ChannelFactoryCache.Remove(tot); - } - cf = CreateChannel(instanceName); - ChannelFactoryCache[tot] = cf; - } - return cf.CreateChannel(); - } - - /// - /// Directly creates a for without caching. This should be eventually closed by the caller - /// - /// The component of the channel to be created - /// The instance to connect to, if any - /// The correct - ChannelFactory CreateChannel(string instanceName) - { - var accessPath = instanceName == null ? Definitions.MasterInterfaceName : String.Format("{0}/{1}", Definitions.InstanceInterfaceName, instanceName); - if (!IsRemoteConnection) - return CreateLocalChannel(instanceName, accessPath); - return CreateRemoteChannel(instanceName, accessPath); - } - - //NOTE: This needs to be kept seperate from CreateRemoteChannel because not all our client implementations have NetNamedPipeBinding and attempting to call this function on those platforms will throw an exception - - /// - /// Directly creates a for on a local connection without caching. This should be eventually closed by the caller - /// - /// The component of the channel to be created - /// The instance to connect to, if any - /// The URL of the interface to connect to - /// The correct - ChannelFactory CreateLocalChannel(string instanceName, string accessPath) - { - var interfaceName = typeof(T).Name; - var res2 = new ChannelFactory( - new NetNamedPipeBinding { SendTimeout = new TimeSpan(0, 0, 30), MaxReceivedMessageSize = Definitions.TransferLimitLocal }, new EndpointAddress(String.Format("net.pipe://localhost/{0}/{1}", accessPath, interfaceName))); //10 megs - res2.Credentials.Windows.AllowedImpersonationLevel = TokenImpersonationLevel.Impersonation; - return res2; - } - - /// - /// Directly creates a for on a remote connection without caching. This should be eventually closed by the caller - /// - /// The component of the channel to be created - /// The instance to connect to, if any - /// The URL of the interface to connect to - /// The correct - ChannelFactory CreateRemoteChannel(string instanceName, string accessPath) - { - //tls memes - ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12; - //okay we're going over - var binding = new BasicHttpsBinding() - { - SendTimeout = new TimeSpan(0, 0, 40), - MaxReceivedMessageSize = Definitions.TransferLimitRemote - }; - - var interfaceName = typeof(T).Name; - var requireAuth = interfaceName != typeof(ITGConnectivity).Name; - var url = String.Format("https://{0}:{1}/{2}/{3}", LoginInfo.IP, LoginInfo.Port, accessPath, interfaceName); - var address = new EndpointAddress(url); - var res = new ChannelFactory(binding, address); - if (requireAuth) - { - var applicator = new AuthenticationHeaderApplicator(LoginInfo); - var t = Type.GetType("Mono.Runtime"); - KeyedCollection behaviours; - if (t != null) - { - var prop = res.Endpoint.GetType() - .GetTypeInfo() - .GetDeclaredProperty("Behaviors"); - behaviours = (KeyedCollection)prop - .GetValue(res.Endpoint); - } - else - behaviours = res.Endpoint.EndpointBehaviors; - behaviours.Add(applicator); - } - return res; - } - - /// - public ConnectivityLevel ConnectionStatus() - { - return ConnectionStatus(out string unused); - } - - /// - public ConnectivityLevel ConnectionStatus(out string error) - { - try - { - GetComponent(null).VerifyConnection(); - } - catch (Exception e) - { - error = e.ToString(); - return ConnectivityLevel.None; - } - - try - { - GetComponent(null).Version(); - } - catch(Exception e) - { - error = e.ToString(); - return ConnectivityLevel.Connected; - } - - try - { - GetComponent(null).Version(); - error = null; - return ConnectivityLevel.Administrator; - } - catch(Exception e) - { - error = e.ToString(); - return ConnectivityLevel.Authenticated; - } - } - } -} diff --git a/TGS.Interface/Command.cs b/TGS.Interface/Command.cs deleted file mode 100644 index ac64ab1914..0000000000 --- a/TGS.Interface/Command.cs +++ /dev/null @@ -1,92 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; - -namespace TGS.Interface -{ - /// - /// Helper for creating a text tree - /// - public abstract class Command - { - /// - /// Exit codes for s - /// - public enum ExitCode : int - { - /// - /// The ran successfully - /// - Normal = 0, - /// - /// The connection to the service was interrupted during the - /// - ConnectionError = 1, - /// - /// Invalid parameters for - /// - BadCommand = 2, - /// - /// The command failed due to conditions on the service - /// - ServerError = 3, - } - /// - /// Proc that will show a message to the invoker. Do not call directly, use instead - /// - public static ThreadLocal> OutputProcVar = new ThreadLocal>(); - /// - /// Write output to the invoker - /// - /// The output to display - protected static void OutputProc(string message) - { - OutputProcVar.Value(message); - } - /// - /// The text that invokes this . Set in constructor - /// - public string Keyword { get; protected set; } - /// - /// The number of parameters this requires. Set in Constructor - /// - public int RequiredParameters { get; protected set; } - /// - /// Caller of , can be used to modify the root behaviour of the - /// - /// List of parameters passed to the - /// An describing the execution of the - public virtual ExitCode DoRun(IList parameters) - { - return Run(parameters); - } - /// - /// Override to do the actions of the - /// - /// List of parameters passed to the . Guaranteed to have at least non-empty/whitespace entries - /// An describing the execution of the - protected abstract ExitCode Run(IList parameters); - /// - /// Prints usage text of the to the invoker - /// - public virtual void PrintHelp() - { - var argstr = GetArgumentString(); - OutputProc(String.Format("{0} {1}- {2}", Keyword, argstr.Length > 0 ? argstr + " " : "", GetHelpText())); - } - /// - /// Override to add argument text to the - /// Format is <required> <arguments> [optional] [arguments] - /// - /// Formatted argument text for the - public virtual string GetArgumentString() - { - return ""; - } - /// - /// Override to add usage text to the - /// - /// Formatted usage text for the - public abstract string GetHelpText(); - } -} diff --git a/TGS.Interface/Components/Administration.cs b/TGS.Interface/Components/Administration.cs deleted file mode 100644 index 884c5b725a..0000000000 --- a/TGS.Interface/Components/Administration.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.ServiceModel; - -namespace TGS.Interface.Components -{ - /// - /// Manage the group that is used to access the service, can only be used by an administrator - /// - [ServiceContract] - public interface ITGAdministration - { - /// - /// Returns the name of the windows group allowed to use the service other than administrator - /// - /// The name of the windows group allowed to use the service other than administrator, "ADMIN" if it's unset, on failure - [OperationContract] - string GetCurrentAuthorizedGroup(); - - /// - /// Searches the windows machine for the group named , sets it as the authorized group if it's found - /// - /// The name of the windows group to search for or null to clear the setting - /// The name of the windows group that is now authorized to use the service on success, on failure, "ADMIN" on clearing - [OperationContract] - string SetAuthorizedGroup(string groupName); - - /// - /// Renames the current static folder to a backup name and recreates is from the current repo using TGS3.json - /// - /// on success, error message on failure - [OperationContract] - string RecreateStaticFolder(); - } -} diff --git a/TGS.Interface/Components/Byond.cs b/TGS.Interface/Components/Byond.cs deleted file mode 100644 index 7563000999..0000000000 --- a/TGS.Interface/Components/Byond.cs +++ /dev/null @@ -1,43 +0,0 @@ -using System.ServiceModel; - -namespace TGS.Interface.Components -{ - - /// - /// For managing the BYOND installation the server runs - /// - [ServiceContract] - public interface ITGByond - { - /// - /// Gets the current status of any BYOND updates - /// - /// The current status of the byond updater - [OperationContract] - ByondStatus CurrentStatus(); - - /// - /// updates the used byond version to that of version .. The change won't take place until DD reboots. Calls if blocked by a running DD instance. Runs asyncronously, use to check progress - /// - /// Major BYOND version. E.g. 511 - /// Minor BYOND version. E.g. 1381 - /// if the update started, if another operation was in progress or DreamDaemon is running - [OperationContract] - bool UpdateToVersion(int major, int minor); - - /// - /// Check the last update error. Checking this will clear the value - /// - /// The last update error, if any. otherwise. - [OperationContract] - string GetError(); - - /// - /// Get the currently installed version as a string formatted as Major.Minor - /// - /// The type of version to retrieve - /// if no version is detected, the version string otherwise - [OperationContract] - string GetVersion(ByondVersion type); - } -} diff --git a/TGS.Interface/Components/Chat.cs b/TGS.Interface/Components/Chat.cs deleted file mode 100644 index 5333e4e649..0000000000 --- a/TGS.Interface/Components/Chat.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System.Collections.Generic; -using System.ServiceModel; - -namespace TGS.Interface.Components -{ - /// - /// Interface for handling chat bot - /// - [ServiceContract] - public interface ITGChat - { - /// - /// Sets a chat provider - /// - /// The info to set - [OperationContract] - string SetProviderInfo(ChatSetupInfo info); - /// - /// Returns for all s - /// - /// A list of all s - [OperationContract] - IList ProviderInfos(); - - /// - /// Checks connection status - /// - /// The type of provider to check if connected - /// if connected, otherwise - [OperationContract] - bool Connected(ChatProvider providerType); - - /// - /// Reconnect a specific to it's chat service - /// - /// The type of provider to reconnect - /// on success, error message on failure - [OperationContract] - string Reconnect(ChatProvider providerType); - } -} diff --git a/TGS.Interface/Components/Compiler.cs b/TGS.Interface/Components/Compiler.cs deleted file mode 100644 index ce87ff125e..0000000000 --- a/TGS.Interface/Components/Compiler.cs +++ /dev/null @@ -1,61 +0,0 @@ -using System.ServiceModel; - -namespace TGS.Interface.Components -{ - /// - /// For managing the Game A/B/Live folders, compiling, and hotswapping them - /// - [ServiceContract] - public interface ITGCompiler - { - /// - /// Sets up the symlinks for hotswapping game code. This will reset everything in the Game folder. Requires the repository to be set up and locks it once the compilation stage starts. Runs asyncronously from this call - /// - /// if the operation began, if it could not start - [OperationContract] - bool Initialize(); - - /// - /// Does all the necessary actions to take the revision currently in the repository and compile it to be run on the next server reboot. Requires BYOND to be set up and the to return . Runs asyncronously - /// - /// If no message for compilation start will be printed - /// if the operation began, if it could not start - [OperationContract] - bool Compile(bool silent = false); - - /// - /// Cancels the current compilation - /// - /// on success, error message on failure - [OperationContract] - string Cancel(); - - /// - /// Returns the current compiler status - /// - /// The current compiler status - [OperationContract] - CompilerStatus GetStatus(); - - /// - /// Returns the error message of the last operation. Reading this will clear the stored value - /// - /// the error message of the last operation if it failed or if it succeeded - [OperationContract] - string CompileError(); - - /// - /// Returns the relative path of the dme the compiler will look for without the .dme part - /// - /// The relative path of the dme the compiler will look for without the .dme part - [OperationContract] - string ProjectName(); - - /// - /// Sets the relative path of the dme the compiler will look for without the .dme part - /// - /// The relative path of the dme the compiler will look for without the .dme part - [OperationContract] - void SetProjectName(string projectName); - } -} diff --git a/TGS.Interface/Components/Config.cs b/TGS.Interface/Components/Config.cs deleted file mode 100644 index 2e9409228e..0000000000 --- a/TGS.Interface/Components/Config.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System.Collections.Generic; -using System.ServiceModel; - -namespace TGS.Interface.Components -{ - /// - /// For modifying the in game config - /// Most if not all of these will not apply until the next server reboot - /// - [ServiceContract] - public interface ITGConfig - { - - /// - /// Returns the file contents of the specified server directory - /// Subdirectories will be prefixed with '/' - /// - /// Subdirectory to enumerate, enumerates the root directory if null - /// null on success, error message on failure - /// This will be true if error is set to a message that indicates the current user does not have access to the specified file - /// A list of files in the enumerated static directory on success, null on failure - [OperationContract] - IList ListStaticDirectory(string subpath, out string error, out bool unauthorized); - - /// - /// Read from a static file - /// - /// The path from the Static dir. E.g. config/config.txt - /// if true, the file will be read from the repository instead of the static dir - /// null on success, error message on failure - /// This will be true if error is set to a message that indicates the current user does not have access to the specified file - /// The full text of the file on success, null on failure - /// Along with implied disconnect exceptions, if the file exceeds transfer limits - [OperationContract] - string ReadText(string staticRelativePath, bool repo, out string error, out bool unauthorized); - - /// - /// Write to a static file - /// - /// The path from the Static dir. E.g. config/config.txt - /// The full text of the config file - /// The original data that the client knows about. Set to null to skip checks - /// This will be true if error is set to a message that indicates the current user does not have access to the specified file - /// null on success, error message on failure - /// Along with implied disconnect exceptions, if the file exceeds transfer limits - [OperationContract] - string WriteText(string staticRelativePath, string data, string originalData, out bool unauthorized); - - /// - /// Deletes the target static file - /// - /// The path from the Static dir. E.g. config/config.txt - /// This will be true if error is set to a message that indicates the current user does not have access to the specified file - /// null on success, error message on failure - [OperationContract] - string DeleteFile(string staticRelativePath, out bool unauthorized); - } -} diff --git a/TGS.Interface/Components/Connectivity.cs b/TGS.Interface/Components/Connectivity.cs deleted file mode 100644 index d7c5b57f64..0000000000 --- a/TGS.Interface/Components/Connectivity.cs +++ /dev/null @@ -1,17 +0,0 @@ -using System.ServiceModel; - -namespace TGS.Interface.Components -{ - /// - /// Used for testing connections to the service without authentication - /// - [ServiceContract] - public interface ITGConnectivity - { - /// - /// Does nothing on the server end, but if the call completes, you can be sure you are connected. WCF won't throw until you try until you actually use the API - /// - [OperationContract] - void VerifyConnection(); - } -} diff --git a/TGS.Interface/Components/DreamDaemon.cs b/TGS.Interface/Components/DreamDaemon.cs deleted file mode 100644 index 0892e3f8a1..0000000000 --- a/TGS.Interface/Components/DreamDaemon.cs +++ /dev/null @@ -1,146 +0,0 @@ -using System.ServiceModel; - -namespace TGS.Interface.Components -{ - - /// - /// Interface for managing the actual BYOND game server - /// - [ServiceContract] - public interface ITGDreamDaemon - { - /// - /// Gets the status of DreamDaemon - /// - /// The appropriate - [OperationContract] - DreamDaemonStatus DaemonStatus(); - - /// - /// Returns a human readable string of the current server status - /// - /// If , the status will include the server's current visibility and security levels - /// A human readable of the current server status - [OperationContract] - string StatusString(bool includeMetaInfo); - - /// - /// Check if a call to will fail. Of course, be aware of race conditions with other interfaces - /// - /// The error that would occur, otherwise - [OperationContract] - string CanStart(); - - /// - /// Starts the server if it isn't running - /// - /// on success or error message on failure - [OperationContract] - string Start(); - - /// - /// Immediately kills the server - /// - /// on success or error message on failure - [OperationContract] - string Stop(); - - /// - /// Immediately kills and restarts the server - /// - /// on success or error message on failure - [OperationContract] - string Restart(); - - /// - /// Restart the server after the currently running world reboots. Has no effect if the server isn't running - /// - [OperationContract] - void RequestRestart(); - - /// - /// Stop the server after the currently running world reboots. Has no effect if the server isn't running - /// - [OperationContract] - void RequestStop(); - - /// - /// Get the configured (not necessarily running) security level - /// - /// The configured (not necessarily running) - [OperationContract] - DreamDaemonSecurity SecurityLevel(); - - /// - /// Sets the security level of the server. Requires server reboot to apply. Calls . Note that anything higher than Trusted will disable interop from DD - /// - /// The new security level - /// if the change was immediately applied, otherwise and a call to was made - [OperationContract] - bool SetSecurityLevel(DreamDaemonSecurity level); - - /// - /// Get the configured port. Not necessarily the running port if it has since changed - /// - /// The configured port - [OperationContract] - ushort Port(); - - /// - /// Set the port to host DD on. Requires reboot to apply. Calls . - /// - /// The new port - [OperationContract] - void SetPort(ushort new_port); - - /// - /// Check if the watchdog will start when the service starts - /// - /// if autostart is enabled, otherwise - [OperationContract] - bool Autostart(); - - /// - /// Set the autostart config - /// - /// to start the watchdog with the service, to disable that functionality - [OperationContract] - void SetAutostart(bool on); - - /// - /// Check if the BYOND webclient is currently enabled for the server - /// - /// if the webclient is enabled, otherwise - [OperationContract] - bool Webclient(); - - /// - /// Set the webclient config. Calls - /// - /// to enable the byond webclient for the server, otherwise - [OperationContract] - void SetWebclient(bool on); - - /// - /// Checks if a server stop has been requested - /// - /// if has been called since the last server start, otherwise - [OperationContract] - bool ShutdownInProgress(); - - /// - /// Sends a message to everyone on the server - /// - /// The message to send - /// on success, error message on failure - [OperationContract] - string WorldAnnounce(string msg); - - /// - /// Returns the number of connected players. Requires game to use API version >= 3.1.0.1 - /// - /// The number of connected players or -1 on error - [OperationContract] - int PlayerCount(); - } -} diff --git a/TGS.Interface/Components/Instance.cs b/TGS.Interface/Components/Instance.cs deleted file mode 100644 index 1cc79e153b..0000000000 --- a/TGS.Interface/Components/Instance.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.ServiceModel; - -namespace TGS.Interface.Components -{ - /// - /// Metadata for a server instance - /// - [ServiceContract] - public interface ITGInstance - { - /// - /// Return the directory of the server on the host machine - /// - /// The path to the directory on success, null on failure - [OperationContract] - string ServerDirectory(); - - /// - /// Retrieve's the service's version - /// - /// The service's version - [OperationContract] - string Version(); - } -} diff --git a/TGS.Interface/Components/InstanceManager.cs b/TGS.Interface/Components/InstanceManager.cs deleted file mode 100644 index f148acb644..0000000000 --- a/TGS.Interface/Components/InstanceManager.cs +++ /dev/null @@ -1,62 +0,0 @@ -using System.ServiceModel; - -namespace TGS.Interface.Components -{ - /// - /// Used for managing s - /// - [ServiceContract] - public interface ITGInstanceManager - { - /// - /// Creates a new - /// - /// The name of the instance - /// The path to the instance - /// on success, error message on failure - [OperationContract] - string CreateInstance(string Name, string path); - - /// - /// Registers an existing server instance - /// - /// The path to the instance - /// on success, error message on failure - [OperationContract] - string ImportInstance(string path); - - /// - /// Checks if an instance is online - /// - /// The name of the instance - /// if the Instance exists and is online, otherwise - [OperationContract] - bool InstanceEnabled(string Name); - - /// - /// Sets an instance's enabled status - /// - /// The instance whom's status should be changed - /// to enable the instance, to disable it - /// on success, error message on failure - [OperationContract] - string SetInstanceEnabled(string Name, bool enabled); - - /// - /// Renames an instance, this will restart the instance if it is enabled - /// - /// The current name of the instance - /// The new name of the instance - /// on success, error message on failure - [OperationContract] - string RenameInstance(string name, string new_name); - - /// - /// Disables and unregisters an instance, allowing the folder and data to be manipulated manually - /// - /// The instance to detach - /// on success, error message on failure - [OperationContract] - string DetachInstance(string name); - } -} diff --git a/TGS.Interface/Components/Interop.cs b/TGS.Interface/Components/Interop.cs deleted file mode 100644 index 80dc2c7c7c..0000000000 --- a/TGS.Interface/Components/Interop.cs +++ /dev/null @@ -1,19 +0,0 @@ -using System.ServiceModel; - -namespace TGS.Interface.Components -{ - /// - /// Used by DreamDaemon to access the interop API with call()(). Restrictions are in place so that only a DreamDaemon instance launched by the service can use this API - /// - [ServiceContract] - public interface ITGInterop - { - /// - /// Called from /world/ExportService(command) - /// - /// The command to run - /// on success, on failure - [OperationContract] - bool InteropMessage(string command); - } -} diff --git a/TGS.Interface/Components/Landing.cs b/TGS.Interface/Components/Landing.cs deleted file mode 100644 index da7cd28b9e..0000000000 --- a/TGS.Interface/Components/Landing.cs +++ /dev/null @@ -1,26 +0,0 @@ -using System.Collections.Generic; -using System.ServiceModel; - -namespace TGS.Interface.Components -{ - /// - /// Used for general authentication and listing s - /// - [ServiceContract] - public interface ITGLanding - { - /// - /// Retrieve's the service's version - /// - /// The service's version - [OperationContract] - string Version(); - - /// - /// List instances that the caller can access - /// - /// A of instance names relating to their paths - [OperationContract] - IList ListInstances(); - } -} diff --git a/TGS.Interface/Components/Repository.cs b/TGS.Interface/Components/Repository.cs deleted file mode 100644 index 6a518fca0a..0000000000 --- a/TGS.Interface/Components/Repository.cs +++ /dev/null @@ -1,204 +0,0 @@ -using System.Collections.Generic; -using System.ServiceModel; - -namespace TGS.Interface.Components -{ - /// - /// Interface for managing the code repository - /// - [ServiceContract] - public interface ITGRepository - { - /// - /// If the repo is currently undergoing an operation - /// - /// if the repo is busy, otherwise - [OperationContract] - bool OperationInProgress(); - - /// - /// Gets the progress of repository operations, not all operations are supported - /// - /// A value between 0 and 100 inclusive representing the progress of the current operation or -1 if the operation cannot be monitored - [OperationContract] - int CheckoutProgress(); - - /// - /// Check if the repository is valid, if not must be called - /// - /// if the repository is valid, otherwise - [OperationContract] - bool Exists(); - - /// - /// Deletes whatever may be left over and clones the repo at and checks out . Will move config and data dirs to a backup location if they exist. Runs asyncronously - /// - /// The address of the repo to clone. If ssh protocol is used, private_key.txt and public_key.txt must exist in the server RepoKey directory. - /// The branch of the repo to checkout - /// on success, error message on failure - [OperationContract] - string Setup(string remote, string branch = "master"); - - /// - /// Gets the sha of the current HEAD - /// - /// If set to true and HEAD is currently a branch, will instead return the sha of the tracked remote branch if it exists - /// on success, error message on failure - /// The sha of the current HEAD on success, on failure - [OperationContract] - string GetHead(bool useTracked, out string error); - - /// - /// Gets the name of the current branch - /// - /// on success, error message on failure - /// The name of the current branch on success, on failure - [OperationContract] - string GetBranch(out string error); - - /// - /// Gets the url of the current origin - /// - /// on success, error message on failure - /// The url of the current origin on success, on failure - [OperationContract] - string GetRemote(out string error); - - /// - /// Hard checks out the passed object name - /// - /// The branch, commit, or tag to checkout - /// on success, error message on failure - [OperationContract] - string Checkout(string objectName); - - /// - /// Fetches the origin and merges it into the current branch - /// - /// If , the operation will perform a hard reset instead of a merge - /// on success, error message on failure - [OperationContract] - string Update(bool reset); - - /// - /// Runs git reset --hard - /// - /// Changes command to git reset --hard origin/branch_name if - /// on success, error message on failure - [OperationContract] - string Reset(bool tracked); - - /// - /// Merges the target pull request into the current branch if the remote is a github repository - /// - /// The github pull request number in the remote repository - /// The SHA of the pull request to merge - /// Suppresses chat messages if - /// on success, error message on failure - [OperationContract] - string MergePullRequest(int PRnumber, string atSHA = null, bool silent = false); - - /// - /// Merges the target pull requests into the current branch if the remote is a github repository - /// - /// of s containing pull request numbers and shas in the remote repository - /// Suppresses chat messages if - /// on success, list of error messages if any fail failure. Those with indexes of those that succeed will have null entries - [OperationContract] - IEnumerable MergePullRequests(IEnumerable pullRequestInfos, bool silent = false); - - /// - /// Get the currently merged pull requests. Note that switching branches will delete this list and switching back won't restore it - /// - /// on success, error message on failure - /// A of - [OperationContract] - List MergedPullRequests(out string error); - - /// - /// Gets the name of the configured git committer - /// - /// The name of the configured git committer - [OperationContract] - string GetCommitterName(); - - /// - /// Sets the name of the configured git committer - /// - /// The name to set - [OperationContract] - void SetCommitterName(string newName); - - /// - /// Gets the email of the configured git committer - /// - /// The email of the configured git committer - [OperationContract] - string GetCommitterEmail(); - - /// - /// Sets the email of the configured git committer - /// - /// The email to set - [OperationContract] - void SetCommitterEmail(string newEmail); - - /// - /// Updates the html changelog - /// - /// on success, error on failure - /// The output of the python script - [OperationContract] - string GenerateChangelog(out string error); - - /// - /// Pushes the paths listed in TGS3.json to the currentl git remote. No other commit differences may exist for this function to succeed - /// - /// on success, error on failure - [OperationContract] - string SynchronizePush(); - - /// - /// List the tagged commits of the repo at which compiles took place - /// - /// on success, error message on failure - /// A of tag name -> commit on success, on failure - [OperationContract] - IDictionary ListBackups(out string error); - - /// - /// Updates the cached TGS3.json to the repo's version. Compiles will not succeed if these two to not match - /// - /// on success, error message on failure - [OperationContract] - string UpdateTGS3Json(); - - /// - /// (De)Activate and set the interval for the automatic server updater - /// - /// Interval to check for updates in minutes, disables if 0 - [OperationContract] - void SetAutoUpdateInterval(ulong newInterval); - - /// - /// Get the current autoupdate interval - /// - /// The current auto update interval or 0 if it's disabled - [OperationContract] - ulong AutoUpdateInterval(); - - /// - /// Check if we push a temporary branch to the remote when we make testmerge commits - /// - /// if we publish testmerge commits to the remote, otherwise - [OperationContract] - bool PushTestmergeCommits(); - - /// - /// Set if we push a temporary branch to the remote when we make testmerge commits - /// - /// if we testmerge commits should be published to the remote, otherwise - [OperationContract] - void SetPushTestmergeCommits(bool newValue); - } -} diff --git a/TGS.Interface/Components/Service.cs b/TGS.Interface/Components/Service.cs deleted file mode 100644 index 17fc5f7096..0000000000 --- a/TGS.Interface/Components/Service.cs +++ /dev/null @@ -1,57 +0,0 @@ -using System.Collections.Generic; -using System.ServiceModel; - -namespace TGS.Interface.Components -{ - /// - /// Interface for managing the service - /// - [ServiceContract] - public interface ITGSService - { - - /// - /// Next stop of the service will not close DD and sets a flag for it to reattach once it restarts - /// - [OperationContract] - void PrepareForUpdate(); - - /// - /// Get the port used for remote operation - /// - /// The port used for remote operation - [OperationContract] - ushort RemoteAccessPort(); - - /// - /// Set the port used for remote operation - /// Requires a service restart to take effect - /// - /// The new port to use for remote operation - /// null on success, error message on failure - [OperationContract] - string SetRemoteAccessPort(ushort port); - - /// - /// Retrieve's the service's version - /// - /// The service's version - [OperationContract] - string Version(); - - /// - /// Sets the path to the python 2.7 installation - /// - /// The new path - /// if the path exists, otherwise - [OperationContract] - bool SetPythonPath(string path); - - /// - /// Gets the path to the python 2.7 installation - /// - /// The path to the python 2.7 installation - [OperationContract] - string PythonPath(); - } -} diff --git a/TGS.Interface/Definitions.cs b/TGS.Interface/Definitions.cs deleted file mode 100644 index 05cc952c33..0000000000 --- a/TGS.Interface/Definitions.cs +++ /dev/null @@ -1,32 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Reflection; -using TGS.Interface.Components; - -namespace TGS.Interface -{ - /// - /// Contains constants for the interface - /// - public static class Definitions - { - /// - /// The maximum message size to and from a local server - /// - public const long TransferLimitLocal = Int32.MaxValue; //2GB can't go higher - - /// - /// The maximum message size to and from a remote server - /// - public const long TransferLimitRemote = 10485760; //10 MB - - /// - /// Base name of communication URLs - /// - public const string MasterInterfaceName = "TGStationServerService"; - /// - /// Base name of instance URLs - /// - public const string InstanceInterfaceName = MasterInterfaceName + "/Instance"; - } -} diff --git a/TGS.Interface/Enumerations.cs b/TGS.Interface/Enumerations.cs deleted file mode 100644 index caca32e31b..0000000000 --- a/TGS.Interface/Enumerations.cs +++ /dev/null @@ -1,178 +0,0 @@ -using System; - -namespace TGS.Interface -{ - /// - /// Description of the connectivity level to an or the - /// - [Flags] - public enum ConnectivityLevel - { - /// - /// The connection could not be made, either a communication error occurred or the specified does not exist - /// - None = 0, - /// - /// The connection could be made - /// - Connected = 1, - /// - /// The connected user is authenticated - /// - Authenticated = 2 | Connected, - /// - /// The connected user is an administrator - /// - Administrator = 4 | Authenticated, - } - - /// - /// The status of a BYOND update job - /// - public enum ByondStatus - { - /// - /// No byond update in progress - /// - Idle, - /// - /// Preparing to update - /// - Starting, - /// - /// Revision is downloading - /// - Downloading, - /// - /// Revision is deflating - /// - Staging, - /// - /// Revision is ready and waiting for DreamDaemon reboot - /// - Staged, - /// - /// Revision is being applied - /// - Updating, - /// - /// Running game code is being recompiled under staged update - /// - CompilingStaged, - } - /// - /// Type of byond version - /// - public enum ByondVersion - { - /// - /// The highest version from http://www.byond.com/download/build/LATEST/ - /// - Latest, - /// - /// The version in the staging directory - /// - Staged, - /// - /// The installed version - /// - Installed, - } - /// - /// The type of chat provider - /// - public enum ChatProvider : int - { - /// - /// IRC chat provider - /// - IRC = 0, - /// - /// Discord chat provider - /// - Discord = 1, - } - - /// - /// Supported irc permission modes - /// - public enum IRCMode : int - { - /// - /// + - /// - Voice, - /// - /// % - /// - Halfop, - /// - /// @ - /// - Op, - /// - /// ~ - /// - Owner, - } - /// - /// The status of the compiler - /// - public enum CompilerStatus - { - /// - /// Game folder is broken or does not exist - /// - Uninitialized, - /// - /// Game folder is being created - /// - Initializing, - /// - /// Game folder is setup, does not imply the dmb is compiled - /// - Initialized, - /// - /// Game is being compiled - /// - Compiling, - } - - /// - /// The status of the DD instance - /// - public enum DreamDaemonStatus - { - /// - /// Server is not running - /// - Offline, - /// - /// Server is being rebooted - /// - HardRebooting, - /// - /// Server is running - /// - Online, - } - - /// - /// DreamDaemon's security level - /// - public enum DreamDaemonSecurity - { - /// - /// Server is unrestricted in terms of file access and shell commands - /// - Trusted = 0, - /// - /// Server will not be able to run shell commands or access files outside it's working directory - /// - Safe, - /// - /// Server will not be able to run shell commands or access anything but temporary files - /// - Ultrasafe - } -} diff --git a/TGS.Interface/Helpers.cs b/TGS.Interface/Helpers.cs deleted file mode 100644 index a7780a1aba..0000000000 --- a/TGS.Interface/Helpers.cs +++ /dev/null @@ -1,49 +0,0 @@ -using System; -using System.Security.Cryptography; -using System.Text; - -namespace TGS.Interface -{ - /// - /// Helper functions used across the server suite - /// - public static class Helpers - { - /// - /// Takes some and returns an encrypted version along with the required to decrypt it - /// - /// The to encrypt - /// The entropy required the decrypt the ciphertext - /// Ciphertext for the - public static string EncryptData(string cleartext, out string entropy) - { - // Generate additional entropy (will be used as the Initialization vector) - byte[] bentropy = new byte[20]; - using (var rng = new RNGCryptoServiceProvider()) - rng.GetBytes(bentropy); - - byte[] ciphertext = ProtectedData.Protect(Encoding.UTF8.GetBytes(cleartext), bentropy, DataProtectionScope.CurrentUser); - - entropy = Convert.ToBase64String(bentropy, 0, bentropy.Length); - return Convert.ToBase64String(ciphertext, 0, ciphertext.Length); - } - - /// - /// Takes ciphertext and entropy from and returns the cleartext. Note that this only works if the OS user of the program is the same one that called - /// - /// A return value from a previous call to - /// The entropy parameter from the previous call to that returned - /// The decrypted on sucess or on failure - public static string DecryptData(string ciphertext, string entropy) - { - try - { - return Encoding.UTF8.GetString(ProtectedData.Unprotect(Convert.FromBase64String(ciphertext), Convert.FromBase64String(entropy), DataProtectionScope.CurrentUser)); - } - catch - { - return null; - } - } - } -} diff --git a/TGS.Interface/IClient.cs b/TGS.Interface/IClient.cs deleted file mode 100644 index 02394200fa..0000000000 --- a/TGS.Interface/IClient.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; - -namespace TGS.Interface -{ - /// - /// Main for communicating the - /// - public interface IClient : IDisposable - { - /// - /// The the connects to - /// - IServer Server { get; } - - /// - /// The for the . Is for local connections - /// - RemoteLoginInfo LoginInfo { get; } - - /// - /// Checks if the is setup for a remote connection - /// - bool IsRemoteConnection { get; } - - /// - /// Returns if the interface being used to connect to a service does not have the same release version as the service - /// - /// An error message to display to the user should this function return - /// if the interface being used to connect to a service does not have the same release version as the service - bool VersionMismatch(out string errorMessage); - - /// - /// See without the error argument - /// - ConnectivityLevel ConnectionStatus(); - - /// - /// Used to test if the is avaiable on the target machine. Note that state can change at any time and any call into the may throw an exception because of communcation errors - /// - /// String of the error that prevented an elevated connectivity level - /// The apporopriate - ConnectivityLevel ConnectionStatus(out string error); - } -} diff --git a/TGS.Interface/IInstance.cs b/TGS.Interface/IInstance.cs deleted file mode 100644 index 66ca33e94f..0000000000 --- a/TGS.Interface/IInstance.cs +++ /dev/null @@ -1,55 +0,0 @@ -using TGS.Interface.Components; - -namespace TGS.Interface -{ - /// - /// Wrapper for components - /// - public interface IInstance : ITGInstance - { - /// - /// Get the for the - /// - InstanceMetadata Metadata { get; } - - /// - /// The component. Will be if the connected user is not an administrator of the - /// - ITGAdministration Administration { get; } - - /// - /// The component - /// - ITGByond Byond { get; } - - /// - /// The component - /// - ITGChat Chat { get; } - - /// - /// The component - /// - ITGCompiler Compiler { get; } - - /// - /// The component - /// - ITGConfig Config { get; } - - /// - /// The component - /// - ITGDreamDaemon DreamDaemon { get; } - - /// - /// The component - /// - ITGInterop Interop { get; } - - /// - /// The component - /// - ITGRepository Repository { get; } - } -} diff --git a/TGS.Interface/IServer.cs b/TGS.Interface/IServer.cs deleted file mode 100644 index e9234f9a58..0000000000 --- a/TGS.Interface/IServer.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System; -using System.Collections.Generic; -using TGS.Interface.Components; - -namespace TGS.Interface -{ - /// - /// Wrapper representing a - /// - public interface IServer - { - /// - /// The of the - /// - Version Version { get; } - - /// - /// Get the s the contains that the current user can access and connect to - /// - IEnumerable Instances { get; } - - /// - /// The component. Will be if the connected user is not an administrator of the - /// - ITGInstanceManager InstanceManager { get; } - - /// - /// The component. Will be if the connected user is not an administrator of the - /// - ITGSService Management { get; } - - /// - /// Gets the specified without connectivity checks - /// - /// The name of the to get - /// The named on success, on failure - IInstance GetInstance(string name); - - /// - /// Rebuilds the internal cached list - /// - void RebuildInstanceList(); - } -} diff --git a/TGS.Interface/Instance.cs b/TGS.Interface/Instance.cs deleted file mode 100644 index 31d2c31a44..0000000000 --- a/TGS.Interface/Instance.cs +++ /dev/null @@ -1,119 +0,0 @@ -using System; -using System.Linq; -using TGS.Interface.Components; - -namespace TGS.Interface -{ - /// - sealed class Instance : IInstance - { - /// - public InstanceMetadata Metadata - { - get - { - if (!metadata.Enabled) - //metadata needs populating - metadata = serverInterface.GetComponent(null).ListInstances().Where(x => x.Name == metadata.Name).First(); - return metadata; - } - } - - /// - /// Whether or not the current user is known to be an administrator of the - /// - bool UserIsAdministrator - { - get - { - lock (this) - { - if (isAdministrator) - return true; - try - { - serverInterface.GetComponent(metadata.Name).GetCurrentAuthorizedGroup(); - isAdministrator = true; - return true; - } - catch - { - return false; - } - } - } - } - - /// - /// The backing - /// - readonly Client serverInterface; - /// - /// The name of the - /// - InstanceMetadata metadata; - /// - /// Backing field for - /// - bool isAdministrator; - - /// - /// Construct an - /// - /// The to use - /// The for the - public Instance(Client _serverInterface, InstanceMetadata _metadata) - { - serverInterface = _serverInterface; - metadata = _metadata; - if (metadata.Enabled) - //run a connectivity check - serverInterface.GetComponent(metadata.Name).VerifyConnection(); - } - - /// - public ITGAdministration Administration => UserIsAdministrator ? serverInterface.GetComponent(metadata.Name) : null; - - /// - public ITGByond Byond => serverInterface.GetComponent(metadata.Name); - - /// - public ITGChat Chat => serverInterface.GetComponent(metadata.Name); - - /// - public ITGCompiler Compiler => serverInterface.GetComponent(metadata.Name); - - /// - public ITGConfig Config => serverInterface.GetComponent(metadata.Name); - - /// - public ITGDreamDaemon DreamDaemon => serverInterface.GetComponent(metadata.Name); - - /// - public ITGInterop Interop => serverInterface.GetComponent(metadata.Name); - - /// - public ITGRepository Repository => serverInterface.GetComponent(metadata.Name); - - /// - public string ServerDirectory() - { - return serverInterface.GetComponent(metadata.Name).ServerDirectory(); - } - - /// - public string Version() - { - return serverInterface.GetComponent(metadata.Name).Version(); - } - - /// - /// Get a string representation of the - /// - /// A string representation of the - public override string ToString() - { - return String.Format("{0}: {1} - {2} - {3}", metadata.LoggingID, metadata.Name, metadata.Path, metadata.Enabled ? "ONLINE" : "OFFLINE"); - } - } -} diff --git a/TGS.Interface/InstanceMetadata.cs b/TGS.Interface/InstanceMetadata.cs deleted file mode 100644 index 8b3e73fa6e..0000000000 --- a/TGS.Interface/InstanceMetadata.cs +++ /dev/null @@ -1,33 +0,0 @@ -using System.Runtime.Serialization; - -namespace TGS.Interface -{ - /// - /// Metadata about an - /// - //Namespace required for compatibility reasons - [DataContract(Namespace = "http://schemas.datacontract.org/2004/07/TGServiceInterface")] - public sealed class InstanceMetadata - { - /// - /// The name of the - /// - [DataMember] - public string Name { get; set; } - /// - /// The path of the - /// - [DataMember] - public string Path { get; set; } - /// - /// Whether or not the is enabled - /// - [DataMember] - public bool Enabled { get; set; } - /// - /// The logging ID of the . Will be 0 if is - /// - [DataMember] - public byte LoggingID { get; set; } - } -} diff --git a/TGS.Interface/Properties/AssemblyInfo.cs b/TGS.Interface/Properties/AssemblyInfo.cs deleted file mode 100644 index c9d6fe7641..0000000000 --- a/TGS.Interface/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("TGStation Server Service Interface")] -[assembly: AssemblyDescription("Used by user programs to access the TGStation Server Service")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("ac4e7e8b-f83a-481c-a8b0-8fa4e8ae59ab")] diff --git a/TGS.Interface/PullRequestInfo.cs b/TGS.Interface/PullRequestInfo.cs deleted file mode 100644 index e6de6188c1..0000000000 --- a/TGS.Interface/PullRequestInfo.cs +++ /dev/null @@ -1,58 +0,0 @@ -using System.Runtime.Serialization; - -namespace TGS.Interface -{ - /// - /// Information about a pull request - /// - [DataContract] - public sealed class PullRequestInfo - { - /// - /// The PR number - /// - [DataMember] - public int Number { get; private set; } - /// - /// The PR's author - /// - [DataMember] - public string Author { get; private set; } - /// - /// The PR's title - /// - [DataMember] - public string Title { get; private set; } - /// - /// The commit the PR was merged locally at - /// - [DataMember] - public string Sha { get; private set; } - - /// - /// Construct a - /// - /// The PR number - /// The PR's author - /// The PR's title - /// The commit the PR was merged locally at - public PullRequestInfo(int number, string author, string title, string sha) - { - Number = number; - Author = author; - Title = title; - Sha = sha; - } - - /// - /// Construct a for a call to - /// - /// The PR number - /// The optional commit to merge the PR at - public PullRequestInfo(int number, string sha = null) - { - Number = number; - Sha = sha; - } - } -} diff --git a/TGS.Interface/RemoteLoginInfo.cs b/TGS.Interface/RemoteLoginInfo.cs deleted file mode 100644 index 4fcc787de3..0000000000 --- a/TGS.Interface/RemoteLoginInfo.cs +++ /dev/null @@ -1,132 +0,0 @@ -using Newtonsoft.Json; -using System; -using System.Collections.Generic; - -namespace TGS.Interface -{ - /// - /// Information representing a remote server connection - /// - public sealed class RemoteLoginInfo : IEquatable - { - /// - /// Used for serialization - /// - const string EntropyFormatter = "{0}Entropy"; - - /// - /// The IP address or URL of the target server - /// - public string IP { get { return _ip; } } - /// - /// The port to connect to the target server - /// - public ushort Port { get { return _port; } } - /// - /// A Windows username for the target server - /// - public string Username { get { return _username; } } - /// - /// The Windows password for - /// - public string Password { internal get; set; } - /// - /// Check if the has been initialized with a - /// - [JsonIgnore] - public bool HasPassword { get { return !String.IsNullOrWhiteSpace(Password); } } - - /// - /// Backing field for - /// - readonly string _ip; - /// - /// Backing field for - /// - readonly ushort _port; - /// - /// Backing field for - /// - readonly string _username; - - /// - /// Construct a - /// - /// The value for - /// The value for - /// The value for - /// The value for - public RemoteLoginInfo(string ip, ushort port, string username, string password) - { - if (String.IsNullOrWhiteSpace(ip)) - throw new InvalidOperationException("ip must be set!"); - _ip = ip; - if (port == 0) - throw new InvalidOperationException("port may not be 0!"); - _port = port; - if (String.IsNullOrWhiteSpace(username)) - throw new InvalidOperationException("username must be set!"); - _username = username; - Password = password; - } - - /// - /// Construct a from JSON - /// - /// The result of a call to - public RemoteLoginInfo(string json) - { - var dic = JsonConvert.DeserializeObject>(json); - var ip = (string)dic[nameof(IP)]; - if (String.IsNullOrWhiteSpace(ip)) - throw new InvalidOperationException("ip must be set!"); - _ip = ip; - var port = (ushort)(long)dic[nameof(Port)]; - if (port == 0) - throw new InvalidOperationException("port may not be 0!"); - _port = port; - var username = (string)dic[nameof(Username)]; - if (String.IsNullOrWhiteSpace(username)) - throw new InvalidOperationException("username must be set!"); - _username = username; - if(dic.ContainsKey(nameof(Password))) - Password = Helpers.DecryptData((string)dic[nameof(Password)], (string)dic[String.Format(EntropyFormatter, nameof(Password))]); - } - - /// - /// Returns - /// - /// - public override string ToString() - { - return IP; - } - - /// - /// Checks if another matches one - /// - /// Another - /// if and have the same , , and - public bool Equals(RemoteLoginInfo other) - { - return other != null && IP == other.IP && Port == other.Port && Username == other.Username; - } - - /// - /// Returns a JSON representation of the with the encrypted - /// - /// A JSON representation of the - public string ToJSON() - { - //serialize it to a dic first so we can store the entropy - var raw = JsonConvert.SerializeObject(this); - var dic = JsonConvert.DeserializeObject>(raw); - if (HasPassword) - { - dic.Add(nameof(Password), Helpers.EncryptData(Password, out string entropy)); - dic.Add(String.Format(EntropyFormatter, nameof(Password)), entropy); - } - return JsonConvert.SerializeObject(dic); - } - } -} diff --git a/TGS.Interface/RootCommand.cs b/TGS.Interface/RootCommand.cs deleted file mode 100644 index 129c772478..0000000000 --- a/TGS.Interface/RootCommand.cs +++ /dev/null @@ -1,105 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace TGS.Interface -{ - /// - /// Helper for creating commands that contain sub commands - /// - public abstract class RootCommand : Command - { - /// - /// s further down the tree from this one. Set in Constructor - /// - public Command[] Children { get; protected set; } = { }; - /// - /// If set to a multiline, detailed list of s will be printed. Otherwise a singleline list of s will be printed - /// - public static bool PrintHelpList = false; - - /// - /// Forward parameters to commands further down the tree - /// - /// List of parameters passed to the - /// The result of a sub or an appropriate if the handled it - protected override ExitCode Run(IList parameters) - { - if (parameters.Count > 0) - { - var LocalKeyword = parameters[0].Trim().ToLower(); - parameters.RemoveAt(0); - switch (LocalKeyword) - { - case "help": - case "?": - PrintHelp(); - return ExitCode.Normal; - default: - foreach (var c in Children) - if (c.Keyword == LocalKeyword) - { - if (parameters.Count > 0) - { - var possibleHelp = parameters[0].ToLower(); - if (possibleHelp == "help" || possibleHelp == "?") - { - c.PrintHelp(); - return ExitCode.Normal; - } - } - if (parameters.Count < c.RequiredParameters) - { - OutputProc("Not enough parameters!"); - return ExitCode.BadCommand; - } - return c.DoRun(parameters); - } - parameters.Insert(0, LocalKeyword); - break; - } - } - OutputProc(String.Format("Invalid command! Type '{0}?' or '{0}help' for available commands.", Keyword != null ? Keyword + " " : "")); - return ExitCode.BadCommand; - } - - /// - public override void PrintHelp() - { - var Final = new List(); - if (PrintHelpList) - { - foreach (var c in Children) - Final.Add(c.Keyword); - OutputProc("Available commands (type '?' or 'help' after command for more info): " + String.Join(", ", Final)); - } - else - { - var Prefixes = new List(); - var Postfixes = new List(); - int MaxPrefixLen = 0; - foreach (var c in Children) - { - var ns = c.Keyword + " " + c.GetArgumentString(); - MaxPrefixLen = Math.Max(MaxPrefixLen, ns.Length); - Prefixes.Add(ns); - Postfixes.Add(c.GetHelpText()); - } - - for (var I = 0; I < Prefixes.Count; ++I) - { - var lp = Prefixes[I]; - for (; lp.Length < MaxPrefixLen + 1; lp += " ") ; - Final.Add(lp + "- " + Postfixes[I]); - } - Final.Sort(); - Final.ForEach(OutputProc); - } - } - - /// - public override string GetHelpText() - { - throw new NotImplementedException(); - } - } -} diff --git a/TGS.Interface/Server.cs b/TGS.Interface/Server.cs deleted file mode 100644 index d5c953ee1f..0000000000 --- a/TGS.Interface/Server.cs +++ /dev/null @@ -1,103 +0,0 @@ -using System; -using System.Collections.Generic; -using TGS.Interface.Components; - -namespace TGS.Interface -{ - /// - sealed class Server : IServer - { - /// - public Version Version => serverInterface.ServerVersion; - - /// - public IEnumerable Instances { get - { - lock (this) - if (knownInstances == null) - RebuildInstanceList(); - foreach (var I in knownInstances) - { - IInstance nextInstance; - try - { - nextInstance = new Instance(serverInterface, I); - } - catch - { - continue; - } - yield return nextInstance; - } - } - } - - /// - public ITGInstanceManager InstanceManager => UserIsAdministrator ? serverInterface.GetComponent(null) : null; - - /// - public ITGSService Management => UserIsAdministrator ? serverInterface.GetComponent(null) : null; - - /// - /// If the connected user is an administrator of the - /// - bool UserIsAdministrator - { - get - { - lock (this) - { - if (isAdministrator) - return true; - try - { - serverInterface.GetComponent(null).Version(); - isAdministrator = true; - return true; - } - catch - { - return false; - } - } - } - } - - /// - /// The backing - /// - readonly Client serverInterface; - - /// - /// Result of a call to - /// - IList knownInstances; - - /// - /// Backing field for - /// - bool isAdministrator; - - /// - /// Construct an - /// - /// The to use - public Server(Client _serverInterface) - { - serverInterface = _serverInterface; - } - - /// - public IInstance GetInstance(string name) - { - return new Instance(serverInterface, new InstanceMetadata { Name = name, Enabled = false }); - } - - /// - public void RebuildInstanceList() - { - lock (this) - knownInstances = serverInterface.GetComponent(null).ListInstances(); - } - } -} diff --git a/TGS.Interface/TGS.Interface.csproj b/TGS.Interface/TGS.Interface.csproj deleted file mode 100644 index 42cb6b71b0..0000000000 --- a/TGS.Interface/TGS.Interface.csproj +++ /dev/null @@ -1,92 +0,0 @@ - - - - - Debug - AnyCPU - {AC4E7E8B-F83A-481C-A8B0-8FA4E8AE59AB} - Library - Properties - TGS.Interface - TGServiceInterface - v4.5.2 - 512 - - - tgs.ico - - - true - bin\Debug\ - DEBUG;TRACE - full - AnyCPU - prompt - MinimumRecommendedRules.ruleset - - - bin\Release\ - TRACE - bin\x86\Release\TGS.Interface.xml - true - true - pdbonly - AnyCPU - prompt - MinimumRecommendedRules.ruleset - false - - - false - - - - ..\packages\Newtonsoft.Json.10.0.3\lib\net45\Newtonsoft.Json.dll - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - \ No newline at end of file diff --git a/TGS.Interface/TGS.Interface.nuspec b/TGS.Interface/TGS.Interface.nuspec deleted file mode 100644 index 80bbf50f80..0000000000 --- a/TGS.Interface/TGS.Interface.nuspec +++ /dev/null @@ -1,16 +0,0 @@ - - - - TGServiceInterface - $version$ - Cyberboss - https://github.com/tgstation/tgstation-server/blob/master/LICENSE - https://github.com/tgstation/tgstation-server - https://tgstation.github.io/tgstation-server/tgs.ico - false - Interface for managing /tg/station server - Copyright 2017 - https://github.com/tgstation/tgstation-server/releases/tag/tgstation-server-v$version$ - tgstation game-server byond service toolset wcf ss13 client-lib tgstation-server api wcf-service soap - - diff --git a/TGS.Interface/packages.config b/TGS.Interface/packages.config deleted file mode 100644 index 810e559c04..0000000000 --- a/TGS.Interface/packages.config +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/TGS.Interface/tgs.ico b/TGS.Interface/tgs.ico deleted file mode 100644 index 6ed69fadfc..0000000000 Binary files a/TGS.Interface/tgs.ico and /dev/null differ diff --git a/TGS.Server.Console/App.config b/TGS.Server.Console/App.config deleted file mode 100644 index 8227adb989..0000000000 --- a/TGS.Server.Console/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/TGS.Server.Console/Console.cs b/TGS.Server.Console/Console.cs deleted file mode 100644 index 9641e5d625..0000000000 --- a/TGS.Server.Console/Console.cs +++ /dev/null @@ -1,73 +0,0 @@ -using System; - -namespace TGS.Server.Console -{ - /// - /// Console runner for a - /// - sealed class Console : ILogger - { - /// - /// Entry point to the - /// - /// - static void Main(string[] args) => new Console(args); - - /// - /// Construct and run a - /// - /// Command line arguments - Console(string[] args) - { - try - { - System.Console.WriteLine("Starting server..."); - var server = new Server(args, this); //no using to avoid including more references - try - { - System.Console.WriteLine("Server started!"); - ExitPrompt(); - } - finally - { - server.Dispose(); - } - } - catch (Exception e) - { - System.Console.WriteLine(String.Format("Unhandled exception: {0}", e.ToString())); - ExitPrompt(); - } - } - - void ExitPrompt() - { - System.Console.WriteLine("Press any key to exit..."); - System.Console.ReadKey(); - } - - /// - public void WriteAccess(string username, bool authSuccess, byte loggingID) - { - System.Console.WriteLine(String.Format("[{0}]: {1}-{4}: Authentication {3} from {2}", DateTime.UtcNow.ToString(), EventID.Authentication, username, authSuccess ? "success" : "fail", loggingID)); - } - - /// - public void WriteError(string message, EventID id, byte loggingID) - { - System.Console.WriteLine(String.Format("[{0}]: {1}-{3}: ERROR: {2}", DateTime.Now.ToString(), id, message, loggingID)); - } - - /// - public void WriteInfo(string message, EventID id, byte loggingID) - { - System.Console.WriteLine(String.Format("[{0}]: {1}-{3}: {2}", DateTime.Now.ToString(), id, message, loggingID)); - } - - /// - public void WriteWarning(string message, EventID id, byte loggingID) - { - System.Console.WriteLine(String.Format("[{0}]: {1}-{3}: Warning: {2}", DateTime.Now.ToString(), id, message, loggingID)); - } - } -} diff --git a/TGS.Server.Console/Properties/AssemblyInfo.cs b/TGS.Server.Console/Properties/AssemblyInfo.cs deleted file mode 100644 index 0ce360e6d0..0000000000 --- a/TGS.Server.Console/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("TGStation Server Console")] -[assembly: AssemblyDescription("Console adapter for TGStation Server")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("509433f6-aefb-44ca-bfe3-c782166d2cc3")] diff --git a/TGS.Server.Console/app.manifest b/TGS.Server.Console/app.manifest deleted file mode 100644 index 467015914e..0000000000 --- a/TGS.Server.Console/app.manifest +++ /dev/null @@ -1,76 +0,0 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/TGS.Server.Console/tgs.ico b/TGS.Server.Console/tgs.ico deleted file mode 100644 index 6ed69fadfc..0000000000 Binary files a/TGS.Server.Console/tgs.ico and /dev/null differ diff --git a/TGS.Server.Service/App.config b/TGS.Server.Service/App.config deleted file mode 100644 index 8227adb989..0000000000 --- a/TGS.Server.Service/App.config +++ /dev/null @@ -1,6 +0,0 @@ - - - - - - diff --git a/TGS.Server.Service/ProjectInstaller.cs b/TGS.Server.Service/ProjectInstaller.cs deleted file mode 100644 index 10c319e3d0..0000000000 --- a/TGS.Server.Service/ProjectInstaller.cs +++ /dev/null @@ -1,35 +0,0 @@ -using System.ComponentModel; -using System.Configuration.Install; -using System.ServiceProcess; - -namespace TGS.Server.Service -{ - /// - /// This tells the .msi there is a Windows in this that needs installation - /// - [RunInstaller(true)] - public sealed class ProjectInstaller : Installer - { - /// - /// Construct a - /// - public ProjectInstaller() - { - Installers.AddRange(new Installer[] { - new ServiceProcessInstaller - { - Account = ServiceAccount.LocalSystem, - Password = null, - Username = null - }, - new ServiceInstaller - { - Description = "/tg/station Server Service", - DisplayName = "TG Station Server", - ServiceName = "TG Station Server", - StartType = ServiceStartMode.Automatic - } - }); - } - } -} diff --git a/TGS.Server.Service/Properties/AssemblyInfo.cs b/TGS.Server.Service/Properties/AssemblyInfo.cs deleted file mode 100644 index e4e22f2673..0000000000 --- a/TGS.Server.Service/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("TGStation Server Service")] -[assembly: AssemblyDescription("Windows service adapter for TGStation Server")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("3f81e398-b223-4006-b40c-c2800714ce29")] diff --git a/TGS.Server.Service/Service.cs b/TGS.Server.Service/Service.cs deleted file mode 100644 index 2314595060..0000000000 --- a/TGS.Server.Service/Service.cs +++ /dev/null @@ -1,71 +0,0 @@ -using System; -using System.Diagnostics; -using System.ServiceProcess; - -namespace TGS.Server.Service -{ - /// - /// Windows adapter for - /// - public sealed class Service : ServiceBase, ILogger - { - /// - /// The entry point for the program. Calls with a new as a parameter - /// - public static void Main() => Run(new Service()); - - /// - /// The the manages - /// - Server activeServer; - - /// - /// Constructs a - /// - Service() - { - ServiceName = "TG Station Server"; - } - - /// - public void WriteAccess(string username, bool authSuccess, byte loggingID) - { - EventLog.WriteEntry(String.Format("Access from: {0}", username), authSuccess ? EventLogEntryType.SuccessAudit : EventLogEntryType.FailureAudit , (int)EventID.Authentication + loggingID); - } - - /// - public void WriteError(string message, EventID id, byte loggingID) - { - EventLog.WriteEntry(message, EventLogEntryType.Error, (int)id + loggingID); - } - - /// - public void WriteInfo(string message, EventID id, byte loggingID) - { - EventLog.WriteEntry(message, EventLogEntryType.Information, (int)id + loggingID); - } - - /// - public void WriteWarning(string message, EventID id, byte loggingID) - { - EventLog.WriteEntry(message, EventLogEntryType.Warning, (int)id + loggingID); - } - - /// - /// Called when the is started. Creates a new - /// - /// The service start arguments - protected override void OnStart(string[] args) - { - activeServer = new Server(args, this); - } - - /// - /// Called when the is stopped. Calls on - /// - protected override void OnStop() - { - activeServer.Dispose(); - } - } -} diff --git a/TGS.Server.Service/TGS.Server.Service.csproj b/TGS.Server.Service/TGS.Server.Service.csproj deleted file mode 100644 index 79fb1a5a22..0000000000 --- a/TGS.Server.Service/TGS.Server.Service.csproj +++ /dev/null @@ -1,69 +0,0 @@ - - - - - Debug - AnyCPU - {3F81E398-B223-4006-B40C-C2800714CE29} - WinExe - TGS.Server.Service - TGServerService - v4.5.2 - 512 - true - - - - AnyCPU - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - AnyCPU - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - TGS.Server.Service.Service - - - tgs.ico - - - - - - - - - Component - - - Component - - - - - - - - - - {f32eda25-0855-411c-af5e-f0d042917e2d} - TGS.Server - - - - - - - \ No newline at end of file diff --git a/TGS.Server.Service/tgs.ico b/TGS.Server.Service/tgs.ico deleted file mode 100644 index 6ed69fadfc..0000000000 Binary files a/TGS.Server.Service/tgs.ico and /dev/null differ diff --git a/TGS.Server/App.config b/TGS.Server/App.config deleted file mode 100644 index 892916e112..0000000000 --- a/TGS.Server/App.config +++ /dev/null @@ -1,8 +0,0 @@ - - - - - - - - diff --git a/TGS.Server/ChatCommands/ByondCommand.cs b/TGS.Server/ChatCommands/ByondCommand.cs deleted file mode 100644 index b983339816..0000000000 --- a/TGS.Server/ChatCommands/ByondCommand.cs +++ /dev/null @@ -1,44 +0,0 @@ -using System.Collections.Generic; -using TGS.Interface; - -namespace TGS.Server.ChatCommands -{ - /// - /// Retrieve the installed, staged, or latest availab - /// - sealed class ByondCommand : ChatCommand - { - /// - /// Construct a - /// - public ByondCommand() - { - Keyword = "byond"; - } - - /// - protected override ExitCode Run(IList parameters) - { - var type = ByondVersion.Installed; - if (parameters.Count > 0) - if (parameters[0].ToLower() == "--staged") - type = ByondVersion.Staged; - else if (parameters[0].ToLower() == "--latest") - type = ByondVersion.Latest; - OutputProc(Instance.GetVersion(type) ?? "None"); - return ExitCode.Normal; - } - - /// - public override string GetHelpText() - { - return "Gets the specified BYOND version"; - } - - /// - public override string GetArgumentString() - { - return "[--staged|--latest]"; - } - } -} diff --git a/TGS.Server/ChatCommands/ChatCommand.cs b/TGS.Server/ChatCommands/ChatCommand.cs deleted file mode 100644 index 96d5803ca1..0000000000 --- a/TGS.Server/ChatCommands/ChatCommand.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System.Collections.Generic; -using System.Threading; -using TGS.Interface; - -namespace TGS.Server.ChatCommands -{ - /// - /// A command heard by a - /// - abstract class ChatCommand : Command - { - /// - /// for the - /// - public static ThreadLocal CommandInfo { get; private set; } = new ThreadLocal(); - /// - /// If set to , the cannot be invoked by a non-admin or outside an admin chat channel - /// - public bool RequiresAdmin { get; protected set; } - /// - /// Shorthand for accessing - /// - protected Instance Instance { get { return CommandInfo.Value.Server; } } - - /// - public override ExitCode DoRun(IList parameters) - { - if (RequiresAdmin) - { - var Info = CommandInfo.Value; - if (!Info.IsAdmin) - { - OutputProc("You are not authorized to use that command!"); - return ExitCode.BadCommand; - } - if (!Info.IsAdminChannel) - { - OutputProc("Use this command in an admin channel!"); - return ExitCode.BadCommand; - } - } - return base.DoRun(parameters); - } - } -} diff --git a/TGS.Server/ChatCommands/CommandInfo.cs b/TGS.Server/ChatCommands/CommandInfo.cs deleted file mode 100644 index 4108fd051f..0000000000 --- a/TGS.Server/ChatCommands/CommandInfo.cs +++ /dev/null @@ -1,25 +0,0 @@ -namespace TGS.Server.ChatCommands -{ - /// - /// Metadata about the currently running - /// - sealed class CommandInfo - { - /// - /// If the was invoked by an admin - /// - public bool IsAdmin { get; set; } - /// - /// If the was invoked from an admin chat channel - /// - public bool IsAdminChannel { get; set; } - /// - /// The name of the invoker - /// - public string Speaker { get; set; } - /// - /// A reference to the that runs the that heard the - /// - public Instance Server { get; set; } - } -} diff --git a/TGS.Server/ChatCommands/KekCommand.cs b/TGS.Server/ChatCommands/KekCommand.cs deleted file mode 100644 index 1826bf1876..0000000000 --- a/TGS.Server/ChatCommands/KekCommand.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Collections.Generic; - -namespace TGS.Server.ChatCommands -{ - /// - /// kek - /// - sealed class KekCommand : ChatCommand - { - /// - /// Construct a - /// - public KekCommand() - { - Keyword = "kek"; - } - - /// - protected override ExitCode Run(IList parameters) - { - OutputProc("kek"); - return ExitCode.Normal; - } - - /// - public override string GetHelpText() - { - return "kek"; - } - } -} diff --git a/TGS.Server/ChatCommands/PullRequestsCommand.cs b/TGS.Server/ChatCommands/PullRequestsCommand.cs deleted file mode 100644 index e03d426c04..0000000000 --- a/TGS.Server/ChatCommands/PullRequestsCommand.cs +++ /dev/null @@ -1,45 +0,0 @@ -using System.Collections.Generic; - -namespace TGS.Server.ChatCommands -{ - /// - /// Retrieve the list of test-merged github pull requests - /// - sealed class PullRequestsCommand : ChatCommand - { - /// - /// Construct a - /// - public PullRequestsCommand() - { - Keyword = "prs"; - } - - /// - protected override ExitCode Run(IList parameters) - { - var PRs = Instance.MergedPullRequests(out string res); - if (PRs == null) - { - OutputProc(res); - return ExitCode.ServerError; - } - if (PRs.Count == 0) - OutputProc("None!"); - else - { - res = ""; - foreach (var I in PRs) - res += "#" + I.Number + " "; - OutputProc(res); - } - return ExitCode.Normal; - } - - /// - public override string GetHelpText() - { - return "Gets the currently merged pull requests in the repository"; - } - } -} diff --git a/TGS.Server/ChatCommands/RevisionCommand.cs b/TGS.Server/ChatCommands/RevisionCommand.cs deleted file mode 100644 index 7fd99c0f28..0000000000 --- a/TGS.Server/ChatCommands/RevisionCommand.cs +++ /dev/null @@ -1,37 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace TGS.Server.ChatCommands -{ - /// - /// Retrieves the git SHA of the live DreamDaemon code - /// - sealed class RevisionCommand : ChatCommand - { - /// - /// Construct a - /// - public RevisionCommand() - { - Keyword = "revision"; - } - /// - protected override ExitCode Run(IList parameters) - { - var res = Instance.LiveSha(); - if (res == "UNKNOWN") - { - OutputProc(res); - return ExitCode.ServerError; - } - OutputProc(String.Format("^{0}", res)); - return ExitCode.Normal; - } - - /// - public override string GetHelpText() - { - return "Prints the current code revision of the repository (not the server)"; - } - } -} diff --git a/TGS.Server/ChatCommands/RootChatCommand.cs b/TGS.Server/ChatCommands/RootChatCommand.cs deleted file mode 100644 index 800147a412..0000000000 --- a/TGS.Server/ChatCommands/RootChatCommand.cs +++ /dev/null @@ -1,25 +0,0 @@ -using System.Collections.Generic; -using TGS.Interface; - -namespace TGS.Server.ChatCommands -{ - /// - /// The main root chat command - /// - sealed class RootChatCommand : RootCommand - { - /// - /// Construct a - /// - /// List of s supplied by DreamDaemon - public RootChatCommand(List serverCommands) - { - var tmp = new List { new PullRequestsCommand(), new VersionCommand(), new RevisionCommand(), new ByondCommand(), new KekCommand() }; - if (serverCommands != null) - tmp.AddRange(serverCommands); - Children = tmp.ToArray(); - serverCommands = new List(); - PrintHelpList = true; - } - } -} diff --git a/TGS.Server/ChatCommands/ServerChatCommand.cs b/TGS.Server/ChatCommands/ServerChatCommand.cs deleted file mode 100644 index fee9f70eb5..0000000000 --- a/TGS.Server/ChatCommands/ServerChatCommand.cs +++ /dev/null @@ -1,46 +0,0 @@ -using System; -using System.Collections.Generic; - -namespace TGS.Server.ChatCommands -{ - /// - /// s generated by DreamDaemon via the API - /// - sealed class ServerChatCommand : ChatCommand - { - /// - /// The help text for the - /// - readonly string HelpText; - - /// - /// Construct a - /// - /// The invocation of the - /// The help text of the - /// If set to , the cannot be invoked by a non-admin or outside an admin chat channel - /// The number of parameters the requires - public ServerChatCommand(string name, string helpText, bool adminOnly, int requiredParameters) - { - Keyword = name; - RequiresAdmin = adminOnly; - HelpText = helpText; - RequiredParameters = requiredParameters; - } - - /// - public override string GetHelpText() - { - return HelpText; - } - - /// - protected override ExitCode Run(IList parameters) - { - var res = Instance.SendCommand(String.Format("{0};sender={1};custom={2}", Keyword, CommandInfo.Value.Speaker, Helpers.SanitizeTopicString(String.Join(" ", parameters)))); - if (res != "SUCCESS" && !String.IsNullOrWhiteSpace(res)) - OutputProc(res); - return ExitCode.Normal; - } - } -} diff --git a/TGS.Server/ChatCommands/VersionCommand.cs b/TGS.Server/ChatCommands/VersionCommand.cs deleted file mode 100644 index 538bcb4e7f..0000000000 --- a/TGS.Server/ChatCommands/VersionCommand.cs +++ /dev/null @@ -1,31 +0,0 @@ -using System.Collections.Generic; - -namespace TGS.Server.ChatCommands -{ - /// - /// Retrieve the current service version - /// - sealed class VersionCommand : ChatCommand - { - /// - /// Construct a - /// - public VersionCommand() - { - Keyword = "version"; - } - - /// - protected override ExitCode Run(IList parameters) - { - OutputProc(Instance.Version()); - return ExitCode.Normal; - } - - /// - public override string GetHelpText() - { - return "Gets the running service version"; - } - } -} diff --git a/TGS.Server/ChatProviders/ChatProvider.cs b/TGS.Server/ChatProviders/ChatProvider.cs deleted file mode 100644 index 974d79eaf8..0000000000 --- a/TGS.Server/ChatProviders/ChatProvider.cs +++ /dev/null @@ -1,77 +0,0 @@ -using System; -using TGS.Interface; - -namespace TGS.Server.ChatProviders -{ - /// - /// Callback for the chat provider recieving a - /// - /// The chat provider the message came from - /// The username of the speaker - /// The name of the channel - /// The message text - /// if is considered a chat admin, otherwise - /// if is an admin chat channel, otherwise - - delegate void OnChatMessage(IChatProvider ChatProvider, string speaker, string channel, string message, bool isAdmin, bool isAdminChannel); - /// - /// Interface for a chat provder service - /// - interface IChatProvider : IDisposable - { - /// - /// Sets for the provider - /// - /// The to set - /// null on success, error message on failure - string SetProviderInfo(ChatSetupInfo info); - - /// - /// Gets the info of the provider - /// - /// The for the chat provider - ChatSetupInfo ProviderInfo(); - - /// - /// Called with chat message info - /// - event OnChatMessage OnChatMessage; - - /// - /// Connects the chat provider if it's enabled - /// - /// on success, error message on failure - string Connect(); - /// - /// Forces a reconnection of the chat provider if it's enabled - /// - /// on success, error message on failure - string Reconnect(); - - /// - /// Checks if the chat provider is connected - /// - /// if the provider is connected, otherwise - bool Connected(); - - /// - /// Disconnects the chat provider - /// - void Disconnect(); - - /// - /// Send a to a - /// - /// The message to send - /// The channel to send to - /// on success, error message on failure - string SendMessageDirect(string message, string channel); - - /// - /// Broadcast a to appropriate channels based on the message type - /// - /// The message to send - /// The - void SendMessage(string message, MessageType mt); - } -} diff --git a/TGS.Server/ChatProviders/DiscordChatProvider.cs b/TGS.Server/ChatProviders/DiscordChatProvider.cs deleted file mode 100644 index 91326ba4bc..0000000000 --- a/TGS.Server/ChatProviders/DiscordChatProvider.cs +++ /dev/null @@ -1,328 +0,0 @@ -using Discord; -using Discord.WebSocket; -using System; -using System.Collections.Generic; -using System.Threading; -using System.Threading.Tasks; -using TGS.Interface; - -namespace TGS.Server.ChatProviders -{ - /// - /// for Discord: https://discordapp.com/ - /// - sealed class DiscordChatProvider : IChatProvider - { - /// - public event OnChatMessage OnChatMessage; - /// - /// The Discord API client - /// - DiscordSocketClient client; - /// - /// The setup info for the provider - /// - DiscordSetupInfo DiscordConfig; - /// - /// Used for multithreading safety - /// - object DiscordLock = new object(); - - /// - /// An of internal identifers => s we have seen - /// - IDictionary SeenPrivateChannels = new Dictionary(); - - /// - /// Construct a - /// - /// The - public DiscordChatProvider(ChatSetupInfo info) - { - Init(info); - } - - /// - public ChatSetupInfo ProviderInfo() - { - return DiscordConfig; - } - - /// - /// Sets up the Discord API and - /// - /// The to init with - void Init(ChatSetupInfo info) - { - DiscordConfig = new DiscordSetupInfo(info); - client = new DiscordSocketClient(); - client.MessageReceived += Client_MessageReceived; - } - - /// - /// Checks if a is considered a chat admin - /// - /// The sender of a message - /// if is a chat admin, otherwise - private bool CheckAdmin(SocketUser user) - { - if (!DiscordConfig.AdminsAreSpecial) - return DiscordConfig.AdminList.Contains(user.Id.ToString()); - if(user is SocketGuildUser sgu) - foreach (var I in sgu.Roles) - if (DiscordConfig.AdminList.Contains(I.Id.ToString())) - return true; - return false; - } - - /// - /// Called when a channel the bot is in recieves a message or the bot is PM'd directly - /// - /// The event arguments - /// The task to run when this occurs - private async Task Client_MessageReceived(SocketMessage e) - { - await Task.Run(() => - { - if (e.Author.Id == client.CurrentUser.Id) - return; - - var pm = e.Channel is IPrivateChannel; - - if (pm && !SeenPrivateChannels.ContainsKey(e.Channel.Id)) - SeenPrivateChannels.Add(e.Channel.Id, e.Channel); - - bool found = false; - - var formattedMessage = e.Content; - foreach (var u in e.MentionedUsers) - if (u.Id == client.CurrentUser.Id) - { - found = true; - formattedMessage = formattedMessage.Replace(u.Mention, ""); - var nicknameMention = u.Mention.Replace("!", ""); - formattedMessage = formattedMessage.Replace(nicknameMention, ""); - break; - } - - if (!found) - { - var splits = formattedMessage.Split(' '); - found = splits[0].ToLower() == "!tgs"; - if (found) - { - var asList = new List(); - asList.AddRange(splits); - asList.RemoveAt(0); - formattedMessage = String.Join(" ", asList); - } - } - - if (!found && !pm) - return; - - formattedMessage = formattedMessage.Trim(); - - var cid = e.Channel.Id.ToString(); - - OnChatMessage(this, e.Author.Id.ToString(), cid, formattedMessage, CheckAdmin(e.Author), pm || DiscordConfig.AdminChannels.Contains(cid)); - }); - } - - /// - public string Connect() - { - try - { - if (Connected() || !DiscordConfig.Enabled) - return null; - lock (DiscordLock) - { - SeenPrivateChannels.Clear(); - client.LoginAsync(TokenType.Bot, DiscordConfig.BotToken).Wait(); - client.StartAsync().Wait(); - Thread.Sleep(1000); //wait for ConnectionState to update - } - return !Connected() ? "Connection failed!" : null; - } - catch (Exception e) - { - return e.ToString(); - } - } - - /// - public bool Connected() - { - lock (DiscordLock) - { - return client.ConnectionState == ConnectionState.Connected; - } - } - - /// - public void Disconnect() - { - try - { - if (!Connected()) - return; - lock (DiscordLock) - { - SeenPrivateChannels.Clear(); - client.StopAsync().Wait(); - client.LogoutAsync().Wait(); - } - } - catch { } - } - - /// - public string Reconnect() - { - Disconnect(); - return Connect(); - } - - /// - public void SendMessage(string msg, MessageType mt) - { - if (!Connected()) - return; - lock (DiscordLock) - { - var tasks = new List(); - foreach (var I in client.Guilds) - foreach (var J in I.TextChannels) - { - var cid = J.Id.ToString(); - var wdc = DiscordConfig.WatchdogChannels; - bool SendToThisChannel = (mt.HasFlag(MessageType.AdminInfo) && DiscordConfig.AdminChannels.Contains(cid)) - || (mt.HasFlag(MessageType.DeveloperInfo) && DiscordConfig.DevChannels.Contains(cid)) - || (mt.HasFlag(MessageType.GameInfo) && DiscordConfig.GameChannels.Contains(cid)) - || (mt.HasFlag(MessageType.WatchdogInfo) && DiscordConfig.WatchdogChannels.Contains(cid)); - - if (SendToThisChannel) - tasks.Add(J.SendMessageAsync(msg)); - } - foreach (var I in tasks) - I.Wait(); - } - } - - /// - public string SendMessageDirect(string message, string channelname) - { - if (!Connected()) - return "Disconnected."; - try - { - lock (DiscordLock) - { - var tasks = new List(); - var channel = Convert.ToUInt64(channelname); - if (SeenPrivateChannels.ContainsKey(channel)) - SeenPrivateChannels[channel].SendMessageAsync(message).Wait(); - else - foreach (var I in client.Guilds) - foreach (var J in I.TextChannels) - if (J.Id == channel) - J.SendMessageAsync(message).Wait(); - return null; - } - } - catch (Exception e) - { - return e.ToString(); - } - } - - /// - /// Shutsdown and disposes - /// - void DisconnectAndDispose() - { - try - { - client.StopAsync().Wait(); - client.LogoutAsync().Wait(); - } - catch { } - client.Dispose(); - } - - /// - public string SetProviderInfo(ChatSetupInfo info) - { - try - { - lock (DiscordLock) - { - var odc = DiscordConfig; - DiscordConfig = new DiscordSetupInfo(info); - if (DiscordConfig.BotToken != odc.BotToken) - { - DisconnectAndDispose(); - Init(info); - } - if (DiscordConfig.Enabled) - { - if (!Connected()) - return Reconnect(); - } - else - Disconnect(); - } - return null; - } - catch (Exception e) - { - return e.ToString(); - } - } - - #region IDisposable Support - /// - /// To detect redundant calls - /// - private bool disposedValue = false; - - /// - /// Implements the pattern. Calls - /// - /// if was called manually, if it was from the finalizer - void Dispose(bool disposing) - { - if (!disposedValue) - { - if (disposing) - { - DisconnectAndDispose(); - // TODO: dispose managed state (managed objects). - } - - // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below. - // TODO: set large fields to null. - - disposedValue = true; - } - } - - // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources. - // ~TGDiscordChatProvider() { - // // Do not change this code. Put cleanup code in Dispose(bool disposing) above. - // Dispose(false); - // } - /// - /// Implements the pattern - /// - public void Dispose() - { - // Do not change this code. Put cleanup code in Dispose(bool disposing) above. - Dispose(true); - // TODO: uncomment the following line if the finalizer is overridden above. - // GC.SuppressFinalize(this); - } - #endregion - } -} diff --git a/TGS.Server/ChatProviders/IRCChatProvider.cs b/TGS.Server/ChatProviders/IRCChatProvider.cs deleted file mode 100644 index 34bfff55c3..0000000000 --- a/TGS.Server/ChatProviders/IRCChatProvider.cs +++ /dev/null @@ -1,378 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Threading; -using TGS.Interface; -using Meebey.SmartIrc4net; - -namespace TGS.Server.ChatProviders -{ - /// - /// for internet relay chat - /// - sealed class IRCChatProvider : IChatProvider - { - /// - /// Header used to mark that a channel is actually a query message - /// - const string PrivateMessageMarker = "---PRIVATE-MSG---"; - /// - /// The irc client - /// - IrcFeatures irc; - /// - /// Used for multithreading safety - /// - object IRCLock = new object(); - - /// - /// The setup info for the provider - /// - IRCSetupInfo IRCConfig; - - /// - public event OnChatMessage OnChatMessage; - - /// - public ChatSetupInfo ProviderInfo() - { - return IRCConfig; - } - - /// - /// Construct a - /// - /// The - public IRCChatProvider(ChatSetupInfo info) - { - IRCConfig = new IRCSetupInfo(info); - irc = new IrcFeatures() - { - SupportNonRfc = true, - CtcpUserInfo = Server.VersionString, - AutoRejoin = true, - AutoRejoinOnKick = true, - AutoRelogin = true, - AutoRetry = true, - AutoRetryLimit = 5, - AutoRetryDelay = 5, - ActiveChannelSyncing = true, - }; - irc.OnChannelMessage += Irc_OnChannelMessage; - irc.OnQueryMessage += Irc_OnQueryMessage; - } - - /// - public string SendMessageDirect(string message, string channel) - { - try - { - if (!Connected()) - return "Disconnected."; - lock (IRCLock) - { - SendType st; - if (channel.Contains(PrivateMessageMarker)) - { - channel = channel.Replace(PrivateMessageMarker, ""); - st = SendType.Notice; - } - else - st = SendType.Message; - irc.SendMessage(st, channel, message); - } - return null; - } - catch (Exception e) - { - return e.ToString(); - } - } - - /// - public string SetProviderInfo(ChatSetupInfo info) - { - var convertedInfo = (IRCSetupInfo)info; - var serverChange = convertedInfo.URL != IRCConfig.URL || convertedInfo.Port != IRCConfig.Port; - IRCConfig = convertedInfo; - if (!IRCConfig.Enabled) - { - Disconnect(); - return null; - } - else if (serverChange || !Connected()) - return Reconnect(); - Login(); - JoinChannels(); - return null; - } - - /// - /// Checks if a message is considered sent from a chat admin - /// - /// The - /// if was sent by a chat admin, otherwise - private bool CheckAdmin(IrcMessageData e) - { - if (IRCConfig.AdminsAreSpecial) - { - if (e.Channel == null) - return false; - var user = (NonRfcChannelUser)irc.GetChannelUser(e.Channel, e.Nick); - if (user != null) - switch (IRCConfig.AuthLevel) - { - case IRCMode.Voice: - if (user.IsVoice) - return true; - goto case IRCMode.Halfop; - case IRCMode.Halfop: - if (user.IsHalfop) - return true; - goto case IRCMode.Op; - case IRCMode.Op: - if (user.IsOp) - return true; - goto case IRCMode.Owner; - case IRCMode.Owner: - if (user.IsOwner) - return true; - break; - } - } - else - { - var lowerNick = e.Nick.ToLower(); - foreach (var I in IRCConfig.AdminList) - if (lowerNick == I.ToLower()) - return true; - } - return false; - } - - /// - /// Called when the bot recieves a query message - /// - /// The sender of the event (usually ) - /// The - private void Irc_OnQueryMessage(object sender, IrcEventArgs e) - { - OnChatMessage(this, e.Data.Nick, e.Data.Nick + PrivateMessageMarker, e.Data.Message, CheckAdmin(e.Data), true); - } - - /// - /// Called when a channel the bot is in recieves a message - /// - /// The sender of the event (usually ) - /// The - private void Irc_OnChannelMessage(object sender, IrcEventArgs e) - { - var formattedMessage = e.Data.Message.Trim(); - - var splits = new List(formattedMessage.Split(' ')); - var test = splits[0]; - if (test.Length > 1 && (test[test.Length - 1] == ':' || test[test.Length - 1] == ',')) - test = test.Substring(0, test.Length - 1); - test = test.ToLower(); - if (test != irc.Nickname.ToLower() && test != "!tgs") - return; - - splits.RemoveAt(0); - formattedMessage = String.Join(" ", splits); - - OnChatMessage(this, e.Data.Nick, e.Data.Channel, formattedMessage, CheckAdmin(e.Data), IRCConfig.AdminChannels.Contains(e.Data.Channel.ToLower())); - } - - /// - /// Joins all channels specified in - /// - void JoinChannels() - { - var hs = new HashSet(); //for unique inserts - foreach (var I in IRCConfig.AdminChannels) - hs.Add(I); - foreach (var I in IRCConfig.DevChannels) - hs.Add(I); - foreach (var I in IRCConfig.GameChannels) - hs.Add(I); - foreach (var I in IRCConfig.WatchdogChannels) - hs.Add(I); - var ToPart = new List(); - foreach (var I in irc.JoinedChannels) - if (!hs.Remove(I)) - ToPart.Add(I); - foreach (var I in ToPart) - irc.RfcPart(I); - foreach (var I in hs) - irc.RfcJoin(I); - } - - /// - /// Sends a login query to with message - /// - void Login() - { - lock (IRCLock) - { - if (IRCConfig.AuthTarget != null) - irc.SendMessage(SendType.Message, IRCConfig.AuthTarget, IRCConfig.AuthMessage); - } - } - /// - public string Connect() - { - if (Connected() || !IRCConfig.Enabled) - return null; - lock (IRCLock) - { - try - { - try - { - irc.Connect(IRCConfig.URL, IRCConfig.Port); - } - catch (Exception e) - { - return "IRC server unreachable: " + e.ToString(); - } - - try - { - irc.Login(IRCConfig.Nickname, IRCConfig.Nickname); - } - catch (Exception e) - { - return "Bot name is already taken: " + e.ToString(); - } - Login(); - JoinChannels(); - - new Thread(new ThreadStart(IRCListen)) { IsBackground = true }.Start(); - return null; - } - catch (Exception e) - { - return e.ToString(); - } - } - } - - /// - /// Runs the listener in a safe loop - /// - void IRCListen() - { - Thread.CurrentThread.Name = "IRC Listener Thread"; - while (irc != null && Connected()) - try - { - irc.ListenOnce(true); - irc.Listen(false); - //ensure we have the correct nick - if (irc.Nickname != IRCConfig.Nickname && irc.GetIrcUser(IRCConfig.Nickname) == null) - irc.RfcNick(IRCConfig.Nickname); - } - catch { } - } - - /// - public string Reconnect() - { - Disconnect(); - return Connect(); - } - - /// - public void Disconnect() - { - try - { - lock (IRCLock) - { - if (irc.IsConnected) - { - irc.RfcQuit("Twas meant to be..."); - irc.Disconnect(); - } - } - } - catch { } - } - /// - public bool Connected() - { - lock (IRCLock) - { - return irc != null && irc.IsConnected; - } - } - /// - public void SendMessage(string message, MessageType mt) - { - if (!Connected()) - return; - lock (IRCLock) - { - string cids = ""; - for (var I = 0; I < irc.JoinedChannels.Count; ++I) - { - var cid = irc.JoinedChannels[I]; - bool SendToThisChannel = (mt.HasFlag(MessageType.AdminInfo) && IRCConfig.AdminChannels.Contains(cid)) - || (mt.HasFlag(MessageType.DeveloperInfo) && IRCConfig.DevChannels.Contains(cid)) - || (mt.HasFlag(MessageType.GameInfo) && IRCConfig.GameChannels.Contains(cid)) - || (mt.HasFlag(MessageType.WatchdogInfo) && IRCConfig.WatchdogChannels.Contains(cid)); - if (SendToThisChannel) - cids += I > 0 ? ',' + cid : cid; - } - irc.SendMessage(SendType.Message, cids, message); - } - } - - - #region IDisposable Support - /// - /// To detect redundant calls - /// - private bool disposedValue = false; - - /// - /// Implements the pattern. Calls and sets to - /// - /// if was called manually, if it was from the finalizer - void Dispose(bool disposing) - { - if (!disposedValue) - { - if (disposing) - { - // TODO: dispose managed state (managed objects). - Disconnect(); - irc = null; - } - - // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below. - // TODO: set large fields to null. - - disposedValue = true; - } - } - - // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources. - // ~TGIRCChatProvider() { - // // Do not change this code. Put cleanup code in Dispose(bool disposing) above. - // Dispose(false); - // } - - // This code added to correctly implement the disposable pattern. - /// - /// Implements the pattern - /// - public void Dispose() - { - // Do not change this code. Put cleanup code in Dispose(bool disposing) above. - Dispose(true); - // TODO: uncomment the following line if the finalizer is overridden above. - // GC.SuppressFinalize(this); - } - #endregion - } -} diff --git a/TGS.Server/DeprecatedInstanceConfig.cs b/TGS.Server/DeprecatedInstanceConfig.cs deleted file mode 100644 index 22affc940a..0000000000 --- a/TGS.Server/DeprecatedInstanceConfig.cs +++ /dev/null @@ -1,51 +0,0 @@ -using System; -using System.Configuration; - -namespace TGS.Server -{ - /// - /// Used to migrate old config settings - /// - class DeprecatedInstanceConfig : InstanceConfig - { - /// - /// The default directory - /// - const string DefaultInstallationPath = "C:\\TGSTATION-SERVER-3"; - - /// - /// Construct a . Used by the deserializer - /// - [Obsolete("This method is for use by the deserializer only.", true)] - public DeprecatedInstanceConfig() : base(DefaultInstallationPath) { } - - /// - /// Construct a for a at - /// - /// The path to the - public DeprecatedInstanceConfig(string path) : base(path) { } - - /// - /// Migrates the from to - /// - public void MigrateToCurrentVersion() - { - for (; Version < CurrentVersion; ++Version) - Migrate(); - } - - /// - /// Migrates the from to + 1 - /// - void Migrate() - { - switch (Version) - { - //add this field - case 0: - ServerStartupTimeout = 60; - break; - } - } - } -} diff --git a/TGS.Server/EventID.cs b/TGS.Server/EventID.cs deleted file mode 100644 index 7f04860da9..0000000000 --- a/TGS.Server/EventID.cs +++ /dev/null @@ -1,343 +0,0 @@ -using System; - -namespace TGS.Server -{ - /// - /// Various events and their IDs in no particular order. Found in the Windows event log. These key incremented by 100 and are guaranteed to never be reused in the future. In the windows event viewer, these IDs will be offset by the to distinguish events between instances. Each event ID may be information, a warning, or error and will be documented accordingly. Warnings will occur due to user, data, or network errors. Errors will occur due to filesystem errors or hard faults - /// - public enum EventID : int - { - /// - /// Info: When the bot recieves a (not necessarily valid) chat command - /// - ChatCommand = 100, - /// - /// Warning: When a fails to - /// - ChatConnectFail = 200, - /// - /// Error: When a fails to construct - /// - ChatProviderStartFail = 300, - /// - /// Error: When a bad is passed - /// - InvalidChatProvider = 400, - /// - /// Not in use anymore - /// - [Obsolete("Not in use anymore", true)] - UpdateRequest = 500, - /// - /// Warning: When the BYOND updater cannot download a revision. Error: When the BYOND updater cannot unzip or apply a revision - /// - BYONDUpdateFail = 600, - /// - /// Info: When the BYOND updater successfully staged a revision but could not apply it due to the being active - /// - BYONDUpdateStaged = 700, - /// - /// Info: When the BYOND updater successfully applies an update - /// - BYONDUpdateComplete = 800, - /// - /// Error: Failed to move the with TGS.Interface.Components.ITGAdministration.MoveServer(string) - /// - [Obsolete("Not in use anymore", true)] - ServerMoveFailed = 900, - /// - /// Warning: Failed to delete the old directory during a TGS.Interface.Components.ITGAdministration.MoveServer(string) operation - /// - [Obsolete("Not in use anymore", true)] - ServerMovePartial = 1000, - /// - /// Info: Successful completion of a TGS.Interface.Components.ITGAdministration.MoveServer(string) operation - /// - [Obsolete("Not in use anymore", true)] - ServerMoveComplete = 1100, - /// - /// Error: An internal error occurred during a operation - /// - DMCompileCrash = 1200, - /// - /// Error: An internal error occurred during a operation - /// - DMInitializeCrash = 1300, - /// - /// Warning: Compile failure of the target .dme in a operation - /// - DMCompileError = 1400, - /// - /// Info: Successful completion of a operation - /// - DMCompileSuccess = 1500, - /// - /// Info: Successful completion of a operation - /// - DMCompileCancel = 1600, - /// - /// Error: Failed to reattach the watchdog to a running DreamDaemon instance after a update - /// - DDReattachFail = 1700, - /// - /// Info: Successfully reattached the watchdog to a running DreamDaemon instance after a update - /// - DDReattachSuccess = 1800, - /// - /// Error: An internal error occurred while running the DreamDaemon watchdog - /// - DDWatchdogCrash = 1900, - /// - /// Info: The watchdog has exited, either for an or , or operation - /// - DDWatchdogExit = 2000, - /// - /// Not in use anymore - /// - [Obsolete("Not in use anymore", true)] - DDWatchdogRebootedServer = 2100, - /// - /// Warning: DreamDaemon has crashed and the watchdog is attempting to reboot it - /// - DDWatchdogRebootingServer = 2200, - /// - /// Info: The watchdog is performing a operation - /// - DDWatchdogRestart = 2300, - /// - /// Info: Successful completion of a operation - /// - DDWatchdogRestarted = 2400, - /// - /// Info: Successful completion of a operation - /// - DDWatchdogStarted = 2500, - /// - /// Info: Successful completion of a operation - /// - [Obsolete("Not in use anymore", true)] - ChatSend = 2600, - /// - /// Info: Successful completion of a operation - /// - [Obsolete("Not in use anymore", true)] - ChatBroadcast = 2700, - /// - /// Not in use anymore - /// - [Obsolete("Not in use anymore", true)] - ChatAdminBroadcast = 2800, - /// - /// Error: When an error occurs during a operation - /// - [Obsolete("Not in use anymore", true)] - ChatDisconnectFail = 2900, - /// - /// Not in use anymore - /// - [Obsolete("Not in use anymore", true)] - TopicSent = 3000, - /// - /// Not in use anymore - /// - [Obsolete("Not in use anymore", true)] - TopicFailed = 3100, - /// - /// Info: When the has been generated - /// - CommsKeySet = 3200, - /// - /// Not in use anymore - /// - [Obsolete("Not in use anymore", true)] - NudgeStartFail = 3300, - /// - /// Not in use anymore - /// - [Obsolete("Not in use anymore", true)] - NudgeCrash = 3400, - /// - /// Info: Successful completion of a operation - /// - RepoClone = 3500, - /// - /// Warning: An error occurred during a operation - /// - RepoCloneFail = 3600, - /// - /// Info: Successful completion of a operation - /// - RepoCheckout = 3700, - /// - /// Warning: An error occurred during a operation - /// - RepoCheckoutFail = 3800, - /// - /// Info: Successful completion of a operation with a parameter - /// - RepoHardUpdate = 3900, - /// - /// Warning: An error occurred during a operation with a parameter - /// - RepoHardUpdateFail = 4000, - /// - /// Info: Successful completion of a operation with a parameter - /// - RepoMergeUpdate = 4100, - /// - /// Warning: An error occurred during a operation with a parameter - /// - RepoMergeUpdateFail = 4200, - /// - /// Info: A backup tag of the repository in it's current state was successfully created - /// - RepoBackupTag = 4300, - /// - /// Warning: A backup tag of the repository in it's current state failed to be created - /// - RepoBackupTagFail = 4400, - /// - /// Info: Successful completion of a operation with a parameter - /// - RepoResetTracked = 4500, - /// - /// Warning: An error occurred during a operation with a parameter - /// - RepoResetTrackedFail = 4600, - /// - /// Info: Successful completion of a operation with a parameter - /// - RepoReset = 4700, - /// - /// Warning: An error occurred during a operation with a parameter - /// - RepoResetFail = 4800, - /// - /// Error: Failed to update or delete the testmerged PR list - /// - RepoPRListError = 4900, - /// - /// Info: Successful completion of a operation - /// - RepoPRMerge = 5000, - /// - /// Warning: An error occurred during a operation - /// - RepoPRMergeFail = 5100, - /// - /// Info: Successfully committed the paths specified in the synchronize_directories field of the 's TGS3.json - /// - RepoCommit = 5200, - /// - /// Warning: An error occurred while committing the paths specified in the synchronize_directories field of the 's TGS3.json - /// - RepoCommitFail = 5300, - /// - /// Info: Successful completion of a operation - /// - RepoPush = 5400, - /// - /// Warning: An error occurred during a operation - /// - RepoPushFail = 5500, - /// - /// Info: Successful completion of a operation - /// - RepoChangelog = 5600, - /// - /// Warning: An error occurred during a operation - /// - RepoChangelogFail = 5700, - /// - /// Info: When the dll is updated for the - /// - BridgeDLLUpdated = 5800, - /// - /// Error: An error occurred while updating the dll for the - /// - BridgeDLLUpdateFail = 5900, - /// - /// Error: An error occurred while starting the - /// - InstanceInitializationFailure = 6000, - /// - /// Error: When an exception occurs while the is stopping - /// - ServiceShutdownFail = 6100, - /// - /// Info: When the reboots in BYOND - /// - WorldReboot = 6200, - /// - /// Info: When the output of is applied to the live - /// - ServerUpdateApplied = 6300, - /// - /// Warning: When an exception occurs during a operation - /// - ChatBroadcastFail = 6400, - /// - /// Not in use anymore - /// - [Obsolete("Not in use anymore", true)] - IRCLogModes = 6500, - /// - /// Warning: When the Repository submodule handler has to reclone a submodule entirely. This is a long operation and is due to an upstream bug - /// Error: When a submodule update operation fails completely - /// - Submodule = 6600, - /// - /// This event is of type or . It occurs when a user different from the previous one tries and either succeeds or fails to access a . DreamDaemon itself successfully accessing will not trigger this - /// - Authentication = 6700, - /// - /// Info: When a Preaction event successfully completes - /// - PreactionEvent = 6800, - /// - /// Warning: When a Preaction event fails - /// - PreactionFail = 6900, - /// - /// Warning: When a command from DreamDaemon fails - /// - InteropCallException = 7000, - /// - /// Warning: When the running DreamDaemon code does not have the correct API to talk to the - /// - APIVersionMismatch = 7100, - /// - /// Warning: If a .dll in the repository's TGS3.json could not be found. Error: If a symlink could not be established to a static .dll or directory - /// - RepoConfigurationFail = 7200, - /// - /// Info: Successfully read of a static path. Warning: An error occurred during a read of a static path - /// - StaticRead = 7300, - /// - /// Info: Successfully write of a static path. Warning: An error occurred during a write of a static path - /// - StaticWrite = 7400, - /// - /// Info: Successfully delete of a static path. Warning: An error occurred during a delete of a static path - /// - StaticDelete = 7500, - /// - /// Info: When an instance's logging ID is first assigned - /// - InstanceIDAssigned = 7600, - /// - /// Info: When a testmerge commit is published - /// Warning: When a testmerge commit failed to be published - /// - ReferencePush = 7700, - /// - /// Warning: When the server exits unexpectedly - /// - DDServerCrash = 7800, - /// - /// Info: When the DreamDaemon process is about to be launched - /// - DDServerStart = 7800, - } -} diff --git a/TGS.Server/Helpers.cs b/TGS.Server/Helpers.cs deleted file mode 100644 index 4dc3f4c1e9..0000000000 --- a/TGS.Server/Helpers.cs +++ /dev/null @@ -1,213 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Threading.Tasks; - -namespace TGS.Server -{ - /// - /// Generic helpers for the - /// - static class Helpers - { - /// - /// Copy a file from to , but first ensure the destination directory exists - /// - /// The source file - /// The destination file - /// If , will overwrite if it is a file. Otherwise, if exists, an exception will be thrown - public static void CopyFileForceDirectories(string source, string dest, bool overwrite) - { - try - { - Directory.CreateDirectory(Path.GetDirectoryName(dest)); - } - catch { } //we don't care if the above errors - File.Copy(source, dest, overwrite); //if this throws errors thats all we care about - } - - //http://stackoverflow.com/questions/1701457/directory-delete-doesnt-work-access-denied-error-but-under-windows-explorer-it - /// - /// Recursive directory deleter - /// - /// The directory to delete - /// If , an empty will remain instead of being deleted fully. Incompatible with - /// If any files or directories in the root level of match anything in this of s, they won't be deleted. Incompatible with - public static void DeleteDirectory(string path, bool ContentsOnly = false, IList excludeRoot = null) - { - if (!ContentsOnly && excludeRoot != null && excludeRoot.Count > 0) - throw new ArgumentException("Cannot fully delete folder with exclusions specified!"); - var di = new DirectoryInfo(path); - if (!di.Exists) - return; - if (excludeRoot != null) - for (var I = 0; I < excludeRoot.Count; ++I) - excludeRoot[I] = excludeRoot[I].ToLower(); - if (CheckDeleteSymlinkDir(di)) - return; - NormalizeAndDelete(di, excludeRoot, !ContentsOnly).Wait(); - } - - /// - /// Find all files in a directory with a given extension - /// - /// The directory to search - /// The extension to look for - /// An of s containing the full paths to files with the given in - public static IEnumerable GetFilesWithExtensionInDirectory(string directory, string extension) - { - if (directory == null) - throw new ArgumentNullException(nameof(directory)); - if (extension == null) - throw new ArgumentNullException(nameof(extension)); - - var di = new DirectoryInfo(directory); - if (!di.Exists) - yield break; - - foreach (var F in di.EnumerateFiles(String.Format(CultureInfo.InvariantCulture, "*.{0}", extension))) - yield return F.FullName; - } - - /// - /// Properly unlinks directory if it is a symlink - /// - /// for the directory in question - /// if was a symlink and deleted, otherwise - static bool CheckDeleteSymlinkDir(DirectoryInfo di) - { - if (!di.Attributes.HasFlag(FileAttributes.Directory) || di.Attributes.HasFlag(FileAttributes.ReparsePoint)) - { //this is probably a symlink - Directory.Delete(di.FullName); - return true; - } - return false; - } - - /// - /// Recursively empty a directory - /// - /// of the directory to empty - /// Lowercase file and directory names to skip while emptying this level. Not passed forward - /// If , deletes on completion - /// A representing the operation - static async Task NormalizeAndDelete(DirectoryInfo dir, IList excludeRoot, bool deleteRoot) - { - var tasks = new List(); - foreach (var subDir in dir.GetDirectories()) - { - var upperName = subDir.Name.ToUpperInvariant(); - if (excludeRoot != null && excludeRoot.Any(x => x.ToUpperInvariant() == upperName)) - continue; - if (CheckDeleteSymlinkDir(subDir)) - continue; - tasks.Add(NormalizeAndDelete(subDir, null, true)); - } - foreach (var file in dir.GetFiles()) - { - var upperName = file.Name.ToUpperInvariant(); - if (excludeRoot != null && excludeRoot.Any(x => x.ToUpperInvariant() == upperName)) - continue; - file.Attributes = FileAttributes.Normal; - file.Delete(); - } - await Task.WhenAll(tasks); - if(deleteRoot) - dir.Delete(true); - } - - /// - /// Recusively copy a directory - /// - /// The directory to copy - /// The destination directory - /// List of files and directories to ignore while copying - /// If no error will be thrown if does not exist - public static void CopyDirectory(string sourceDirName, string destDirName, IList ignore = null, bool ignoreIfNotExists = false) - { - IList realIgnore; - if (ignore != null) - { - realIgnore = new List(); - foreach (var I in ignore) - realIgnore.Add(I.ToLower()); - } - else - realIgnore = null; - CopyDirectoryImpl(sourceDirName, destDirName, realIgnore, ignoreIfNotExists).Wait(); - } - - /// - /// Recusively copy a directory - /// - /// The directory to copy - /// The destination directory - /// List of lowercase files and directories to ignore while copying - /// If no error will be thrown if does not exist - /// A representing the operation - static async Task CopyDirectoryImpl(string sourceDirName, string destDirName, IList ignore, bool ignoreIfNotExists) { - // If the destination directory doesn't exist, create it. - Directory.CreateDirectory(destDirName); - // Get the subdirectories for the specified directory. - DirectoryInfo dir = new DirectoryInfo(sourceDirName); - - if (!dir.Exists) - { - if (ignoreIfNotExists) - return; - throw new DirectoryNotFoundException( - "Source directory does not exist or could not be found: " - + sourceDirName); - } - - var tasks = new List(); - DirectoryInfo[] dirs = dir.GetDirectories(); - // copy them and their contents to new location. - foreach (DirectoryInfo subdir in dirs) - { - var upperName = subdir.Name.ToUpperInvariant(); - if (ignore != null && ignore.Any(x => x.ToUpperInvariant() == upperName)) - continue; - string temppath = Path.Combine(destDirName, subdir.Name); - tasks.Add(CopyDirectoryImpl(subdir.FullName, temppath, ignore, false)); - } - - // Get the files in the directory and copy them to the new location. - FileInfo[] files = dir.GetFiles(); - foreach (FileInfo file in files) - { - var upperName = file.Name.ToUpperInvariant(); - if (ignore != null && ignore.Any(x => x.ToUpperInvariant() == upperName)) - continue; - string temppath = Path.Combine(destDirName, file.Name); - file.CopyTo(temppath, true); - } - - await Task.WhenAll(tasks); - } - - /// - /// Properly escapes characters for a BYOND Topic() packet. See http://www.byond.com/docs/ref/info.html#/proc/list2params - /// - /// The to sanitize - /// The sanitized string - public static string SanitizeTopicString(string input) - { - return input.Replace("%", "%25").Replace("=", "%3d").Replace(";", "%3b").Replace("&", "%26").Replace("+", "%2b"); - } - - /// - /// Normalizes different versions of a path - /// - /// The path to normalize - /// The normalized path - public static string NormalizePath(string path) - { - return Path.GetFullPath(new Uri(path).LocalPath) - .TrimEnd(Path.DirectorySeparatorChar, Path.AltDirectorySeparatorChar) - .ToUpperInvariant(); - } - } -} diff --git a/TGS.Server/ILogger.cs b/TGS.Server/ILogger.cs deleted file mode 100644 index 9615b133b7..0000000000 --- a/TGS.Server/ILogger.cs +++ /dev/null @@ -1,40 +0,0 @@ -namespace TGS.Server -{ - /// - /// Used for writing logs to a provider - /// - public interface ILogger - { - /// - /// Writes information to the log - /// - /// The log message - /// The of the message - /// The 0-99 ID of the log source - void WriteInfo(string message, EventID id, byte loggingID); - - /// - /// Writes an error to the log - /// - /// The log message - /// The of the message - /// The 0-99 ID of the log source - void WriteError(string message, EventID id, byte loggingID); - - /// - /// Writes a warning to the log - /// - /// The log message - /// The of the message - /// The 0-99 ID of the log source - void WriteWarning(string message, EventID id, byte loggingID); - - /// - /// Writes an access event to the log - /// - /// The (un)authenticated Windows user's name - /// if authenticated sucessfully, otherwise - /// The 0-99 ID of the log source - void WriteAccess(string username, bool authSuccess, byte loggingID); - } -} diff --git a/TGS.Server/Instance/Administration.cs b/TGS.Server/Instance/Administration.cs deleted file mode 100644 index 20b7ad4ded..0000000000 --- a/TGS.Server/Instance/Administration.cs +++ /dev/null @@ -1,202 +0,0 @@ -using System; -using System.DirectoryServices.AccountManagement; -using System.Security.Principal; -using System.ServiceModel; -using System.Threading; -using TGS.Interface; -using TGS.Interface.Components; -using TGS.Server.Security; - -namespace TGS.Server -{ - sealed partial class Instance : ServiceAuthorizationManager, ITGAdministration - { - /// - /// The of the Windows group authorized to access the - /// - SecurityIdentifier TheDroidsWereLookingFor; - /// - /// Used for multithreading safety - /// - object authLock = new object(); - /// - /// The of the last to attempt to access the - /// - string LastSeenUser; - - /// - /// The of the account the is running as - /// - readonly SecurityIdentifier ServiceSID = WindowsIdentity.GetCurrent().User; - - void InitAdministration() - { - lock(RootAuthorizationManager.InstanceAuthManagers) - RootAuthorizationManager.InstanceAuthManagers.Add(this); - FindTheDroidsWereLookingFor(); - } - - /// - public string GetCurrentAuthorizedGroup() - { - try - { - if (TheDroidsWereLookingFor == null) - return "ADMIN"; - - string res = null; - try - { - res = GroupPrincipal.FindByIdentity(new PrincipalContext(ContextType.Machine), IdentityType.Sid, TheDroidsWereLookingFor.Value).Name; - } - catch { } - return res ?? GroupPrincipal.FindByIdentity(new PrincipalContext(ContextType.Domain), IdentityType.Sid, TheDroidsWereLookingFor.Value).Name; - } - catch - { - return null; - } - } - - /// - public string SetAuthorizedGroup(string groupName) - { - if (groupName == null) - { - TheDroidsWereLookingFor = null; - Config.AuthorizedUserGroupSID = null; - Config.Save(); - return "ADMIN"; - } - return FindTheDroidsWereLookingFor(groupName); - } - - /// - /// Set based off either an ed name or a string from the config - /// - /// The name of the group to search for - /// Recursive parameter used to check for the group using instead of - /// The name of the group allowed to access the if it could be found, otherwise - string FindTheDroidsWereLookingFor(string search = null, bool useDomain = false) - { - //find the group that is authorized to use the tools - var pc = new PrincipalContext(useDomain ? ContextType.Domain : ContextType.Machine); - var groupName = search ?? Config.AuthorizedUserGroupSID; - if (String.IsNullOrWhiteSpace(groupName)) - return null; - var gp = GroupPrincipal.FindByIdentity(pc, search != null ? IdentityType.Name : IdentityType.Sid, groupName); - if (gp == null) - { - if (search != null) - //try again with all types - gp = GroupPrincipal.FindByIdentity(pc, search); - if (gp == null) - return useDomain ? null : FindTheDroidsWereLookingFor(search, true); - } - TheDroidsWereLookingFor = gp.Sid; - if (search != null) - { - Config.AuthorizedUserGroupSID = TheDroidsWereLookingFor.Value; - Config.Save(); - } - return gp.Name; - } - - /// - /// Cleans up the component - /// - void DisposeAdministration() - { - lock(RootAuthorizationManager.InstanceAuthManagers) - RootAuthorizationManager.InstanceAuthManagers.Remove(this); - } - - /// - /// Called by WCF whenever a component call is made. Checks to see that the supplied user account has access to the requested component - /// - /// Various parameters about the operation supplied by WCF - /// if the supplied user account may use the requested component, otherwise - protected override bool CheckAccessCore(OperationContext operationContext) - { - var contract = operationContext.EndpointDispatcher.ContractName; - - if (contract == typeof(ITGConnectivity).Name) //always allow connectivity checks - return true; - - var windowsIdent = operationContext.ServiceSecurityContext.WindowsIdentity; - - if (contract == typeof(ITGInterop).Name) - { - //only allow the same user the service is running as to use interop, because that's what DD is running as, and don't spam the logs with it unless it fails - var result = windowsIdent.User == ServiceSID; - if(!result) - WriteAccess(windowsIdent.Name, false); - return result; - } - - var wp = new WindowsPrincipal(windowsIdent); - //first allow admins - var authSuccess = wp.IsInRole(WindowsBuiltInRole.Administrator); - - //if we're not an admin, check that we aren't trying to access the admin interface - if (!authSuccess && operationContext.EndpointDispatcher.ContractName != typeof(ITGAdministration).Name && TheDroidsWereLookingFor != null) - authSuccess = wp.IsInRole(new SecurityIdentifier(Config.AuthorizedUserGroupSID)); - - lock (authLock) - { - var user = windowsIdent.Name; - if (LastSeenUser != user) - { - LastSeenUser = user; - WriteAccess(user, authSuccess); - } - } - return authSuccess; - } - - public string RecreateStaticFolder() - { - lock (RepoLock) - { - if (RepoBusy) - return "Repo locked!"; - RepoBusy = true; - } - try - { - if (!Monitor.TryEnter(watchdogLock)) - return "Watchdog locked!"; - try - { - if (!Monitor.TryEnter(configLock)) - return "Static dir locked!"; - try - { - if (currentStatus != DreamDaemonStatus.Offline) - return "Watchdog running!"; - BackupAndDeleteStaticDirectory(); - InitialConfigureRepository(); - } - finally - { - Monitor.Exit(configLock); - } - } - finally - { - Monitor.Exit(watchdogLock); - } - } - catch(Exception e) - { - return e.ToString(); - } - finally - { - lock (RepoLock) - RepoBusy = false; - } - return null; - } - } -} diff --git a/TGS.Server/Instance/Byond.cs b/TGS.Server/Instance/Byond.cs deleted file mode 100644 index 59d1bc4606..0000000000 --- a/TGS.Server/Instance/Byond.cs +++ /dev/null @@ -1,428 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.IO.Compression; -using System.Net; -using System.Diagnostics; -using System.Text.RegularExpressions; -using System.Threading; -using TGS.Interface; -using TGS.Interface.Components; - -namespace TGS.Server -{ - sealed partial class Instance : ITGByond - { - /// - /// The instance directory to store the BYOND installation - /// - const string ByondDirectory = "BYOND"; - /// - /// The instance directory to use when updating the BYOND installation - /// - const string StagingDirectory = "BYOND_staged"; - /// - /// Path to the actual BYOND installation within the - /// - const string StagingDirectoryInner = StagingDirectory + "/byond"; - /// - /// The path in the instance directory to store the downloaded BYOND revision - /// - const string RevisionDownloadPath = "BYONDRevision.zip"; - /// - /// The location of the BYOND version data of an installation - /// - const string VersionFile = "/byond_version.dat"; - /// - /// The URL format string for getting BYOND version {0}.{1} zipfile - /// - const string ByondRevisionsURL = "https://secure.byond.com/download/build/{0}/{0}.{1}_byond.zip"; - /// - /// The URL for getting the latest BYOND version zipfile - /// - const string ByondLatestURL = "https://secure.byond.com/download/build/LATEST/"; - /// - /// The instance directory to modify the BYOND cfg before installation - /// - const string ByondConfigDir = StagingDirectoryInner + "/cfg"; - /// - /// The instance that contains the BYOND directx redistributable - /// - const string ByondDXDir = StagingDirectoryInner + "/directx"; - /// - /// BYOND's DreamDaemon config file in the cfg modification directory - /// - const string ByondDDConfig = ByondConfigDir + "/daemon.txt"; - /// - /// Setting to add to to suppress an invisible user prompt for running a trusted mode .dmb - /// - const string ByondNoPromptTrustedMode = "trusted-check 0"; - - /// - /// Contains cache shit that needs to be cleared out every startup - /// - static readonly string ByondCacheDirectory = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), ByondDirectory, "cache"); - - /// - /// The status of the BYOND updater - /// - ByondStatus updateStat = ByondStatus.Idle; - - /// - /// Used for multithreading safety - /// - object ByondLock = new object(); - /// - /// The last error the BYOND updater encountered - /// - string lastError; - /// - /// Thread used for staging BYOND revisions - /// - Thread RevisionStaging; - - /// - /// Global lock for installing DirectX if necessary - /// - static object DirectXInstallLock = new object(); - - /// - /// Called when the is setup. Prepares the BYOND updater - /// - void InitByond() - { - CleanByondStaging(); - } - - /// - /// Cleans the BYOND staging directory - /// - void CleanByondStaging() - { - var rrdp = RelativePath(RevisionDownloadPath); - //linger not - if (File.Exists(rrdp)) - File.Delete(rrdp); - Helpers.DeleteDirectory(RelativePath(StagingDirectory)); - } - - /// - /// Called when the is shutdown - /// - void DisposeByond() - { - lock (ByondLock) - { - if (RevisionStaging != null) - RevisionStaging.Abort(); - CleanByondStaging(); - } - } - - /// - /// Attempts to clear the - /// - void ClearCacheFolder() - { - try - { - Helpers.DeleteDirectory(ByondCacheDirectory, true, null); - } - catch { } - } - - /// - /// Checks if the updater is considered busy - /// - /// if the updater is considered busy, otherwise - bool BusyCheck() - { - lock (ByondLock) - switch (updateStat) - { - default: - case ByondStatus.Starting: - case ByondStatus.Downloading: - case ByondStatus.Staging: - case ByondStatus.Updating: - case ByondStatus.CompilingStaged: - return true; - case ByondStatus.Idle: - case ByondStatus.Staged: - return false; - } - } - - /// - public ByondStatus CurrentStatus() - { - lock (ByondLock) - { - return updateStat; - } - } - - /// - public string GetError() - { - lock (ByondLock) - { - var error = lastError; - lastError = null; - return error; - } - } - - /// - public string GetVersion(ByondVersion type) - { - try - { - lock (ByondLock) - { - if (type == ByondVersion.Latest) - { - //get the latest version from the website - HttpWebRequest request = (HttpWebRequest)WebRequest.Create(ByondLatestURL); - var results = new List(); - using (HttpWebResponse response = (HttpWebResponse)request.GetResponse()) - { - using (StreamReader reader = new StreamReader(response.GetResponseStream())) - { - string html = reader.ReadToEnd(); - - Regex regex = new Regex("\\\"([^\"]*)\\\""); - MatchCollection matches = regex.Matches(html); - foreach (Match match in matches) - if (match.Success && match.Value.Contains("_byond.exe")) - results.Add(match.Value.Replace("\"", "").Replace("_byond.exe", "")); - } - } - results.Sort(); - results.Reverse(); - return results.Count > 0 ? results[0] : null; - } - else - { - string DirToUse = RelativePath(type == ByondVersion.Staged ? StagingDirectoryInner : ByondDirectory); - if (Directory.Exists(DirToUse)) - { - string file = DirToUse + VersionFile; - if (File.Exists(file)) - return File.ReadAllText(file); - } - } - return null; - } - } - catch (Exception e) - { - return "Error: " + e.ToString(); - } - } - - /// - /// Downloads and unzips a BYOND revision. Calls afterwards if the isn't running, otherwise, calls . Sets on failure - /// - /// Stringified BYOND revision - public void UpdateToVersionImpl(object param) - { - lock (ByondLock) { - if (updateStat != ByondStatus.Starting) - return; - updateStat = ByondStatus.Downloading; - } - bool staged = false; - try - { - CleanByondStaging(); - - var vi = ((string)param).Split('.'); - var major = Convert.ToInt32(vi[0]); - var minor = Convert.ToInt32(vi[1]); - var rrdp = RelativePath(RevisionDownloadPath); - using (var client = new WebClient()) - { - SendMessage(String.Format("BYOND: Updating to version {0}.{1}...", major, minor), MessageType.DeveloperInfo); - - //DOWNLOADING - - try - { - client.DownloadFile(String.Format(ByondRevisionsURL, major, minor), rrdp); - } - catch - { - SendMessage("BYOND: Update download failed. Does the specified version exist?", MessageType.DeveloperInfo); - lastError = String.Format("Download of BYOND version {0}.{1} failed! Does it exist?", major, minor); - WriteWarning(String.Format("Failed to update BYOND to version {0}.{1}!", major, minor), EventID.BYONDUpdateFail); - lock (ByondLock) - { - updateStat = ByondStatus.Idle; - } - return; - } - } - lock (ByondLock) - { - updateStat = ByondStatus.Staging; - } - - //STAGING - - ZipFile.ExtractToDirectory(rrdp, RelativePath(StagingDirectory)); - File.Delete(rrdp); - //IMPORTANT: SET THE BYOND CONFIG TO NOT PROMPT FOR TRUSTED MODE REEE - Directory.CreateDirectory(RelativePath(ByondConfigDir)); - File.WriteAllText(RelativePath(ByondDDConfig), ByondNoPromptTrustedMode); - - if (major >= 512 && minor >= 1427) - { - if (Monitor.TryEnter(DirectXInstallLock)) - try - { - //always install it, it's pretty fast and will do better redundancy checking than us - using (var p = new Process()) - { - p.StartInfo.Arguments = "/silent"; - var rbdx = RelativePath(ByondDXDir); - p.StartInfo.FileName = rbdx + "/DXSETUP.exe"; - p.StartInfo.UseShellExecute = false; - p.StartInfo.WorkingDirectory = rbdx; - p.Start(); - try - { - p.WaitForExit(); - } - finally - { - try - { - p.Kill(); - p.WaitForExit(); - } - catch (InvalidOperationException) { } - } - - if(p.ExitCode != 0) - throw new Exception("Failed to install included DirectX! Exit code: " + p.ExitCode); - } - } - finally - { - Monitor.Exit(DirectXInstallLock); - } - } - - lock (ByondLock) - File.WriteAllText(RelativePath(StagingDirectoryInner + VersionFile), String.Format("{0}.{1}", major, minor)); - - lock (ByondLock) - { - updateStat = ByondStatus.Staged; - } - - switch (DaemonStatus()) - { - case DreamDaemonStatus.Offline: - if(ApplyStagedUpdate()) - lastError = null; - else - lastError = "Failed to apply update!"; - break; - default: - //Compile() will call RequestRestart() - lastError = "Update staged. Awaiting server restart..."; - SendMessage(String.Format("BYOND: Staging complete. Awaiting server restart...", major, minor), MessageType.DeveloperInfo); - WriteInfo(String.Format("BYOND update {0}.{1} staged", major, minor), EventID.BYONDUpdateStaged); - staged = true; - break; - } - } - catch (ThreadAbortException) - { - return; - } - catch (Exception e) - { - WriteError("Revision staging errror: " + e.ToString(), EventID.BYONDUpdateFail); - lock (ByondLock) - { - updateStat = ByondStatus.Idle; - lastError = e.ToString(); - RevisionStaging = null; - } - } - if (staged) - lock (ByondLock) - { - //Some fuckery to trick the compiler into starting even though we're staged - //Won't fuck up the actual compiler thread though - updateStat = ByondStatus.Idle; - Compile(); - updateStat = ByondStatus.Staged; - } - } - /// - public bool UpdateToVersion(int major, int minor) - { - lock (ByondLock) - { - if (!BusyCheck()) - { - updateStat = ByondStatus.Starting; - RevisionStaging = new Thread(new ParameterizedThreadStart(UpdateToVersionImpl)) - { - IsBackground = true //don't slow me down - }; - RevisionStaging.Start(String.Format("{0}.{1}", major, minor)); - return true; - } - return false; - } - } - - /// - /// Attempts to move the staged update from to . Sets on failure - /// - /// on success, on failure - bool ApplyStagedUpdate() - { - lock (CompilerLock) - { - if (compilerCurrentStatus == CompilerStatus.Compiling) - return false; - lock (ByondLock) - { - if (updateStat != ByondStatus.Staged) - return false; - updateStat = ByondStatus.Updating; - } - try - { - var rbd = RelativePath(ByondDirectory); - Helpers.DeleteDirectory(rbd); - Directory.Move(RelativePath(StagingDirectoryInner), rbd); - Helpers.DeleteDirectory(RelativePath(StagingDirectory)); - lastError = null; - SendMessage("BYOND: Update completed!", MessageType.DeveloperInfo); - WriteInfo(String.Format("BYOND update {0} completed!", GetVersion(ByondVersion.Installed)), EventID.BYONDUpdateComplete); - return true; - } - catch (Exception e) - { - lastError = e.ToString(); - SendMessage("BYOND: Update failed!", MessageType.DeveloperInfo); - WriteError("BYOND update failed! Error: " + e.ToString(), EventID.BYONDUpdateFail); - return false; - } - finally - { - lock(ByondLock) { - updateStat = ByondStatus.Idle; - } - } - } - } - } -} diff --git a/TGS.Server/Instance/Chat.cs b/TGS.Server/Instance/Chat.cs deleted file mode 100644 index 205241bfd4..0000000000 --- a/TGS.Server/Instance/Chat.cs +++ /dev/null @@ -1,255 +0,0 @@ -using Newtonsoft.Json; -using System; -using System.Linq; -using System.Collections.Generic; -using TGS.Server.ChatCommands; -using TGS.Server.ChatProviders; -using TGS.Interface; -using TGS.Interface.Components; - -namespace TGS.Server -{ - sealed partial class Instance : ITGChat - { - /// - /// Used for indicating unintialized encrypted data - /// - public const string UninitializedString = "NEEDS INITIALIZING"; - - /// - /// List of s for the - /// - IList ChatProviders; - /// - /// Used for multithreading safety - /// - object ChatLock = new object(); - - /// - /// Set up the for the - /// - public void InitChat() - { - var infos = InitProviderInfos(); - ChatProviders = new List(infos.Count); - foreach (var info in infos) - { - IChatProvider chatProvider; - try - { - switch (info.Provider) - { - case ChatProvider.Discord: - chatProvider = new DiscordChatProvider(info); - break; - case ChatProvider.IRC: - chatProvider = new IRCChatProvider(info); - break; - default: - WriteError(String.Format("Invalid chat provider: {0}", info.Provider), EventID.InvalidChatProvider); - continue; - } - } - catch (Exception e) - { - WriteError(String.Format("Failed to start chat provider {0}! Error: {1}", info.Provider, e.ToString()), EventID.ChatProviderStartFail); - continue; - } - chatProvider.OnChatMessage += ChatProvider_OnChatMessage; - var res = chatProvider.Connect(); - if (res != null) - WriteWarning(String.Format("Unable to connect to chat! Provider {0}, Error: {1}", chatProvider.GetType().ToString(), res), EventID.ChatConnectFail); - ChatProviders.Add(chatProvider); - } - } - - /// - /// Implementation of that recieves messages from all channels of all connected - /// - /// The that heard the - /// The user who wrote the - /// The channel the is from - /// The recieved message - /// if is considered a chat admin, otherwise - /// if is an admin channel, otherwise - private void ChatProvider_OnChatMessage(IChatProvider ChatProvider, string speaker, string channel, string message, bool isAdmin, bool isAdminChannel) - { - var splits = message.Trim().Split(' '); - - if (splits.Length == 1 && splits[0] == "") - { - ChatProvider.SendMessageDirect("Hi!", channel); - return; - } - - var asList = new List(splits); - - Command.OutputProcVar.Value = (m) => ChatProvider.SendMessageDirect(m, channel); - ChatCommand.CommandInfo.Value = new CommandInfo() - { - IsAdmin = isAdmin, - IsAdminChannel = isAdminChannel, - Speaker = speaker, - Server = this, - }; - WriteInfo(String.Format("Chat Command from {0} ({2}): {1}", speaker, String.Join(" ", asList), channel), EventID.ChatCommand); - if (ServerChatCommands == null) - LoadServerChatCommands(); - new RootChatCommand(ServerChatCommands).DoRun(asList); - } - - /// - /// Properly shuts down all - /// - void DisposeChat() - { - SaveChatConfig(); - foreach (var ChatProvider in ChatProviders) - ChatProvider.Dispose(); - ChatProviders = null; - } - - void SaveChatConfig() - { - var infosList = new List>(); - - foreach (var ChatProvider in ChatProviders) - infosList.Add(ChatProvider.ProviderInfo().DataFields); - var rawdata = JsonConvert.SerializeObject(infosList, Formatting.Indented); - Config.ChatProviderData = Interface.Helpers.EncryptData(rawdata, out string entrp); - Config.ChatProviderEntropy = entrp; - Config.Save(); - } - - /// - public IList ProviderInfos() - { - var infosList = new List(); - foreach (var chatProvider in ChatProviders) - infosList.Add(chatProvider.ProviderInfo()); - return infosList; - } - - /// - /// Returns a list of s loaded from the config or the defaults if none are set - /// - /// A list of s loaded from the config or the defaults if none are set - IList InitProviderInfos() - { - lock (ChatLock) - { - var rawdata = Config.ChatProviderData; - if (rawdata == UninitializedString) - return new List() { new IRCSetupInfo() { Nickname = Config.Name }, new DiscordSetupInfo() }; - - string plaintext; - try - { - plaintext = Interface.Helpers.DecryptData(rawdata, Config.ChatProviderEntropy); - - var lists = JsonConvert.DeserializeObject>>(plaintext); - var output = new List(lists.Count); - var foundirc = 0; - var founddiscord = 0; - foreach (var l in lists) - { - var info = new ChatSetupInfo(l); - if (info.Provider == ChatProvider.Discord) - ++founddiscord; - else if (info.Provider == ChatProvider.IRC) - ++foundirc; - output.Add(info); - } - - if (foundirc != 1 || founddiscord != 1) - throw new Exception(); - - return output; - } - catch - { - Config.ChatProviderData = UninitializedString; - Config.Save(); - } - } - //if we get here we want to retry - return InitProviderInfos(); - } - - /// - public string SetProviderInfo(ChatSetupInfo info) - { - info.AdminList.RemoveAll(x => String.IsNullOrWhiteSpace(x)); - info.AdminChannels.RemoveAll(x => String.IsNullOrWhiteSpace(x)); - info.GameChannels.RemoveAll(x => String.IsNullOrWhiteSpace(x)); - info.DevChannels.RemoveAll(x => String.IsNullOrWhiteSpace(x)); - info.WatchdogChannels.RemoveAll(x => String.IsNullOrWhiteSpace(x)); - try - { - lock (ChatLock) - { - foreach (var ChatProvider in ChatProviders) - if (info.Provider == ChatProvider.ProviderInfo().Provider) - { - SaveChatConfig(); - return ChatProvider.SetProviderInfo(info); - } - return "Error: Invalid provider: " + info.Provider.ToString(); - } - } - catch (Exception e) - { - return e.ToString(); - } - } - - /// - public bool Connected(ChatProvider providerType) - { - foreach (var I in ChatProviders) - if (I.ProviderInfo().Provider == providerType) - return I.Connected(); - return false; - } - - /// - /// Reconnect servers that are enabled and disconnected. Checked every time DreamDaemon reboots - /// - void ChatConnectivityCheck() - { - foreach (ChatProvider I in Enum.GetValues(typeof(ChatProvider))) - if(!Connected(I)) - Reconnect(I); - } - - /// - public string Reconnect(ChatProvider providerType) - { - foreach (var I in ChatProviders) - if (I.ProviderInfo().Provider == providerType) - return I.Reconnect(); - return "Could not find specified provider!"; - } - - /// - /// Broadcast a message to appropriate channels based on the message type - /// - /// The message to send - /// The message type - public void SendMessage(string msg, MessageType mt) - { - lock (ChatLock) - { - foreach (var ChatProvider in ChatProviders) - try - { - ChatProvider.SendMessage(msg, mt); - } - catch (Exception e) - { - WriteWarning(String.Format("Chat broadcast failed (Provider: {3}) (Flags: {0}) (Message: {1}): {2}", mt, msg, e.ToString(), ChatProvider.ProviderInfo().Provider), EventID.ChatBroadcastFail); - } - } - } - } -} diff --git a/TGS.Server/Instance/Compiler.cs b/TGS.Server/Instance/Compiler.cs deleted file mode 100644 index 3596a1068a..0000000000 --- a/TGS.Server/Instance/Compiler.cs +++ /dev/null @@ -1,685 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.Globalization; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Runtime.InteropServices; -using System.Text; -using System.Threading; -using TGS.Interface; -using TGS.Interface.Components; - -namespace TGS.Server -{ - sealed partial class Instance : ITGCompiler - { - #region Win32 Shit - [DllImport("kernel32.dll", SetLastError = true)] - static extern bool CreateSymbolicLink(string lpSymlinkFileName, string lpTargetFileName, SymbolicLink dwFlags); - enum SymbolicLink - { - File = 0, - Directory = 1 - } - #endregion - - const string StaticDirs = "Static"; - const string StaticBackupDir = "Static_BACKUP"; - - const string GameDir = "Game"; - const string GameDirA = GameDir + "/A"; - const string GameDirB = GameDir + "/B"; - const string GameDirLive = GameDir + "/Live"; - - const string LiveFile = "/TestLive.lk"; - const string ADirTest = GameDirA + LiveFile; - const string BDirTest = GameDirB + LiveFile; - const string LiveDirTest = GameDirLive + LiveFile; - - object CompilerLock = new object(); - CompilerStatus compilerCurrentStatus; - string lastCompilerError; - - Thread CompilerThread; - bool compilationCancellationRequestation = false; - bool canCancelCompilation = false; - bool silentCompile = false; - - bool UpdateStaged = false; - - //deletes leftovers and checks current status - void InitCompiler() - { - var rldt = RelativePath(LiveDirTest); - if (File.Exists(rldt)) - File.Delete(rldt); - compilerCurrentStatus = IsInitialized(); - } - - /// - public CompilerStatus GetStatus() - { - lock (CompilerLock) - { - return compilerCurrentStatus; - } - } - - /// - public string CompileError() - { - lock (CompilerLock) - { - var err = lastCompilerError; - lastCompilerError = null; - return err; - } - } - - //kills the compiler if its running - void DisposeCompiler() - { - lock (CompilerLock) - { - if (CompilerThread == null || !CompilerThread.IsAlive) - return; - CompilerThread.Abort(); //this will safely kill dm - InitCompiler(); //also cleanup - } - } - - //translates the win32 api call into an exception if it fails - static void CreateSymlink(string link, string target) - { - if (!CreateSymbolicLink(new DirectoryInfo(link).FullName, new DirectoryInfo(target).FullName, File.Exists(target) ? SymbolicLink.File : SymbolicLink.Directory)) - throw new Exception(String.Format("Failed to create symlink from {0} to {1}! Error: {2}", target, link, Marshal.GetLastWin32Error())); - } - - //requires CompilerLock to be locked - bool CompilerIdleNoLock() - { - return compilerCurrentStatus == CompilerStatus.Uninitialized || compilerCurrentStatus == CompilerStatus.Initialized; - } - - /// - public bool Initialize() - { - lock (CompilerLock) - { - if (!CompilerIdleNoLock()) - return false; - lastCompilerError = null; - compilerCurrentStatus = CompilerStatus.Initializing; - CompilerThread = new Thread(new ThreadStart(InitializeImpl)); - CompilerThread.Start(); - return true; - } - } - - //what is says on the tin - CompilerStatus IsInitialized() - { - if (File.Exists(RelativePath(Path.Combine(GameDirLive, BridgeDLLName))) || File.Exists(RelativePath(Path.Combine(GameDirLive, Assembly.GetAssembly(typeof(IClient)).GetName().Name + ".dll")))) //its a good tell, jim - return CompilerStatus.Initialized; - return CompilerStatus.Uninitialized; - } - - //we need to remove symlinks before we can recursively delete - void CleanGameFolder() - { - var GameDirABridge = RelativePath(Path.Combine(GameDirA, BridgeDLLName)); - if (Directory.Exists(GameDirABridge)) - Directory.Delete(GameDirABridge); - - var GameDirBBridge = RelativePath(Path.Combine(GameDirB, BridgeDLLName)); - if (Directory.Exists(GameDirBBridge)) - Directory.Delete(GameDirBBridge); - - var rgdl = RelativePath(GameDirLive); - if (Directory.Exists(rgdl)) - Directory.Delete(rgdl); - } - - /// - /// Copies .dm files from to and returns #include lines for them - /// - /// The directory to copy .dm files to - /// DM #include lines for .dm files in - IEnumerable GetAndCopyIncludeLines(string stagingDir) - { - if (stagingDir == null) - throw new ArgumentNullException(nameof(stagingDir)); - var baseURI = new Uri(stagingDir); - foreach (var I in Helpers.GetFilesWithExtensionInDirectory(RelativePath(StaticDirs), "dm")) - { - var fileURI = new Uri(I); - var fileName = Path.GetFileName(I); - File.Copy(I, Path.Combine(stagingDir, fileName)); - yield return String.Format(CultureInfo.InvariantCulture, "#include \"{0}\"", fileName); - } - } - - /// - /// Adds includes for .dm files in - /// - /// The directory to operate on - /// The full path the the .dme in - void HandleDMEModifications(string stagingDir, string dmePath) - { - if (stagingDir == null) - throw new ArgumentNullException(nameof(stagingDir)); - if (dmePath == null) - throw new ArgumentNullException(nameof(dmePath)); - if (!File.Exists(dmePath)) - return; //someone else will deal with this - var newInclusions = new List(); - var newLines = new List(); - - var lines = File.ReadAllLines(dmePath).ToList(); - var initalCount = lines.Count; - var enumerator = GetAndCopyIncludeLines(stagingDir); - for (var I = 0; I < lines.Count; ++I) - { - var line = lines[I]; - if (line.Contains("BEGIN_INCLUDE")) - { - lines.InsertRange(I + 1, enumerator); - break; - } - } - if (lines.Count == initalCount) - lines.InsertRange(0, enumerator); - - if (lines.Count > initalCount) - File.WriteAllLines(dmePath, lines); - } - - //Initializing thread - public void InitializeImpl() - { - Thread.CurrentThread.Name = "Initialization Thread"; - try - { - if (DaemonStatus() != DreamDaemonStatus.Offline) - { - lock (CompilerLock) - { - lastCompilerError = "Dream daemon must not be running"; - compilerCurrentStatus = IsInitialized(); - return; - } - } - - if (!Exists()) //repo - { - lock (CompilerLock) - { - lastCompilerError = "Repository is not setup!"; - compilerCurrentStatus = IsInitialized(); - return; - } - } - - if (!RepoConfigsMatch()) - { - lock (CompilerLock) - { - lastCompilerError = "Repository TGS3.json does not match cached version! Please update the config appropriately!"; - compilerCurrentStatus = IsInitialized(); - return; - } - } - try - { - SendMessage("DM: Setting up symlinks...", MessageType.DeveloperInfo); - CleanGameFolder(); - Helpers.DeleteDirectory(RelativePath(GameDir)); - - Directory.CreateDirectory(RelativePath(GameDirA)); - Directory.CreateDirectory(RelativePath(GameDirB)); - - var rep_config = GetCachedRepoConfig(); - - if (rep_config != null) { - foreach (var I in rep_config.StaticDirectoryPaths) - CreateSymlink(RelativePath(Path.Combine(GameDirA, I)), RelativePath(Path.Combine(StaticDirs, I))); - foreach (var I in rep_config.DLLPaths) - CreateSymlink(RelativePath(Path.Combine(GameDirA, I)), RelativePath(Path.Combine(StaticDirs, I))); - } - - var rbdlln = RelativePath(BridgeDLLName); - CreateSymlink(RelativePath(Path.Combine(GameDirA, BridgeDLLName)), rbdlln); - CreateSymlink(RelativePath(Path.Combine(GameDirB, BridgeDLLName)), rbdlln); - - CreateSymlink(RelativePath(GameDirLive), RelativePath(GameDirA)); - - lock (CompilerLock) - { - compilerCurrentStatus = CompilerStatus.Compiling; - silentCompile = true; - } - } - catch (ThreadAbortException) - { - return; - } - catch (Exception e) - { - lock (CompilerLock) - { - SendMessage("DM: Setup failed!", MessageType.DeveloperInfo); - WriteError("Compiler Initialization Error: " + e.ToString(), EventID.DMInitializeCrash); - lastCompilerError = e.ToString(); - compilerCurrentStatus = CompilerStatus.Uninitialized; - return; - } - } - } - catch (ThreadAbortException) - { - return; - } - CompileImpl(); - } - - //Returns the A or B dir in which the game is NOT running - string GetStagingDir() - { - string TheDir; - if (!Directory.Exists(RelativePath(GameDirLive))) - TheDir = GameDirA; - else - { - File.Create(RelativePath(LiveDirTest)).Close(); - try - { - if (File.Exists(RelativePath(ADirTest))) - TheDir = GameDirA; - else if (File.Exists(RelativePath(BDirTest))) - TheDir = GameDirB; - else - throw new Exception("Unable to determine current live directory!"); - } - finally - { - File.Delete(RelativePath(LiveDirTest)); - } - - - TheDir = InvertDirectory(TheDir); - } - - //So TheDir is what the Live folder is NOT pointing to - //Now we need to check if DD is running that folder and swap it if necessary - - var rsclock = RelativePath(TheDir + "/" + Config.ProjectName + ".rsc.lk"); - if (File.Exists(rsclock)) - { - try - { - File.Delete(rsclock); - } - catch //held open by byond - { - //This means there is a staged update waiting to be applied, we have to unstage it before we can work - Directory.Delete(RelativePath(GameDirLive)); - CreateSymlink(RelativePath(GameDirLive), RelativePath(TheDir)); - return RelativePath(InvertDirectory(TheDir)); - } - } - return RelativePath(TheDir); - } - - //I hope you can read this - string InvertDirectory(string gameDirectory) - { - if (gameDirectory == GameDirA) - return GameDirB; - else - return GameDirA; - } - - //Compiler thread - void CompileImpl() - { - if(Thread.CurrentThread.Name == null) - Thread.CurrentThread.Name = "Compiler Thread"; - try - { - if (!RepoConfigsMatch()) - { - lock (CompilerLock) - { - lastCompilerError = "Repository TGS3.json does not match cached version! Please update the config appropriately!"; - compilerCurrentStatus = IsInitialized(); - return; - } - } - string resurrectee; - bool repobusy_check = false; - - lock (RepoLock) - { - if (RepoBusy) - repobusy_check = true; - else - RepoBusy = true; - } - - if (repobusy_check) - { - SendMessage("DM: Copy aborted, repo locked!", MessageType.DeveloperInfo); - lock (CompilerLock) - { - lastCompilerError = "The repo could not be locked for copying"; - compilerCurrentStatus = CompilerStatus.Initialized; //still fairly valid - return; - } - } - - string CurrentSha; - string dmeName, dmePath; - try - { - bool silent; - lock (CompilerLock) - { - silent = silentCompile; - silentCompile = false; - } - - if (!silent) - SendMessage("DM: Compiling...", MessageType.DeveloperInfo); - - resurrectee = GetStagingDir(); //non-relative - - var Config = GetCachedRepoConfig(); - var deleteExcludeList = new List { BridgeDLLName }; - deleteExcludeList.AddRange(Config.StaticDirectoryPaths); - deleteExcludeList.AddRange(Config.DLLPaths); - Helpers.DeleteDirectory(resurrectee, true, deleteExcludeList); - - - Directory.CreateDirectory(resurrectee + "/.git/logs"); - - foreach (var I in Config.StaticDirectoryPaths) - { - var the_path = Path.Combine(resurrectee, I); - if (!Directory.Exists(the_path)) - CreateSymlink(Path.Combine(resurrectee, I), RelativePath(Path.Combine(StaticDirs, I))); - } - foreach (var I in Config.DLLPaths) - { - var the_path = Path.Combine(resurrectee, I); - if (!File.Exists(the_path)) - CreateSymlink(the_path, RelativePath(Path.Combine(StaticDirs, I))); - } - - if (!File.Exists(Path.Combine(resurrectee, BridgeDLLName))) - CreateSymlink(Path.Combine(resurrectee, BridgeDLLName), RelativePath(BridgeDLLName)); - - deleteExcludeList.Add(".git"); - Helpers.CopyDirectory(RelativePath(RepoPath), resurrectee, deleteExcludeList); - CurrentSha = GetShaOrBranchNoLock(out string error, false, false); - //just the tip - const string GitLogsDir = "/.git/logs"; - Helpers.CopyDirectory(RelativePath(RepoPath + GitLogsDir), resurrectee + GitLogsDir); - dmeName = String.Format(CultureInfo.InvariantCulture, "{0}.dme", ProjectName()); - dmePath = Path.Combine(resurrectee, dmeName); - HandleDMEModifications(resurrectee, dmePath); - try - { - File.Copy(RelativePath(PRJobFile), Path.Combine(resurrectee, PRJobFile)); - } - catch { } - } - finally - { - lock (RepoLock) - RepoBusy = false; - } - - var res = CreateBackup(); - if (res != null) - lock (CompilerLock) - { - lastCompilerError = res; - compilerCurrentStatus = CompilerStatus.Initialized; - return; - } - - if (!File.Exists(dmePath)) - { - var errorMsg = String.Format("Could not find {0}!", dmeName); - SendMessage("DM: " + errorMsg, MessageType.DeveloperInfo); - WriteError(errorMsg, EventID.DMCompileCrash); - lock (CompilerLock) - { - lastCompilerError = errorMsg; - compilerCurrentStatus = CompilerStatus.Initialized; - return; - } - } - - if (!PrecompileHook()) - { - lastCompilerError = "The precompile hook failed"; - compilerCurrentStatus = CompilerStatus.Initialized; //still fairly valid - WriteWarning("Precompile hook failed!", EventID.DMCompileError); - return; - } - - bool stagedBuild; - lock (ByondLock) - { - stagedBuild = updateStat == ByondStatus.Staged; - if (stagedBuild) - updateStat = ByondStatus.CompilingStaged; - else if (GetVersion(ByondVersion.Installed) == null) - lock (CompilerLock) - { - lastCompilerError = "BYOND not installed!"; - compilerCurrentStatus = CompilerStatus.Initialized; - return; - } - } - - using (var DM = new Process()) //will kill the process if the thread is terminated - { - DM.StartInfo.FileName = RelativePath(Path.Combine(stagedBuild ? StagingDirectoryInner : ByondDirectory, "bin/dm.exe")); - DM.StartInfo.Arguments = String.Format("-clean {0}", dmePath); - DM.StartInfo.RedirectStandardOutput = true; - DM.StartInfo.UseShellExecute = false; - var OutputList = new StringBuilder(); - DM.OutputDataReceived += new DataReceivedEventHandler( - delegate (object sender, DataReceivedEventArgs e) - { - OutputList.Append(Environment.NewLine); - OutputList.Append(e.Data); - } - ); - try - { - lock (CompilerLock) - { - if (compilationCancellationRequestation) - return; - canCancelCompilation = true; - } - - DM.Start(); - DM.BeginOutputReadLine(); - while (!DM.HasExited) - DM.WaitForExit(100); - DM.CancelOutputRead(); - - lock (CompilerLock) - { - canCancelCompilation = false; - compilationCancellationRequestation = false; - } - } - catch - { - if (!DM.HasExited) - { - DM.Kill(); - DM.WaitForExit(); - } - throw; - } - finally - { - lock (CompilerLock) - { - canCancelCompilation = false; - } - if (stagedBuild) - { - lock (ByondLock) - updateStat = ByondStatus.Staged; - RequestRestart(); - } - } - - if (DM.ExitCode == 0) - { - lock (watchdogLock) - { - //gotta go fast - var online = currentStatus == DreamDaemonStatus.Online; - if (online) - Proc.Suspend(); - try - { - var rgdl = RelativePath(GameDirLive); - if (Directory.Exists(rgdl)) - //these next two lines should be atomic but this is the best we can do - Directory.Delete(rgdl); - CreateSymlink(rgdl, resurrectee); - } - finally - { - if (online && !Proc.HasExited) - Proc.Resume(); - } - } - var staged = DaemonStatus() != DreamDaemonStatus.Offline; - if (!PostcompileHook()) - { - lastCompilerError = "The postcompile hook failed"; - compilerCurrentStatus = CompilerStatus.Initialized; //still fairly valid - WriteWarning("Postcompile hook failed!", EventID.DMCompileError); - return; - } - UpdateLiveSha(CurrentSha); - var msg = String.Format("Compile complete!{0}", !staged ? "" : " Server will update on reboot."); - WorldAnnounce("Server updated, changes will be applied on reboot..."); - SendMessage("DM: " + msg, MessageType.DeveloperInfo); - WriteInfo(msg, EventID.DMCompileSuccess); - lock (CompilerLock) - { - if (staged) - UpdateStaged = true; - lastCompilerError = null; - compilerCurrentStatus = CompilerStatus.Initialized; //still fairly valid - } - } - else - { - SendMessage("DM: Compile failed!", MessageType.DeveloperInfo); //Also happens for warnings - WriteWarning("Compile error: " + OutputList.ToString(), EventID.DMCompileError); - lock (CompilerLock) - { - lastCompilerError = "DM compile failure"; - compilerCurrentStatus = CompilerStatus.Initialized; - } - } - } - } - catch (ThreadAbortException) - { - return; - } - catch (Exception e) - { - SendMessage("DM: Compiler thread crashed!", MessageType.DeveloperInfo); - WriteError("Compile manager errror: " + e.ToString(), EventID.DMCompileCrash); - lock (CompilerLock) - { - lastCompilerError = e.ToString(); - compilerCurrentStatus = CompilerStatus.Initialized; //still fairly valid - } - } - finally - { - lock (CompilerLock) - { - canCancelCompilation = false; - if (compilationCancellationRequestation) - { - compilerCurrentStatus = CompilerStatus.Initialized; - compilationCancellationRequestation = false; - SendMessage("DM: Compile cancelled!", MessageType.DeveloperInfo); - WriteInfo("Compilation cancelled", EventID.DMCompileCancel); - } - } - } - } - //kicks off the compiler thread - /// - public bool Compile(bool silent = false) - { - lock (CompilerLock) - { - if (compilerCurrentStatus != CompilerStatus.Initialized) - return false; - lock (ByondLock) - if (updateStat == ByondStatus.Staged) - return false; - silentCompile = silent; - lastCompilerError = null; - compilerCurrentStatus = CompilerStatus.Compiling; - CompilerThread = new Thread(new ThreadStart(CompileImpl)); - CompilerThread.Start(); - } - return true; - } - - /// - public string ProjectName() - { - lock (CompilerLock) - { - return Config.ProjectName; - } - } - - /// - public void SetProjectName(string projectName) - { - lock (CompilerLock) - { - Config.ProjectName = projectName; - Config.Save(); - } - } - - public string Cancel() - { - lock (CompilerLock) - { - if (compilerCurrentStatus != CompilerStatus.Compiling) - return "Invalid state for cancellation!"; - compilationCancellationRequestation = true; - if (canCancelCompilation) - CompilerThread.Abort(); - else - return "Compilation will be cancelled when the repo copy is complete"; - return null; - } - } - } -} diff --git a/TGS.Server/Instance/Config.cs b/TGS.Server/Instance/Config.cs deleted file mode 100644 index 5eda46466e..0000000000 --- a/TGS.Server/Instance/Config.cs +++ /dev/null @@ -1,250 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.ServiceModel; -using TGS.Interface.Components; - -namespace TGS.Server -{ - //knobs and such - sealed partial class Instance : ITGConfig - { - /// - /// Used for multithreading safety - /// - object configLock = new object(); //for atomic reads/writes - - /// - public string ReadText(string staticRelativePath, bool repo, out string error, out bool unauthorized) - { - string path = null; - try - { - var configDir = RelativePath(repo ? RepoPath : StaticDirs); - - path = configDir + '/' + staticRelativePath; //do not use path.combine or it will try and take the root - lock (configLock) - { - var di1 = new DirectoryInfo(configDir); - if (repo) - { - //ensure we aren't trying to read anything outside the static dirs - var Config = GetCachedRepoConfig(); - if (Config == null) - { - error = "Unable to load static directory configuration"; - unauthorized = false; - return null; - } - var Found = false; - foreach (var I in Config.StaticDirectoryPaths) - { - if (di1.FullName == new DirectoryInfo(Path.Combine(RelativePath(RepoPath), I)).FullName) - { - Found = true; - break; - } - } - if (!Found) - { - error = "File is not in a configured static directory!"; - unauthorized = false; - return null; - } - } - - var di2 = new DirectoryInfo(new FileInfo(path).Directory.FullName); - - var good = false; - while (di2 != null) - { - if (di2.FullName == di1.FullName) - { - good = true; - break; - } - else di2 = di2.Parent; - } - - if (!good) - { - error = "Cannot read above static directories!"; - unauthorized = false; - return null; - } - - string output; - using (Server.BeginImpersonation()) - output = File.ReadAllText(path); - WriteInfo("Read of " + path, EventID.StaticRead); - error = null; - unauthorized = false; - return output; - } - } - catch (UnauthorizedAccessException e) - { - //no need for the full stacktrace - error = e.Message; - unauthorized = true; - return null; - } - catch (Exception e) - { - error = e.ToString(); - WriteWarning(String.Format("Read of {0} failed! Error: {1}", path, e.ToString()), EventID.StaticRead); - unauthorized = false; - return null; - } - } - - /// - public string WriteText(string staticRelativePath, string data, string originalData, out bool unauthorized) - { - var path = RelativePath(StaticDirs) + '/' + staticRelativePath; //do not use path.combine or it will try and take the root - try - { - lock (configLock) - { - var di1 = new DirectoryInfo(RelativePath(StaticDirs)); - var destdir = new FileInfo(path).Directory.FullName; - var di2 = new DirectoryInfo(destdir); - - var good = false; - while (di2 != null) - { - if (di2.FullName == di1.FullName) - { - good = true; - break; - } - else di2 = di2.Parent; - } - - if (!good) - { - unauthorized = false; - return "Cannot write above static directories!"; - } - - using (Server.BeginImpersonation()) - { - if (originalData != null && File.ReadAllText(path) != originalData) - { - unauthorized = false; - return "File has changed since being initially read. Please refresh!"; - } - Directory.CreateDirectory(destdir); - File.WriteAllText(path, data); - } - WriteInfo("Write to " + path, EventID.StaticWrite); - unauthorized = false; - return null; - } - } - catch (UnauthorizedAccessException e) - { - //no need for the full stacktrace - unauthorized = true; - return e.Message; - } - catch (Exception e) - { - unauthorized = false; - WriteWarning(String.Format("Write of {0} failed! Error: {1}", path, e.ToString()), EventID.StaticRead); - return e.ToString(); - } - } - /// - public string DeleteFile(string staticRelativePath, out bool unauthorized) - { - var path = RelativePath(StaticDirs + '/' + staticRelativePath); //do not use path.combine or it will try and take the root - try - { - lock (configLock) - { - var di1 = new DirectoryInfo(RelativePath(StaticDirs)); - var fi = new FileInfo(path); - var di2 = new DirectoryInfo(fi.Directory.FullName); - - var good = false; - while (di2 != null) - { - if (di2.FullName == di1.FullName) - { - good = true; - break; - } - else di2 = di2.Parent; - } - - if (!good) - { - unauthorized = false; - return "Cannot delete above static directories!"; - } - - using (Server.BeginImpersonation()) - if (fi.Exists) - File.Delete(path); - else if (Directory.Exists(path)) - Helpers.DeleteDirectory(path); - WriteInfo("Delete of " + path, EventID.StaticDelete); - unauthorized = false; - return null; - } - } - catch (UnauthorizedAccessException e) - { - //no need for the full stacktrace - unauthorized = true; - return e.Message; - } - catch (Exception e) - { - unauthorized = false; - WriteWarning(String.Format("Delete of {0} failed! Error: {1}", path, e.ToString()), EventID.StaticRead); - return e.ToString(); - } - } - - /// - public IList ListStaticDirectory(string subDir, out string error, out bool unauthorized) - { - try - { - if (!Directory.Exists(RelativePath(StaticDirs))) - { - error = null; - unauthorized = false; - return new List(); - } - using (Server.BeginImpersonation()) - { - DirectoryInfo dirToEnum = new DirectoryInfo(RelativePath(StaticDirs) + '/' + subDir ?? ""); //do not use path.combine or it will try and take the root - var result = new List(); - foreach (var I in dirToEnum.GetFiles()) - result.Add(I.Name); - foreach (var I in dirToEnum.GetDirectories()) - result.Add('/' + I.Name); - error = null; - unauthorized = false; - return result; - } - } - catch (UnauthorizedAccessException e) - { - //no need for the full stacktrace - error = e.Message; - unauthorized = true; - return null; - } - catch (Exception e) - { - error = e.ToString(); - unauthorized = false; - return null; - } - } - } -} diff --git a/TGS.Server/Instance/DreamDaemon.cs b/TGS.Server/Instance/DreamDaemon.cs deleted file mode 100644 index 78815061d2..0000000000 --- a/TGS.Server/Instance/DreamDaemon.cs +++ /dev/null @@ -1,773 +0,0 @@ -using System; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Threading; -using System.Timers; -using TGS.Interface; -using TGS.Interface.Components; - -namespace TGS.Server -{ - //manages the dd window. - //It's not possible to actually click it while starting it in CL mode, so in order to change visibility, security, etc. It restarts the process when the world reboots - sealed partial class Instance : ITGDreamDaemon - { - enum ShutdownRequestPhase - { - None, - Requested, - Pinged, - } - - /// - /// Directory for storing DreamDaemon diagnostic files - /// - const string DiagnosticsDir = "Diagnostics"; - /// - /// Directory for storing DreamDaemon ResourceUsage files - /// - const string ResourceDiagnosticsDir = DiagnosticsDir + "/Resources"; - /// - /// If DreamDaemon crashes before this time it is considered a bad startup - /// - const int DDBadStartTime = 10; - - /// - /// The DreamDaemon process - /// - Process Proc; - /// - /// CPU performance information for - /// - PerformanceCounter pcpu; - - /// - /// Used for multithreading safety - /// - object watchdogLock = new object(); - /// - /// The thread that monitors the status of DreamDaemon - /// - Thread DDWatchdog; - /// - /// The of - /// - DreamDaemonStatus currentStatus; - /// - /// Current logfile in use in - /// - string CurrentDDLog; - /// - /// Current port DreamDaemon is running on - /// - ushort currentPort = 0; - - /// - /// Used for multithreading safety - /// - object restartLock = new object(); - /// - /// Used to indicate if an intentional restart is in progress on the watchdog. Requires to access - /// - bool RestartInProgress = false; - /// - /// Used to indicate if an service restart is in progress on the watchdog. Requires to access - /// - bool ReattachInsteadOfRestart = false; - - /// - /// Current level of DreamDaemon - /// - DreamDaemonSecurity StartingSecurity; - - /// - /// Indicator of progress on a or operation - /// - ShutdownRequestPhase AwaitingShutdown; - - /// - /// Setup or reattach the watchdog, depending on of , and create the - /// - void InitDreamDaemon() - { - Directory.CreateDirectory(RelativePath(DiagnosticsDir)); - Directory.CreateDirectory(RelativePath(ResourceDiagnosticsDir)); - var Reattach = Config.ReattachRequired; - if (Reattach) - try - { - Proc = Process.GetProcessById(Config.ReattachProcessID); - if (Proc == null) - throw new Exception("GetProcessById returned null!"); - WriteInfo("Reattached to running DD process!", EventID.DDReattachSuccess); - ThreadPool.QueueUserWorkItem(_ => - { - Thread.Sleep(5000); - SendMessage("DD: Update complete. Watch dog reactivated...", MessageType.WatchdogInfo); - }); - - //start wd - lock (restartLock) - { - RestartInProgress = true; - ReattachInsteadOfRestart = true; - } - currentPort = Config.ReattachPort; - serviceCommsKey = Config.ReattachCommsKey; - try - { - GameAPIVersion = new Version(Config.ReattachAPIVersion); - } - catch { } - currentStatus = DreamDaemonStatus.Online; - DDWatchdog = new Thread(new ThreadStart(Watchdog)); - DDWatchdog.Start(); - } - catch (Exception e) - { - WriteError(String.Format("Failed to reattach to DreamDaemon! PID: {0}. Exception: {1}", Config.ReattachProcessID, e.ToString()), EventID.DDReattachFail); - } - finally - { - Config.ReattachRequired = false; - Config.Save(); - } - - if (Proc == null) - Proc = new Process(); - - Proc.StartInfo.FileName = RelativePath(ByondDirectory + "/bin/dreamdaemon.exe"); - Proc.StartInfo.UseShellExecute = false; - - if (Reattach) - return; - - //autostart the server - if (Config.Autostart) - //break this off so we don't hold up starting the service - ThreadPool.QueueUserWorkItem(_ => { Start(); }); - } - - /// - /// Either let go of for reattachment or terminate it, depending on of - /// - void DisposeDreamDaemon() - { - var Detach = Config.ReattachRequired; - bool RenameLog = false; - if (DaemonStatus() == DreamDaemonStatus.Online) - { - if (!Detach) - { - WorldAnnounce("Server service stopped"); - Thread.Sleep(1000); - } - else - { - RenameLog = CurrentDDLog != null; - SendMessage("DD: Detaching watch dog for update!", MessageType.WatchdogInfo); - WriteCurrentDDLog("Service updating! Splitting diagnostics..."); - } - } - else if (Detach) - Config.ReattachRequired = false; - Stop(); - if (pcpu != null) - pcpu.Dispose(); - if (RenameLog) - try - { - var rrdd = RelativePath(ResourceDiagnosticsDir); - File.Move(Path.Combine(rrdd, CurrentDDLog), Path.Combine(rrdd, "SU-" + CurrentDDLog)); - } - catch { } - } - - /// - public DreamDaemonStatus DaemonStatus() - { - lock (watchdogLock) - { - return currentStatus; - } - } - - /// - public void RequestRestart() - { - SendCommand(SCHardReboot); - } - - /// - public void RequestStop() - { - lock (watchdogLock) - { - if (currentStatus != DreamDaemonStatus.Online || AwaitingShutdown != ShutdownRequestPhase.None) - return; - AwaitingShutdown = ShutdownRequestPhase.Pinged; - } - SendCommand(SCGracefulShutdown); - } - - /// - public string Stop() - { - Thread t; - lock (watchdogLock) - { - t = DDWatchdog; - DDWatchdog = null; - } - if (t != null && t.IsAlive) - { - t.Abort(); - t.Join(); - return null; - } - else - return "Server not running"; - } - - /// - public void SetPort(ushort new_port) - { - lock (watchdogLock) - { - Config.Port = new_port; - Config.Save(); - RequestRestart(); - } - } - - //handle a kill request from the server - public void KillMe(bool silent) - { - bool DoRestart; - lock (watchdogLock) - { - DoRestart = AwaitingShutdown == ShutdownRequestPhase.None; - if (!DoRestart) - AwaitingShutdown = ShutdownRequestPhase.Pinged; - } - //Do this is a seperate thread or we'll kill this thread in the middle of rebooting - if (DoRestart) - ThreadPool.QueueUserWorkItem(_ => { RestartImpl(silent); }); - else - ThreadPool.QueueUserWorkItem(_ => { Stop(); }); - } - - /// - public string Restart() - { - if (DaemonStatus() == DreamDaemonStatus.Offline) - return Start(); - return RestartImpl(false); - } - - /// - /// Restarts DD - /// - /// If no chat message will be sent - /// on success, error message on failure - string RestartImpl(bool silent) - { - lock (restartLock) - { - if (RestartInProgress) - return "Restart already in progress"; - RestartInProgress = true; - } - if (!silent) - SendMessage("DD: Hard restart triggered", MessageType.WatchdogInfo); - Stop(); - var res = Start(); - if (res != null) - lock (restartLock) - { - RestartInProgress = false; - } - return res; - } - - /// - /// Write a to the . A timestamp will be prepended to it - /// - /// The message to log - void WriteCurrentDDLog(string message) - { - lock (watchdogLock) - { - if (currentStatus != DreamDaemonStatus.Online || CurrentDDLog == null) - return; - File.AppendAllText(Path.Combine(RelativePath(ResourceDiagnosticsDir), CurrentDDLog), String.Format("[{0}]: {1}\n", DateTime.Now.ToLongTimeString(), message)); - } - } - - /// - /// Threaded loop that keeps DreamDaemon from unintentionally stopping - /// - void Watchdog() - { - Thread.CurrentThread.Name = "Watchdog Thread"; - try - { - lock (restartLock) - { - if (!RestartInProgress) - { - SendMessage("DD: Server started, watchdog active...", MessageType.WatchdogInfo); - WriteInfo("Watchdog started", EventID.DDWatchdogStarted); - } - else - { - RestartInProgress = false; - if (!ReattachInsteadOfRestart) - WriteInfo("Watchdog restarted", EventID.DDWatchdogRestarted); - else - ReattachInsteadOfRestart = false; - } - } - var retries = 0; - - var MemTrackTimer = new System.Timers.Timer - { - AutoReset = true, - Interval = 5000 //every 5 seconds - }; - MemTrackTimer.Elapsed += MemTrackTimer_Elapsed; - while (true) - { - var starttime = DateTime.Now; - - lock (watchdogLock) - { - if (AwaitingShutdown == ShutdownRequestPhase.Requested) - SendCommand(SCGracefulShutdown); - } - - //all good to go, let's start monitoring - var Now = DateTime.Now; - lock (watchdogLock) - { - CurrentDDLog = DateTime.UtcNow.ToString("yyyy-MM-ddTHH-mm-ssZ"); - WriteCurrentDDLog("Starting monitoring..."); - } - try - { - pcpu = new PerformanceCounter("Process", "% Processor Time", Proc.ProcessName, true); - MemTrackTimer.Start(); - } - catch (InvalidOperationException) { /*process already exited*/ } - try - { - Proc.WaitForExit(); - } - finally - { - lock (watchdogLock) //synchronize - { - MemTrackTimer.Stop(); - pcpu?.Dispose(); - } - } - - WriteCurrentDDLog("Crash detected! Exit code: " + Proc.ExitCode); - var runtimeS = (DateTime.Now - starttime).TotalSeconds; - WriteWarning("DD crashed: Exit Code: " + Proc.ExitCode + " Seconds running: " + runtimeS, EventID.DDServerCrash); - - - lock (watchdogLock) - { - currentStatus = DreamDaemonStatus.HardRebooting; - currentPort = 0; - Proc.Close(); - - if (AwaitingShutdown == ShutdownRequestPhase.Pinged) - return; - var BadStart = runtimeS < DDBadStartTime; - if (BadStart) - { - ++retries; - var sleep_time = (int)Math.Min(Math.Pow(2, retries), 3600); //max of one hour - SendMessage(String.Format("DD: Watchdog server startup failed! Retrying in {0} seconds...", sleep_time), MessageType.WatchdogInfo); - Thread.Sleep(sleep_time * 1000); - } - else - { - retries = 0; - var msg = "DD: DreamDaemon crashed! Watchdog rebooting DD..."; - SendMessage(msg, MessageType.WatchdogInfo); - WriteWarning(msg, EventID.DDWatchdogRebootingServer); - } - } - - var res = StartImpl(true); - if (res != null) - throw new Exception("Hard restart failed: " + res); - } - } - catch (ThreadAbortException) - { - //No Mr bond, I expect you to die - try - { - if (!Config.ReattachRequired) - { - Proc.Kill(); - Proc.WaitForExit(); - } - else - { - Config.ReattachProcessID = Proc.Id; - Config.ReattachPort = currentPort; - Config.ReattachAPIVersion = GameAPIVersion != null ? GameAPIVersion.ToString() : null; - Config.ReattachCommsKey = serviceCommsKey; - lock (restartLock) - { - RestartInProgress = true; - } - } - Proc.Close(); - } - catch - { } - } - catch (Exception e) - { - SendMessage("DD: Watchdog thread crashed!", MessageType.WatchdogInfo); - WriteError("Watch dog thread crashed: " + e.ToString(), EventID.DDWatchdogCrash); - } - finally - { - lock (watchdogLock) - { - currentStatus = DreamDaemonStatus.Offline; - currentPort = 0; - AwaitingShutdown = ShutdownRequestPhase.None; - lock (restartLock) - { - if (!RestartInProgress) - { - if (!Config.ReattachRequired) - SendMessage("DD: Server stopped, watchdog exiting...", MessageType.WatchdogInfo); - WriteInfo("Watch dog exited", EventID.DDWatchdogExit); - } - else - WriteInfo("Watch dog restarting...", EventID.DDWatchdogRestart); - } - } - } - } - - /// - /// Called every five seconds while DreamDaemon is running to log it's current state to the - /// - /// The event sender, an instance of - /// The - private void MemTrackTimer_Elapsed(object sender, ElapsedEventArgs e) - { - ulong megamem; - float cputime; - lock (watchdogLock) - { - cputime = pcpu.NextValue(); - using (var pcm = new PerformanceCounter("Process", "Working Set - Private", Proc.ProcessName, true)) - megamem = Convert.ToUInt64(pcm.NextValue()) / 1024; - } - var PercentCpuTime = (int)Math.Round((Decimal)cputime); - WriteCurrentDDLog(String.Format("CPU: {1}% Memory: {0}KB", megamem, PercentCpuTime.ToString("D3"))); - } - - /// - public string CanStart() - { - if (GetVersion(ByondVersion.Installed) == null) - return "Byond is not installed!"; - var DMB = RelativePath(GameDirLive + "/" + Config.ProjectName + ".dmb"); - if (!File.Exists(DMB)) - return String.Format("Unable to find {0}!", DMB); - return null; - } - - /// - public string Start() - { - if (CurrentStatus() == ByondStatus.Staged) - { - //IMPORTANT: SLEEP FOR A MOMENT OR WONDOWS WON'T RELEASE THE FUCKING BYOND DLL HANDLES!!!! REEEEEEE - Thread.Sleep(3000); - ApplyStagedUpdate(); - } - lock (watchdogLock) - { - if (currentStatus != DreamDaemonStatus.Offline) - return "Server already running"; - var res = CanStart(); - if (res != null) - return res; - currentPort = 0; - currentStatus = DreamDaemonStatus.HardRebooting; - } - return StartImpl(false); - } - - /// - /// Translate the configured level into a byond command line param - /// - /// If bases it's result on , uses of otherwise - /// "safe", "trusted", or "ultrasafe" depending on the it checks - string SecurityWord(bool starting = false) - { - var level = starting ? StartingSecurity : Config.Security; - switch (level) - { - case DreamDaemonSecurity.Safe: - return "safe"; - case DreamDaemonSecurity.Trusted: - return "trusted"; - case DreamDaemonSecurity.Ultrasafe: - return "ultrasafe"; - default: - throw new Exception(String.Format("Bad DreamDaemon security level: {0}", level)); - } - } - - /// - /// Copies from the program directory to the the directory - /// - /// If , overwrites the 's current interface .dll if it exists - void UpdateBridgeDll(bool overwrite) - { - var rbdlln = RelativePath(BridgeDLLName); - var FileExists = File.Exists(rbdlln); - if (FileExists && !overwrite) - return; - //Copy the interface dll to the static dir - - var InterfacePath = Assembly.GetAssembly(typeof(IClient)).Location; - //bridge is installed next to the interface - var BridgePath = Path.Combine(Path.GetDirectoryName(InterfacePath), BridgeDLLName); -#if DEBUG - //We could be debugging from the project directory - if (!File.Exists(BridgePath)) - //A little hackish debug mode doctoring never hurt anyone - BridgePath = Path.Combine(Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(Path.GetDirectoryName(InterfacePath)))), "TGS.Interface.Bridge/bin/x86/Debug", BridgeDLLName); -#endif - try - { - //Use reflection to ensure these are the droids we're looking for - Assembly.ReflectionOnlyLoadFrom(BridgePath).GetType(String.Format("{0}.{1}.{2}.{3}", nameof(TGS), nameof(Interface), DreamDaemonBridgeNamespace, DreamDaemonBridgeType), true); - } - catch (Exception e) - { - WriteError(String.Format("Unable to locate {0}! Error: {1}", BridgeDLLName, e.ToString()), EventID.BridgeDLLUpdateFail); - return; - } - - try - { - if (FileExists) - { - var Old = File.ReadAllBytes(rbdlln); - var New = File.ReadAllBytes(BridgePath); - if (Old.SequenceEqual(New)) - return; //no need - } - File.Copy(BridgePath, rbdlln, overwrite); - } - catch - { - try - { - //ok the things being stupid and hasn't released the dll yet, try ONCE more - Thread.Sleep(1000); - File.Copy(BridgePath, rbdlln, overwrite); - } - catch (Exception e) - { - //intentionally using the fi - WriteError("Failed to update bridge DLL! Error: " + e.ToString(), EventID.BridgeDLLUpdateFail); - return; - } - } - WriteInfo("Updated interface DLL", EventID.BridgeDLLUpdated); - } - - /// - /// Clears the current , calls with a parameter, and attempts to start the DreamDaemon - /// - /// If , sets to a new pointing to and starts it - /// on success, error message on failure - string StartImpl(bool watchdog) - { - try - { - lock (watchdogLock) - { - var res = CanStart(); - if (res != null) - return res; - - var DMB = RelativePath(GameDirLive + "/" + Config.ProjectName + ".dmb"); - - GenCommsKey(); - StartingSecurity = Config.Security; - Proc.StartInfo.Arguments = String.Format("{0} -port {1} {5}-close -verbose -params \"server_service={3}&server_service_version={4}&{6}={7}\" -{2} -public", DMB, Config.Port, SecurityWord(), serviceCommsKey, Version(), Config.Webclient ? "-webclient " : "", SPInstanceName, Config.Name); - UpdateBridgeDll(true); - ClearCacheFolder(); - lock (topicLock) - { - GameAPIVersion = null; //needs updating - } - WriteInfo("Starting DD: " + Proc.StartInfo.FileName + " " + Proc.StartInfo.Arguments, EventID.DDServerStart); - Proc.Start(); - Proc.PriorityClass = ProcessPriorityClass.AboveNormal; - - var timeout = Config.ServerStartupTimeout; - if (!Proc.WaitForInputIdle(timeout * 1000)) - { - Proc.Kill(); - Proc.WaitForExit(); - Proc.Close(); - currentStatus = DreamDaemonStatus.Offline; - currentPort = 0; - return String.Format("Server start is taking more than {0}s! Aborting!", timeout); - } - currentPort = Config.Port; - currentStatus = DreamDaemonStatus.Online; - if (!watchdog) - { - DDWatchdog = new Thread(new ThreadStart(Watchdog)); - DDWatchdog.Start(); - } - return null; - } - } - catch (Exception e) - { - currentStatus = DreamDaemonStatus.Offline; - return e.ToString(); - } - } - - /// - public DreamDaemonSecurity SecurityLevel() - { - lock (watchdogLock) - { - return Config.Security; - } - } - - /// - public bool SetSecurityLevel(DreamDaemonSecurity level) - { - bool needReboot; - lock (watchdogLock) - { - needReboot = Config.Security != level; - Config.Security = level; - Config.Save(); - } - if (needReboot) - RequestRestart(); - return DaemonStatus() != DreamDaemonStatus.Online; - } - - /// - public bool Autostart() - { - return Config.Autostart; - } - - /// - public void SetAutostart(bool on) - { - Config.Autostart = on; - Config.Save(); - } - - /// - public string StatusString(bool includeMetaInfo) - { - const string visSecStr = " (Sec: {0})"; - string res; - var ds = DaemonStatus(); - switch (ds) - { - case DreamDaemonStatus.Offline: - res = "OFFLINE"; - break; - case DreamDaemonStatus.HardRebooting: - res = "REBOOTING"; - break; - case DreamDaemonStatus.Online: - res = "ONLINE"; - if (includeMetaInfo) - { - string secandvis; - lock (watchdogLock) - { - secandvis = String.Format(visSecStr, SecurityWord(true)); - } - res += secandvis; - } - break; - default: - res = "NULL AND ERRORS"; - break; - } - if (includeMetaInfo && ds != DreamDaemonStatus.Online) - res += String.Format(visSecStr, SecurityWord()); - return res; - } - - /// - public ushort Port() - { - return Config.Port; - } - - /// - public bool ShutdownInProgress() - { - lock (watchdogLock) - { - return AwaitingShutdown != ShutdownRequestPhase.None; - } - } - - /// - public string WorldAnnounce(string message) - { - var res = SendCommand(SCWorldAnnounce + ";message=" + Helpers.SanitizeTopicString(message)); - if (res == "SUCCESS") - return null; - return res; - } - - /// - public bool Webclient() - { - return Config.Webclient; - } - - /// - public void SetWebclient(bool on) - { - lock (watchdogLock) - { - var diff = on != Config.Webclient; - if (diff) - { - Config.Webclient = on; - Config.Save(); - RequestRestart(); - } - } - } - } -} diff --git a/TGS.Server/Instance/Instance.cs b/TGS.Server/Instance/Instance.cs deleted file mode 100644 index e28d58dc72..0000000000 --- a/TGS.Server/Instance/Instance.cs +++ /dev/null @@ -1,186 +0,0 @@ -using System; -using System.Diagnostics; -using System.IO; -using System.ServiceModel; -using TGS.Interface.Components; - -namespace TGS.Server -{ - //I know the fact that this is one massive partial class is gonna trigger everyone - //There really was no other succinct way to do it (<= He's lying through his teeth, don't listen to him) - - //this line basically says take one instance of the service, use it multithreaded for requests, and never delete it - - /// - /// The class which holds all interface components. There are no safeguards for call race conditions so these must be guarded against internally - /// - [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.Single)] - sealed partial class Instance : IDisposable, ITGConnectivity, ITGInstance - { - /// - /// Used to assign the instance to event IDs - /// - public readonly byte LoggingID; - /// - /// The configuration settings for the instance - /// - readonly IInstanceConfig Config; - /// - /// Constructs and a - /// - public Instance(IInstanceConfig config, byte logID) - { - LoggingID = logID; - Config = config; - InitAdministration(); - InitEventHandlers(); - InitChat(); - InitRepo(); - InitByond(); - InitDreamDaemon(); - InitCompiler(); - } - - /// - /// Cleans up the - /// - void RunDisposals() - { - DisposeDreamDaemon(); - DisposeCompiler(); - DisposeByond(); - DisposeRepo(); - DisposeChat(); - DisposeAdministration(); - Config.Save(); - } - - /// - /// Writes information to the Windows event log - /// - /// The log message - /// The of the message - void WriteInfo(string message, EventID id) - { - Server.Logger.WriteInfo(message, id, LoggingID); - } - - /// - /// Writes an error to the Windows event log - /// - /// The log message - /// The of the message - void WriteError(string message, EventID id) - { - Server.Logger.WriteError(message, id, LoggingID); - } - - /// - /// Writes a warning to the Windows event log - /// - /// The log message - /// The of the message - void WriteWarning(string message, EventID id) - { - Server.Logger.WriteWarning(message, id, LoggingID); - } - - /// - /// Writes an access event to the Windows event log - /// - /// The (un)authenticated Windows user's name - /// if authenticated sucessfully, otherwise - void WriteAccess(string username, bool authSuccess) - { - Server.Logger.WriteAccess(String.Format("Access from: {0}", username), authSuccess, LoggingID); - } - - /// - /// Converts relative paths to full directory paths - /// - /// - string RelativePath(string path) - { - return Path.Combine(Config.Directory, path); - } - - /// - public string Version() - { - return Server.VersionString; - } - - /// - public void VerifyConnection() { } - - /// - public void Reattach(bool silent) - { - Config.ReattachRequired = true; - if(!silent) - SendMessage("SERVICE: Update started...", MessageType.DeveloperInfo); - } - - /// - public string ServerDirectory() - { - return Config.Directory; - } - - /// - /// Sets of to - /// - public void Offline() - { - Config.Enabled = false; - } - - //mostly generated code with a call to RunDisposals() - //you don't need to open this - #region IDisposable Support - /// - /// To detect redundant calls - /// - private bool disposedValue = false; - - /// - /// Implements the pattern. Calls - /// - /// if was called manually, if it was from the finalizer - void Dispose(bool disposing) - { - if (!disposedValue) - { - if (disposing) - { - RunDisposals(); - // TODO: dispose managed state (managed objects). - } - - // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below. - // TODO: set large fields to null. - - disposedValue = true; - } - } - - // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources. - // ~TGStationServer() { - // // Do not change this code. Put cleanup code in Dispose(bool disposing) above. - // Dispose(false); - // } - - // This code added to correctly implement the disposable pattern. - /// - /// Implements the pattern - /// - public void Dispose() - { - // Do not change this code. Put cleanup code in Dispose(bool disposing) above. - Dispose(true); - // TODO: uncomment the following line if the finalizer is overridden above. - // GC.SuppressFinalize(this); - } - #endregion - } -} diff --git a/TGS.Server/Instance/Interop.cs b/TGS.Server/Instance/Interop.cs deleted file mode 100644 index 4e4aa22cd7..0000000000 --- a/TGS.Server/Instance/Interop.cs +++ /dev/null @@ -1,281 +0,0 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System; -using System.Collections.Generic; -using System.Net; -using System.Net.Sockets; -using System.Text; -using System.Threading; -using System.Web.Security; -using TGS.Server.ChatCommands; -using TGS.Interface; -using TGS.Interface.Components; - -namespace TGS.Server -{ - //handles talking between the world and us - sealed partial class Instance : ITGInterop - { - - object topicLock = new object(); - const int CommsKeyLen = 64; - string serviceCommsKey; //regenerated every DD restart - - //range of supported api versions - const int AllowedMajorAPIVersion = 2; - Version GameAPIVersion; - - const string SPInstanceName = "server_instance"; - - //See code/modules/server_tools/server_tools.dm for command switch - const string SCHardReboot = "hard_reboot"; //requests that dreamdaemon restarts when the round ends - const string SCGracefulShutdown = "graceful_shutdown"; //requests that dreamdaemon stops when the round ends - const string SCWorldAnnounce = "world_announce"; //sends param 'message' to the world - const string SCListCustomCommands = "list_custom_commands"; //Get a list of commands supported by the server - const string SCAPICompat = "api_compat"; //Tells the server we understand each other - const string SCPlayerCount = "client_count"; //Gets the number of connected client - - /// - /// String returned when a command completes successfully with no output - /// - const string SRetSuccess = "SUCCESS"; - - const string SRKillProcess = "killme"; - const string SRKillProcessSilent = "killmesilent"; - const string SRIRCBroadcast = "irc"; - const string SRIRCAdminChannelMessage = "send2irc"; - const string SRWorldReboot = "worldreboot"; - const string SRAPIVersion = "api_ver"; - - const string CCPHelpText = "help_text"; - const string CCPAdminOnly = "admin_only"; - const string CCPRequiredParameters = "required_parameters"; - - /// - /// The file name of the .dll that contains the bridge class - /// - const string BridgeDLLName = "TGDreamDaemonBridge.dll"; - /// - /// The namespace that contains the bridge class. Used for reflection - /// - const string DreamDaemonBridgeNamespace = "Bridge"; - /// - /// The bridge class. Used for reflection - /// - const string DreamDaemonBridgeType = "DreamDaemonBridge"; - - List ServerChatCommands; - - void LoadServerChatCommands() - { - if (DaemonStatus() != DreamDaemonStatus.Online) - return; - var json = SendCommand(SCListCustomCommands); - if (String.IsNullOrWhiteSpace(json)) - return; - List tmp = new List(); - try - { - foreach (var I in JsonConvert.DeserializeObject>(json)) - { - var innerDick = ((JObject)I.Value).ToObject>(); - var helpText = (string)innerDick[CCPHelpText]; - var adminOnly = ((long)innerDick[CCPAdminOnly]) == 1; - var requiredParams = (int)((long)innerDick[CCPRequiredParameters]); - tmp.Add(new ServerChatCommand(I.Key, helpText, adminOnly, requiredParams)); - } - ServerChatCommands = tmp; - } - catch { } - } - - //raw command string sent here via world.ExportService - void HandleCommand(string cmd) - { - var splits = new List(cmd.Split(' ')); - cmd = splits[0]; - splits.RemoveAt(0); - - bool APIValid; - lock (topicLock) - { - APIValid = CheckAPIVersionConstraints(); - } - - if (!APIValid && cmd != SRAPIVersion) - return; //SPEAK THE LANGUAGE!!! - - var showKillMessage = true; - switch (cmd) - { - case SRIRCBroadcast: - SendMessage("GAME: " + String.Join(" ", splits), MessageType.GameInfo); - break; - case SRKillProcessSilent: - showKillMessage = false; - goto case SRKillProcess; - case SRKillProcess: - KillMe(!showKillMessage); - break; - case SRIRCAdminChannelMessage: - SendMessage("RELAY: " + String.Join(" ", splits), MessageType.AdminInfo); - break; - case SRWorldReboot: - WriteInfo("World Rebooted", EventID.WorldReboot); - WriteCurrentDDLog("World rebooted"); - ServerChatCommands = null; - ChatConnectivityCheck(); - lock (CompilerLock) - { - if (UpdateStaged) - { - UpdateStaged = false; - lock (topicLock) - { - GameAPIVersion = null; //needs updating - } - WriteInfo("Staged update applied", EventID.ServerUpdateApplied); - } - } - break; - case SRAPIVersion: - lock (topicLock) - { - try - { - GameAPIVersion = new Version(splits[0]); - if (!CheckAPIVersionConstraints()) - throw new Exception(); - } - catch - { - WriteWarning(String.Format("API version of the game ({0}) is incompatible with the current supported API versions (3.{2}.x.x). Interop disabled.", splits.Count > 1 ? splits[1] : "NULL", AllowedMajorAPIVersion), EventID.APIVersionMismatch); - GameAPIVersion = null; - break; - } - } - //This needs to be done asyncronously otherwise DD won't be able to process it, because it's waiting for THIS THREAD to return - ThreadPool.QueueUserWorkItem(_ => SendCommand(SCAPICompat)); - break; - } - } - - public string SendCommand(string cmd) - { - lock (watchdogLock) - { - if (currentStatus != DreamDaemonStatus.Online) - return "Error: Server Offline!"; - return SendTopic(String.Format("serviceCommsKey={0};command={1}", serviceCommsKey, cmd), currentPort); - } - } - - public int PlayerCount() - { - try - { - return Convert.ToInt32(SendCommand(SCPlayerCount)); - } - catch - { - return -1; - } - } - - //requires topiclock - bool CheckAPIVersionConstraints() - { - //major will never change for all of TGS3 - //we treat minor as major, build as minor, and revision as patch - return !(GameAPIVersion == null || GameAPIVersion.Minor != AllowedMajorAPIVersion); - } - - //Fuckery to diddle byond with the right packet to accept our girth - string SendTopic(string topicdata, ushort port) - { - //santize the escape characters in accordance with http://www.byond.com/docs/ref/info.html#/proc/params2list - lock (topicLock) { - if (!CheckAPIVersionConstraints()) - return "Incompatible API!"; - - StringBuilder stringPacket = new StringBuilder(); - stringPacket.Append((char)'\x00', 8); - stringPacket.Append('?' + topicdata); - stringPacket.Append((char)'\x00'); - string fullString = stringPacket.ToString(); - var packet = Encoding.ASCII.GetBytes(fullString); - packet[1] = 0x83; - var FinalLength = packet.Length - 4; - if (FinalLength > UInt16.MaxValue) - return "Error: Topic too long"; - - var lengthBytes = BitConverter.GetBytes((ushort)FinalLength); - - packet[2] = lengthBytes[1]; //fucking endianess - packet[3] = lengthBytes[0]; - - var returnedString = "NULL"; - var returnedData = new byte[UInt16.MaxValue]; - using (var topicSender = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp) { SendTimeout = 5000, ReceiveTimeout = 10000 }) - try - { - topicSender.Connect(IPAddress.Loopback, port); - topicSender.Send(packet); - - try - { - topicSender.Receive(returnedData); - var raw_string = Encoding.ASCII.GetString(returnedData).TrimEnd(new char[] { (char)0 }).Trim(); - if (raw_string.Length > 6) - returnedString = raw_string.Substring(5, raw_string.Length - 5).Trim(); - } - catch - { - returnedString = "Topic recieve error!"; - } - finally - { - topicSender.Shutdown(SocketShutdown.Both); - } - } - catch - { - return "Topic delivery failed!"; - } - - return returnedString; - } - } - - //Every time we make a new DD process we generate a new comms key for security - //It's in world.params['server_service'] - void GenCommsKey() - { - var charsToRemove = new string[] { "!", "@", "#", "$", "%", "^", "&", "*", "(", ")", "_", "-", "+", "=", "[", "{", "]", "}", ";", ":", "<", ">", "|", ".", "/", "?" }; - serviceCommsKey = String.Empty; - do { - var tmp = Membership.GeneratePassword(CommsKeyLen, 0); - foreach (var c in charsToRemove) - tmp = tmp.Replace(c, String.Empty); - serviceCommsKey += tmp; - } while (serviceCommsKey.Length < CommsKeyLen); - serviceCommsKey = serviceCommsKey.Substring(0, CommsKeyLen); - WriteInfo("Service Comms Key set to: " + serviceCommsKey, EventID.CommsKeySet); - } - - /// - public bool InteropMessage(string command) - { - try - { - HandleCommand(command); - return true; - } - catch(Exception e) - { - WriteWarning(String.Format("Handle command for \"{0}\" failed: {1}", command, e.ToString()), EventID.InteropCallException); - return false; - } - } - } -} diff --git a/TGS.Server/Instance/PreactionHandler.cs b/TGS.Server/Instance/PreactionHandler.cs deleted file mode 100644 index 8fc1d14792..0000000000 --- a/TGS.Server/Instance/PreactionHandler.cs +++ /dev/null @@ -1,102 +0,0 @@ -using System; -using System.Diagnostics; -using System.IO; - -namespace TGS.Server -{ - // Some useful functions for triggering pre action events - sealed partial class Instance - { - /// - /// The instance directory for Preaction handlers - /// - const string EventFolder = "EventHandlers/"; - - /// - /// Creates the - /// - void InitEventHandlers() - { - Directory.CreateDirectory(RelativePath(EventFolder)); - } - - /// - /// Gets the path of an event given an - /// - /// The name of the event - /// The path to the event handler - string GetEventPath(string eventName) - { - return string.Format("{0}{1}.bat", RelativePath(EventFolder), eventName); - } - - /// - /// Check if an event handler for exists - /// - /// The name of the event - /// if the event handler exists, otherwise - bool EventHandlerExists(string eventName) - { - return File.Exists(GetEventPath(eventName)); - } - - /// - /// Runs an event named if it exists - /// - /// The name of the event - /// if the event handler exists and failed to run, otherwise - bool HandleEvent(string eventName) - { - - if (!EventHandlerExists(eventName)) - { - // We don't need a handler, so let's just fail silently. - return true; - } - - var process = new Process - { - StartInfo = new ProcessStartInfo - { - FileName = GetEventPath(eventName), - UseShellExecute = false, - RedirectStandardOutput = true, - RedirectStandardError = true, - CreateNoWindow = true - } - }; - process.Start(); - process.WaitForExit(); - - var stdout = process.StandardOutput.ReadToEnd(); - var stderr = process.StandardError.ReadToEnd(); - var success = process.ExitCode == 0; - var eventData = String.Format("Preaction Event: {0} @ {1} ran. Stdout:\n{2}\nStderr:\n{3}", eventName, GetEventPath(eventName), stdout, stderr); - - if (success) - WriteInfo(eventData, EventID.PreactionEvent); - else - WriteWarning(eventData, EventID.PreactionFail); - - return success; - } - - /// - /// Run the "precompile" event - /// - /// if the event handler exists and failed to run, otherwise - public bool PrecompileHook() - { - return HandleEvent("precompile"); - } - - /// - /// Run the "postcompile" event - /// - /// if the event handler exists and failed to run, otherwise - public bool PostcompileHook() - { - return HandleEvent("postcompile"); - } - } -} diff --git a/TGS.Server/Instance/Repository.cs b/TGS.Server/Instance/Repository.cs deleted file mode 100644 index 00fc1e03e6..0000000000 --- a/TGS.Server/Instance/Repository.cs +++ /dev/null @@ -1,1467 +0,0 @@ -using LibGit2Sharp; -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System; -using System.Collections.Generic; -using System.Diagnostics; -using System.IO; -using System.Linq; -using System.Net; -using System.Threading; -using TGS.Interface; -using TGS.Interface.Components; - -namespace TGS.Server -{ - sealed partial class Instance : ITGRepository, IDisposable - { - /// - /// The directory for the repository - /// - const string RepoPath = "Repository"; - /// - /// The branch name used for publishing testmerge commits - /// - const string RemoteTempBranchName = "___TGS3TempBranch"; - /// - /// The path to the Repository's json - /// - const string RepoTGS3SettingsPath = RepoPath + "/TGS3.json"; - /// - /// Path to the 's json - /// - const string CachedTGS3SettingsPath = "TGS3.json"; - /// - /// Error message for when a merge operation fails due to the target branch already having the source branch's commits - /// - const string RepoErrorUpToDate = "Already up to date!"; - /// - /// Git remote for push operations - /// - const string SSHPushRemote = "ssh_push_target"; - /// - /// The directory for the repository SSH keys - /// - const string RepoKeyDir = "RepoKey/"; - /// - /// The path to the private ssh-rsa key file - /// - const string PrivateKeyPath = RepoKeyDir + "private_key.txt"; - /// - /// The path to the public ssh-rsa key file - /// - const string PublicKeyPath = RepoKeyDir + "public_key.txt"; - /// - /// File name for monitoring which github pull requests are currently test merged - /// - const string PRJobFile = "prtestjob.json"; - /// - /// The branch that points to the current commit that is live or staged to be live in DreamDaemon - /// - const string LiveTrackingBranch = "___TGSLiveCommitTrackingBranch"; - /// - /// Commit message for - /// - const string CommitMessage = "Automatic changelog compile, [ci skip]"; - - /// - /// Used in conjunction with for multithreading safety - /// - object RepoLock = new object(); - /// - /// Used for multithreading safety. You may not use the repo if this is not locked or . Either old the lock while you do work or set to then release it, do your work, reaquire it, and set back to - /// - bool RepoBusy = false; - /// - /// Whether or not a git operation is in progress. See - /// - bool Cloning = false; - - /// - /// The repository object - /// - Repository Repo; - /// - /// Used for reporting operation progress to the - /// - int currentProgress = -1; - - /// - /// Used for automatically updating the - /// - System.Timers.Timer autoUpdateTimer = new System.Timers.Timer() - { - AutoReset = true - }; - - /// - /// Initializes the repository - /// - void InitRepo() - { - Directory.CreateDirectory(RelativePath(RepoKeyDir)); - if (Exists()) - UpdateBridgeDll(false); - if (LoadRepo() == null) - DisableGarbageCollectionNoLock(); - //start the autoupdate timer - autoUpdateTimer.Elapsed += AutoUpdateTimer_Elapsed; - SetAutoUpdateInterval(Config.AutoUpdateInterval); - } - - /// - /// Checks if the and json files match. - /// - /// if the jsons match, otherwise - bool RepoConfigsMatch() - { - //this should never be called while the repo is busy - RepoConfig I = null; - lock (RepoLock) - if (!RepoBusy && LoadRepo() == null) - I = new RepoConfig(RelativePath(RepoTGS3SettingsPath)); - if (I == null) - throw new Exception("Unable to load TGS3.json from repo!"); - var J = GetCachedRepoConfig(); - return I == J; - } - - /// - /// Gets the for - /// - /// The for - RepoConfig GetCachedRepoConfig() - { - return new RepoConfig(RelativePath(CachedTGS3SettingsPath)); - } - - /// - public bool OperationInProgress() - { - lock (RepoLock) - return RepoBusy; - } - - /// - public int CheckoutProgress() - { - return currentProgress; - } - - /// - /// Initializes - /// - /// on success, error message on failure - string LoadRepo() - { - if (Repo != null) - return null; - if (!Repository.IsValid(RelativePath(RepoPath))) - return "Repository does not exist"; - try - { - Repo = new Repository(RelativePath(RepoPath)); - } - catch (Exception e) - { - return e.ToString(); - } - return null; - } - - /// - /// Cleans up - /// - void DisposeRepo() - { - if (Repo != null) - { - Repo.Dispose(); - Repo = null; - } - } - - /// - public bool Exists() - { - lock (RepoLock) - return !Cloning && Repository.IsValid(RelativePath(RepoPath)); - } - - /// - /// Updates with the progess of the current transfer operation - /// - /// The of the current transfer operation - /// - bool HandleTransferProgress(TransferProgress progress) - { - currentProgress = ((int)(((float)progress.ReceivedObjects / progress.TotalObjects) * 100) / 2) + ((int)(((float)progress.IndexedObjects / progress.TotalObjects) * 100) / 2); - return true; - } - - /// - /// Updates with the progess of the current checkout operation - /// - /// Ignored - /// Dividend for progress calculation - /// Divisor for progress calculation - void HandleCheckoutProgress(string path, int completedSteps, int totalSteps) - { - currentProgress = (int)(((float)completedSteps / totalSteps) * 100); - } - - /// - /// Backups up the and deletes the current if they exist. Clones the given branch of the given remote. Sets up new - /// - /// Remote and branch name, seperated by a ' ' - void Clone(object twostrings) - { - //busy flag set by caller - var ts = ((string)twostrings).Split(' '); - var RepoURL = ts[0]; - var BranchName = ts[1]; - try - { - SendMessage(String.Format("REPO: {2} started: Cloning {0} branch of {1} ...", BranchName, RepoURL, Repository.IsValid(RepoPath) ? "Full reset" : "Setup"), MessageType.DeveloperInfo); - try - { - DisposeRepo(); - Helpers.DeleteDirectory(RelativePath(RepoPath)); - DeletePRList(); - /* - lock (configLock) - { - BackupAndDeleteStaticDirectory(); - }*/ - - var Opts = new CloneOptions() - { - BranchName = BranchName, - RecurseSubmodules = true, - OnTransferProgress = HandleTransferProgress, - OnCheckoutProgress = HandleCheckoutProgress, - CredentialsProvider = GenerateGitCredentials, - }; - - Repository.Clone(RepoURL, RelativePath(RepoPath), Opts); - currentProgress = -1; - LoadRepo(); - - DisableGarbageCollectionNoLock(); - - //create an ssh remote for pushing - Repo.Network.Remotes.Add(SSHPushRemote, RepoURL.Replace("git://", "ssh://").Replace("https://", "ssh://")); - - if (!Directory.Exists(RelativePath(StaticDirs))) - InitialConfigureRepository(); - - SendMessage("REPO: Clone complete!", MessageType.DeveloperInfo); - WriteInfo("Repository {0}:{1} successfully cloned", EventID.RepoClone); - } - finally - { - currentProgress = -1; - } - } - catch (Exception e) - - { - SendMessage("REPO: Setup failed!", MessageType.DeveloperInfo); - WriteWarning(String.Format("Failed to clone {2}:{0}: {1}", BranchName, e.ToString(), RepoURL), EventID.RepoCloneFail); - } - finally - { - lock (RepoLock) - { - RepoBusy = false; - Cloning = false; - } - } - } - - /// - /// Turns off the gc.auto git config setting - /// - - void DisableGarbageCollectionNoLock() - { - Repo.Config.Set("gc.auto", false); - } - - /// - /// Copies the Static directory to the first available Static_BACKUP path in the then deleted the old directory - /// - void BackupAndDeleteStaticDirectory() - { - var rsd = RelativePath(StaticDirs); - if (Directory.Exists(rsd)) - { - int count = 1; - - var rsbd = RelativePath(StaticBackupDir); - string path = Path.GetDirectoryName(rsbd); - string newFullPath = rsbd; - - while (File.Exists(newFullPath) || Directory.Exists(newFullPath)) - { - string tempDirName = string.Format("{0}({1})", rsbd, count++); - newFullPath = Path.Combine(path, tempDirName); - } - - Helpers.CopyDirectory(rsd, newFullPath); - } - Helpers.DeleteDirectory(rsd); - } - - /// - /// Updates the with the - /// - /// on success, error message on failure - public string UpdateTGS3Json() - { - try - { - if (File.Exists(RelativePath(RepoTGS3SettingsPath))) - File.Copy(RelativePath(RepoTGS3SettingsPath), RelativePath(CachedTGS3SettingsPath), true); - else if (File.Exists(RelativePath(CachedTGS3SettingsPath))) - File.Delete(RelativePath(CachedTGS3SettingsPath)); - } - catch (Exception e) - { - return e.ToString(); - } - return null; - } - - /// - /// Initial setup for the and - /// - void InitialConfigureRepository() - { - Directory.CreateDirectory(RelativePath(StaticDirs)); - UpdateBridgeDll(false); - UpdateTGS3Json(); - var Config = GetCachedRepoConfig(); //RepoBusy is set if we're here - foreach (var I in Config.StaticDirectoryPaths) - { - try - { - var source = Path.Combine(RelativePath(RepoPath), I); - var dest = Path.Combine(RelativePath(StaticDirs), I); - if (Directory.Exists(source)) - Helpers.CopyDirectory(source, dest); - else - Directory.CreateDirectory(dest); - } - catch - { - WriteError("Could not setup static directory: " + I, EventID.RepoConfigurationFail); - } - } - foreach (var I in Config.DLLPaths) - { - try - { - var source = Path.Combine(RelativePath(RepoPath), I); - if (!File.Exists(source)) - { - WriteWarning("Could not find DLL: " + I, EventID.RepoConfigurationFail); - continue; - } - var dest = Path.Combine(RelativePath(StaticDirs), I); - Helpers.CopyFileForceDirectories(source, dest, false); - } - catch - { - WriteError("Could not setup static DLL: " + I, EventID.RepoConfigurationFail); - } - } - } - - /// - public string Setup(string RepoURL, string BranchName) - { - lock (RepoLock) - { - if (RepoBusy) - return "Repo is busy!"; - lock (CompilerLock) - { - if (!CompilerIdleNoLock()) - return "Compiler is running!"; - } - if (RepoURL.Contains("ssh://") && !SSHAuth()) - return String.Format("SSH url specified but either {0} or {1} does not exist in the server directory!", PrivateKeyPath, PublicKeyPath); - RepoBusy = true; - Cloning = true; - new Thread(new ParameterizedThreadStart(Clone)) - { - IsBackground = true //make sure we don't hold up shutdown - }.Start(RepoURL + ' ' + BranchName); - return null; - } - } - - /// - /// Gets the SHA or branch name of the 's HEAD - /// - /// on success, error message on failure - /// If , returns the branch name instead of the SHA - /// If , returns the tracked branch SHA and ignores - /// The SHA or branch name of the 's HEAD - string GetShaOrBranch(out string error, bool branch, bool tracked) - { - lock (RepoLock) - { - if (RepoBusy) - { - error = "Repo is busy!"; - return null; - } - return GetShaOrBranchNoLock(out error, branch, tracked); - } - } - - /// - /// Gets the SHA or branch name of the 's HEAD. Requires - /// - /// on success, error message on failure - /// If , returns the branch name instead of the SHA - /// If , returns the tracked branch SHA and ignores - /// The SHA or branch name of the 's HEAD - string GetShaOrBranchNoLock(out string error, bool branch, bool tracked) - { - var result = LoadRepo(); - if (result != null) - { - error = result; - return null; - } - - try - { - error = null; - if (tracked && Repo.Head.TrackedBranch != null) - return Repo.Head.TrackedBranch.Tip.Sha; - return branch ? Repo.Head.FriendlyName : Repo.Head.Tip.Sha; - } - catch (Exception e) - { - error = e.ToString(); - return null; - } - } - - /// - public string GetHead(bool useTracked, out string error) - { - return GetShaOrBranch(out error, false, useTracked); - } - - /// - public string GetBranch(out string error) - { - return GetShaOrBranch(out error, true, false); - } - - /// - public string GetRemote(out string error) - { - try - { - var res = LoadRepo(); - if (res != null) - { - error = res; - return null; - } - error = null; - return Repo.Network.Remotes["origin"].Url; - } - catch (Exception e) - { - error = e.ToString(); - return null; - } - } - - /// - /// Equivalent of running `git reset --hard` on the repository. Requires - /// - /// If not , reset to this branch instead of HEAD - /// on success, error message on failure - string ResetNoLock(Branch targetBranch) - { - try - { - if (targetBranch != null) - Repo.Reset(ResetMode.Hard, targetBranch.Tip); - else - Repo.Reset(ResetMode.Hard); - return null; - } - catch (Exception e) - { - return e.ToString(); - } - } - - /// - public string Checkout(string sha) - { - if (sha == LiveTrackingBranch) - return "I'm sorry Dave, I'm afraid I can't do that..."; - lock (RepoLock) - { - if (RepoBusy) - return "Repo is busy!"; - var result = LoadRepo(); - if (result != null) - return result; - SendMessage("REPO: Checking out object: " + sha, MessageType.DeveloperInfo); - try - { - if (Repo.Branches[sha] == null) - { - //see if origin has the branch - result = Fetch(); - var trackedBranch = Repo.Branches[String.Format("origin/{0}", sha)]; - if (trackedBranch != null) - { - var newBranch = Repo.CreateBranch(sha, trackedBranch.Tip); - //track it - Repo.Branches.Update(newBranch, b => b.TrackedBranch = trackedBranch.CanonicalName); - } - else if (result != null) - return result; - } - - //if this is a tracked branch, we need to reset to remote first and delete the PR list - ResetNoLock(Repo.Head.TrackedBranch ?? Repo.Head); - DeletePRList(); - - var Opts = new CheckoutOptions() - { - CheckoutModifiers = CheckoutModifiers.Force, - OnCheckoutProgress = HandleCheckoutProgress, - }; - Commands.Checkout(Repo, sha, Opts); - var res = ResetNoLock(null); - UpdateSubmodules(); - SendMessage("REPO: Checkout complete!", MessageType.DeveloperInfo); - WriteInfo("Repo checked out " + sha, EventID.RepoCheckout); - return res; - } - catch (Exception e) - { - SendMessage("REPO: Checkout failed!", MessageType.DeveloperInfo); - WriteWarning(String.Format("Repo checkout of {0} failed: {1}", sha, e.ToString()), EventID.RepoCheckoutFail); - return e.ToString(); - } - } - } - - /// - /// Merges given into the current branch - /// - /// The sha/branch/tag to merge - /// The commit message for the merge commit - /// on success, error message on failure - string MergeBranch(string committish, string mergeMessage) - { - var mo = new MergeOptions() - { - OnCheckoutProgress = HandleCheckoutProgress - }; - if (mergeMessage != null) - { - mo.CommitOnSuccess = false; - mo.FastForwardStrategy = FastForwardStrategy.NoFastForward; - } - var sig = MakeSig(); - var Result = Repo.Merge(committish, sig, mo); - currentProgress = -1; - switch (Result.Status) - { - case MergeStatus.Conflicts: - ResetNoLock(null); - SendMessage(String.Format("REPO: Merge of {0} conflicted, aborted.", committish), MessageType.DeveloperInfo); - return "Merge conflict occurred."; - case MergeStatus.UpToDate: - return RepoErrorUpToDate; - } - if (mergeMessage != null) - Repo.Commit(mergeMessage, sig, sig); - return null; - } - - void PushTestmergeCommit() - { - if (Config.PushTestmergeCommits && SSHAuth()) - { - string NewB = null; - var targetRemote = Repo.Network.Remotes[SSHPushRemote]; - var options = new PushOptions() - { - CredentialsProvider = GenerateGitCredentials - }; - try - { - //now try and push the commit to the remote so they can be referenced - NewB = Repo.CreateBranch(RemoteTempBranchName).CanonicalName; - Repo.Network.Push(targetRemote, NewB, options); //push the branch - Repo.Branches.Remove(NewB); - var removalString = String.Format(":{0}", NewB); - NewB = null; - //we need to delay the second operation a LOT otherwise we get ssh errors - Thread.Sleep(10000); - Repo.Network.Push(targetRemote, removalString, options); //delete the branch - WriteInfo("Pushed reference commit: " + Repo.Head.Tip.Sha, EventID.ReferencePush); - } - catch (Exception e) - { - WriteWarning(String.Format("Failed to push reference commit: {0}. Error: {1}", Repo.Head.Tip.Sha, e.ToString()), EventID.ReferencePush); - } - finally - { - if (NewB != null) - { - //Try to delete the branches regardless - try - { - Repo.Branches.Remove(NewB); - } - catch { } - try - { - Repo.Network.Push(targetRemote, String.Format(":{0}", NewB), options); - } - catch { } - } - } - } - } - - /// - public string Update(bool reset) - { - return UpdateImpl(reset, true); - } - - /// - /// Fetches the origin and merges it into the current branch - /// - /// If , the operation will perform a hard reset instead of a merge - /// If , a return value of will be changed to - /// on success, error message on failure - string UpdateImpl(bool reset, bool successOnUpToDate) - { - lock (RepoLock) - { - if (RepoBusy) - return "Repo is busy!"; - var result = LoadRepo(); - if (result != null) - return result; - try - { - if (Repo.Head == null || !Repo.Head.IsTracking) - return "Cannot update while not on a tracked branch"; - - var res = Fetch(); - if (res != null) - return res; - - var originBranch = Repo.Head.TrackedBranch; - if (!successOnUpToDate && Repo.Head.Tip.Sha == originBranch.Tip.Sha) - return RepoErrorUpToDate; - - SendMessage(String.Format("REPO: Updating origin branch...{0}", reset ? "" : "(Merge)"), MessageType.DeveloperInfo); - - if (reset) - { - var error = ResetNoLock(Repo.Head.TrackedBranch); - UpdateSubmodules(); - if (error != null) - throw new Exception(error); - DeletePRList(); - WriteInfo("Repo hard updated to " + originBranch.Tip.Sha, EventID.RepoHardUpdate); - return error; - } - res = MergeBranch(originBranch.FriendlyName, "Merge origin into current testmerge"); - if (!LocalIsRemote()) //might be fast forward - PushTestmergeCommit(); - if (res != null) - throw new Exception(res); - UpdateSubmodules(); - WriteInfo("Repo merge updated to " + originBranch.Tip.Sha, EventID.RepoMergeUpdate); - return null; - } - catch (Exception E) - { - SendMessage("REPO: Update failed!", MessageType.DeveloperInfo); - WriteWarning(String.Format("Repo{0} update failed", reset ? " hard" : ""), reset ? EventID.RepoHardUpdateFail : EventID.RepoMergeUpdateFail); - return E.ToString(); - } - } - } - - /// - /// Properly updates any git submodules in the repository - /// - private void UpdateSubmodules() - { - var suo = new SubmoduleUpdateOptions - { - Init = true - }; - foreach (var I in Repo.Submodules) - try - { - Repo.Submodules.Update(I.Name, suo); - } - catch (Exception e) - { - try - { - //workaround for https://github.com/libgit2/libgit2/issues/3820 - //kill off the modules/ folder in .git and try again - try - { - Helpers.DeleteDirectory(String.Format("{0}/.git/modules/{1}", RepoPath, I.Path)); - } - catch - { - throw e; - } - Repo.Submodules.Update(I.Name, suo); - var msg = String.Format("I had to reclone submodule {0}. If this is happening a lot find a better hack or fix https://github.com/libgit2/libgit2/issues/3820!", I.Name); - SendMessage(String.Format("REPO: {0}", msg), MessageType.DeveloperInfo); - WriteWarning(msg, EventID.Submodule); - } - catch (Exception ex) - { - WriteError(String.Format("Failed to update submodule {0}! Error: {1}", I.Name, ex.ToString()), EventID.Submodule); - } - } - } - - /// - /// Creates a date and timestamped tag of the current HEAD - /// - /// on success, error message on failure - string CreateBackup() - { - try - { - lock (RepoLock) - { - if (RepoBusy) - return "Repo is busy!"; - var res = LoadRepo(); - if (res != null) - return res; - - //Make sure we don't already have a backup at this commit - var HEAD = Repo.Head.Tip.Sha; - foreach (var T in Repo.Tags) - if (T.Target.Sha == HEAD) - return null; - - var tagName = "TGS-Compile-Backup-" + DateTime.Now.ToString("yyyy-MM-dd--HH.mm.ss"); - var tag = Repo.ApplyTag(tagName); - - if (tag != null) - { - WriteInfo("Repo backup created at tag: " + tagName + " commit: " + HEAD, EventID.RepoBackupTag); - return null; - } - throw new Exception("Tag creation failed!"); - } - } - catch (Exception e) - { - WriteWarning(String.Format("Failed backup tag creation at commit {0}!", Repo.Head.Tip.Sha), EventID.RepoBackupTagFail); - return e.ToString(); - } - } - - /// - /// Lists tags in the repository created by the - /// - /// on success, error message on failure - /// A dictionary of tag title -> commit SHA on success, on failure - public IDictionary ListBackups(out string error) - { - try - { - lock (RepoLock) - { - if (RepoBusy) - { - error = "Repo is busy!"; - return null; - } - error = LoadRepo(); - if (error != null) - return null; - - var res = new Dictionary(); - foreach (var T in Repo.Tags) - if (T.FriendlyName.Contains("TGS")) - res.Add(T.FriendlyName, T.Target.Sha); - return res; - } - } - catch (Exception e) - { - error = e.ToString(); - return null; - } - } - - /// - public string Reset(bool trackedBranch) - { - lock (RepoLock) - { - if (RepoBusy) - return "Repo is busy!"; - var res = LoadRepo() ?? ResetNoLock(trackedBranch ? (Repo.Head.TrackedBranch ?? Repo.Head) : Repo.Head); - if (res == null) - { - SendMessage(String.Format("REPO: Hard reset to {0}branch", trackedBranch ? "tracked " : ""), MessageType.DeveloperInfo); - if (trackedBranch) - DeletePRList(); - WriteInfo(String.Format("Repo branch reset{0}", trackedBranch ? " to tracked branch" : ""), trackedBranch ? EventID.RepoResetTracked : EventID.RepoReset); - return null; - } - WriteWarning(String.Format("Failed to reset{0}: {1}", trackedBranch ? " to tracked branch" : "", res), trackedBranch ? EventID.RepoResetTrackedFail : EventID.RepoResetFail); - return res; - } - } - - /// - /// Creates a commit based off of the configured name and e-mail - /// - /// The created - Signature MakeSig() - { - return new Signature(new Identity(Config.CommitterName, Config.CommitterEmail), DateTimeOffset.Now); - } - - /// - /// Deletes the 's - /// - void DeletePRList() - { - if (File.Exists(RelativePath(PRJobFile))) - try - { - File.Delete(RelativePath(PRJobFile)); - } - catch (Exception e) - { - WriteError("Failed to delete PR list: " + e.ToString(), EventID.RepoPRListError); - } - } - - /// - /// Deserializes the - /// - /// A of . The outer one is keyed by PR# the inner one is keyed by internal s - IDictionary> GetCurrentPRList() - { - if (!File.Exists(RelativePath(PRJobFile))) - return new Dictionary>(); - var rawdata = File.ReadAllText(RelativePath(PRJobFile)); - return JsonConvert.DeserializeObject>>(rawdata); - } - - /// - /// Serializes pull request info into - /// - /// - void SetCurrentPRList(IDictionary> list) - { - var rawdata = JsonConvert.SerializeObject(list, Formatting.Indented); - File.WriteAllText(RelativePath(PRJobFile), rawdata); - } - - /// - public IEnumerable MergePullRequests(IEnumerable pullRequestInfos, bool silent) - { - var results = new List(); - var amount = pullRequestInfos.Count(); - if (amount < 1) - return results; - - if (!silent) { - var messages = new List(); - foreach (var I in pullRequestInfos) - { - var shortSha = I.Sha?.Substring(0, Math.Min(I.Sha.Length, 7)); - messages.Add(String.Format("#{0}{1}", I.Number, shortSha != null ? String.Format(" at commit {0}", shortSha) : shortSha)); - } - SendMessage(String.Format("REPO: Merging PR{0} {1}...", amount == 1 ? "" : "s", String.Join(", ", messages)), MessageType.DeveloperInfo); - } - - bool foundBad = false; - foreach (var I in pullRequestInfos) - { - var res = MergePullRequest(I.Number, I.Sha, true); - if (res != null) - foundBad = true; - results.Add(res); - } - return foundBad ? results : null; - } - - /// - public string MergePullRequest(int PRNumber, string atSHA, bool silent) - { - lock (RepoLock) - { - if (RepoBusy) - return "Repo is busy!"; - var result = LoadRepo(); - if (result != null) - return result; - if(!silent) - SendMessage(String.Format("REPO: Merging PR #{0}{1}...", PRNumber, atSHA != null ? String.Format(" at commit {0}", atSHA): ""), MessageType.DeveloperInfo); - result = ResetNoLock(null); - if (result != null) - return result; - try - { - //only supported with github - var remoteUrl = Repo.Network.Remotes["origin"].Url; - if (!remoteUrl.Contains("github.com")) - return "Only supported with Github based repositories."; - - - var Refspec = new List(); - var PRBranchName = String.Format("pr-{0}", PRNumber); - var LocalBranchName = String.Format("pull/{0}/headrefs/heads/{1}", PRNumber, PRBranchName); - Refspec.Add(String.Format("pull/{0}/head:{1}", PRNumber, PRBranchName)); - var logMessage = ""; - - //Need to delete the branch first in case of rebase - Repo.Branches.Remove(LocalBranchName); - Repo.Branches.Remove(PRBranchName); - - Commands.Fetch(Repo, "origin", Refspec, GenerateFetchOptions(), logMessage); //shitty api has no failure state for this - - currentProgress = -1; - - var branch = Repo.Branches[LocalBranchName]; - if (branch == null) - { - SendMessage(String.Format("REPO: PR {0}could not be fetched. Does it exist?", silent ? String.Format("#{0} ", PRNumber) : ""), MessageType.DeveloperInfo); - return String.Format("PR #{0} could not be fetched. Does it exist?", PRNumber); - } - - //give it a better name - branch = Repo.CreateBranch(PRBranchName, branch.Tip); - Repo.Branches.Remove(LocalBranchName); - - if (atSHA != null) - { - //find the commit - Commit commit = null; - string error = null; - try - { - commit = Repo.Lookup(atSHA); - } - catch (Exception e) - { - error = e.ToString(); - } - if (commit == null) - { - SendMessage("REPO: Commit could not be found, aborting!", MessageType.DeveloperInfo); - return error ?? String.Format("Commit {0} could not be found in the repository!", atSHA); - } - } - - //so we'll know if this fails - var Result = MergeBranch(atSHA ?? branch.CanonicalName, String.Format("[ci skip] Test merge commit for pull request #{0}{1}Server Instance: {2}", PRNumber, Environment.NewLine, Config.Name)); - - if (Result == null) - try - { - UpdateSubmodules(); - } - catch (Exception e) - { - Result = e.ToString(); - } - - if (Result == null) - { - WriteInfo(String.Format("Merged pull request #{0}", PRNumber), EventID.RepoPRMerge); - try - { - var CurrentPRs = GetCurrentPRList(); - var PRNumberString = PRNumber.ToString(); - CurrentPRs.Remove(PRNumberString); - var newPR = new Dictionary(); - - //do some excellent remote fuckery here to get the api page - var prAPI = remoteUrl; - prAPI = prAPI.Replace("/.git", ""); - prAPI = prAPI.Replace(".git", ""); - prAPI = prAPI.Replace("git:", "https:"); - prAPI = prAPI.Replace("github.com", "api.github.com/repos"); - prAPI += "/pulls/" + PRNumberString + ".json"; - string json; - using (var wc = new WebClient()) - { - wc.Headers.Add("user-agent", "TGS.Server"); - json = wc.DownloadString(prAPI); - } - - var dick = JsonConvert.DeserializeObject>(json); - var user = ((JObject)dick["user"]).ToObject>(); - - newPR.Add("commit", atSHA ?? branch.Tip.Sha); - newPR.Add("author", (string)user["login"]); - newPR.Add("title", (string)dick["title"]); - CurrentPRs.Add(PRNumberString, newPR); - SetCurrentPRList(CurrentPRs); - } - catch (Exception e) - { - WriteError("Failed to update PR list", EventID.RepoPRListError); - return "PR Merged, JSON update failed: " + e.ToString(); - } - - PushTestmergeCommit(); - } - return Result; - } - catch (Exception E) - { - SendMessage("REPO: PR merge failed!", MessageType.DeveloperInfo); - WriteWarning(String.Format("Failed to merge pull request #{0}: {1}", PRNumber, E.ToString()), EventID.RepoPRMergeFail); - return E.ToString(); - } - } - } - - /// - public List MergedPullRequests(out string error) - { - lock (RepoLock) - { - if (RepoBusy) - { - error = "Repo is busy!"; - return null; - } - var result = LoadRepo(); - if (result != null) - { - error = result; - return null; - } - try - { - var PRRawData = GetCurrentPRList(); - var output = new List(); - foreach (var I in GetCurrentPRList()) - output.Add(new PullRequestInfo(Convert.ToInt32(I.Key), I.Value["author"], I.Value["title"], I.Value["commit"])); - error = null; - return output; - } - catch (Exception e) - { - error = e.ToString(); - return null; - } - } - } - - /// - public string GetCommitterName() - { - return Config.CommitterName; - } - - /// - public void SetCommitterName(string newName) - { - Config.CommitterName = newName; - Config.Save(); - } - - /// - public string GetCommitterEmail() - { - return Config.CommitterEmail; - } - - /// - public void SetCommitterEmail(string newEmail) - { - Config.CommitterEmail = newEmail; - Config.Save(); - } - - /// - public string SynchronizePush() - { - lock (RepoLock) - { - if (RepoBusy) - return "Repo is busy!"; - var Config = GetCachedRepoConfig(); - if (Config == null) - return "Error reading changelog configuration"; - if (!Config.ChangelogSupport || !SSHAuth()) - return null; - var res = LoadRepo(); - if (res != null) - return res; - return LocalIsRemote() ? Commit(Config) ?? Push() : "Can't push changelog: HEAD does not match tracked remote branch"; - } - } - - /// - /// Create that Prune and have the appropriate credentials and progress handler - /// - /// Properly configured - FetchOptions GenerateFetchOptions() - { - return new FetchOptions() - { - CredentialsProvider = GenerateGitCredentials, - OnTransferProgress = HandleTransferProgress, - Prune = true, - }; - } - - /// - /// Fetches origin - /// - /// null on success, error message on failure - string Fetch() - { - try - { - string logMessage = ""; - var R = Repo.Network.Remotes["origin"]; - IEnumerable refSpecs = R.FetchRefSpecs.Select(X => X.Specification); - Commands.Fetch(Repo, R.Name, refSpecs, GenerateFetchOptions(), logMessage); - return null; - } - catch (Exception e) - { - return e.ToString(); - } - } - - /// - /// Check if the current HEAD matches the tracked remote branch HEAD - /// - /// if the current HEAD matches the tracked remote branch HEAD, otherwise - bool LocalIsRemote() - { - lock (RepoLock) - { - if (RepoBusy) - return false; - if (LoadRepo() != null) - return false; - if (Fetch() != null) - return false; - try - { - return Repo.Head.IsTracking && Repo.Head.TrackedBranch.Tip.Sha == Repo.Head.Tip.Sha; - } - catch - { - return false; - } - } - } - - /// - /// Create a commit based on a . Requires and - /// - /// A with - /// on success, error message on failure - string Commit(RepoConfig Config) - { - try - { - // Stage the file - foreach (var I in Config.PathsToStage) - Commands.Stage(Repo, I); - - if (Repo.RetrieveStatus().Staged.Count() == 0) //nothing to commit - return null; - - // Create the committer's signature and commit - var authorandcommitter = MakeSig(); - - // Commit to the repository - WriteInfo(String.Format("Commit {0} created from changelogs", Repo.Commit(CommitMessage, authorandcommitter, authorandcommitter)), EventID.RepoCommit); - DeletePRList(); - return null; - } - catch (Exception e) - { - WriteWarning("Repo commit failed: " + e.ToString(), EventID.RepoCommitFail); - return e.ToString(); - } - } - - /// - /// Push HEAD to . Requires repo be locked, , , and - /// - /// on success, error message on failure - string Push() - { - try - { - var options = new PushOptions() - { - CredentialsProvider = GenerateGitCredentials, - }; - Repo.Network.Push(Repo.Network.Remotes[SSHPushRemote], Repo.Head.CanonicalName, options); - WriteInfo("Repo pushed up to commit: " + Repo.Head.Tip.Sha, EventID.RepoPush); - return null; - } - catch (Exception e) - { - WriteWarning("Repo push failed: " + e.ToString(), EventID.RepoPushFail); - return e.ToString(); - } - } - - /// - /// Check if the is configured for SSH pushing - /// - /// if the see cref="Instance"/> is configured for SSH pushing, otherwise - bool SSHAuth() - { - return File.Exists(RelativePath(PrivateKeyPath)) && File.Exists(RelativePath(PublicKeyPath)); - } - - /// - /// SSH credentials callback. Properly sets up SSH keys for an authorization operation - /// - /// Ignored - /// The username to use for the operation - /// The for the operation - /// The proper for the operation - Credentials GenerateGitCredentials(string url, string usernameFromUrl, SupportedCredentialTypes types) - { - var user = usernameFromUrl ?? "git"; - if (types == SupportedCredentialTypes.UsernameQuery) - return new UsernameQueryCredentials() - { - Username = user, - }; - return new SshUserKeyCredentials() - { - Username = user, - PrivateKey = RelativePath(PrivateKeyPath), - PublicKey = RelativePath(PublicKeyPath), - Passphrase = "", - }; - } - - /// - public string GenerateChangelog(out string error) - { - return GenerateChangelogImpl(out error); - } - - /// - /// Updates the html changelog - /// - /// on success, error on failure - /// If , prevents a recursive call to this function after updating pip dependencies - /// The output of the python script - public string GenerateChangelogImpl(out string error, bool recurse = false) - { - var RConfig = GetCachedRepoConfig(); - if (RConfig == null) - { - error = null; - return "Error loading changelog config!"; - } - if (!RConfig.ChangelogSupport) - { - error = null; - return null; - } - - string ChangelogPy = RConfig.PathToChangelogPy; - if (!Exists()) - { - error = "Repo does not exist!"; - return null; - } - - lock (RepoLock) - { - if (RepoBusy) - { - error = "Repo is busy!"; - return null; - } - if (!File.Exists(Path.Combine(RelativePath(RepoPath), ChangelogPy))) - { - error = "Missing changelog generation script!"; - return null; - } - - var pp = Server.Config.PythonPath; - var PythonFile = Path.Combine(pp, "python.exe"); - if (!File.Exists(PythonFile)) - { - error = "Cannot locate python!"; - return null; - } - try - { - string result; - int exitCode; - using (var python = new Process()) - { - python.StartInfo.FileName = PythonFile; - python.StartInfo.Arguments = String.Format("{0} {1}", ChangelogPy, RConfig.ChangelogPyArguments); - python.StartInfo.UseShellExecute = false; - python.StartInfo.WorkingDirectory = new DirectoryInfo(RelativePath(RepoPath)).FullName; - python.StartInfo.RedirectStandardOutput = true; - python.StartInfo.RedirectStandardError = true; - python.Start(); - using (StreamReader reader = python.StandardOutput) - result = reader.ReadToEnd(); - using (StreamReader reader = python.StandardError) - result += reader.ReadToEnd(); - python.WaitForExit(); - exitCode = python.ExitCode; - } - if (exitCode != 0) - { - if (recurse || RConfig.PipDependancies.Count == 0) - { - error = "Script failed!"; - return result; - } - //update pip deps and try again - - string PipFile = Path.Combine(pp, "scripts", "pip.exe"); - foreach(var I in RConfig.PipDependancies) - using (var pip = new Process()) - { - pip.StartInfo.FileName = PipFile; - pip.StartInfo.Arguments = "install " + I; - pip.StartInfo.UseShellExecute = false; - pip.StartInfo.RedirectStandardOutput = true; - pip.StartInfo.RedirectStandardError = true; - pip.Start(); - using (StreamReader reader = pip.StandardOutput) - result += "\r\n---BEGIN-PIP-OUTPUT---\r\n" + reader.ReadToEnd(); - using (StreamReader reader = pip.StandardError) - result += reader.ReadToEnd(); - pip.WaitForExit(); - if (pip.ExitCode != 0) - { - error = "Script and pip failed!"; - return result; - } - } - //and recurse - return GenerateChangelogImpl(out error, true); - } - error = null; - WriteInfo("Changelog generated" + error, EventID.RepoChangelog); - return result; - } - catch (Exception e) - { - error = e.ToString(); - WriteWarning("Changelog generation failed: " + error, EventID.RepoChangelogFail); - return null; - } - } - } - - /// - public void SetAutoUpdateInterval(ulong newInterval) - { - lock (autoUpdateTimer) - { - autoUpdateTimer.Stop(); - if (newInterval > 0) { - autoUpdateTimer.Interval = newInterval * 60 * 1000; //convert from minutes to ms - autoUpdateTimer.Start(); - } - } - Config.AutoUpdateInterval = newInterval; - Config.Save(); - } - - /// - public ulong AutoUpdateInterval() - { - return Config.AutoUpdateInterval; - } - - /// - /// Runs on the configured of and tries to and the - /// - /// A - /// The event arguments - private void AutoUpdateTimer_Elapsed(object sender, System.Timers.ElapsedEventArgs e) - { - if (UpdateImpl(true, false) == null) - { - Compile(true); - } - } - - /// - /// Updates with the staged 's SHA - /// - /// The commit SHA that was staged - void UpdateLiveSha(string newSha) - { - if (LoadRepo() != null) - return; - var B = Repo.Branches[LiveTrackingBranch]; - if (B != null) - Repo.Branches.Remove(B); - Repo.CreateBranch(LiveTrackingBranch, newSha); - } - - /// - /// Gets the current staged or live commit SHA - /// - /// The current staged or live commit SHA - public string LiveSha() - { - var B = Repo.Branches[LiveTrackingBranch]; - return B != null ? B.Tip.Sha : "UNKNOWN"; - } - - /// - public bool PushTestmergeCommits() - { - return Config.PushTestmergeCommits; - } - - /// - public void SetPushTestmergeCommits(bool newValue) - { - Config.PushTestmergeCommits = newValue; - Config.Save(); - } - } -} diff --git a/TGS.Server/InstanceConfig.cs b/TGS.Server/InstanceConfig.cs deleted file mode 100644 index 5cf13fcaf0..0000000000 --- a/TGS.Server/InstanceConfig.cs +++ /dev/null @@ -1,242 +0,0 @@ -using Newtonsoft.Json; -using System.IO; -using TGS.Interface; - -namespace TGS.Server -{ - /// - /// Configuration settings for a - /// - public interface IInstanceConfig - { - /// - /// The directory this is for - /// - string Directory { get; } - - /// - /// Actual version of the . Migrated up via - /// - ulong Version { get; } - - /// - /// The name of the - /// - string Name { get; set; } - - /// - /// If the is active - /// - bool Enabled { get; set; } - - /// - /// The name of the .dme/.dmb the uses - /// - string ProjectName { get; set; } - - /// - /// The port the runs on - /// - ushort Port { get; set; } - - /// - /// The level for the - /// - DreamDaemonSecurity Security { get; set; } - - /// - /// Whether or not the should immediately start DreamDaemon when activated - /// - bool Autostart { get; set; } - - /// - /// Whether or not DreamDaemon allows connections from webclients - /// - bool Webclient { get; set; } - - /// - /// Author and committer name for synchronize commits - /// - string CommitterName { get; set; } - /// - /// Author and committer e-mail for synchronize commits - /// - string CommitterEmail { get; set; } - - /// - /// Encrypted serialized s - /// - string ChatProviderData { get; set; } - - /// - /// Entropy for - /// - string ChatProviderEntropy { get; set; } - - /// - /// If the should reattach to a running DreamDaemon - /// - bool ReattachRequired { get; set; } - - /// - /// The of the runnning DreamDaemon - /// - int ReattachProcessID { get; set; } - - /// - /// The port the runnning DreamDaemon was launched on - /// - ushort ReattachPort { get; set; } - - /// - /// The serviceCommsKey the runnning DreamDaemon was launched on - /// - string ReattachCommsKey { get; set; } - - /// - /// The API version of the runnning DreamDaemon - /// - string ReattachAPIVersion { get; set; } - - /// - /// The user group allowed to use the - /// - string AuthorizedUserGroupSID { get; set; } - - /// - /// The auto update interval for the - /// - ulong AutoUpdateInterval { get; set; } - - /// - /// Whether or not testmerge commits are published to a temporary remote branch - /// - bool PushTestmergeCommits { get; set; } - - /// - /// Time in seconds before DD is considered dead in the water - /// - int ServerStartupTimeout { get; set; } - - /// - /// Saves the to it's - /// - void Save(); - } - - /// - public class InstanceConfig : IInstanceConfig - { - /// - /// The name the file is saved as in the - /// - [JsonIgnore] - public const string JSONFilename = "Instance.json"; - - /// - /// The current version of the config - /// - [JsonIgnore] - protected const ulong CurrentVersion = 1; //Literally any time you add/deprecated a field, this number needs to be bumped - - /// - [JsonIgnore] - public string Directory { get; private set; } - - /// - public ulong Version { get; protected set; } = CurrentVersion; - - /// - public string Name { get; set; } = "TG Station Server"; - - /// - public bool Enabled { get; set; } = true; - - /// - public string ProjectName { get; set; } = "tgstation"; - - /// - public ushort Port { get; set; } = 1337; - - /// - public DreamDaemonSecurity Security { get; set; } = DreamDaemonSecurity.Trusted; - - /// - public bool Autostart { get; set; } = false; - - /// - public bool Webclient { get; set; } = false; - - /// - public string CommitterName { get; set; } = "tgstation-server"; - - /// - public string CommitterEmail { get; set; } = "tgstation-server@tgstation13.org"; - - /// - public string ChatProviderData { get; set; } = Instance.UninitializedString; - - /// - public string ChatProviderEntropy { get; set; } - - /// - public bool ReattachRequired { get; set; } = false; - - /// - public int ReattachProcessID { get; set; } - - /// - public ushort ReattachPort { get; set; } - - /// - public string ReattachCommsKey { get; set; } - - /// - public string ReattachAPIVersion { get; set; } - - /// - public string AuthorizedUserGroupSID { get; set; } = null; - - /// - public ulong AutoUpdateInterval { get; set; } = 0; - - /// - public bool PushTestmergeCommits { get; set; } = false; - - /// - public int ServerStartupTimeout { get; set; } = 60; - - /// - /// Construct a for a at - /// - /// The path to the - public InstanceConfig(string path) - { - Directory = path; - } - - /// - public void Save() - { - string data; - lock(this) - data = JsonConvert.SerializeObject(this, Formatting.Indented); - var path = Path.Combine(Directory, JSONFilename); - File.WriteAllText(path, data); - } - - /// - /// Loads and migrates an from a at - /// - /// The path to the directory - /// The migrated - public static IInstanceConfig Load(string path) - { - var configtext = File.ReadAllText(Path.Combine(path, JSONFilename)); - var res = JsonConvert.DeserializeObject(configtext); - res.Directory = path; - res.MigrateToCurrentVersion(); - return res; - } - } -} diff --git a/TGS.Server/MessageType.cs b/TGS.Server/MessageType.cs deleted file mode 100644 index 72d930bb0e..0000000000 --- a/TGS.Server/MessageType.cs +++ /dev/null @@ -1,28 +0,0 @@ -using System; - -namespace TGS.Server -{ - /// - /// Type of chat message, these may be OR'd together - /// - [Flags] - enum MessageType - { - /// - /// Send message to the admin channels - /// - AdminInfo = 1, - /// - /// Send message to the game channels - /// - GameInfo = 2, - /// - /// Send message to the watchdog channels - /// - WatchdogInfo = 4, - /// - /// Send message to the coder channels - /// - DeveloperInfo = 8, - } -} diff --git a/TGS.Server/ProcessExtension.cs b/TGS.Server/ProcessExtension.cs deleted file mode 100644 index 0d24a8351c..0000000000 --- a/TGS.Server/ProcessExtension.cs +++ /dev/null @@ -1,72 +0,0 @@ -using System; -using System.Diagnostics; -using System.Runtime.InteropServices; - -namespace TGS.Server -{ - /// - /// Helpers to ing and a . Lightly massaged code from https://stackoverflow.com/a/13109774. Documentation linked from MSDN on 20/10/2017 - /// - static class ProcessExtension - { - /// - /// https://msdn.microsoft.com/en-us/library/windows/desktop/ms686769(v=vs.85).aspx - /// - enum ThreadAccess : int - { - SUSPEND_RESUME = (0x0002), - } - /// - /// https://msdn.microsoft.com/en-us/library/windows/desktop/ms684335(v=vs.85).aspx - /// - [DllImport("kernel32.dll")] - static extern IntPtr OpenThread(ThreadAccess dwDesiredAccess, bool bInheritHandle, uint dwThreadId); - /// - /// https://msdn.microsoft.com/en-us/library/windows/desktop/ms724211(v=vs.85).aspx - /// - [DllImport("kernel32.dll")] - static extern bool CloseHandle(IntPtr hObject); - /// - /// https://msdn.microsoft.com/en-us/library/windows/desktop/ms686345(v=vs.85).aspx - /// - [DllImport("kernel32.dll")] - static extern uint SuspendThread(IntPtr hThread); - /// - /// https://msdn.microsoft.com/en-us/library/windows/desktop/ms685086(v=vs.85).aspx - /// - [DllImport("kernel32.dll")] - static extern int ResumeThread(IntPtr hThread); - - /// - /// Suspends all threads for a running - /// - /// The to suspend - public static void Suspend(this Process process) - { - foreach (ProcessThread thread in process.Threads) - { - var pOpenThread = OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)thread.Id); - if (pOpenThread == IntPtr.Zero) - continue; - SuspendThread(pOpenThread); - CloseHandle(pOpenThread); - } - } - - /// - /// Resumes all threads for a running - /// - /// The to suspend - public static void Resume(this Process process) - { - foreach (ProcessThread thread in process.Threads) - { - var pOpenThread = OpenThread(ThreadAccess.SUSPEND_RESUME, false, (uint)thread.Id); - if (pOpenThread == IntPtr.Zero) - continue; - ResumeThread(pOpenThread); - CloseHandle(pOpenThread); - } - } - } -} diff --git a/TGS.Server/Properties/AssemblyInfo.cs b/TGS.Server/Properties/AssemblyInfo.cs deleted file mode 100644 index 5dc00523b0..0000000000 --- a/TGS.Server/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,16 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -// General Information about an assembly is controlled through the following -// set of attributes. Change these attribute values to modify the information -// associated with an assembly. -[assembly: AssemblyTitle("TGStation Server")] -[assembly: AssemblyDescription("Server management suite for running BYOND games")] - -// Setting ComVisible to false makes the types in this assembly not visible -// to COM components. If you need to access a type in this assembly from -// COM, set the ComVisible attribute to true on that type. -[assembly: ComVisible(false)] - -// The following GUID is for the ID of the typelib if this project is exposed to COM -[assembly: Guid("f32eda25-0855-411c-af5e-f0d042917e2d")] diff --git a/TGS.Server/RepoConfig.cs b/TGS.Server/RepoConfig.cs deleted file mode 100644 index edbcf32622..0000000000 --- a/TGS.Server/RepoConfig.cs +++ /dev/null @@ -1,151 +0,0 @@ -using Newtonsoft.Json; -using Newtonsoft.Json.Linq; -using System; -using System.Collections.Generic; -using System.IO; -using System.Linq; - -namespace TGS.Server -{ - /// - /// Repository specific information for a - /// - sealed class RepoConfig : IEquatable - { - /// - /// If this json is setup to support - /// - public readonly bool ChangelogSupport; - /// - /// Path to the repository's changelog generator script - /// - public readonly string PathToChangelogPy; - /// - /// Arguments for the changelog gennerator script - /// - public readonly string ChangelogPyArguments; - /// - /// List of python pip dependencies for the changelog generator script - /// - public readonly IList PipDependancies = new List(); - /// - /// Paths to commit and push to the remote repository - /// - public readonly IList PathsToStage = new List(); - /// - /// Directory's whose contents should not be touched when the updates - /// - public readonly IList StaticDirectoryPaths = new List(); - /// - /// DLL's used by DreamDaemon call()() operations. Must be handled as symlinks to avoid lockups during update operations - /// - public readonly IList DLLPaths = new List(); - - /// - /// Construct a RepoConfig - /// - /// Path to the config JSON to use - public RepoConfig(string path) - { - if (!File.Exists(path)) - return; - var rawdata = File.ReadAllText(path); - var json = JsonConvert.DeserializeObject>(rawdata); - try - { - var details = ((JObject)json["changelog"]).ToObject>(); - PathToChangelogPy = (string)details["script"]; - ChangelogPyArguments = (string)details["arguments"]; - ChangelogSupport = true; - try - { - PipDependancies = LoadArray(details["pip_dependancies"]); - } - catch { } - } - catch - { - ChangelogSupport = false; - } - try - { - PathsToStage = LoadArray(json["synchronize_paths"]); - } - catch { } - try - { - StaticDirectoryPaths = LoadArray(json["static_directories"]); - } - catch { } - try - { - DLLPaths = LoadArray(json["dlls"]); - } - catch { } - } - - /// - /// Convert an array of s to a of s - /// - /// The array to convert - /// - private static IList LoadArray(object o) - { - var res = new List(); - foreach (var I in ((JArray)o)) - res.Add(I.ToObject()); - return res; - } - - /// - public override bool Equals(object obj) - { - return Equals(obj as RepoConfig); - } - - /// - /// Check if two s have the same contents - /// - /// The first - /// The second - /// if the s match, otherwise - private static bool ListEquals(IList A, IList B) - { - return A.All(B.Contains) && A.Count == B.Count; - } - - public bool Equals(RepoConfig other) - { - return ChangelogSupport == other.ChangelogSupport - && PathToChangelogPy == other.PathToChangelogPy - && ChangelogPyArguments == other.ChangelogPyArguments - && ListEquals(PipDependancies, other.PipDependancies) - && ListEquals(PathsToStage, other.PathsToStage) - && ListEquals(StaticDirectoryPaths, other.StaticDirectoryPaths) - && ListEquals(DLLPaths, other.DLLPaths); - } - - public override int GetHashCode() - { - var hashCode = 1890628544; - hashCode = hashCode * -1521134295 + ChangelogSupport.GetHashCode(); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(PathToChangelogPy); - hashCode = hashCode * -1521134295 + EqualityComparer.Default.GetHashCode(ChangelogPyArguments); - hashCode = hashCode * -1521134295 + EqualityComparer>.Default.GetHashCode(PipDependancies); - hashCode = hashCode * -1521134295 + EqualityComparer>.Default.GetHashCode(PathsToStage); - hashCode = hashCode * -1521134295 + EqualityComparer>.Default.GetHashCode(StaticDirectoryPaths); - hashCode = hashCode * -1521134295 + EqualityComparer>.Default.GetHashCode(DLLPaths); - return hashCode; - } - - public static bool operator ==(RepoConfig config1, RepoConfig config2) - { - return EqualityComparer.Default.Equals(config1, config2); - } - - public static bool operator !=(RepoConfig config1, RepoConfig config2) - { - return !(config1 == config2); - } - } -} diff --git a/TGS.Server/Security/AuthenticationHeaderDecoder.cs b/TGS.Server/Security/AuthenticationHeaderDecoder.cs deleted file mode 100644 index ea58cd043f..0000000000 --- a/TGS.Server/Security/AuthenticationHeaderDecoder.cs +++ /dev/null @@ -1,59 +0,0 @@ -using System; -using System.Collections.ObjectModel; -using System.Collections.Generic; -using System.IdentityModel.Policy; -using System.Runtime.InteropServices; -using System.Security.Principal; -using System.ServiceModel; -using System.ServiceModel.Channels; - -namespace TGS.Server.Security -{ - sealed class AuthenticationHeaderDecoder : ServiceAuthenticationManager - { - /// - /// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa378184(v=vs.85).aspx - /// - [DllImport("advapi32.dll", SetLastError = true)] - static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr phToken); - /// - /// See https://msdn.microsoft.com/en-us/library/windows/desktop/ms724211(v=vs.85).aspx - /// - [DllImport("kernel32.dll", SetLastError = true)] - static extern bool CloseHandle(IntPtr handle); - - /// - /// Authenticates a given - /// - /// The default s - /// The at which the message was received. - /// The to be authenticated - /// A of s. Either or one containing a single if this function successfully creates one - public override ReadOnlyCollection Authenticate(ReadOnlyCollection authPolicy, Uri listenUri, ref Message message) - { - try - { - var userPosition = message.Headers.FindHeader("Username", "http://tempuri.org"); - var passPosition = message.Headers.FindHeader("Password", "http://tempuri.org"); - - if (userPosition != -1 && passPosition != -1) - { - var user = message.Headers.GetHeader(userPosition); - var pass = message.Headers.GetHeader(passPosition); - - var splits = user.Split('\\'); - - var res = LogonUser(splits.Length > 1 ? splits[1] : splits[0], splits.Length > 1 ? splits[0] : null, pass, 3 /*LOGON32_LOGON_NETWORK*/, 0 /*LOGON32_PROVIDER_DEFAULT*/, out IntPtr token); - if (res) - { - //IMPORTANT: logoff the user after all is said and done or they'll stay logged in on the system for as long as we are running - OperationContext.Current.OperationCompleted += (a, b) => { CloseHandle(token); }; - return new ReadOnlyCollection(new List { new WindowsAuthorizationPolicy(new WindowsIdentity(token)) }); - } - } - } - catch { } - return authPolicy; - } - } -} diff --git a/TGS.Server/Security/RootAuthorizationManager.cs b/TGS.Server/Security/RootAuthorizationManager.cs deleted file mode 100644 index 809e87078c..0000000000 --- a/TGS.Server/Security/RootAuthorizationManager.cs +++ /dev/null @@ -1,41 +0,0 @@ -using System; -using System.Collections.Generic; -using System.Linq; -using System.Security.Principal; -using System.ServiceModel; -using TGS.Interface.Components; - -namespace TGS.Server.Security -{ - /// - /// A used to determine only if the caller is an admin - /// - sealed class RootAuthorizationManager : ServiceAuthorizationManager - { - string LastSeenUser; - public static readonly IList InstanceAuthManagers = new List(); - protected override bool CheckAccessCore(OperationContext operationContext) - { - var contract = operationContext.EndpointDispatcher.ContractName; - if (contract == typeof(ITGConnectivity).Name) //always allow connectivity checks - return true; - - var windowsIdent = operationContext.ServiceSecurityContext.WindowsIdentity; - - var wp = new WindowsPrincipal(windowsIdent); - //first allow admins - var authSuccess = wp.IsInRole(WindowsBuiltInRole.Administrator); - if(!authSuccess && contract == typeof(ITGLanding).Name) - lock(InstanceAuthManagers) - return InstanceAuthManagers.Any(x => x.CheckAccess(operationContext)); - - var user = windowsIdent.Name; - if (LastSeenUser != user) - { - LastSeenUser = user; - Server.Logger.WriteAccess(String.Format("Root access from: {0}", user), authSuccess, Server.LoggingID); - } - return authSuccess; - } - } -} diff --git a/TGS.Server/Security/WindowsAuthorizationPolicy.cs b/TGS.Server/Security/WindowsAuthorizationPolicy.cs deleted file mode 100644 index bc070f2c6a..0000000000 --- a/TGS.Server/Security/WindowsAuthorizationPolicy.cs +++ /dev/null @@ -1,50 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IdentityModel.Claims; -using System.IdentityModel.Policy; -using System.Security.Principal; - -namespace TGS.Server.Security -{ - /// - /// Implements an for - /// - sealed class WindowsAuthorizationPolicy : IAuthorizationPolicy - { - /// - /// The to use - /// - readonly WindowsIdentity identity; - - /// - /// Construct a - /// - /// The value of - public WindowsAuthorizationPolicy(WindowsIdentity ident) - { - identity = ident; - } - - /// - /// Unused implementation of - /// - public ClaimSet Issuer => throw new NotImplementedException(); - - /// - /// Unused implementation of - /// - public string Id => throw new NotImplementedException(); - - /// - /// Attaches to 's properties - /// - /// The - /// A state object - /// - public bool Evaluate(EvaluationContext evaluationContext, ref object state) - { - evaluationContext.Properties["Identities"] = new List { identity }; - return true; - } - } -} diff --git a/TGS.Server/Server.cs b/TGS.Server/Server.cs deleted file mode 100644 index c907261038..0000000000 --- a/TGS.Server/Server.cs +++ /dev/null @@ -1,704 +0,0 @@ -using System; -using System.Collections.Generic; -using System.IO; -using System.Net; -using System.Reflection; -using System.Security.Principal; -using System.ServiceModel; -using TGS.Interface; -using TGS.Interface.Components; -using TGS.Server.Security; - -namespace TGS.Server -{ - /// - /// The windows service the application runs as - /// - [ServiceBehavior(ConcurrencyMode = ConcurrencyMode.Multiple, InstanceContextMode = InstanceContextMode.Single)] - public sealed class Server : ITGSService, ITGConnectivity, ITGLanding, ITGInstanceManager, IDisposable - { - /// - /// The logging ID used for events - /// - public const byte LoggingID = 0; - - /// - /// The directory to use when importing a .NET settings based config - /// - public const string MigrationConfigDirectory = "C:\\TGSSettingUpgradeTempDir"; - - /// - /// The service version based on the 's - /// - public static readonly string VersionString = String.Format("/tg/station 13 Server v{0}", Assembly.GetExecutingAssembly().GetName().Version); - - /// - /// Singleton - /// - public static ILogger Logger { get; private set; } - - /// - /// The for the - /// - public static ServerConfig Config { get; private set; } - - /// - /// The directory to load and save s to - /// - static readonly string DefaultConfigDirectory = Directory.CreateDirectory(Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "TGS.Server")).FullName; - - /// - /// Begins user impersonation to allow proper restricted file access - /// - /// A representing the impersonation - public static WindowsImpersonationContext BeginImpersonation() - { - return WindowsIdentity.Impersonate(OperationContext.Current.ServiceSecurityContext.WindowsIdentity.Token); - } - - /// - /// Checks an for illegal characters - /// - /// The name to check - /// if contains no illegal characters, error message otherwise - static string CheckInstanceName(string instanceName) - { - char[] bannedCharacters = { ';', '&', '=', '%' }; - foreach (var I in bannedCharacters) - if (instanceName.Contains(I.ToString())) - return "Instance names may not contain the following characters: ';', '&', '=', or '%'"; - return null; - } - - /// - /// The WCF host that contains connects to - /// - ServiceHost serviceHost; - /// - /// Map of to the respective hosting the - /// - IDictionary hosts; - /// - /// List of s in use - /// - IList UsedLoggingIDs = new List(); - - /// - /// Construct a - /// - /// Command line arguments for the - /// The to use - public Server(string[] args, ILogger logger) - { - //tls1.2 meme - ServicePointManager.SecurityProtocol |= SecurityProtocolType.Tls12; - - Logger = logger; - - Environment.CurrentDirectory = Directory.CreateDirectory(Path.Combine(Path.GetTempPath(), Assembly.GetExecutingAssembly().GetName().Name)).FullName; //MOVE THIS POINTER BECAUSE ONE TIME I ALMOST ACCIDENTALLY NUKED MYSELF BY REFACTORING! http://imgur.com/zvGEpJD.png - - SetupConfig(); - - ChangePortFromCommandLine(args); - - SetupService(); - - SetupInstances(); - - OnlineAllHosts(); - } - - /// - /// Enumerates configured s. Detaches those that fail to load - /// - /// Each configured - IEnumerable GetInstanceConfigs() - { - var pathsToRemove = new List(); - lock (this) - { - var IPS = Config.InstancePaths; - foreach (var I in IPS) - { - IInstanceConfig ic; - try - { - ic = InstanceConfig.Load(I); - } - catch (Exception e) - { - Logger.WriteError(String.Format("Unable load instance config at path {0}. Error: {1} Detaching...", I, e.ToString()), EventID.InstanceInitializationFailure, LoggingID); - pathsToRemove.Add(I); - continue; - } - yield return ic; - } - foreach (var I in pathsToRemove) - IPS.Remove(I); - } - } - - /// - /// Overrides and saves the configured if requested by command line parameters - /// - /// The command line parameters for the - void ChangePortFromCommandLine(string[] args) - { - for (var I = 0; I < args.Length - 1; ++I) - if (args[I].ToLower() == "-port") - { - try - { - var res = Convert.ToUInt16(args[I + 1]); - if (res == 0) - throw new Exception("Cannot bind to port 0"); - Config.RemoteAccessPort = res; - } - catch (Exception e) - { - throw new Exception("Invalid argument for \"-port\"", e); - } - Config.Save(DefaultConfigDirectory); - break; - } - } - - /// - /// Upgrades up the service configuration - /// - void SetupConfig() - { - Directory.CreateDirectory(DefaultConfigDirectory); - try - { - Config = ServerConfig.Load(DefaultConfigDirectory); - } - catch - { - try - { - //assume we're upgrading - Config = ServerConfig.Load(MigrationConfigDirectory); - Config.Save(DefaultConfigDirectory); - Helpers.DeleteDirectory(MigrationConfigDirectory); - } - catch - { - //new baby - Config = new ServerConfig(); - } - } - } - - /// - /// Creates the for - /// - void SetupService() - { - serviceHost = CreateHost(this, Definitions.MasterInterfaceName); - AddEndpoint(serviceHost, typeof(ITGConnectivity)); - AddEndpoint(serviceHost, typeof(ITGInstanceManager)); - AddEndpoint(serviceHost, typeof(ITGLanding)); - AddEndpoint(serviceHost, typeof(ITGSService)); - serviceHost.Authorization.ServiceAuthorizationManager = new RootAuthorizationManager(); //only admins can diddle us - serviceHost.Authentication.ServiceAuthenticationManager = new AuthenticationHeaderDecoder(); - } - - /// - /// Opens all created s - /// - void OnlineAllHosts() - { - serviceHost.Open(); - foreach (var I in hosts) - I.Value.Open(); - } - - /// - /// Creates a for using the default pipe, CloseTimeout for the , and the configured - /// - /// The - /// The URL to access components on the - /// The created - static ServiceHost CreateHost(object singleton, string endpointPostfix) - { - return new ServiceHost(singleton, new Uri[] { new Uri(String.Format("net.pipe://localhost/{0}", endpointPostfix)), new Uri(String.Format("https://localhost:{0}/{1}", Config.RemoteAccessPort, endpointPostfix)) }) - { - CloseTimeout = new TimeSpan(0, 0, 5) - }; - } - - /// - /// Creates s for all s as listed in , detaches bad ones - /// - void SetupInstances() - { - hosts = new Dictionary(); - var pathsToRemove = new List(); - var seenNames = new List(); - foreach (var I in GetInstanceConfigs()) - { - if (seenNames.Contains(I.Name)) - { - Logger.WriteError(String.Format("Instance at {0} has a duplicate name! Detaching...", I.Directory), EventID.InstanceInitializationFailure, LoggingID); - pathsToRemove.Add(I.Directory); - } - if (I.Enabled && SetupInstance(I) == null) - pathsToRemove.Add(I.Directory); - else - seenNames.Add(I.Name); - } - foreach (var I in pathsToRemove) - Config.InstancePaths.Remove(I); - } - - /// - /// Unlocks a acquired with - /// - /// The to unlock - void UnlockLoggingID(byte ID) - { - lock (UsedLoggingIDs) - { - UsedLoggingIDs.Remove(ID); - } - } - - /// - /// Gets and locks a - /// - /// A logging ID for the must be released using - byte LockLoggingID() - { - lock (UsedLoggingIDs) - { - for (byte I = 1; I < 100; ++I) - if (!UsedLoggingIDs.Contains(I)) - { - UsedLoggingIDs.Add(I); - return I; - } - } - throw new Exception("All logging IDs in use!"); - } - - /// - /// Creates and starts a for a at - /// - /// The for the - /// The inactive on success, on failure - ServiceHost SetupInstance(IInstanceConfig config) - { - Instance instance; - string instanceName; - try - { - if (hosts.ContainsKey(config.Directory)) - { - var datInstance = ((Instance)hosts[config.Directory].SingletonInstance); - Logger.WriteError(String.Format("Unable to start instance at path {0}. Has the same name as instance at path {1}. Detaching...", config.Directory, datInstance.ServerDirectory()), EventID.InstanceInitializationFailure, LoggingID); - return null; - } - if (!config.Enabled) - return null; - var ID = LockLoggingID(); - Logger.WriteInfo(String.Format("Instance {0} ({1}) assigned logging ID {2}", config.Name, config.Directory, ID), EventID.InstanceIDAssigned, ID); - instanceName = config.Name; - instance = new Instance(config, ID); - } - catch (Exception e) - { - Logger.WriteError(String.Format("Unable to start instance at path {0}. Detaching... Error: {1}", config.Directory, e.ToString()), EventID.InstanceInitializationFailure, LoggingID); - return null; - } - - var host = CreateHost(instance, String.Format("{0}/{1}", Definitions.InstanceInterfaceName, instanceName)); - hosts.Add(instanceName, host); - - AddEndpoint(host, typeof(ITGConnectivity)); - AddEndpoint(host, typeof(ITGAdministration)); - AddEndpoint(host, typeof(ITGByond)); - AddEndpoint(host, typeof(ITGChat)); - AddEndpoint(host, typeof(ITGCompiler)); - AddEndpoint(host, typeof(ITGConfig)); - AddEndpoint(host, typeof(ITGDreamDaemon)); - AddEndpoint(host, typeof(ITGInstance)); - AddEndpoint(host, typeof(ITGInterop)); - AddEndpoint(host, typeof(ITGRepository)); - - host.Authorization.ServiceAuthorizationManager = instance; - host.Authentication.ServiceAuthenticationManager = new AuthenticationHeaderDecoder(); - return host; - } - - /// - /// Adds a WCF endpoint for a component - /// - /// The service host to add the component to - /// The type of the component - void AddEndpoint(ServiceHost host, Type typetype) - { - var bindingName = typetype.Name; - host.AddServiceEndpoint(typetype, new NetNamedPipeBinding() { SendTimeout = new TimeSpan(0, 0, 30), MaxReceivedMessageSize = Definitions.TransferLimitLocal }, bindingName); - var httpsBinding = new BasicHttpsBinding() - { - SendTimeout = new TimeSpan(0, 0, 40), - MaxReceivedMessageSize = Definitions.TransferLimitRemote - }; - var requireAuth = typetype.Name != typeof(ITGConnectivity).Name; - host.AddServiceEndpoint(typetype, httpsBinding, bindingName); - } - - /// - /// Shuts down all active s and calls on it's - /// - void OnStop() - { - lock (this) - { - try - { - foreach (var I in hosts) - { - var host = I.Value; - var instance = (Instance)host.SingletonInstance; - host.Close(); - instance.Dispose(); - UnlockLoggingID(instance.LoggingID); - } - } - catch (Exception e) - { - Logger.WriteError(e.ToString(), EventID.ServiceShutdownFail, LoggingID); - } - serviceHost.Close(); - } - Config.Save(DefaultConfigDirectory); - } - - /// - public void VerifyConnection() { } - - /// - public void PrepareForUpdate() - { - foreach (var I in hosts) - ((Instance)I.Value.SingletonInstance).Reattach(false); - } - - /// - public ushort RemoteAccessPort() - { - return Config.RemoteAccessPort; - } - - /// - public string SetRemoteAccessPort(ushort port) - { - if (port == 0) - return "Cannot bind to port 0"; - Config.RemoteAccessPort = port; - return null; - } - - /// - public string Version() - { - return VersionString; - } - - /// - public bool SetPythonPath(string path) - { - if (!Directory.Exists(path)) - return false; - Config.PythonPath = Path.GetFullPath(path); - return true; - } - - /// - public string PythonPath() - { - return Config.PythonPath; - } - - /// - public IList ListInstances() - { - var result = new List(); - lock (this) - foreach (var ic in GetInstanceConfigs()) - result.Add(new InstanceMetadata - { - Name = ic.Name, - Path = ic.Directory, - Enabled = ic.Enabled, - LoggingID = (byte)(ic.Enabled ? ((Instance)hosts[ic.Name].SingletonInstance).LoggingID : 0) - }); - return result; - } - - /// - public string CreateInstance(string Name, string path) - { - path = Helpers.NormalizePath(path); - var res = CheckInstanceName(Name); - if (res != null) - return res; - if (File.Exists(path) || Directory.Exists(path)) - return "Cannot create instance at pre-existing path!"; - lock (this) - { - if (Config.InstancePaths.Contains(path)) - return String.Format("Instance at {0} already exists!", path); - foreach (var oic in GetInstanceConfigs()) - if (Name == oic.Name) - return String.Format("Instance named {0} already exists!", oic.Name); - IInstanceConfig ic; - try - { - ic = new InstanceConfig(path) - { - Name = Name - }; - Directory.CreateDirectory(path); - ic.Save(); - Config.InstancePaths.Add(path); - } - catch (Exception e) - { - return e.ToString(); - } - return SetupOneInstance(ic); - } - } - - /// - /// Starts and onlines an instance located at - /// - /// The for the - /// on success, error message on failure - string SetupOneInstance(IInstanceConfig config) - { - if (!config.Enabled) - return null; - try - { - var host = SetupInstance(config); - if (host != null) - host.Open(); - else - lock (this) - Config.InstancePaths.Remove(config.Directory); - return null; - } - catch (Exception e) - { - return "Instance set up but an error occurred while starting it: " + e.ToString(); - } - } - - /// - public string ImportInstance(string path) - { - path = Helpers.NormalizePath(path); - lock (this) - { - if (Config.InstancePaths.Contains(path)) - return String.Format("Instance at {0} already exists!", path); - if(!Directory.Exists(path)) - return String.Format("There is no instance located at {0}!", path); - IInstanceConfig ic; - try - { - ic = InstanceConfig.Load(path); - foreach(var oic in GetInstanceConfigs()) - if(ic.Name == oic.Name) - return String.Format("Instance named {0} already exists!", oic.Name); - ic.Save(); - Config.InstancePaths.Add(path); - } - catch (Exception e) - { - return e.ToString(); - } - return SetupOneInstance(ic); - } - } - - /// - public bool InstanceEnabled(string Name) - { - lock(this) - { - return hosts.ContainsKey(Name); - } - } - - /// - public string SetInstanceEnabled(string Name, bool enabled) - { - return SetInstanceEnabledImpl(Name, enabled, out string path); - } - - - /// - /// Sets a 's enabled status - /// - /// The whom's status should be changed - /// to enable the , to disable it - /// The path to the modified - /// on success, error message on failure - string SetInstanceEnabledImpl(string Name, bool enabled, out string path) - { - path = null; - lock (this) - { - var hostIsOnline = hosts.ContainsKey(Name); - if (enabled) - { - if (hostIsOnline) - return null; - foreach (var ic in GetInstanceConfigs()) - if (ic.Name == Name) - { - path = ic.Directory; - ic.Enabled = true; - ic.Save(); - return SetupOneInstance(ic); - } - return String.Format("Instance {0} does not exist!", Name); - } - else - { - if (!hostIsOnline) - return null; - var host = hosts[Name]; - hosts.Remove(Name); - var inst = (Instance)host.SingletonInstance; - host.Close(); - path = inst.ServerDirectory(); - inst.Offline(); - inst.Dispose(); - UnlockLoggingID(inst.LoggingID); - return null; - } - } - } - - /// - public string RenameInstance(string name, string new_name) - { - if (name == new_name) - return null; - var res = CheckInstanceName(new_name); - if (res != null) - return res; - lock (this) - { - //we have to check em all anyway - IInstanceConfig the_droid_were_looking_for = null; - foreach (var ic in GetInstanceConfigs()) - if (ic.Name == name) - { - the_droid_were_looking_for = ic; - break; - } - else if (ic.Name == new_name) - return String.Format("There is already another instance named {0}!", new_name); - if (the_droid_were_looking_for == null) - return String.Format("There is no instance named {0}!", name); - var ie = InstanceEnabled(name); - if(ie) - SetInstanceEnabled(name, false); - the_droid_were_looking_for.Name = new_name; - string result = ""; - try - { - the_droid_were_looking_for.Save(); - result = null; - } - catch(Exception e) - { - result = "Could not save instance config! Error: " + e.ToString(); - } - finally - { - if (ie) - { - var resRestore = SetInstanceEnabled(new_name, true); - if (resRestore != null) - result = (result + " " + resRestore).Trim(); - } - } - return result; - } - } - - /// - public string DetachInstance(string name) - { - lock (this) - { - var res = SetInstanceEnabledImpl(name, false, out string path); - if (res != null) - return res; - if (path == null) //gotta find it ourselves - foreach (var ic in GetInstanceConfigs()) - if (ic.Name == name) - { - path = ic.Directory; - break; - } - if (path == null) - return String.Format("No instance named {0} exists!", name); - Config.InstancePaths.Remove(path); - return null; - } - } - - #region IDisposable Support - /// - /// To detect redundant calls - /// - private bool disposedValue = false; - - /// - /// Implements the pattern. Calls - /// - /// if was called manually, if it was from the finalizer - void Dispose(bool disposing) - { - if (!disposedValue) - { - if (disposing) - { - OnStop(); - } - - // TODO: free unmanaged resources (unmanaged objects) and override a finalizer below. - // TODO: set large fields to null. - - disposedValue = true; - } - } - - // TODO: override a finalizer only if Dispose(bool disposing) above has code to free unmanaged resources. - // ~Server() { - // // Do not change this code. Put cleanup code in Dispose(bool disposing) above. - // Dispose(false); - // } - - /// - /// Implements the pattern - /// - public void Dispose() - { - // Do not change this code. Put cleanup code in Dispose(bool disposing) above. - Dispose(true); - // TODO: uncomment the following line if the finalizer is overridden above. - // GC.SuppressFinalize(this); - } - #endregion - } -} diff --git a/TGS.Server/ServerConfig.cs b/TGS.Server/ServerConfig.cs deleted file mode 100644 index cd1ea22fa9..0000000000 --- a/TGS.Server/ServerConfig.cs +++ /dev/null @@ -1,65 +0,0 @@ -using Newtonsoft.Json; -using System.Collections.Generic; -using System.IO; - -namespace TGS.Server -{ - /// - /// Class for storing server wide settings - /// - public sealed class ServerConfig - { - /// - /// The filename to save the as - /// - [JsonIgnore] - const string JSONFilename = "ServerConfig.json"; - /// - /// The most recent version of the config file - /// - [JsonIgnore] - const ulong CurrentVersion = 8; - - /// - /// The version of the - /// - public ulong Version { get; private set; } = CurrentVersion; - - /// - /// List of paths that contain s - /// - public List InstancePaths { get; private set; } = new List(); - - /// - /// Port used to access the remotely - /// - public ushort RemoteAccessPort { get; set; } = 38607; - - /// - /// Path to the directory containing the Python2.7 installation - /// - public string PythonPath { get; set; } = "C:\\Python27"; - - /// - /// Saves the to a target - /// - /// The directory in which to save the - public void Save(string directory) - { - var data = JsonConvert.SerializeObject(this, Formatting.Indented); - var path = Path.Combine(directory, JSONFilename); - File.WriteAllText(path, data); - } - - /// - /// Load a from a given - /// - /// The directory containing the - /// The loaded - public static ServerConfig Load(string directory) - { - var configtext = File.ReadAllText(Path.Combine(directory, JSONFilename)); - return JsonConvert.DeserializeObject(configtext); - } - } -} diff --git a/TGS.Server/TGS.Server.csproj b/TGS.Server/TGS.Server.csproj deleted file mode 100644 index 83f970c8c1..0000000000 --- a/TGS.Server/TGS.Server.csproj +++ /dev/null @@ -1,147 +0,0 @@ - - - - - - Debug - AnyCPU - {F32EDA25-0855-411C-AF5E-F0D042917E2D} - Library - TGS.Server - TGS.Server - v4.5.2 - 512 - true - - - - - - tgs.ico - - - true - bin\Debug\ - DEBUG;TRACE - full - AnyCPU - prompt - MinimumRecommendedRules.ruleset - - - bin\Release\ - TRACE - bin\x86\Release\TGS.Server.xml - true - true - pdbonly - AnyCPU - prompt - MinimumRecommendedRules.ruleset - - - false - - - - - - - ..\packages\Discord.Net.Core.1.0.2\lib\net45\Discord.Net.Core.dll - - - ..\packages\Discord.Net.Rest.1.0.2\lib\net45\Discord.Net.Rest.dll - - - ..\packages\Discord.Net.WebSocket.1.0.2\lib\net45\Discord.Net.WebSocket.dll - - - ..\packages\LibGit2Sharp-SSH.1.0.22\lib\net40\LibGit2Sharp.dll - - - ..\packages\SmartIrc4net.1.1\lib\Meebey.SmartIrc4net.dll - - - ..\packages\Newtonsoft.Json.10.0.2\lib\net45\Newtonsoft.Json.dll - - - - ..\packages\System.Collections.Immutable.1.3.1\lib\portable-net45+win8+wp8+wpa81\System.Collections.Immutable.dll - - - - - - - ..\packages\System.Interactive.Async.3.1.1\lib\net45\System.Interactive.Async.dll - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Designer - - - Designer - - - - - {ac4e7e8b-f83a-481c-a8b0-8fa4e8ae59ab} - TGS.Interface - - - - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - \ No newline at end of file diff --git a/TGS.Server/packages.config b/TGS.Server/packages.config deleted file mode 100644 index 9d3af3538f..0000000000 --- a/TGS.Server/packages.config +++ /dev/null @@ -1,13 +0,0 @@ - - - - - - - - - - - - - \ No newline at end of file diff --git a/TGS.Server/tgs.ico b/TGS.Server/tgs.ico deleted file mode 100644 index 6ed69fadfc..0000000000 Binary files a/TGS.Server/tgs.ico and /dev/null differ diff --git a/TGS.Tests/CommandLine/Commands/Repository/RepositoryCommandTest.cs b/TGS.Tests/CommandLine/Commands/Repository/RepositoryCommandTest.cs deleted file mode 100644 index 4735b8ccd5..0000000000 --- a/TGS.Tests/CommandLine/Commands/Repository/RepositoryCommandTest.cs +++ /dev/null @@ -1,22 +0,0 @@ -using Moq; -using TGS.Interface; -using TGS.Interface.Components; -using TGServiceTests; - -namespace TGS.CommandLine.Commands.Repository.Tests -{ - public abstract class RepositoryCommandTest : OutputProcOverriderTest - { - /// - /// Creates a mock that returns a specific when that component is requested - /// - /// The the resulting should return - /// A mock - protected IInstance MockInterfaceToRepo(ITGRepository repo) - { - var mock = new Mock(); - mock.Setup(foo => foo.Repository).Returns(repo); - return mock.Object; - } - } -} diff --git a/TGS.Tests/CommandLine/Commands/Repository/TestRepoSetPushTestmergeCommitsCommand.cs b/TGS.Tests/CommandLine/Commands/Repository/TestRepoSetPushTestmergeCommitsCommand.cs deleted file mode 100644 index a5f2b72ede..0000000000 --- a/TGS.Tests/CommandLine/Commands/Repository/TestRepoSetPushTestmergeCommitsCommand.cs +++ /dev/null @@ -1,53 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using System.Collections.Generic; -using TGS.Interface; -using TGS.Interface.Components; - -namespace TGS.CommandLine.Commands.Repository.Tests -{ - /// - /// Tests for - /// - [TestClass] - public class TestRepoSetPushTestmergeCommitsCommand : RepositoryCommandTest - { - /// - /// Ensure it can be turned on - /// - [TestMethod] - public void TestTurnOn() - { - var ran = false; - var repo = new Mock(); - repo.Setup(foo => foo.SetPushTestmergeCommits(true)).Callback(() => { ran = true; }); - ConsoleCommand.Instance = MockInterfaceToRepo(repo.Object); - Assert.AreEqual(new RepoSetPushTestmergeCommitsCommand().DoRun(new List { "on" }), Command.ExitCode.Normal); - Assert.IsTrue(ran); - } - - /// - /// Ensure that it can be turned off - /// - [TestMethod] - public void TestTurnOff() - { - - var ran = false; - var repo = new Mock(); - repo.Setup(foo => foo.SetPushTestmergeCommits(false)).Callback(() => { ran = true; }); - ConsoleCommand.Instance = MockInterfaceToRepo(repo.Object); - Assert.AreEqual(new RepoSetPushTestmergeCommitsCommand().DoRun(new List { "off" }), Command.ExitCode.Normal); - Assert.IsTrue(ran); - } - - /// - /// Ensure that is returned for gibberish parameters - /// - [TestMethod] - public void TestGibberishParam() - { - Assert.AreEqual(new RepoSetPushTestmergeCommitsCommand().DoRun(new List { "gibberish" }), Command.ExitCode.BadCommand); - } - } -} diff --git a/TGS.Tests/CommandLine/Commands/Repository/TestRepoStatusCommand.cs b/TGS.Tests/CommandLine/Commands/Repository/TestRepoStatusCommand.cs deleted file mode 100644 index f1aea5ce1d..0000000000 --- a/TGS.Tests/CommandLine/Commands/Repository/TestRepoStatusCommand.cs +++ /dev/null @@ -1,46 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using System.Collections.Generic; -using TGS.Interface; -using TGS.Interface.Components; - -namespace TGS.CommandLine.Commands.Repository.Tests -{ - /// - /// Tests for - /// - [TestClass] - public class TestRepoStatusCommand : RepositoryCommandTest - { - /// - /// Set the default returns for the mock to be - /// - /// A of with default return values - Mock GetDefaultMock() - { - var mock = new Mock(); - mock.SetReturnsDefault(null); - return mock; - } - - /// - /// Tests the results of - /// - [TestMethod] - public void TestPushTestmergeCommits() - { - var ran = false; - var mock = GetDefaultMock(); - mock.Setup(foo => foo.PushTestmergeCommits()).Returns(false).Callback(() => ran = true); - ConsoleCommand.Instance = MockInterfaceToRepo(mock.Object); - Assert.AreEqual(new RepoStatusCommand().DoRun(new List { }), Command.ExitCode.Normal); - Assert.IsTrue(ran); - - ran = false; - mock.Setup(foo => foo.PushTestmergeCommits()).Returns(true).Callback(() => ran = true); - ConsoleCommand.Instance = MockInterfaceToRepo(mock.Object); - Assert.AreEqual(new RepoStatusCommand().DoRun(new List { }), Command.ExitCode.Normal); - Assert.IsTrue(ran); - } - } -} diff --git a/TGS.Tests/ControlPanel/TestInstanceSelector.cs b/TGS.Tests/ControlPanel/TestInstanceSelector.cs deleted file mode 100644 index 2d65a3b1db..0000000000 --- a/TGS.Tests/ControlPanel/TestInstanceSelector.cs +++ /dev/null @@ -1,28 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using Moq; -using System.Collections.Generic; -using TGS.Interface; -using TGS.Interface.Components; - -namespace TGS.ControlPanel.Tests -{ - /// - /// Tests for - /// - [TestClass] - public class TestInstanceSelector - { - /// - /// Test that an can be created without issue - /// - [TestMethod] - public void TestInstatiation() - { - var mockLanding = new Mock(); - mockLanding.Setup(x => x.ListInstances()).Returns(new List()); - var mockInter = new Mock(); - - new InstanceSelector(mockInter.Object).Dispose(); - } - } -} diff --git a/TGS.Tests/OutputProcOverriderTest.cs b/TGS.Tests/OutputProcOverriderTest.cs deleted file mode 100644 index f73a473e0a..0000000000 --- a/TGS.Tests/OutputProcOverriderTest.cs +++ /dev/null @@ -1,26 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using TGS.Interface; - -namespace TGServiceTests -{ - /// - /// For tests that need to override - /// - public abstract class OutputProcOverriderTest - { - /// - /// Set to - /// - [TestInitialize] - public void Setup() - { - Command.OutputProcVar.Value = OutputProc; - } - - /// - /// Whe - /// - /// - protected virtual void OutputProc(string message) { } - } -} diff --git a/TGS.Tests/Properties/AssemblyInfo.cs b/TGS.Tests/Properties/AssemblyInfo.cs deleted file mode 100644 index 4bde2505b7..0000000000 --- a/TGS.Tests/Properties/AssemblyInfo.cs +++ /dev/null @@ -1,9 +0,0 @@ -using System.Reflection; -using System.Runtime.InteropServices; - -[assembly: AssemblyTitle("TGStation Server Test Suite")] -[assembly: AssemblyDescription("Unit tests for the TGStation Server suite")] - -[assembly: ComVisible(false)] - -[assembly: Guid("fb693ffb-17e3-4e84-8cbf-6ffa9c8fd971")] diff --git a/TGS.Tests/Server/TestHelpers.cs b/TGS.Tests/Server/TestHelpers.cs deleted file mode 100644 index b9cf624171..0000000000 --- a/TGS.Tests/Server/TestHelpers.cs +++ /dev/null @@ -1,35 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System; -using System.IO; -using System.Linq; -using TGServiceTests; - -namespace TGS.Server.Tests -{ - /// - /// Tests for - /// - [TestClass] - public sealed class TestHelpers : TempDirectoryRequiredTest - { - [TestMethod] - public void TestGetFilesWithExtensionInDirectory() - { - Assert.ThrowsException(() => Helpers.GetFilesWithExtensionInDirectory(null, null).ToList()); - Assert.ThrowsException(() => Helpers.GetFilesWithExtensionInDirectory("", null).ToList()); - Assert.ThrowsException(() => Helpers.GetFilesWithExtensionInDirectory(null, "").ToList()); - - Assert.AreEqual(0, Helpers.GetFilesWithExtensionInDirectory("Z:/", "dm").Count()); - - Assert.AreEqual(0, Helpers.GetFilesWithExtensionInDirectory(TempPath, "dm").Count()); - - File.WriteAllText(Path.Combine(TempPath, "somefile.txt"), "asdf"); - - Assert.AreEqual(0, Helpers.GetFilesWithExtensionInDirectory(TempPath, "dm").Count()); - - File.WriteAllText(Path.Combine(TempPath, "somefile.dm"), "asdf"); - - Assert.AreEqual(1, Helpers.GetFilesWithExtensionInDirectory(TempPath, "dm").Count()); - } - } -} diff --git a/TGS.Tests/ServiceInterface/TestHelpers.cs b/TGS.Tests/ServiceInterface/TestHelpers.cs deleted file mode 100644 index 2742f1e854..0000000000 --- a/TGS.Tests/ServiceInterface/TestHelpers.cs +++ /dev/null @@ -1,53 +0,0 @@ -using System; -using Microsoft.VisualStudio.TestTools.UnitTesting; - -namespace TGS.Interface.Tests -{ - /// - /// Tests for - /// - [TestClass] - public class TestHelpers - { - /// - /// Sample cleartext - /// - const string PlainText = "According to all known laws of aviation, there is no way a bee should be able to fly. Its wings are too small to get its fat little body off the ground. The bee, of course, flies anyway because bees don't care what humans think is impossible."; - - /// - /// Run assertions for a successful call to - /// - /// The out string for the entropy parameter of - /// The result of with as a parameter - string AssertEncryptData(out string entropy) - { - var result = Helpers.EncryptData(PlainText, out entropy); - Assert.AreNotEqual(PlainText, entropy); - Assert.AreNotEqual(PlainText, result); - Assert.AreNotEqual(result, entropy); - Assert.IsFalse(String.IsNullOrWhiteSpace(result)); - Assert.IsFalse(String.IsNullOrWhiteSpace(entropy)); - return result; - } - - /// - /// Tests that can execute successfully - /// - [TestMethod] - public void TestEncryptDataWorks() - { - AssertEncryptData(out string entropy); - } - - /// - /// Tests that can execute successfully - /// - [TestMethod] - public void TestDecryptDataWorks() - { - var result = AssertEncryptData(out string entropy); - var decrypted = Helpers.DecryptData(result, entropy); - Assert.AreEqual(decrypted, PlainText); - } - } -} diff --git a/TGS.Tests/ServiceInterface/TestInterface.cs b/TGS.Tests/ServiceInterface/TestInterface.cs deleted file mode 100644 index 1b51fa2b50..0000000000 --- a/TGS.Tests/ServiceInterface/TestInterface.cs +++ /dev/null @@ -1,97 +0,0 @@ -using System; -using System.Net; -using System.ServiceModel; -using Microsoft.VisualStudio.TestTools.UnitTesting; -using TGS.Interface.Components; - -namespace TGS.Interface.Tests -{ - /// - /// Tests for - /// - [TestClass] - public class TestInterface - { - /// - /// Name to use for testing s - /// - const string TestInstanceName = "TestInstance"; - /// - /// Test that can execute successfully - /// - [TestMethod] - public void TestSetBadCertificateHandler() - { - Func func = (message) => - { - Assert.IsFalse(String.IsNullOrWhiteSpace(message)); - return true; - }; - Client.SetBadCertificateHandler(func); - } - - /// - /// Test that properly sets - /// - [TestMethod] - public void TestBadCertificateHandler() - { - var ran = false; - Client.SetBadCertificateHandler(_ => - { - ran = true; - return true; - }); - ServicePointManager.ServerCertificateValidationCallback(this, new System.Security.Cryptography.X509Certificates.X509Certificate(), new System.Security.Cryptography.X509Certificates.X509Chain(), System.Net.Security.SslPolicyErrors.RemoteCertificateChainErrors); - Assert.IsTrue(ran); - } - - /// - /// Creates a remote configured pointing at an invalid address - /// - /// The created - Client CreateFakeRemoteInterface() - { - return new Client(new RemoteLoginInfo("some.fake.url.420", 34752, "user", "password")); - } - - /// - /// Test that can execute successfully and creates a local connection - /// - [TestMethod] - public void TestLocalInstantiation() - { - Assert.IsFalse(new Client().IsRemoteConnection); - } - - /// - /// Test that can execute successfully - /// - [TestMethod] - public void TestRemoteInstatiation() - { - Assert.IsTrue(CreateFakeRemoteInterface().IsRemoteConnection); - } - - [TestMethod] - public void TestCopyRemoteInterface() - { - var first = CreateFakeRemoteInterface(); - var second = new Client(first.LoginInfo); - Assert.AreEqual(first.LoginInfo.IP, second.LoginInfo.IP); - Assert.AreEqual(first.LoginInfo.Port, second.LoginInfo.Port); - Assert.AreEqual(first.LoginInfo.Username, second.LoginInfo.Username); - Assert.AreEqual(first.LoginInfo.Password, second.LoginInfo.Password); - Assert.IsTrue(second.IsRemoteConnection); - } - - [TestMethod] - public void TestLocalAccessInterfaceAllowsWindowsImpersonation() - { - var inter = new Client(); - var po = new PrivateObject(inter); - var cf = (ChannelFactory)po.Invoke("CreateChannel", new Type[] { typeof(string) }, new object[] { TestInstanceName }, new Type[] { typeof(ITGConfig) }); - Assert.AreEqual(cf.Credentials.Windows.AllowedImpersonationLevel, System.Security.Principal.TokenImpersonationLevel.Impersonation); - } - } -} diff --git a/TGS.Tests/TGS.Tests.csproj b/TGS.Tests/TGS.Tests.csproj deleted file mode 100644 index c0cc4f3a84..0000000000 --- a/TGS.Tests/TGS.Tests.csproj +++ /dev/null @@ -1,111 +0,0 @@ - - - - - Debug - AnyCPU - {FB693FFB-17E3-4E84-8CBF-6FFA9C8FD971} - Library - Properties - TGS.Tests - TGS.Tests - v4.6.1 - 512 - {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} - 15.0 - $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) - $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages - False - UnitTest - - - - - true - full - false - bin\Debug\ - DEBUG;TRACE - prompt - 4 - - - pdbonly - true - bin\Release\ - TRACE - prompt - 4 - - - false - - - - ..\packages\Castle.Core.4.2.1\lib\net45\Castle.Core.dll - - - ..\packages\MSTest.TestFramework.1.1.18\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll - - - ..\packages\MSTest.TestFramework.1.1.18\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll - - - ..\packages\Moq.4.7.145\lib\net45\Moq.dll - - - - - - - - - - - - - - - - - - - - - - - - - - - {89191f69-b18e-4b59-b72e-e12f9b6811a0} - TGS.CommandLine - - - {394e7643-6b8c-416f-ab18-95ac12648cdc} - TGS.ControlPanel - - - {8956d4c3-bfb9-448e-bf5f-ee7e6f9996f9} - TGS.Installer.UI - - - {f32eda25-0855-411c-af5e-f0d042917e2d} - TGS.Server - - - {ac4e7e8b-f83a-481c-a8b0-8fa4e8ae59ab} - TGS.Interface - - - - - - - This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. - - - - - - \ No newline at end of file diff --git a/TGS.Tests/TempDirectoryRequiredTest.cs b/TGS.Tests/TempDirectoryRequiredTest.cs deleted file mode 100644 index b18858ccff..0000000000 --- a/TGS.Tests/TempDirectoryRequiredTest.cs +++ /dev/null @@ -1,42 +0,0 @@ -using Microsoft.VisualStudio.TestTools.UnitTesting; -using System.IO; - -namespace TGServiceTests -{ - /// - /// To be the parent of test classes that required a temporary directory - /// - [TestClass] - public abstract class TempDirectoryRequiredTest - { - /// - /// The path to the temporary directory - /// - protected string TempPath; - - /// - /// Construct a - /// - protected TempDirectoryRequiredTest() { } - - /// - /// Setup - /// - [TestInitialize] - public void Setup() - { - TempPath = Path.GetTempFileName(); - File.Delete(TempPath); - Directory.CreateDirectory(TempPath); - } - - /// - /// Cleanup - /// - [TestCleanup] - public void Cleanup() - { - Directory.Delete(TempPath, true); - } - } -} diff --git a/TGS.Tests/packages.config b/TGS.Tests/packages.config deleted file mode 100644 index 968620fd09..0000000000 --- a/TGS.Tests/packages.config +++ /dev/null @@ -1,7 +0,0 @@ - - - - - - - \ No newline at end of file diff --git a/TGS3.json b/TGS3.json deleted file mode 100644 index a6e21bf294..0000000000 --- a/TGS3.json +++ /dev/null @@ -1,58 +0,0 @@ -{ - "documentation": [ - "Place this file in the root of your repo in order to configure TGS3 per repository options", - "This file is not required, it merely configures changelog generation, static directories, and .dlls", - "All keys except the ones below are ignored by the service" - ], - "changelog": { - "script": "path/to/changelog/generator.py", - "arguments": "space delimited \"script arguments\"", - "pip_dependancies": [ - "optional", - "list", - "of", - "pip", - "install", - "packages" - ] - }, - "synchronize_paths": [ - "list", - "of", - "regex", - "paths", - "to", - "stage", - "for", - "git", - "commit" - ], - "static_directories": [ - "list", - "of", - "directories", - "that", - "should", - "not", - "be", - "changed", - "with", - "repo", - "updates", - "et al" - ], - "dlls": [ - "list", - "of", - ".dlls", - "that", - "your", - "game", - "uses", - "these", - "will", - "be", - "handled", - "properly" - ] -} \ No newline at end of file diff --git a/TGStationServer3.sln b/TGStationServer3.sln deleted file mode 100644 index c31cc7e5ac..0000000000 --- a/TGStationServer3.sln +++ /dev/null @@ -1,207 +0,0 @@ - -Microsoft Visual Studio Solution File, Format Version 12.00 -# Visual Studio 15 -VisualStudioVersion = 15.0.26730.16 -MinimumVisualStudioVersion = 10.0.40219.1 -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TGS.Server", "TGS.Server\TGS.Server.csproj", "{F32EDA25-0855-411C-AF5E-F0D042917E2D}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TGS.ControlPanel", "TGS.ControlPanel\TGS.ControlPanel.csproj", "{394E7643-6B8C-416F-AB18-95AC12648CDC}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TGS.CommandLine", "TGS.CommandLine\TGS.CommandLine.csproj", "{89191F69-B18E-4B59-B72E-E12F9B6811A0}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TGS.Interface", "TGS.Interface\TGS.Interface.csproj", "{AC4E7E8B-F83A-481C-A8B0-8FA4E8AE59AB}" -EndProject -Project("{930C7802-8A8C-48F9-8165-68863BCCD9DD}") = "TGS.Installer", "TGS.Installer\TGS.Installer.wixproj", "{154435F6-0890-42D4-9AEC-B743D4FBC1CB}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{2D8FC6AA-1D33-44B6-81C2-35ED7A87EDBC}" - ProjectSection(SolutionItems) = preProject - .codecov.yml = .codecov.yml - .gitignore = .gitignore - .travis.yml = .travis.yml - appveyor.yml = appveyor.yml - AssemblyInfo.global.cs = AssemblyInfo.global.cs - README.md = README.md - tgs.ico = tgs.ico - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TGS.Installer.UI", "TGS.Installer.UI\TGS.Installer.UI.csproj", "{8956D4C3-BFB9-448E-BF5F-EE7E6F9996F9}" - ProjectSection(ProjectDependencies) = postProject - {AC4E7E8B-F83A-481C-A8B0-8FA4E8AE59AB} = {AC4E7E8B-F83A-481C-A8B0-8FA4E8AE59AB} - {154435F6-0890-42D4-9AEC-B743D4FBC1CB} = {154435F6-0890-42D4-9AEC-B743D4FBC1CB} - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "legacy", "legacy", "{0DDF886A-7ABB-4DCA-96A3-349B2DA00016}" - ProjectSection(SolutionItems) = preProject - legacy\config.bat = legacy\config.bat - legacy\copyexclude.txt = legacy\copyexclude.txt - legacy\disable crash dialog.reg = legacy\disable crash dialog.reg - legacy\Fix Errors.bat = legacy\Fix Errors.bat - legacy\install.bat = legacy\install.bat - legacy\Merge pr without updating.bat = legacy\Merge pr without updating.bat - legacy\OnServerBoot.bat = legacy\OnServerBoot.bat - legacy\README.md = legacy\README.md - legacy\Recompile.bat = legacy\Recompile.bat - legacy\Reset and Recompile.bat = legacy\Reset and Recompile.bat - legacy\Start Bot.bat = legacy\Start Bot.bat - legacy\Start Server.bat = legacy\Start Server.bat - legacy\Update Server.bat = legacy\Update Server.bat - legacy\Update to PR.bat = legacy\Update to PR.bat - legacy\Update without resetting.bat = legacy\Update without resetting.bat - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "bin", "bin", "{3BB3DA66-970C-4AAE-A77C-9E40F5E7E56A}" - ProjectSection(SolutionItems) = preProject - legacy\bin\activepr.bat = legacy\bin\activepr.bat - legacy\bin\build.bat = legacy\bin\build.bat - legacy\bin\copyfromgit.bat = legacy\bin\copyfromgit.bat - legacy\bin\findab.bat = legacy\bin\findab.bat - legacy\bin\findbyond.bat = legacy\bin\findbyond.bat - legacy\bin\findgit.bat = legacy\bin\findgit.bat - legacy\bin\getcurdate.bat = legacy\bin\getcurdate.bat - legacy\bin\getunixtime.bat = legacy\bin\getunixtime.bat - legacy\bin\unixtime.vbs = legacy\bin\unixtime.vbs - legacy\bin\updategit.bat = legacy\bin\updategit.bat - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DMAPI", "DMAPI", "{9032B448-5E2B-4E71-8ECB-D5FB828E049C}" - ProjectSection(SolutionItems) = preProject - DMAPI\tgs.dm = DMAPI\tgs.dm - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Tools", "Tools", "{081BB0BB-2E84-47D4-9419-A2AB8E814ABF}" - ProjectSection(SolutionItems) = preProject - Tools\build_byond.sh = Tools\build_byond.sh - Tools\BuildDox.ps1 = Tools\BuildDox.ps1 - Tools\Config.dm = Tools\Config.dm - Tools\CoverageExclusions.runsettings = Tools\CoverageExclusions.runsettings - Tools\Doxyfile = Tools\Doxyfile - Tools\install_byond.sh = Tools\install_byond.sh - Tools\PostCIBuild.ps1 = Tools\PostCIBuild.ps1 - Tools\SignBasics.ps1 = Tools\SignBasics.ps1 - Tools\SignMSI.ps1 = Tools\SignMSI.ps1 - Tools\Test.dm = Tools\Test.dm - Tools\tgstation13.org.pfx = Tools\tgstation13.org.pfx - Tools\travistester.dme = Tools\travistester.dme - Tools\UploadCoverage.ps1 = Tools\UploadCoverage.ps1 - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = ".github", ".github", "{287B900C-1AFB-49B8-8BDD-C9058971C20C}" - ProjectSection(SolutionItems) = preProject - .github\CONTRIBUTING.md = .github\CONTRIBUTING.md - EndProjectSection -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TGS.Interface.Bridge", "TGS.Interface.Bridge\TGS.Interface.Bridge.csproj", "{9A01EF03-8EAE-45CB-8B87-4A17BD904557}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TGS.Tests", "TGS.Tests\TGS.Tests.csproj", "{FB693FFB-17E3-4E84-8CBF-6FFA9C8FD971}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TGS.Server.Service", "TGS.Server.Service\TGS.Server.Service.csproj", "{3F81E398-B223-4006-B40C-C2800714CE29}" -EndProject -Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "TGS.Server.Console", "TGS.Server.Console\TGS.Server.Console.csproj", "{509433F6-AEFB-44CA-BFE3-C782166D2CC3}" -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tgs", "tgs", "{E7103B1A-8097-4BD4-B642-E7A2FAB1B0F3}" - ProjectSection(SolutionItems) = preProject - DMAPI\tgs\includes.dm = DMAPI\tgs\includes.dm - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "core", "core", "{D30454E2-ABAF-4698-B5EC-D2E7C9641F43}" - ProjectSection(SolutionItems) = preProject - DMAPI\tgs\core\_definitions.dm = DMAPI\tgs\core\_definitions.dm - DMAPI\tgs\core\core.dm = DMAPI\tgs\core\core.dm - DMAPI\tgs\core\datum.dm = DMAPI\tgs\core\datum.dm - DMAPI\tgs\core\default_event_handler.dm = DMAPI\tgs\core\default_event_handler.dm - EndProjectSection -EndProject -Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "v3210", "v3210", "{FFECCA21-8B4A-4A70-B524-33DDDCA04850}" - ProjectSection(SolutionItems) = preProject - DMAPI\tgs\v3210\api.dm = DMAPI\tgs\v3210\api.dm - DMAPI\tgs\v3210\commands.dm = DMAPI\tgs\v3210\commands.dm - EndProjectSection -EndProject -Global - GlobalSection(SolutionConfigurationPlatforms) = preSolution - Debug|Any CPU = Debug|Any CPU - Debug-Client|Any CPU = Debug-Client|Any CPU - Release|Any CPU = Release|Any CPU - Release-Client|Any CPU = Release-Client|Any CPU - EndGlobalSection - GlobalSection(ProjectConfigurationPlatforms) = postSolution - {F32EDA25-0855-411C-AF5E-F0D042917E2D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {F32EDA25-0855-411C-AF5E-F0D042917E2D}.Debug|Any CPU.Build.0 = Debug|Any CPU - {F32EDA25-0855-411C-AF5E-F0D042917E2D}.Debug-Client|Any CPU.ActiveCfg = Debug|Any CPU - {F32EDA25-0855-411C-AF5E-F0D042917E2D}.Release|Any CPU.ActiveCfg = Release|Any CPU - {F32EDA25-0855-411C-AF5E-F0D042917E2D}.Release|Any CPU.Build.0 = Release|Any CPU - {F32EDA25-0855-411C-AF5E-F0D042917E2D}.Release-Client|Any CPU.ActiveCfg = Release|Any CPU - {394E7643-6B8C-416F-AB18-95AC12648CDC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {394E7643-6B8C-416F-AB18-95AC12648CDC}.Debug|Any CPU.Build.0 = Debug|Any CPU - {394E7643-6B8C-416F-AB18-95AC12648CDC}.Debug-Client|Any CPU.ActiveCfg = Debug|Any CPU - {394E7643-6B8C-416F-AB18-95AC12648CDC}.Debug-Client|Any CPU.Build.0 = Debug|Any CPU - {394E7643-6B8C-416F-AB18-95AC12648CDC}.Release|Any CPU.ActiveCfg = Release|Any CPU - {394E7643-6B8C-416F-AB18-95AC12648CDC}.Release|Any CPU.Build.0 = Release|Any CPU - {394E7643-6B8C-416F-AB18-95AC12648CDC}.Release-Client|Any CPU.ActiveCfg = Release|Any CPU - {394E7643-6B8C-416F-AB18-95AC12648CDC}.Release-Client|Any CPU.Build.0 = Release|Any CPU - {89191F69-B18E-4B59-B72E-E12F9B6811A0}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {89191F69-B18E-4B59-B72E-E12F9B6811A0}.Debug|Any CPU.Build.0 = Debug|Any CPU - {89191F69-B18E-4B59-B72E-E12F9B6811A0}.Debug-Client|Any CPU.ActiveCfg = Debug|Any CPU - {89191F69-B18E-4B59-B72E-E12F9B6811A0}.Debug-Client|Any CPU.Build.0 = Debug|Any CPU - {89191F69-B18E-4B59-B72E-E12F9B6811A0}.Release|Any CPU.ActiveCfg = Release|Any CPU - {89191F69-B18E-4B59-B72E-E12F9B6811A0}.Release|Any CPU.Build.0 = Release|Any CPU - {89191F69-B18E-4B59-B72E-E12F9B6811A0}.Release-Client|Any CPU.ActiveCfg = Release|Any CPU - {89191F69-B18E-4B59-B72E-E12F9B6811A0}.Release-Client|Any CPU.Build.0 = Release|Any CPU - {AC4E7E8B-F83A-481C-A8B0-8FA4E8AE59AB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {AC4E7E8B-F83A-481C-A8B0-8FA4E8AE59AB}.Debug|Any CPU.Build.0 = Debug|Any CPU - {AC4E7E8B-F83A-481C-A8B0-8FA4E8AE59AB}.Debug-Client|Any CPU.ActiveCfg = Debug|Any CPU - {AC4E7E8B-F83A-481C-A8B0-8FA4E8AE59AB}.Debug-Client|Any CPU.Build.0 = Debug|Any CPU - {AC4E7E8B-F83A-481C-A8B0-8FA4E8AE59AB}.Release|Any CPU.ActiveCfg = Release|Any CPU - {AC4E7E8B-F83A-481C-A8B0-8FA4E8AE59AB}.Release|Any CPU.Build.0 = Release|Any CPU - {AC4E7E8B-F83A-481C-A8B0-8FA4E8AE59AB}.Release-Client|Any CPU.ActiveCfg = Release|Any CPU - {AC4E7E8B-F83A-481C-A8B0-8FA4E8AE59AB}.Release-Client|Any CPU.Build.0 = Release|Any CPU - {154435F6-0890-42D4-9AEC-B743D4FBC1CB}.Debug|Any CPU.ActiveCfg = Release|x86 - {154435F6-0890-42D4-9AEC-B743D4FBC1CB}.Debug|Any CPU.Build.0 = Release|x86 - {154435F6-0890-42D4-9AEC-B743D4FBC1CB}.Debug-Client|Any CPU.ActiveCfg = Release|x86 - {154435F6-0890-42D4-9AEC-B743D4FBC1CB}.Release|Any CPU.ActiveCfg = Release|x86 - {154435F6-0890-42D4-9AEC-B743D4FBC1CB}.Release|Any CPU.Build.0 = Release|x86 - {154435F6-0890-42D4-9AEC-B743D4FBC1CB}.Release-Client|Any CPU.ActiveCfg = Release|x86 - {8956D4C3-BFB9-448E-BF5F-EE7E6F9996F9}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {8956D4C3-BFB9-448E-BF5F-EE7E6F9996F9}.Debug|Any CPU.Build.0 = Debug|Any CPU - {8956D4C3-BFB9-448E-BF5F-EE7E6F9996F9}.Debug-Client|Any CPU.ActiveCfg = Debug|Any CPU - {8956D4C3-BFB9-448E-BF5F-EE7E6F9996F9}.Release|Any CPU.ActiveCfg = Release|Any CPU - {8956D4C3-BFB9-448E-BF5F-EE7E6F9996F9}.Release|Any CPU.Build.0 = Release|Any CPU - {8956D4C3-BFB9-448E-BF5F-EE7E6F9996F9}.Release-Client|Any CPU.ActiveCfg = Release|Any CPU - {9A01EF03-8EAE-45CB-8B87-4A17BD904557}.Debug|Any CPU.ActiveCfg = Debug|x86 - {9A01EF03-8EAE-45CB-8B87-4A17BD904557}.Debug|Any CPU.Build.0 = Debug|x86 - {9A01EF03-8EAE-45CB-8B87-4A17BD904557}.Debug-Client|Any CPU.ActiveCfg = Debug|x86 - {9A01EF03-8EAE-45CB-8B87-4A17BD904557}.Release|Any CPU.ActiveCfg = Release|x86 - {9A01EF03-8EAE-45CB-8B87-4A17BD904557}.Release|Any CPU.Build.0 = Release|x86 - {9A01EF03-8EAE-45CB-8B87-4A17BD904557}.Release-Client|Any CPU.ActiveCfg = Release|x86 - {FB693FFB-17E3-4E84-8CBF-6FFA9C8FD971}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {FB693FFB-17E3-4E84-8CBF-6FFA9C8FD971}.Debug|Any CPU.Build.0 = Debug|Any CPU - {FB693FFB-17E3-4E84-8CBF-6FFA9C8FD971}.Debug-Client|Any CPU.ActiveCfg = Debug|Any CPU - {FB693FFB-17E3-4E84-8CBF-6FFA9C8FD971}.Release|Any CPU.ActiveCfg = Release|Any CPU - {FB693FFB-17E3-4E84-8CBF-6FFA9C8FD971}.Release|Any CPU.Build.0 = Release|Any CPU - {FB693FFB-17E3-4E84-8CBF-6FFA9C8FD971}.Release-Client|Any CPU.ActiveCfg = Release|Any CPU - {3F81E398-B223-4006-B40C-C2800714CE29}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {3F81E398-B223-4006-B40C-C2800714CE29}.Debug|Any CPU.Build.0 = Debug|Any CPU - {3F81E398-B223-4006-B40C-C2800714CE29}.Debug-Client|Any CPU.ActiveCfg = Debug|Any CPU - {3F81E398-B223-4006-B40C-C2800714CE29}.Release|Any CPU.ActiveCfg = Release|Any CPU - {3F81E398-B223-4006-B40C-C2800714CE29}.Release|Any CPU.Build.0 = Release|Any CPU - {3F81E398-B223-4006-B40C-C2800714CE29}.Release-Client|Any CPU.ActiveCfg = Release|Any CPU - {509433F6-AEFB-44CA-BFE3-C782166D2CC3}.Debug|Any CPU.ActiveCfg = Debug|Any CPU - {509433F6-AEFB-44CA-BFE3-C782166D2CC3}.Debug|Any CPU.Build.0 = Debug|Any CPU - {509433F6-AEFB-44CA-BFE3-C782166D2CC3}.Debug-Client|Any CPU.ActiveCfg = Debug|Any CPU - {509433F6-AEFB-44CA-BFE3-C782166D2CC3}.Release|Any CPU.ActiveCfg = Release|Any CPU - {509433F6-AEFB-44CA-BFE3-C782166D2CC3}.Release|Any CPU.Build.0 = Release|Any CPU - {509433F6-AEFB-44CA-BFE3-C782166D2CC3}.Release-Client|Any CPU.ActiveCfg = Release|Any CPU - EndGlobalSection - GlobalSection(SolutionProperties) = preSolution - HideSolutionNode = FALSE - EndGlobalSection - GlobalSection(NestedProjects) = preSolution - {3BB3DA66-970C-4AAE-A77C-9E40F5E7E56A} = {0DDF886A-7ABB-4DCA-96A3-349B2DA00016} - {E7103B1A-8097-4BD4-B642-E7A2FAB1B0F3} = {9032B448-5E2B-4E71-8ECB-D5FB828E049C} - {D30454E2-ABAF-4698-B5EC-D2E7C9641F43} = {E7103B1A-8097-4BD4-B642-E7A2FAB1B0F3} - {FFECCA21-8B4A-4A70-B524-33DDDCA04850} = {E7103B1A-8097-4BD4-B642-E7A2FAB1B0F3} - EndGlobalSection - GlobalSection(ExtensibilityGlobals) = postSolution - SolutionGuid = {1B511D10-AB55-43E0-B363-7F8838E9E977} - EndGlobalSection -EndGlobal diff --git a/Tools/CoverageExclusions.runsettings b/Tools/CoverageExclusions.runsettings deleted file mode 100644 index 9cf93cc0e8..0000000000 --- a/Tools/CoverageExclusions.runsettings +++ /dev/null @@ -1,28 +0,0 @@ - - - - - - - - - - - - - .*\\TGS.Tests\\.* - - - - - True - True - True - False - - - - - - - \ No newline at end of file diff --git a/Tools/PostCIBuild.ps1 b/Tools/PostCIBuild.ps1 deleted file mode 100644 index f875c61386..0000000000 --- a/Tools/PostCIBuild.ps1 +++ /dev/null @@ -1,77 +0,0 @@ -$bf = $Env:APPVEYOR_BUILD_FOLDER - - -function CodeSign -{ - param($file) - &'C:\Program Files (x86)\Microsoft SDKs\ClickOnce\SignTool\signtool.exe' sign /f "$bf/Tools/tgstation13.org.pfx" /p "$env:snk_passphrase" /t http://timestamp.verisign.com/scripts/timstamp.dll "$file" -} - -#Sign the output files -if (Test-Path env:snk_passphrase) -{ - CodeSign "$bf/TGS.Tests/bin/Release/TGS.Tests.dll" - CodeSign "$bf/TGS.Server.Console/bin/Release/TGS.Server.Console.exe" - CodeSign "$bf/TGS.Installer.UI/bin/Release/TG Station Server Installer.exe" - $env:snk_passphrase = "" -} - -$src = "$bf\TGS.Installer.UI\bin\Release" -$version = [System.Diagnostics.FileVersionInfo]::GetVersionInfo("$src\TG Station Server Installer.exe").FileVersion - -$destination = "$bf\TGS3-Server-v$version.exe" - -Move-Item -Path "$src\TG Station Server Installer.exe" -Destination "$destination" - -Add-Type -assembly "system.io.compression.filesystem" - -$destination_md5sha = "$bf\MD5-SHA1-Server-v$version.txt" - -$src2 = "$bf\ClientApps" -[system.io.directory]::CreateDirectory($src2) -Copy-Item "$bf\TGS.CommandLine\bin\Release\TGCommandLine.exe" "$src2\TGCommandLine.exe" -Copy-Item "$bf\TGS.ControlPanel\bin\Release\TGControlPanel.exe" "$src2\TGControlPanel.exe" -Copy-Item "$bf\TGS.ControlPanel\bin\Release\Octokit.dll" "$src2\Octokit.dll" -Copy-Item "$bf\TGS.Interface\bin\Release\TGServiceInterface.dll" "$src2\TGServiceInterface.dll" -Copy-Item "$bf\TGS.Interface\bin\Release\Newtonsoft.Json.dll" "$src2\Newtonsoft.Json.dll" - -$dest2 = "$bf\TGS3-Client-v$version.zip" - -[io.compression.zipfile]::CreateFromDirectory($src2, $dest2) - -$src3 = "$bf\ServerConsole" -[system.io.directory]::CreateDirectory($src3) -Copy-Item "$bf\TGS.CommandLine\bin\Release\TGCommandLine.exe" "$src3\TGCommandLine.exe" -Copy-Item "$bf\TGS.ControlPanel\bin\Release\TGControlPanel.exe" "$src3\TGControlPanel.exe" -Copy-Item "$bf\TGS.ControlPanel\bin\Release\Octokit.dll" "$src3\Octokit.dll" -Copy-Item "$bf\TGS.Interface\bin\Release\TGServiceInterface.dll" "$src3\TGServiceInterface.dll" -Copy-Item "$bf\TGS.Server\bin\Release\TGS.Server.dll" "$src3\TGS.Server.dll" -Copy-Item "$bf\TGS.Server\bin\Release\Discord.Net.Core.dll" "$src3\Discord.Net.Core.dll" -Copy-Item "$bf\TGS.Server\bin\Release\Discord.Net.Rest.dll" "$src3\Discord.Net.Rest.dll" -Copy-Item "$bf\TGS.Server\bin\Release\Discord.Net.WebSocket.dll" "$src3\Discord.Net.WebSocket.dll" -Copy-Item "$bf\TGS.Server\bin\Release\LibGit2Sharp.dll" "$src3\LibGit2Sharp.dll" -Copy-Item "$bf\TGS.Server\bin\Release\Meebey.SmartIrc4net.dll" "$src3\Meebey.SmartIrc4net.dll" -Copy-Item "$bf\TGS.Server\bin\Release\Newtonsoft.Json.dll" "$src3\Newtonsoft.Json.dll" -Copy-Item "$bf\TGS.Server\bin\Release\System.Collections.Immutable.dll" "$src3\System.Collections.Immutable.dll" -Copy-Item "$bf\TGS.Server\bin\Release\System.Interactive.Async.dll" "$src3\System.Interactive.Async.dll" -Copy-Item "$bf\TGS.Interface.Bridge\bin\x86\Release\TGDreamDaemonBridge.dll" "$src3\TGDreamDaemonBridge.dll" -[system.io.directory]::CreateDirectory("$src3\lib\win32\x86") -Copy-Item "$bf\TGS.Server\bin\Release\lib\win32\x86\git2-ssh-baa87df.dll" "$src3\lib\win32\x86\git2-ssh-baa87df.dll" -Copy-Item "$bf\TGS.Server\bin\Release\lib\win32\x86\libssh2.dll" "$src3\lib\win32\x86\libssh2.dll" -Copy-Item "$bf\TGS.Server\bin\Release\lib\win32\x86\zlib.dll" "$src3\lib\win32\x86\zlib.dll" -[system.io.directory]::CreateDirectory("$src3\lib\win32\x64") -Copy-Item "$bf\TGS.Server\bin\Release\lib\win32\x64\git2-ssh-baa87df.dll" "$src3\lib\win32\x64\git2-ssh-baa87df.dll" -Copy-Item "$bf\TGS.Server\bin\Release\lib\win32\x64\libssh2.dll" "$src3\lib\win32\x64\libssh2.dll" -Copy-Item "$bf\TGS.Server\bin\Release\lib\win32\x64\zlib.dll" "$src3\lib\win32\x86\zlib.dll" -Copy-Item "$bf\TGS.Server.Console\bin\Release\TGS.Server.Console.exe" "$src3\TGS.Server.Console.exe" - -$dest3 = "$bf\TGS3-ServerConsole-v$version.zip" - -[io.compression.zipfile]::CreateFromDirectory($src3, $dest3) - -$destination_md5sha2 = "$bf\MD5-SHA1-Client-v$version.txt" -$destination_md5sha3 = "$bf\MD5-SHA1-ServerConsole-v$version.txt" - -& fciv -both $destination > $destination_md5sha -& fciv -both $dest2 > $destination_md5sha2 -& fciv -both $dest3 > $destination_md5sha3 diff --git a/Tools/SignBasics.ps1 b/Tools/SignBasics.ps1 deleted file mode 100644 index 65765abd26..0000000000 --- a/Tools/SignBasics.ps1 +++ /dev/null @@ -1,18 +0,0 @@ -$bf = $Env:APPVEYOR_BUILD_FOLDER - -function CodeSign -{ - param($file) - &'C:\Program Files (x86)\Microsoft SDKs\ClickOnce\SignTool\signtool.exe' sign /f "$bf/Tools/tgstation13.org.pfx" /p "$env:snk_passphrase" /t http://timestamp.verisign.com/scripts/timstamp.dll "$file" -} - -#Sign the output files -if (Test-Path env:snk_passphrase) -{ - CodeSign "$bf/TGS.CommandLine/bin/Release/TGCommandLine.exe" - CodeSign "$bf/TGS.ControlPanel/bin/Release/TGControlPanel.exe" - CodeSign "$bf/TGS.Server/bin/Release/TGS.Server.dll" - CodeSign "$bf/TGS.Server.Service/bin/Release/TGServerService.exe" - CodeSign "$bf/TGS.Interface.Bridge/bin/x86/Release/TGDreamDaemonBridge.dll" - CodeSign "$bf/TGS.Interface/bin/Release/TGServiceInterface.dll" -} diff --git a/Tools/SignMSI.ps1 b/Tools/SignMSI.ps1 deleted file mode 100644 index dd921d188b..0000000000 --- a/Tools/SignMSI.ps1 +++ /dev/null @@ -1,13 +0,0 @@ -$bf = $Env:APPVEYOR_BUILD_FOLDER - -function CodeSign -{ - param($file) - &'C:\Program Files (x86)\Microsoft SDKs\ClickOnce\SignTool\signtool.exe' sign /f "$bf/Tools/tgstation13.org.pfx" /p "$env:snk_passphrase" /t http://timestamp.verisign.com/scripts/timstamp.dll "$file" -} - -#Sign the output files -if (Test-Path env:snk_passphrase) -{ - CodeSign "$bf/TGS.Installer/bin/Release/TGServiceInstaller.msi" -} diff --git a/Tools/ViewTGS3Logs.xml b/Tools/ViewTGS3Logs.xml deleted file mode 100644 index 7323220bb1..0000000000 --- a/Tools/ViewTGS3Logs.xml +++ /dev/null @@ -1 +0,0 @@ -Application0TG Station Server,TG1,2,3,4,0,5TrueTG Station Server Service Log diff --git a/Tools/tgstation13.org.pfx b/Tools/tgstation13.org.pfx deleted file mode 100644 index 79defe0df1..0000000000 Binary files a/Tools/tgstation13.org.pfx and /dev/null differ diff --git a/appveyor.yml b/appveyor.yml index 79b3fb9596..92577863f2 100644 --- a/appveyor.yml +++ b/appveyor.yml @@ -1,61 +1,84 @@ +version: '{build}' pull_requests: do_not_increment_build_number: true environment: repo_token: secure: lJNGAXwiB5HlWdthz3K4PetqpTG5IEAyRgKaiKxFMQ8HW8CcOjRtB97B05op7BsK - snk_passphrase: - secure: dsRGlNewYn/JoycPaMoxLCrb3wkL31RsdU7Xwbo7TIAmClwShCVWeRPN+eGA+fMZX12MDNRBs8Fws4IxqLKQ36thQo6p2B+kPksAUJiAOR5QKJoT6nt8JB1wBMFS2kH4 -skip_branch_with_pr: true +branches: + only: + - master + - V4 +skip_tags: true image: Visual Studio 2017 -configuration: Release +configuration: + - Debug + - Release shallow_clone: true artifacts: -- path: TGS3-Server-v*.exe - name: TGS3Server -- path: MD5-SHA1-Server-v*.txt - name: MD5SHA1Server -- path: TGS3-ServerConsole-v*.zip - name: TGS3ServerConsole -- path: MD5-SHA1-ServerConsole-v*.txt - name: MD5SHA1ServerConsole -- path: TGS3-Client-v*.zip - name: TGS3Client -- path: MD5-SHA1-Client-v*.txt - name: MD5SHA1Client + - path: artifacts/ServerConsole + name: ServerConsole + - path: artifacts/ServerService + name: ServerService + - path: artifacts/ServerHost + name: ServerUpdatePackage cache: - - packages -> **\packages.config + - ~\.nuget\packages -> **\*.csproj - C:\ProgramData\chocolatey\bin -> appveyor.yml - C:\ProgramData\chocolatey\lib -> appveyor.yml install: - - choco install fciv doxygen.portable codecov -before_build: - - nuget restore TGStationServer3.sln + - choco install doxygen.portable codecov + - nuget restore tgstation-server.sln build: - project: TGStationServer3.sln + project: tgstation-server.sln parallel: false verbosity: minimal publish_nuget: true -after_build: - - ps: Tools/PostCIBuild.ps1 - - ps: if($env:APPVEYOR_REPO_COMMIT_MESSAGE -match "\[TGSDeploy\]"){ if($env:APPVEYOR_REPO_BRANCH -match "master"){ $env:TGSDeploy = "Do it." }} - - ps: if($env:APPVEYOR_REPO_COMMIT_MESSAGE -match "\[NugetDeploy\]"){ if($env:APPVEYOR_REPO_BRANCH -match "master"){ $env:NugetDeploy = "Do it." }} - - ps: $env:TGSVersion = [System.Diagnostics.FileVersionInfo]::GetVersionInfo("$env:APPVEYOR_BUILD_FOLDER/TGS.Server/bin/Release/TGS.Server.dll").FileVersion test_script: - set path=%ProgramFiles(x86)%\Microsoft Visual Studio\2017\TestAgent\Common7\IDE\CommonExtensions\Microsoft\TestWindow;%path% - copy "%ProgramFiles(x86)%\Microsoft Visual Studio\2017\Community\Common7\IDE\CommonExtensions\Microsoft\TestWindow\Extensions\appveyor.*" "%ProgramFiles(x86)%\Microsoft Visual Studio\2017\TestAgent\Common7\IDE\CommonExtensions\Microsoft\TestWindow\Extensions" /y - - vstest.console /logger:trx;LogFileName=results.trx "TGS.Tests\bin\Release\TGS.Tests.dll" /Enablecodecoverage /Settings:"Tools/CoverageExclusions.runsettings" /inIsolation /Platform:x64 + - vstest.console /logger:trx;LogFileName=results.trx "tests\Tgstation.Server.Api.Tests\bin\%CONFIGURATION%\netcoreapp2.0\Tgstation.Server.Api.Tests.dll" /Enablecodecoverage /inIsolation /Platform:x64 + - ps: $wc = New-Object 'System.Net.WebClient' + - ps: $wc.UploadFile("https://ci.appveyor.com/api/testresults/mstest/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\TestResults\results.trx)) + - vstest.console /logger:trx;LogFileName=results.trx "tests\Tgstation.Server.Client.Tests\bin\%CONFIGURATION%\netcoreapp2.0\Tgstation.Server.Client.Tests.dll" /Enablecodecoverage /inIsolation /Platform:x64 + - ps: $wc = New-Object 'System.Net.WebClient' + - ps: $wc.UploadFile("https://ci.appveyor.com/api/testresults/mstest/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\TestResults\results.trx)) + - vstest.console /logger:trx;LogFileName=results.trx "tests\Tgstation.Server.CommandLine.Tests\bin\%CONFIGURATION%\netcoreapp2.0\Tgstation.Server.CommandLine.Tests.dll" /Enablecodecoverage /inIsolation /Platform:x64 + - ps: $wc = New-Object 'System.Net.WebClient' + - ps: $wc.UploadFile("https://ci.appveyor.com/api/testresults/mstest/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\TestResults\results.trx)) + - vstest.console /logger:trx;LogFileName=results.trx "tests\Tgstation.Server.Host.Tests\bin\%CONFIGURATION%\netcoreapp2.0\Tgstation.Server.Host.Tests.dll" /Enablecodecoverage /inIsolation /Platform:x64 + - ps: $wc = New-Object 'System.Net.WebClient' + - ps: $wc.UploadFile("https://ci.appveyor.com/api/testresults/mstest/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\TestResults\results.trx)) + - vstest.console /logger:trx;LogFileName=results.trx "tests\Tgstation.Server.Host.Console.Tests\bin\%CONFIGURATION%\netcoreapp2.0\Tgstation.Server.Host.Console.Tests.dll" /Enablecodecoverage /inIsolation /Platform:x64 + - ps: $wc = New-Object 'System.Net.WebClient' + - ps: $wc.UploadFile("https://ci.appveyor.com/api/testresults/mstest/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\TestResults\results.trx)) + - vstest.console /logger:trx;LogFileName=results.trx "tests\Tgstation.Server.ControlPanel.Tests\bin\%CONFIGURATION%\Tgstation.Server.ControlPanel.Tests.dll" /Enablecodecoverage /inIsolation /Platform:x64 - ps: $wc = New-Object 'System.Net.WebClient' - ps: $wc.UploadFile("https://ci.appveyor.com/api/testresults/mstest/$($env:APPVEYOR_JOB_ID)", (Resolve-Path .\TestResults\results.trx)) after_test: - - ps: Tools/UploadCoverage.ps1 - - ps: Tools/BuildDox.ps1 + - ps: build/UploadCoverage.ps1 + - ps: build/BuildDox.ps1 + #host updater + - dotnet publish src/Tgstation.Server.Host/Tgstation.Server.Host.csproj -o ../../artifacts/ServerHost -c %CONFIGURATION% + #console + - dotnet publish src/Tgstation.Server.Host.Console/Tgstation.Server.Host.Console.csproj -o ../../artifacts/ServerConsole -c %CONFIGURATION% + - ps: Copy-Item -path "artifacts/ServerHost" -destination artifacts/ServerConsole/lib/Default -recurse + - ps: Move-Item -path artifacts/ServerConsole/lib/Default/appsettings.json -destination artifacts/ServerConsole/ + #service + - ps: Copy-Item -path "src/Tgstation.Server.Host.Service/bin/$env:CONFIGURATION" -destination artifacts/ServerService -recurse + - ps: Copy-Item -path "artifacts/ServerHost" -destination artifacts/ServerService/lib/Default -recurse + - ps: Move-Item -path artifacts/ServerService/lib/Default/appsettings.json -destination artifacts/ServerService/ + #deploy stuff + - ps: Remove-Item artifacts/ServerHost/appsettings.json + - ps: $env:TGSVersion = [System.Diagnostics.FileVersionInfo]::GetVersionInfo("$env:APPVEYOR_BUILD_FOLDER/artifacts/ServerHost/Tgstation.Server.Host.dll").FileVersion + - ps: if($env:APPVEYOR_REPO_COMMIT_MESSAGE -match "\[TGSDeploy\]"){ if($env:APPVEYOR_REPO_BRANCH -match "master"){ if($env:CONFIGURATION -match "Release"){ $env:TGSDeploy = "Do it." }}} + - ps: if($env:APPVEYOR_REPO_COMMIT_MESSAGE -match "\[NugetDeploy\]"){ if($env:APPVEYOR_REPO_BRANCH -match "master"){ if($env:CONFIGURATION -match "Release"){ $env:NugetDeploy = "Do it." }}} deploy: - provider: GitHub release: "tgstation-server-v$(TGSVersion)" description: 'The /tg/station server suite' auth_token: secure: lJNGAXwiB5HlWdthz3K4PetqpTG5IEAyRgKaiKxFMQ8HW8CcOjRtB97B05op7BsK - artifact: TGS3Server,MD5SHA1Server,TGS3Client,MD5SHA1Client,TGS3ServerConsole,MD5SHA1ServerConsole + artifact: ServerConsole,ServerService,ServerUpdatePackage draft: true on: TGSDeploy: "Do it." diff --git a/Tools/BuildDox.ps1 b/build/BuildDox.ps1 similarity index 70% rename from Tools/BuildDox.ps1 rename to build/BuildDox.ps1 index 3b7d97b684..c9ad450975 100644 --- a/Tools/BuildDox.ps1 +++ b/build/BuildDox.ps1 @@ -11,19 +11,19 @@ if($publish_dox){ echo "Cloning https://git@$github_url..." git clone -b gh-pages --single-branch "https://git@$github_url" "$doxdir" 2>$null rm -r "$doxdir\*" - Add-Content "$bf\Tools\Doxyfile" "`nPROJECT_NUMBER = $version`nINPUT = $bf`nOUTPUT_DIRECTORY = $doxdir`nPROJECT_LOGO = $bf/tgs.ico" + Add-Content "$bf\build\Doxyfile" "`nPROJECT_NUMBER = $version`nINPUT = $bf`nOUTPUT_DIRECTORY = $doxdir`nPROJECT_LOGO = $bf/build/tgs.ico" }else{ - Add-Content "$bf\Tools\Doxyfile" "`nPROJECT_NUMBER = $version`nINPUT = $bf`nOUTPUT_DIRECTORY = $doxdir`nPROJECT_LOGO = $bf/tgs.ico" + Add-Content "$bf\build\Doxyfile" "`nPROJECT_NUMBER = $version`nINPUT = $bf`nOUTPUT_DIRECTORY = $doxdir`nPROJECT_LOGO = $bf/build/tgs.ico" } -doxygen.exe "$bf\Tools\Doxyfile" +doxygen.exe "$bf\build\Doxyfile" if($publish_dox){ cd $doxdir git config --global push.default simple git config user.name "tgstation-server" git config user.email "tgstation-server@tgstation13.org" - echo '# THIS BRANCH IS AUTO GENERATED BY APPVEYOR CI. SEE Tools/TGS3Build.ps1 ON MASTER' > README.md + echo '# THIS BRANCH IS AUTO GENERATED BY APPVEYOR CI' > README.md # Need to create a .nojekyll file to allow filenames starting with an underscore # to be seen on the gh-pages site. Therefore creating an empty .nojekyll file. @@ -31,6 +31,5 @@ if($publish_dox){ git add --all git commit -m "Deploy code docs to GitHub Pages for Appveyor build $Env:APPVEYOR_BUILD_NUMBER" -m "Commit: $Env:APPVEYOR_REPO_COMMIT" git push -f "https://$Env:repo_token@$github_url" 2>&1 | out-null - $Env:repo_token = "" cd "$bf" } diff --git a/build/Dockerfile b/build/Dockerfile new file mode 100644 index 0000000000..af174c8317 --- /dev/null +++ b/build/Dockerfile @@ -0,0 +1,50 @@ +FROM microsoft/dotnet:2.1-sdk AS build +WORKDIR /src + +COPY tgstation-server.sln ./ + +COPY src/Tgstation.Server.Host.Console/Tgstation.Server.Host.Console.csproj src/Tgstation.Server.Host.Console/ +COPY src/Tgstation.Server.Host.Watchdog/Tgstation.Server.Host.Watchdog.csproj src/Tgstation.Server.Host.Watchdog/ +COPY src/Tgstation.Server.Host/Tgstation.Server.Host.csproj src/Tgstation.Server.Host/ +COPY src/Tgstation.Server.Api/Tgstation.Server.Api.csproj src/Tgstation.Server.Api/ + +COPY tests/Tgstation.Server.Api.Tests/Tgstation.Server.Api.Tests.csproj tests/Tgstation.Server.Api.Tests/ +COPY tests/Tgstation.Server.Host.Tests/Tgstation.Server.Host.Tests.csproj tests/Tgstation.Server.Host.Tests/ +COPY tests/Tgstation.Server.Host.Watchdog.Tests/Tgstation.Server.Host.Watchdog.Tests.csproj tests/Tgstation.Server.Host.Watchdog.Tests/ +COPY tests/Tgstation.Server.Host.Console.Tests/Tgstation.Server.Host.Console.Tests.csproj tests/Tgstation.Server.Host.Console.Tests/ + +RUN dotnet restore -nowarn:MSB3202,nu1503 -p:RestoreUseSkipNonexistentTargets=false + +COPY . . + +WORKDIR /src/tests/Tgstation.Server.Api.Tests +RUN dotnet test -c Release + +WORKDIR /src/tests/Tgstation.Server.Host.Tests +RUN dotnet test -c Release + +WORKDIR /src/tests/Tgstation.Server.Host.Watchdog.Tests +RUN dotnet test -c Release + +WORKDIR /src/tests/Tgstation.Server.Host.Console.Tests +RUN dotnet test -c Release + +WORKDIR /src/src/Tgstation.Server.Host.Console +RUN dotnet publish -c Release -o /app + +WORKDIR /src/src/Tgstation.Server.Host +RUN dotnet publish -c Release -o /app/lib/Default && mv /app/lib/Default/appsettings* /app + +FROM microsoft/dotnet:2.1-aspnetcore-runtime +EXPOSE 80 + +WORKDIR /app + +COPY --from=build /app . +COPY --from=build /src/build/tgs.docker.sh tgs.sh +COPY --from=build /src/src/Tgstation.Server.Host/appsettings.Docker.json . + +RUN mkdir /config_data && mv appsettings.Docker.json /config_data/appsettings.Production.json +VOLUME ["/config_data", "/tgs_logs"] + +ENTRYPOINT ["./tgs.sh"] diff --git a/Tools/Doxyfile b/build/Doxyfile similarity index 99% rename from Tools/Doxyfile rename to build/Doxyfile index 1f90072117..b4ec282ad2 100644 --- a/Tools/Doxyfile +++ b/build/Doxyfile @@ -865,7 +865,7 @@ RECURSIVE = YES # run. #We want the main .md -EXCLUDE = legacy/README.md packages +EXCLUDE = legacy packages # The EXCLUDE_SYMLINKS tag can be used to select whether or not files or # directories that are symbolic links (a Unix file system feature) are excluded diff --git a/Tools/UploadCoverage.ps1 b/build/UploadCoverage.ps1 similarity index 100% rename from Tools/UploadCoverage.ps1 rename to build/UploadCoverage.ps1 diff --git a/Tools/install_byond.sh b/build/install_byond.sh similarity index 100% rename from Tools/install_byond.sh rename to build/install_byond.sh diff --git a/build/tgs.docker.sh b/build/tgs.docker.sh new file mode 100755 index 0000000000..ac9a5d2151 --- /dev/null +++ b/build/tgs.docker.sh @@ -0,0 +1,5 @@ +#!/bin/sh + +cp -r /config_data/* ./ + +exec dotnet Tgstation.Server.Host.Console.dll diff --git a/TGS.CommandLine/tgs.ico b/build/tgs.ico similarity index 100% rename from TGS.CommandLine/tgs.ico rename to build/tgs.ico diff --git a/legacy/Fix Errors.bat b/legacy/Fix Errors.bat deleted file mode 100644 index bbe540c82c..0000000000 --- a/legacy/Fix Errors.bat +++ /dev/null @@ -1,24 +0,0 @@ -@echo off -title Automated Error Fixer. -echo This will reset some things, the byond server (DreamDaemon) and the start server script must not be running. Some error messages are normal. -echo You will be prompted to press any key 3 times -pause -pause -pause -cls -echo Resetting folders -mkdir gamecode\a -mkdir gamecode\b -del /S /F /Q gamefolder >nul 2>nul -rmdir /S /q gamefolder -mklink /d gamefolder gamecode\a -call bin\findab.bat -cls -echo Re-Initializing code -call bin\copyfromgit.bat -cls -echo Recompiling the game. If you plan to update or testmerge you can just close this program now and continue. -call bin\build.bat -cls -echo Done! (hopefully) -pause \ No newline at end of file diff --git a/legacy/Merge pr without updating.bat b/legacy/Merge pr without updating.bat deleted file mode 100644 index 813116a006..0000000000 --- a/legacy/Merge pr without updating.bat +++ /dev/null @@ -1,112 +0,0 @@ -@echo off -@title Server Updater -SETLOCAL ENABLEDELAYEDEXPANSION -set HOME = %USERPROFILE% - -call config.bat -rem if the first arg to nudge.py is not a channel, it is treated as the "source" -if not defined UPDATE_LOG_CHANNEL set UPDATE_LOG_CHANNEL="UPDATER" -call bin\getcurdate.bat -call bin\findgit.bat -echo This will handle merging a pr locally, compiling the server, and applying the PR test job. - -:PROMPT -SET /P UserInput=Please enter the pr number (without a # or anything of the sorts): -SET /A PR=UserInput - -if %PR% EQU %UserInput% ( - if %PR% GTR 0 ( - echo updating to pr %PR% - ) else ( - echo Bad input - goto PROMPT - ) -) else ( - echo Bad input - goto PROMPT -) -if exist updating.lk ( - echo ERROR: A current update script has been detected running. if you know this is a mistake: - pause - echo Please be double sure that an update script is not currently running, if you think one might be, close this window. otherwise: - pause -) - -echo lock>updating.lk - -call python bot\nudge.py %UPDATE_LOG_CHANNEL% "PR test job started. Merging PR #%PR% locally" >nul 2>nul - - -cd gitrepo -git fetch -f origin pull/%PR%/head:pr-%PR% -if %ERRORLEVEL% neq 0 ( - cd .. - echo git fetch failed. Aborting test merge. - call python bot\nudge.py %UPDATE_LOG_CHANNEL% "Git fetch failed. Aborting test merge" - del updating.lk >nul 2>nul - pause - exit /b 1 -) -git merge pr-%PR% -if %ERRORLEVEL% neq 0 ( - cd .. - echo git merge of PR #%PR% failed. Aborting test merge - call python bot\nudge.py %UPDATE_LOG_CHANNEL% "git merge of PR #%PR% failed. Aborting PR test merge" >nul 2>nul - cd gitrepo - git merge --abort - if !ERRORLEVEL! neq 0 ( - echo ERROR: Error aborting test merge, resetting repo. - cd .. - call python bot\nudge.py %UPDATE_LOG_CHANNEL% "Error aborting test merge, resetting git repo to head" >nul 2>nul - cd gitrepo - git reset --hard - git clean -fd - bin/activepr.bat - del /F /Q prtestjob.lk >nul 2>nul - echo NOTICE: We had to reset the repo's state, all other active test merges were undone. - ) - cd .. - del updating.lk >nul 2>nul - pause - exit /b 1 -) -cd .. - -echo %PR%>>prtestjob.lk -> bin/activepr.bat - -echo ################################## -echo ################################## -echo: -echo Test merging pr done, compiling in 10 seconds. If you want to preform other actions (like more test merges) You can close this now and do them. -echo: -del updating.lk >nul 2>nul - -timeout 10 - -echo lock>updating.lk -call bin\findab.bat - -call bin\copyfromgit.bat - -echo compiling change log -cd gamecode\%AB% -call python tools\ss13_genchangelog.py html/changelog.html html/changelogs -cd ..\.. - -echo Compiling game. -call bin\build.bat -if %DM_EXIT% neq 0 ( - echo DM compile failed. Aborting. - call python bot\nudge.py %UPDATE_LOG_CHANNEL% "DM compile failed. Aborting test merge." >nul 2>nul - del /F /Q updating.lk >nul 2>nul - pause - exit /b 1 -) - -del updating.lk >nul 2>nul -rmdir /q gamefolder -mklink /d gamefolder gamecode\%AB% >nul -call python bot\nudge.py %UPDATE_LOG_CHANNEL% "Test merge job finished. Test merge will take place next round." >nul 2>nul -echo Done. The test merge will automatically take place at round restart. -timeout 300 diff --git a/legacy/OnServerBoot.bat b/legacy/OnServerBoot.bat deleted file mode 100644 index db1f5cc230..0000000000 --- a/legacy/OnServerBoot.bat +++ /dev/null @@ -1,47 +0,0 @@ -@echo off -title Server Boot Detected. -echo This script is only meant to start byond when the server first boots, do not manually run this. -echo If you manually ran this, close this window NOW. -timeout 15 -start cmd /c "Start Bot.bat" -cls -echo Server boot detected. Starting byond and Space Station 13. -echo Prepping code -mkdir gamecode\a -cls -echo Server boot detected. Starting byond and Space Station 13. -echo Prepping code -mkdir gamecode\b -cls -echo Server boot detected. Starting byond and Space Station 13. -echo Prepping code -del /S /F /Q gamefolder >nul 2>nul -cls -echo Server boot detected. Starting byond and Space Station 13. -echo Prepping code -rmdir /S /q gamefolder -cls -echo Server boot detected. Starting byond and Space Station 13. -echo Prepping code -mklink /d gamefolder gamecode\a -cls -echo Server boot detected. Starting byond and Space Station 13. -echo Prepping code -call bin\findab.bat -cls -echo Server boot detected. Starting byond and Space Station 13. -echo Re-Initializing code -call bin\copyfromgit.bat -cls -echo Server boot detected. Starting byond and Space Station 13. -echo Re-Initializing code -cls -cls -echo Server boot detected. Starting byond and Space Station 13. -echo Compiling the game. If you plan to update or testmerge you can just close this program now and continue. -call bin\build.bat -cls -echo Server boot detected. Starting byond and Space Station 13. -echo Starting server and bot. -start cmd /c "Start Server.bat" -timeout 10 \ No newline at end of file diff --git a/legacy/README.md b/legacy/README.md deleted file mode 100644 index 10831fd461..0000000000 --- a/legacy/README.md +++ /dev/null @@ -1,111 +0,0 @@ -# Tgstation Toolkit: -This is a toolset to manage a production server of /tg/Station13 (and its forks). It includes an update script that is able to update the server without having to stop or shutdown the server (the update will take effect next round) and a script to start the server and restart it if it crashes (optional, requires registry tweaks to disable the Windows crash dialog system wide) as well as systems for fixing errors and merging GitHub Pull Requests locally. - -Generally, updates force a live tracking of the configured git repo, resetting local modifications. If you plan to make modifications, set up a new git repo to store your version of the code in, and point this script to that in the config (explained below). This can be on github or a local repo using file:/// urls. - -These tools require UAC to be disabled. (There has been limited luck getting it to work under UAC on windows 10 using run as admin, but in other versions of windows, running as admin resets the current directory and this breaks things) -(Note: There is no security risk to disabling UAC because UAC is not a security boundary, there exists active unpatched exploits against it that have been around since vista and work up to windows 10) - -## Install: -1. Move this folder to where you want your server to run from (you may also rename this folder if you wish) -1. Right click on `config.bat` and select `Edit`. -1. Configure the port, git repo, and other setting, You may configure the location of git, but if you installed using git for windows, we will auto detect it if its not in path. -1. You may also need to change the location of the git repo url, and if you renamed your `dme`/"dream maker environment" file, you will need to change the project name configuration setting to be the name of your dme minus the extension (eg: if you renamed `tgstation.dme` to `ntstation.dme`, project name should be set to `ntstation` -1. Finally, run install.bat and hope for no error. - * It will clone the git repo, setup some folders, and add cross reference links everywhere. - -### Optional: -If you plan to use the `Start Server.bat` script to start the server and restart it if it crashes, you need to run the `disable crash dialog.reg` registry script to disable the "program.exe has stopped working" dialog. This will get disabled system wide so if you rely on this dialog, you should skip this step. - -## Usage: -### Folders: -* `gamecode/` - * This will house two copies of the game code, one for updating and one for live. When updating, it will automatically swap them. - -* `gamedata/` - * This contains the `data/` and `config/` folders from the code. They are stored here and a symbolic link is created in the `gamecode/` folders pointing to here. - * This also makes backing them up easy. -(you may copy and paste your existing `data/` and `config/` folders here after the install script has ran.) - -* `bot/` - * This is a copy of the bot folder. you should run the bot from here.a link to nudge.py is created in the code folders so that the game can use the announcement feature. - * The start server script and update script will send messages out thru the bot, but if its not running or python is not installed, they will gracefully continue what they are doing. - -* `gamefolder/` - * This is a symbolic link pointing to current "live" folder. - * When the server is updated, we just point this to the updating folder so that the update takes place next round. - -* `gitrepo/` - * This contains the actual git repository, all changes in here will be overwritten during update operations, the configured branch will always be forced to track from live. - * On the first update of the day, the current code state is saved to a branch called `backup-YYYY-MM-DD` before updating, to make local reverts easy. - * Following update operations on the same day do not make branches because I was too lazy to detect such edge cases. - -* `bin/` - * This contains random helper batch files. - * Running these on their own is a bad idea. - -### Starting the game server: -To run the game server, Run `Start Server.bat` - -It will restart the game server if it shutdowns for any reason, with delays if the game server had recently been (re)started. - - -### Updating the server: -To update the server, just run `Update Server.bat`. (it will git pull, compile, all that jazz) - -(Note: Updating automatically does a code reset, clearing ALL changes to the local git repo, including test merges (explained below) and manual changes (This will not change any configs/settings or clear any data in the `gamedata/` folder)) - -Updates do not require the server to be shutdown, changes will apply next round if the server is currently running. - -Updates create a branch with the current state of the repo called `backup-YYYY-MM-DD` (only one is created in any given day) - -There is also a `Update without resetting.bat` file that does the same without resetting the code, used to update without clearing test merges or local changes. Prone to merge conflicts. - - -### Locally merge GitHub Pull Requests (PR test merge): -This feature currently only works if github is the remote(git server), it could be adapted for gitlab as well. - -Running these will merge the pull request then recompile the server, changes take effect the next round if the server is currently running. - -There are multiple flavors: -* `Update To PR.bat` - * Updates the server, resetting state, and merges a PR(Pull Request) by number. -* `Merge PR Without Updating.bat` - * Merges a PR without updating the server before hand or resetting the state (can be used to test merge multiple PRs). - -You can clear all active test merges using `Reset and Recompile.bat` (explained below) - -### Resetting, Recompiling, and troubleshooting. -* `Recompile.bat` - * Just recompiles the game code and stages it to apply next round -* `Reset and Recompile.bat` - * Like the above but resets the git repo to the state of the last update operation (clearing any changes or test merges) (Does not reset `gamedata/` data/settings) - * Generally used to clear out test merges -* `Fix Errors.bat` - * Requires the server not be running, rebuilds the staging A/B folders and then does all of the above. (Used to fix errors that can prevent the server from starting or cause it to crash on reboot) - -### Using the bot -1. Install python 3 -1. Edit the config in `bot/` as needed -1. Start the bot using the `Start Bot.bat` file - - -### Starting everything when the computer/server boots -1. Use autologin to set it up so the user account logs in at boot - * (There are some security implications with doing this, this is only a tiny bit more secure then putting a file on the hard drive with the server's remote login password titled `totally not a password.txt`) - * https://technet.microsoft.com/en-us/sysinternals/bb963905 -1. Setup something to run OnServerBoot.bat on login (setting up a link/shortcut to this file in the startup folder of the start menu works, creating a scheduled task in windows administrative tools also works) -1. OnServerBoot.bat does not update, but it does re-compile and reinitialize the A/B folders. - -### Updating byond after this is all setup -1. Download installer from byond website -1. Close watch dog/start server script -1. Wait for current round to end -1. Exit DreamDeamon -1. Run installer -1. After installing, run recompile.bat (or update if you want) -1. Run start server.bat - - - - diff --git a/legacy/Recompile.bat b/legacy/Recompile.bat deleted file mode 100644 index 40b746365e..0000000000 --- a/legacy/Recompile.bat +++ /dev/null @@ -1,51 +0,0 @@ -@echo off -@title Server Updater -set HOME = %USERPROFILE% - -call config.bat -call bin\getcurdate.bat - -echo This will handle recompiling the server, and applying the new version. -echo ready? - -timeout 120 - -if exist updating.lk ( - echo ERROR: A current update script has been detected running. if you know this is a mistake: - pause - echo Please be double sure that an update script is not currently running, if you think one might be, close this window. otherwise: - pause -) - -echo lock>updating.lk - -rem if the first arg to nudge.py is not a channel, it is treated as the "source" -if not defined UPDATE_LOG_CHANNEL set UPDATE_LOG_CHANNEL="UPDATER" - -call python bot\nudge.py %UPDATE_LOG_CHANNEL% "Recompile job started" >nul 2>nul - -call bin\findab.bat - -call bin\copyfromgit.bat - -echo compiling change log -cd gamecode\%AB% -call python tools\ss13_genchangelog.py html/changelog.html html/changelogs -cd ..\.. - -echo Compiling game. -call bin\build.bat -if %DM_EXIT% neq 0 ( - echo DM compile failed. Aborting. - call python bot\nudge.py %UPDATE_LOG_CHANNEL% "DM compile failed Aborting recompile." >nul 2>nul - del /F /Q updating.lk >nul 2>nul - pause - exit /b 1 -) - -del updating.lk >nul 2>nul -rmdir /q gamefolder -mklink /d gamefolder gamecode\%AB% >nul -call python bot\nudge.py %UPDATE_LOG_CHANNEL% "Recompile finished. Recompiled code will take place next round." >nul 2>nul -echo Done. The recompile will automatically take place at round restart. -timeout 300 \ No newline at end of file diff --git a/legacy/Reset and Recompile.bat b/legacy/Reset and Recompile.bat deleted file mode 100644 index f0127fd3ac..0000000000 --- a/legacy/Reset and Recompile.bat +++ /dev/null @@ -1,66 +0,0 @@ -@echo off -@title Server Updater -set HOME = %USERPROFILE% - -call config.bat -call bin\getcurdate.bat -call bin\findgit.bat - -echo This will handle resetting the git repo, recompiling the server, and applying the new version. -echo Ready? - -timeout 120 - -if exist updating.lk ( - echo ERROR: A current update script has been detected running. if you know this is a mistake: - pause - echo Please be double sure that an update script is not currently running, if you think one might be, close this window. otherwise: - pause -) - -echo lock>updating.lk - -rem if the first arg to nudge.py is not a channel, it is treated as the "source" -if not defined UPDATE_LOG_CHANNEL set UPDATE_LOG_CHANNEL="UPDATER" - -call python bot\nudge.py %UPDATE_LOG_CHANNEL% "Reset local changes job started" >nul 2>nul - -cd gitrepo -git reset --hard -git clean -df -cd .. -echo ################################## -echo ################################## -echo: -echo Resetting done, compiling in 10 seconds. If you want to preform other actions (like test merges) You can close this now and do them. -echo: -del updating.lk >nul 2>nul - -timeout 10 - -echo lock>updating.lk -call bin\findab.bat - -call bin\copyfromgit.bat - -echo compiling change log -cd gamecode\%AB% -call python tools\ss13_genchangelog.py html/changelog.html html/changelogs -cd ..\.. - -echo Compiling game. -call bin\build.bat -if %DM_EXIT% neq 0 ( - echo DM compile failed. Aborting. - call python bot\nudge.py %UPDATE_LOG_CHANNEL% "DM compile failed Aborting Reset." >nul 2>nul - del /F /Q updating.lk >nul 2>nul - pause - exit /b 1 -) - -del updating.lk >nul 2>nul -rmdir /q gamefolder -mklink /d gamefolder gamecode\%AB% >nul -call python bot\nudge.py %UPDATE_LOG_CHANNEL% "Reset finished. Reset will take place next round." >nul 2>nul -echo Done. The Reset will automatically take place at round restart. -timeout 300 diff --git a/legacy/Start Bot.bat b/legacy/Start Bot.bat deleted file mode 100644 index a3b1cba0d8..0000000000 --- a/legacy/Start Bot.bat +++ /dev/null @@ -1,19 +0,0 @@ -@echo off -@title NT IRC BOT -echo Welcome to the start bot script, This will start the bot and make sure it stays running. This assumes python in the path. To continue, press any key or wait 15 seconds. -timeout 15 -cd bot -:START -call ..\bin\getcurdate.bat -if not exist ..\gamedata\data\logs\bot mkdir ..\gamedata\data\logs\bot\ -cls -echo NT IRC Bot -echo Bot Running. Watching for Bot exits. -start /WAIT python minibot.py ^>^>..\gamedata\data\logs\bot\bot-%CUR_DATE%.txt -cls -echo NT IRC Bot -echo Bot exit detected. Restarting in 15 minutes. -REM this is so long because we want to avoid the bot spamming the server and getting klined/glined/or akilled -timeout 900 - -goto :START diff --git a/legacy/Start Server.bat b/legacy/Start Server.bat deleted file mode 100644 index b926db5c61..0000000000 --- a/legacy/Start Server.bat +++ /dev/null @@ -1,39 +0,0 @@ -@echo off -@title SERVER WATCHDOG -call config.bat -call bin\findbyond.bat - -echo Welcome to the start server watch dog script, This will start the server and make sure it stays running. To continue, press any key or wait 30 seconds. -timeout 30 - -if not exist gamedata\data\logs\runtimes mkdir gamedata\data\logs\runtimes\ - -@call python bot\nudge.py "WATCHDOG" "Watch Dog online. Starting server" >nul 2>nul -:START - -call bin\getcurdate.bat - -call bin\getunixtime.bat UNIXTIME - -echo %UNIXTIME% - -set STARTTIME=%UNIXTIME% - -cls -echo Watch Dog. -echo Server Running. Watching for server exits. -start /WAIT /ABOVENORMAL "" dreamdaemon.exe gamefolder\%PROJECTNAME%.dmb -port %PORT% -trusted -close -public -verbose -cls - -call bin\getunixtime.bat UNIXTIME - -SET /A Result=%UNIXTIME% - %STARTTIME% -SET /A Result=180 - (%Result%/3) -if %Result% LSS 0 set /A Result=0 - -echo Watch Dog. -echo Server exit detected. Restarting in %Result% seconds. -@python bot\nudge.py "WATCHDOG" "Server exit detected. Restarting server in %Result% seconds." >nul 2>nul -timeout %Result% - -goto :START diff --git a/legacy/Update Server.bat b/legacy/Update Server.bat deleted file mode 100644 index 1d7d8644af..0000000000 --- a/legacy/Update Server.bat +++ /dev/null @@ -1,99 +0,0 @@ -@echo off -title Server Updater -SETLOCAL ENABLEDELAYEDEXPANSION -set HOME = %USERPROFILE% - -call config.bat -call bin\getcurdate.bat - -echo This will handle downloading git, compiling the server, and applying the update. -echo Ready? - -timeout 120 - -if exist updating.lk ( - echo ERROR: A current update script has been detected running. if you know this is a mistake: - pause - echo Please be double sure that an update script is not currently running, if you think one might be, close this window. otherwise: - pause -) - -if exist prtestjob.lk ( - call bin/activepr.bat - echo WARNING: The server is currently testing the following PRs !PR!. This update would override that. Do you still want to update? Close this window if not, otherwise: - pause -) - -del /F /Q prtestjob.lk >nul 2>nul - -echo lock>updating.lk - bin/activepr.bat - -rem if the first arg to nudge.py is not a channel, it is treated as the "source" -if not defined UPDATE_LOG_CHANNEL set UPDATE_LOG_CHANNEL="UPDATER" - -call python bot\nudge.py %UPDATE_LOG_CHANNEL% "Update job started" >nul 2>nul - -call bin\updategit.bat -if %GIT_EXIT% neq 0 ( - echo git pull failed. Aborting update - call python bot\nudge.py %UPDATE_LOG_CHANNEL% "Git fetch failed. Aborting update" - del updating.lk >nul 2>nul - pause - exit /b 1 -) - -if defined PUSHCHANGELOGTOGIT ( - cd gitrepo - echo compiling change log - python tools\ss13_genchangelog.py html/changelog.html html/changelogs - if !ERRORLEVEL! == 0 ( - echo pushing compiled changelog to server - git add -u html/changelog.html - git add -u html/changelogs - git commit -m "Automatic changelog compile, [ci skip]" - if !ERRORLEVEL! == 0 ( - git push - ) - REM an error here generally means there was nothing to commit. - ) - cd .. -) - -echo ################################## -echo ################################## -echo: -echo Updating done, compiling in 10 seconds. If you want to preform other actions (like test merge) You can close this now and do them. -echo: -del updating.lk >nul 2>nul - -timeout 10 - -echo lock>updating.lk -call bin\findab.bat - -call bin\copyfromgit.bat - -if not defined PUSHCHANGELOGTOGIT ( - echo compiling change log - cd gamecode\%AB% - call python tools\ss13_genchangelog.py html/changelog.html html/changelogs - cd ..\.. -) - -echo Compiling game. -call bin\build.bat -if %DM_EXIT% neq 0 ( - echo DM compile failed. Aborting. - call python bot\nudge.py %UPDATE_LOG_CHANNEL% "DM compile failed Aborting update." >nul 2>nul - del /F /Q updating.lk >nul 2>nul - pause - exit /b 1 -) - -del updating.lk >nul 2>nul -rmdir /q gamefolder -mklink /d gamefolder gamecode\%AB% >nul -call python bot\nudge.py %UPDATE_LOG_CHANNEL% "Update job finished. Update will take place next round." >nul 2>nul -echo Done. The update will automatically take place at round restart. -timeout 300 \ No newline at end of file diff --git a/legacy/Update to PR.bat b/legacy/Update to PR.bat deleted file mode 100644 index a9ab59e02e..0000000000 --- a/legacy/Update to PR.bat +++ /dev/null @@ -1,121 +0,0 @@ -@echo off -title Server Updater -SETLOCAL ENABLEDELAYEDEXPANSION -set HOME = %USERPROFILE% - -call config.bat -rem if the first arg to nudge.py is not a channel, it is treated as the "source" -if not defined UPDATE_LOG_CHANNEL set UPDATE_LOG_CHANNEL="UPDATER" -call bin\getcurdate.bat -call bin\findgit.bat -echo This will handle Resetting the repo downloading from git, merging the pr locally, compiling the server, and applying the PR test job. - -:PROMPT -SET /P UserInput=Please enter the pr number (without a # or anything of the sorts): -SET /A PR=UserInput - -if %PR% EQU %UserInput% ( - if %PR% GTR 0 ( - echo updating to pr %PR% - ) else ( - echo Bad input - goto PROMPT - ) -) else ( - echo Bad input - goto PROMPT -) -if exist updating.lk ( - echo ERROR: A current update script has been detected running. if you know this is a mistake: - pause - echo Please be double sure that an update script is not currently running, if you think one might be, close this window. otherwise: - pause -) - -echo lock>updating.lk - bin/activepr.bat -del /F /Q prtestjob.lk >nul 2>nul - -call python bot\nudge.py %UPDATE_LOG_CHANNEL% "PR test job started. Updating master and merging PR #%PR% locally" >nul 2>nul - -call bin\updategit.bat -if %GIT_EXIT% neq 0 ( - echo git pull failed. Aborting update - call python bot\nudge.py %UPDATE_LOG_CHANNEL% "Git fetch failed. Aborting update" - del updating.lk >nul 2>nul - pause - exit /b 1 -) - -if defined PUSHCHANGELOGTOGIT ( - cd gitrepo - echo compiling change log - call python tools\ss13_genchangelog.py html/changelog.html html/changelogs - if !ERRORLEVEL! == 0 ( - echo pushing compiled changelog to server - git add -u html/changelog.html - git add -u html/changelogs - git commit -m "Automatic changelog compile, [ci skip]" - if !ERRORLEVEL! == 0 ( - git push - ) - REM an error here generally means there was nothing to commit. - ) - cd .. -) - -cd gitrepo -git fetch -f origin pull/%PR%/head:pr-%PR% -git merge pr-%PR% -if %ERRORLEVEL% neq 0 ( - git reset --hard - git clean -fd - cd .. - echo git merge of PR #%PR% failed. Aborting test merge. - echo An update was successfully preformed but not applied, if you want you can apply it by running recompile.bat - call python bot\nudge.py %UPDATE_LOG_CHANNEL% "git merge of PR #%PR% failed. Aborting PR test job" >nul 2>nul - del updating.lk >nul 2>nul - pause - exit /b 1 -) -cd .. - -echo %PR%>prtestjob.lk -> bin/activepr.bat - -echo ################################## -echo ################################## -echo: -echo Updating to pr done, compiling in 10 seconds. If you want to preform other actions (like more test merges) You can close this now and do them. -echo: -del updating.lk >nul 2>nul - -timeout 10 - -echo lock>updating.lk -call bin\findab.bat - -call bin\copyfromgit.bat - - -echo compiling change log (again) -cd gamecode\%AB% -call python tools\ss13_genchangelog.py html/changelog.html html/changelogs -cd ..\.. - -echo Compiling game. -call bin\build.bat -if %DM_EXIT% neq 0 ( - echo DM compile failed. Aborting. - call python bot\nudge.py %UPDATE_LOG_CHANNEL% "DM compile failed. Aborting test merge." >nul 2>nul - del /F /Q updating.lk >nul 2>nul - pause - exit /b 1 -) - -del updating.lk >nul 2>nul -rmdir /q gamefolder -mklink /d gamefolder gamecode\%AB% >nul -call python bot\nudge.py %UPDATE_LOG_CHANNEL% "Test merge job finished. Test merge will take place next round." >nul 2>nul -echo Done. The test merge will automatically take place at round restart. -timeout 300 diff --git a/legacy/Update without resetting.bat b/legacy/Update without resetting.bat deleted file mode 100644 index 9a08573fb1..0000000000 --- a/legacy/Update without resetting.bat +++ /dev/null @@ -1,100 +0,0 @@ -@echo off -@title Server Updater -SETLOCAL ENABLEDELAYEDEXPANSION -set HOME = %USERPROFILE% - -call config.bat -call bin\getcurdate.bat -call bin\findgit.bat - -echo This will update the server without resetting local changes like test merges. -echo Note: This doesn't update the changelog like a normal update does. -echo Ready? - -timeout 120 - -if exist updating.lk ( - echo ERROR: A current update script has been detected running. if you know this is a mistake: - pause - echo Please be double sure that an update script is not currently running, if you think one might be, close this window. otherwise: - pause -) - -echo lock>updating.lk - -rem if the first arg to nudge.py is not a channel, it is treated as the "source" -if not defined UPDATE_LOG_CHANNEL set UPDATE_LOG_CHANNEL="UPDATER" - -call python bot\nudge.py %UPDATE_LOG_CHANNEL% "Update job started (No reset mode)" >nul 2>nul - -cd gitrepo -git fetch origin -if %ERRORLEVEL% neq 0 ( - cd .. - echo git fetch failed. Aborting update. - call python bot\nudge.py %UPDATE_LOG_CHANNEL% "Git fetch failed. Aborting update" - del updating.lk >nul 2>nul - pause - exit /b 1 -) -git merge origin/%REPO_BRANCH% -if %ERRORLEVEL% neq 0 ( - cd .. - echo git merge of upstream master failed, aborting update. - call python bot\nudge.py %UPDATE_LOG_CHANNEL% "git merge of upstream master failed, aborting update." >nul 2>nul - cd gitrepo - git merge --abort - if %ERRORLEVEL% neq 0 ( - echo ERROR: Error aborting update, resetting repo. - cd .. - call python bot\nudge.py %UPDATE_LOG_CHANNEL% "Error aborting merge, Resetting git repo" >nul 2>nul - cd gitrepo - git reset --hard - git clean -fd - bin/activepr.bat - del /F /Q prtestjob.lk >nul 2>nul - echo NOTICE: We had to reset the repo's state, all active test merges were undone. - ) - cd .. - del updating.lk >nul 2>nul - pause - exit /b 1 -) -cd .. - - -echo ################################## -echo ################################## -echo: -echo In place update done, compiling in 10 seconds. If you want to preform other actions (like more test merges) You can close this now and do them. -echo: -del updating.lk >nul 2>nul - -timeout 10 - -echo lock>updating.lk -call bin\findab.bat - -call bin\copyfromgit.bat - -echo compiling change log -cd gamecode\%AB% -call python tools\ss13_genchangelog.py html/changelog.html html/changelogs -cd ..\.. - -echo Compiling game. -call bin\build.bat -if %DM_EXIT% neq 0 ( - echo DM compile failed. Aborting. - call python bot\nudge.py %UPDATE_LOG_CHANNEL% "DM compile failed Aborting update." >nul 2>nul - del /F /Q updating.lk >nul 2>nul - pause - exit /b 1 -) - -del updating.lk >nul 2>nul -rmdir /q gamefolder -mklink /d gamefolder gamecode\%AB% >nul -call python bot\nudge.py %UPDATE_LOG_CHANNEL% "Update job finished. Update will take place next round." >nul 2>nul -echo Done. The update will automatically take place at round restart. -timeout 300 diff --git a/legacy/bin/activepr.bat b/legacy/bin/activepr.bat deleted file mode 100644 index 554142925d..0000000000 --- a/legacy/bin/activepr.bat +++ /dev/null @@ -1 +0,0 @@ -set PR= \ No newline at end of file diff --git a/legacy/bin/build.bat b/legacy/bin/build.bat deleted file mode 100644 index e231c6b920..0000000000 --- a/legacy/bin/build.bat +++ /dev/null @@ -1,25 +0,0 @@ -call config.bat -call bin/findbyond.bat -set DME_FOLDER=gamefolder\ -if defined AB set DME_FOLDER=gamecode\%AB%\ - -set DME_LOCATION=%DME_FOLDER%%PROJECTNAME%.dme -set MDME_LOCATION=%DME_FOLDER%%PROJECTNAME%.mdme - -@del %MDME_LOCATION% >nul 2>nul -if not defined MAPFILE goto BUILD - -echo #define MAP_OVERRIDE >>%MDME_LOCATION% -echo #include "_maps\%MAPFILE%.dm" >>%MDME_LOCATION% - -:BUILD -echo #define SERVERTOOLS 1 >>%MDME_LOCATION% -echo #define PUTONHUB 1 >>%MDME_LOCATION% -type %DME_LOCATION% >>%MDME_LOCATION% - -dm -clean %MDME_LOCATION% -set DM_EXIT=%ERRORLEVEL% -@del %DME_FOLDER%%PROJECTNAME%.dmb >nul 2>nul -@del %DME_FOLDER%%PROJECTNAME%.rsc >nul 2>nul -@move %DME_FOLDER%%PROJECTNAME%.mdme.dmb %DME_FOLDER%%PROJECTNAME%.dmb >nul 2>nul -@move %DME_FOLDER%%PROJECTNAME%.mdme.rsc %DME_FOLDER%%PROJECTNAME%.rsc >nul 2>nul diff --git a/legacy/bin/copyfromgit.bat b/legacy/bin/copyfromgit.bat deleted file mode 100644 index acb9114902..0000000000 --- a/legacy/bin/copyfromgit.bat +++ /dev/null @@ -1,21 +0,0 @@ -echo Removing old files -rem delete the symlinks manually to ensure their targets don't get recursively deleted -rmdir /q gamecode\%AB%\data >nul 2>nul -rmdir /q gamecode\%AB%\config >nul 2>nul -del /q gamecode\%AB%\nudge.py >nul 2>nul -del /q gamecode\%AB%\libmysql.dll >nul 2>nul - -del /S /F /Q gamecode\%AB%\ >nul 2>nul - -echo Copying files -xcopy gitrepo gamecode\%AB% /Y /X /K /R /H /I /C /V /E /Q /EXCLUDE:copyexclude.txt >nul -mkdir gamecode\%AB%\.git\logs\refs\remotes\origin -copy gitrepo\.git\logs\HEAD gamecode\%AB%\.git\logs\HEAD /D /V /Y >nul -copy gitrepo\.git\logs\refs\remotes\origin\master gamecode\%AB%\.git\logs\refs\remotes\origin\master /D /V /Y >nul - -mklink gamecode\%AB%\nudge.py ..\..\bot\nudge.py >nul -rmdir /q gamecode\%AB%\data >nul 2>nul -rmdir /s /q gamecode\%AB%\data >nul 2>nul -mklink /d gamecode\%AB%\data ..\..\gamedata\data >nul -mklink /d gamecode\%AB%\config ..\..\gamedata\config >nul -mklink gamecode\%AB%\libmysql.dll ..\..\gamedata\libmysql.dll >nul diff --git a/legacy/bin/findab.bat b/legacy/bin/findab.bat deleted file mode 100644 index 0c24a7571b..0000000000 --- a/legacy/bin/findab.bat +++ /dev/null @@ -1,45 +0,0 @@ -del gamecode\a\updater.temp >nul 2>nul -del gamecode\b\updater.temp >nul 2>nul - -echo test >gamefolder\updater.temp - -if exist gamefolder\%PROJECTNAME%.rsc.lk ( - rem we attempt to delete the lock file to see if the server is currently running. - del /q gamefolder\%PROJECTNAME%.rsc.lk >nul 2>nul - if exist gamefolder\%PROJECTNAME%.rsc.lk set RUNNING=1 -) - -if exist gamecode\a\updater.temp ( - if defined RUNNING ( - echo Current folder detected to be the "A" folder. Game is currently running. Updating to the "B" folder. - set AB=b - ) else ( - echo Current folder detected to be the "A" folder. Game is not currently running, Updating to the "A" folder. - if exist gamecode\b\%PROJECTNAME%.rsc.lk ( - rem we attempt to delete the lock file to see if the server is currently running. - del /q gamecode\b\%PROJECTNAME%.rsc.lk >nul 2>nul - if exist gamecode\b\%PROJECTNAME%.rsc.lk set RUNNING=1 - rmdir /q gamefolder - mklink /d gamefolder gamecode\b >nul - echo Game is in actually currently running on the "B" folder, Resetting current folder to the "B" folder first - ) - set AB=a - ) -) else if exist gamecode\b\updater.temp ( - if defined RUNNING ( - echo Current folder detected to be the "B" folder. Game is currently running, Updating to the "A" folder. - set AB=a - ) else ( - echo Current folder detected to be the "B" folder. Game is not currently running, Updating to the "B" folder. - if exist gamecode\a\%PROJECTNAME%.rsc.lk ( - rem we attempt to delete the lock file to see if the server is currently running. - del /q gamecode\a\%PROJECTNAME%.rsc.lk >nul 2>nul - if exist gamecode\a\%PROJECTNAME%.rsc.lk set RUNNING=1 - rmdir /q gamefolder - mklink /d gamefolder gamecode\a >nul - echo Game is in actually currently running on the "A" folder, Resetting current folder to the "A" folder first - ) - set AB=b - ) -) -del gamefolder\updater.temp >nul 2>nul \ No newline at end of file diff --git a/legacy/bin/findbyond.bat b/legacy/bin/findbyond.bat deleted file mode 100644 index ff07af2dd6..0000000000 --- a/legacy/bin/findbyond.bat +++ /dev/null @@ -1,26 +0,0 @@ - -@dm.exe -h >nul 2>nul -IF %ERRORLEVEL% NEQ 9009 ( - goto :eof -) - -set PATH=%PATH%;%BYOND_LOCATION_PATH% -@dm.exe -h >nul 2>nul -IF %ERRORLEVEL% NEQ 9009 ( - goto :eof -) - -@"c:\Program Files (x86)\BYOND\bin\dm.exe" -h >nul 2>nul -IF %ERRORLEVEL% NEQ 9009 ( - set "PATH=%PATH%;c:\Program Files (x86)\BYOND\bin\" - goto :eof -) -@"c:\Program Files\BYOND\bin\dm.exe" -h >nul 2>nul -IF %ERRORLEVEL% NEQ 9009 ( - set "PATH=%PATH%;c:\Program Files\BYOND\bin\" - goto :eof -) - -echo byond not found. Aborting. If byond is installed, set the GIT_LOCATION variable inside config.bat -timeout 60 -exit 11 \ No newline at end of file diff --git a/legacy/bin/findgit.bat b/legacy/bin/findgit.bat deleted file mode 100644 index 9c802b54c6..0000000000 --- a/legacy/bin/findgit.bat +++ /dev/null @@ -1,31 +0,0 @@ -REM check if git is already in path -git --version >nul 2>nul && goto :eof - -REM now lets try our override. -set PATH=%PATH%;%GIT_LOCATION_PATH% -@git --version >nul 2>nul && goto :eof - -REM credit to sschuberth@http://stackoverflow.com/questions/8507368/finding-the-path-where-git-is-installed-on-a-windows-system -REM Read the Git for Windows installation path from the Registry. - -:REG_QUERY -for /f "skip=2 delims=: tokens=1*" %%a in ('reg query "HKLM\SOFTWARE%WOW%\Microsoft\Windows\CurrentVersion\Uninstall\Git_is1" /v InstallLocation 2^> nul') do ( - for /f "tokens=3" %%z in ("%%a") do ( - set GIT=%%z:%%b - ) -) -if "%GIT%"=="" ( - if "%WOW%"=="" ( - rem Attempt to find it on the 32bit register section - set WOW=\Wow6432Node - goto REG_QUERY - ) -) - -set PATH=%GIT%bin;%PATH% - -@git --version >nul 2>nul && goto :eof - -echo Git not found. Aborting. If git is installed, set the GIT_LOCATION variable inside config.bat -timeout 60 -exit 10 \ No newline at end of file diff --git a/legacy/bin/getcurdate.bat b/legacy/bin/getcurdate.bat deleted file mode 100644 index f5360756ce..0000000000 --- a/legacy/bin/getcurdate.bat +++ /dev/null @@ -1,2 +0,0 @@ -FOR /f %%a in ('WMIC OS GET LocalDateTime ^| find "."') DO set DTS=%%a -set CUR_DATE=%DTS:~0,4%-%DTS:~4,2%-%DTS:~6,2% \ No newline at end of file diff --git a/legacy/bin/getunixtime.bat b/legacy/bin/getunixtime.bat deleted file mode 100644 index 2b4115faab..0000000000 --- a/legacy/bin/getunixtime.bat +++ /dev/null @@ -1,12 +0,0 @@ -REM setlocal -call :GetUnixTime UNIXTIME -goto :EOF - -:GetUnixTime -REM setlocal enableextensions -for /f %%x in ('wmic path win32_utctime get /format:list ^| findstr "="') do ( - set %%x) -set /a z=(14-100%Month%%%100)/12, y=10000%Year%%%10000-z -set /a ut=y*365+y/4-y/100+y/400+(153*(100%Month%%%100+12*z-3)+2)/5+Day-719469 -set /a ut=ut*86400+100%Hour%%%100*3600+100%Minute%%%100*60+100%Second%%%100 -endlocal & set "%1=%ut%" & goto :EOF \ No newline at end of file diff --git a/legacy/bin/unixtime.vbs b/legacy/bin/unixtime.vbs deleted file mode 100644 index f5e1ae78f0..0000000000 --- a/legacy/bin/unixtime.vbs +++ /dev/null @@ -1 +0,0 @@ -WScript.Echo DateDiff("s", "01/01/1970 00:00:00", Now()) \ No newline at end of file diff --git a/legacy/bin/updategit.bat b/legacy/bin/updategit.bat deleted file mode 100644 index e16cfc3922..0000000000 --- a/legacy/bin/updategit.bat +++ /dev/null @@ -1,12 +0,0 @@ -call bin\findgit.bat -echo Updating repo - -cd gitrepo - -git branch backup-%CUR_DATE% >nul 2>nul -git fetch -set GIT_EXIT=%ERRORLEVEL% - -git reset origin/%REPO_BRANCH% --hard - -cd .. \ No newline at end of file diff --git a/legacy/config.bat b/legacy/config.bat deleted file mode 100644 index eb3ecd13e7..0000000000 --- a/legacy/config.bat +++ /dev/null @@ -1,42 +0,0 @@ -@echo off -REM Server Tools configuration file. Lines starting with rem are comments and ignored. - -REM This must be set to the name of your dme without the .dme part. (should be fine leaving this alone unless you renamed the code) -set PROJECTNAME=tgstation - - -REM location of the repo. (use an ssh url if you plan to push compiled changlogs) -REM Only set during install, do not re-run install.bat if you change this, instead manually change the remote of the gitrepo folder using git tools -set REPO_URL=https://github.com/tgstation/-tg-station.git - - -REM What branch of the repo to use. -set REPO_BRANCH=master - - -REM Override Map (This disables map roation and forces the selected map to always be loaded) -REM set MAPFILE=tgstation2 -REM set MAPFILE=metastation - - -REM port to use when starting the server -set PORT=2337 - - -REM This is the channel to log updates to. Leave blank to log to the bot's default logging channel (this is done via the tgstation minibot bot, optional) -set UPDATE_LOG_CHANNEL=#devbus,#coderbus,#tgstation13 - - -REM Attempt to push the compiled changelog to the configured git server? (set to anything) -REM This requires you configure git with authentication for the upstream server. (git for windows users should just put an ssh key in c:\users\USERNAME_HERE\.ssh\ as the filename id_rsa) And you should have installed this with a git, ssh, or file url) -set PUSHCHANGELOGTOGIT= - - -REM location of git. The script will attempt to auto detect this, but if it fails, you can set it manually. -REM github for windows users see http://www.chambaud.com/2013/07/08/adding-git-to-path-when-using-github-for-windows/ (an example is provided below) - -set GIT_LOCATION_PATH= -REM set GIT_LOCATION_PATH=C:\Users\Administrator\AppData\Local\GitHub\PortableGit_c2ba306e536fdf878271f7fe636a147ff37326ad\bin;C:\Users\Administrator\AppData\Local\GitHub\PortableGit_c2ba306e536fdf878271f7fe636a147ff37326ad\cmd - -REM path to the byond bin folder. (same rules as git path above, almost always auto detected, but you could point this to the output of the zip version for install-less setups, and even abuse that to make updating byond versions less of a pain by just changing this config and then crashing the server.) -set BYOND_LOCATION_PATH= diff --git a/legacy/copyexclude.txt b/legacy/copyexclude.txt deleted file mode 100644 index 3cf4643303..0000000000 --- a/legacy/copyexclude.txt +++ /dev/null @@ -1,9 +0,0 @@ -//This is a list of files to exclude from copying. See xcopy's documentation for /exclude for more info. -// - -gitrepo\config\ -gitrepo\data\ -gitrepo\bot\ -gitrepo\.git\ -gitrepo\libmysql.dll - diff --git a/legacy/disable crash dialog.reg b/legacy/disable crash dialog.reg deleted file mode 100644 index 07e792d874..0000000000 --- a/legacy/disable crash dialog.reg +++ /dev/null @@ -1,4 +0,0 @@ -Windows Registry Editor Version 5.00 - -[HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\Windows\Windows Error Reporting] -"DontShowUI"=dword:00000001 diff --git a/legacy/install.bat b/legacy/install.bat deleted file mode 100644 index c00e9d3849..0000000000 --- a/legacy/install.bat +++ /dev/null @@ -1,81 +0,0 @@ -@echo off -@title Server Tools Installer. -set HOME = %USERPROFILE% -call config.bat - -echo This will download the game code from git and install the all the files and folders and symbolic links needed to use the server tools in to the current directory. - -echo This requires git be installed. - -echo Once this is done, you can safely delete this file if you wish. - -echo Ready? -pause - -call bin/findgit.bat - -echo Downloading repo.... -git clone %REPO_URL% gitrepo -IF %ERRORLEVEL% NEQ 0 ( - echo git clone failed. aborting. - pause - goto ABORT -) -cd gitrepo -git checkout %REPO_BRANCH% -cd .. - -echo Repo downloaded. -echo Setting up folders... -mkdir gamecode\a -mkdir gamecode\b -mkdir gamedata -mkdir bot - -echo Copying things around.... -echo (1/3) -xcopy gitrepo\data gamedata\data /Y /X /K /R /H /I /C /V /E /Q >nul -xcopy gitrepo\config gamedata\config /Y /X /K /R /H /I /C /V /E /Q >nul -xcopy gitrepo\tools\minibot bot /Y /X /K /R /H /I /C /V /E /Q >nul -copy gitrepo\libmysql.dll gamedata\libmysql.dll /D /V /Y >nul -echo (2/3) -xcopy gitrepo gamecode\a /Y /X /K /R /H /I /C /V /E /Q /EXCLUDE:copyexclude.txt >nul -mkdir gamecode\a\.git\logs\ -copy gitrepo\.git\logs\HEAD gamecode\a\.git\logs\HEAD /D /V /Y >nul -echo (3/3) -xcopy gitrepo gamecode\b /Y /X /K /R /H /I /C /V /E /Q /EXCLUDE:copyexclude.txt >nul -mkdir gamecode\b\.git\logs >nul -copy gitrepo\.git\logs\HEAD gamecode\b\.git\logs\HEAD /D /V /Y >nul -echo done. - -echo Setting up symbolic links. -mklink gamecode\a\nudge.py ..\..\bot\nudge.py -mklink gamecode\a\libmysql.dll ..\..\gamedata\libmysql.dll -mklink /d gamecode\a\data ..\..\gamedata\data -mklink /d gamecode\a\config ..\..\gamedata\config - -mklink gamecode\b\nudge.py ..\..\bot\nudge.py -mklink gamecode\b\libmysql.dll ..\..\gamedata\libmysql.dll -mklink /d gamecode\b\data ..\..\gamedata\data -mklink /d gamecode\b\config ..\..\gamedata\config - -mklink /d gamefolder gamecode\a - -echo prepping python (if installed) -pip install PyYaml -pip install beautifulsoup4 - -echo Compiling for the first time. - -echo Compiling change log. -cd gamefolder -call python tools\ss13_genchangelog.py html/changelog.html html/changelogs -cd .. -echo Compiling game. -call bin\build.bat -if %DM_EXIT% neq 0 echo DM compile failed. - -echo Done. You may start the server using the start server program or change the game config in gamedata\config -pause - -:ABORT diff --git a/DMAPI/tgs.dm b/src/DMAPI/tgs.dm similarity index 91% rename from DMAPI/tgs.dm rename to src/DMAPI/tgs.dm index e885b06397..628b17cac6 100644 --- a/DMAPI/tgs.dm +++ b/src/DMAPI/tgs.dm @@ -44,6 +44,15 @@ //EVENT CODES +#define TGS_EVENT_PORT_SWAP -2 //before a port change is about to happen, extra parameter is new port +#define TGS_EVENT_REBOOT_MODE_CHANGE -1 //before a reboot mode change, extras parameters are the current and new reboot mode enums + +//OTHER ENUMS + +#define TGS_REBOOT_MODE_NORMAL 0 +#define TGS_REBOOT_MODE_SHUTDOWN 1 +#define TGS_REBOOT_MODE_RESTART 2 + //TODO //REQUIRED HOOKS @@ -88,20 +97,21 @@ /datum/tgs_chat_channel var/id //internal channel representation var/friendly_name //user friendly channel name - var/server_name //server name the channel resides on - var/provider_name //chat provider for the channel + var/connection_name //the name of the configured chat connection var/is_admin_channel //if the server operator has marked this channel for game admins only var/is_private_channel //if this is a private chat channel //represents a chat user /datum/tgs_chat_user - var/id //Internal user representation + var/id //Internal user representation, requires channel to be unique var/friendly_name //The user's public name var/mention //The text to use to ping this user in a message var/datum/tgs_chat_channel/channel //The /datum/tgs_chat_channel this user was from //user definable callback for handling events -/datum/tgs_event_handler/proc/HandleEvent(event_code) +//extra parameters may be specified depending on the event +/datum/tgs_event_handler/proc/HandleEvent(event_code, ...) + set waitfor = FALSE return //user definable chat command diff --git a/DMAPI/tgs/core/_definitions.dm b/src/DMAPI/tgs/core/_definitions.dm similarity index 100% rename from DMAPI/tgs/core/_definitions.dm rename to src/DMAPI/tgs/core/_definitions.dm diff --git a/DMAPI/tgs/core/core.dm b/src/DMAPI/tgs/core/core.dm similarity index 99% rename from DMAPI/tgs/core/core.dm rename to src/DMAPI/tgs/core/core.dm index d24bc2c9ae..198ddc7d07 100644 --- a/DMAPI/tgs/core/core.dm +++ b/src/DMAPI/tgs/core/core.dm @@ -20,7 +20,7 @@ /world/proc/SelectTgsApi(tgs_version) //remove the old 3.0 header tgs_version = replacetext(tgs_version, "/tg/station 13 Server v", "") - + var/list/version_bits = splittext(tgs_version, ".") var/super = text2num(version_bits[1]) diff --git a/DMAPI/tgs/core/datum.dm b/src/DMAPI/tgs/core/datum.dm similarity index 98% rename from DMAPI/tgs/core/datum.dm rename to src/DMAPI/tgs/core/datum.dm index 6af39a75df..f81569136c 100644 --- a/DMAPI/tgs/core/datum.dm +++ b/src/DMAPI/tgs/core/datum.dm @@ -3,7 +3,7 @@ TGS_DEFINE_AND_SET_GLOBAL(tgs, null) /datum/tgs_api /datum/tgs_api/latest - parent_type = /datum/tgs_api/v3210 + parent_type = /datum/tgs_api/v4 TGS_PROTECT_DATUM(/datum/tgs_api) diff --git a/DMAPI/tgs/core/default_event_handler.dm b/src/DMAPI/tgs/core/default_event_handler.dm similarity index 100% rename from DMAPI/tgs/core/default_event_handler.dm rename to src/DMAPI/tgs/core/default_event_handler.dm diff --git a/DMAPI/tgs/includes.dm b/src/DMAPI/tgs/includes.dm similarity index 78% rename from DMAPI/tgs/includes.dm rename to src/DMAPI/tgs/includes.dm index 7ca906c840..2fdd5ed1e0 100644 --- a/DMAPI/tgs/includes.dm +++ b/src/DMAPI/tgs/includes.dm @@ -4,3 +4,5 @@ #include "core\default_event_handler.dm" #include "v3210\api.dm" #include "v3210\commands.dm" +#include "v4\api.dm" +#include "v4\commands.dm" diff --git a/DMAPI/tgs/v3210/api.dm b/src/DMAPI/tgs/v3210/api.dm similarity index 99% rename from DMAPI/tgs/v3210/api.dm rename to src/DMAPI/tgs/v3210/api.dm index 2302249763..1c04be9212 100644 --- a/DMAPI/tgs/v3210/api.dm +++ b/src/DMAPI/tgs/v3210/api.dm @@ -188,7 +188,7 @@ /datum/tgs_api/v3210/ChatTargetedBroadcast(message, admin_only) ExportService("[admin_only ? SERVICE_REQUEST_IRC_ADMIN_CHANNEL_MESSAGE : SERVICE_REQUEST_IRC_BROADCAST] [message]") -/datum/tgs_api/v3210/ChatPrivateMessage(message, admin_only) +/datum/tgs_api/v3210/ChatPrivateMessage(message, datum/tgs_chat_user/user) return TGS_UNIMPLEMENTED #undef REBOOT_MODE_NORMAL diff --git a/DMAPI/tgs/v3210/commands.dm b/src/DMAPI/tgs/v3210/commands.dm similarity index 100% rename from DMAPI/tgs/v3210/commands.dm rename to src/DMAPI/tgs/v3210/commands.dm diff --git a/src/DMAPI/tgs/v4/api.dm b/src/DMAPI/tgs/v4/api.dm new file mode 100644 index 0000000000..e62de1c25e --- /dev/null +++ b/src/DMAPI/tgs/v4/api.dm @@ -0,0 +1,257 @@ +#define TGS4_PARAM_INFO_JSON "tgs_json" + +#define TGS4_INTEROP_ACCESS_IDENTIFIER "tgs_tok" + +#define TGS4_RESPONSE_SUCCESS "tgs_succ" + +#define TGS4_TOPIC_CHANGE_PORT "tgs_port" +#define TGS4_TOPIC_CHANGE_REBOOT_MODE "tgs_rmode" +#define TGS4_TOPIC_CHAT_COMMAND "tgs_chat_comm" +#define TGS4_TOPIC_EVENT "tgs_event" + +#define TGS4_COMM_ONLINE "tgs_on" +#define TGS4_COMM_IDENTIFY "tgs_ident" +#define TGS4_COMM_VALIDATE "tgs_vali" +#define TGS4_COMM_SERVER_PRIMED "tgs_prime" +#define TGS4_COMM_WORLD_REBOOT "tgs_reboot" +#define TGS4_COMM_END_PROCESS "tgs_kill" +#define TGS4_COMM_CHAT "tgs_chat_send" + +#define TGS4_PARAMETER_COMMAND "tgs_com" +#define TGS4_PARAMETER_DATA "tgs_data" + +#define TGS4_PARAMETER_NEW_PORT "new_port" +#define TGS4_PARAMETER_NEW_REBOOT_MODE "new_rmode" + +/datum/tgs_api/v4 + var/access_identifier + var/instance_name + var/host_path + var/json_path + var/chat_channels_json_path + var/chat_commands_json_path + var/reboot_mode = TGS_REBOOT_MODE_NORMAL + + var/list/intercepted_message_queue + + var/list/custom_commands + + var/list/cached_test_merges + var/datum/tgs_revision_information/cached_revision + + var/datum/tgs_event_handler/event_handler + +/datum/tgs_api/v4/ApiVersion() + return "4.0.0.0" + +/datum/tgs_api/v4/OnWorldNew(datum/tgs_event_handler/event_handler) + json_path = world.params[TGS4_PARAM_INFO_JSON] + if(!json_path) + TGS_ERROR_LOG("Missing [TGS4_PARAM_INFO_JSON] world parameter!") + return + var/json_file = file2text(json_path) + if(!json_file) + TGS_ERROR_LOG("Missing specified json file: [json_path]") + return + var/cached_json = json_decode(json_file) + if(!cached_json) + TGS_ERROR_LOG("Failed to decode info json: [json_file]") + return + + access_identifier = cached_json["access_identifier"] + instance_name = text2num(cached_json["instance_name"]) + host_path = cached_json["host_path"] + if(cached_json["api_validate_only"]) + Export(TGS4_COMM_VALIDATE) + del(world) + + chat_channels_json_path = cached_json["chat_channels_json"] + chat_commands_json_path = cached_json["chat_commands_json"] + src.event_handler = event_handler + instance_name = cached_json["instance_name"] + + cached_test_merges = list() + var/json = cached_json["test_merges"] + for(var/I in json) + var/datum/tgs_revision_information/test_merge/tm = new + tm.number = text2num(I) + var/list/entry = json[I] + tm.pull_request_commit = entry["pr_commit"] + tm.author = entry["author"] + tm.title = entry["title"] + tm.commit = entry["commit"] + tm.origin_commit = entry["origin_commit"] + tm.time_merged = text2num(entry["time_merged"]) + tm.comment = entry["comment"] + tm.url = entry["url"] + + cached_revision = new + cached_revision.commit = cached_json["commit"] + cached_revision.origin_commit = cached_json["origin_commit"] + + ListCustomCommands() + +/datum/tgs_api/v4/OnInitializationComplete() + Export(TGS4_COMM_SERVER_PRIMED) + var/tgs4_secret_sleep_offline_sauce = 24051994 + var/old_sleep_offline = world.sleep_offline + sleep(1) + if(world.sleep_offline == tgs4_secret_sleep_offline_sauce) //if not someone changed it + world.sleep_offline = old_sleep_offline + +/datum/tgs_api/v4/OnTopic(T) + var/list/params = params2list(T) + var/their_sCK = params[TGS4_INTEROP_ACCESS_IDENTIFIER] + if(!their_sCK) + return FALSE //continue world/Topic + + if(their_sCK != access_identifier) + return "Invalid comms key!"; + + var/command = params[TGS4_INTEROP_ACCESS_IDENTIFIER] + if(!command) + return "No command!" + + switch(command) + if(TGS4_TOPIC_CHAT_COMMAND) + var/result = HandleCustomCommand(params[TGS4_TOPIC_CHAT_COMMAND]) + if(!result) + return json_encode(list("error" = "Error running chat command!")) + return result + if(TGS4_TOPIC_EVENT) + intercepted_message_queue = list() + event_handler.HandleEvent(text2num(params[TGS4_TOPIC_EVENT])) + . = json_encode(intercepted_message_queue) + intercepted_message_queue = null + return + + return "Unknown command: [command]" + +/datum/tgs_api/v4/proc/Export(command, list/data) + var/url = "[host_path]/Interop?[TGS4_INTEROP_ACCESS_IDENTIFIER]=[url_encode(access_identifier)]&[TGS4_PARAMETER_COMMAND]=[url_encode(command)]" + if(data) + url = "[url]&[TGS4_PARAMETER_DATA]=[url_encode(json_encode(data))]" + var/list/res = world.Export(url) + if(!istype(res)) + TGS_ERROR_LOG("Error contacting TGS webapi at [host_path] (command: [command])! world.Export returned something not a list (probably null): [res]") + return + var/byond_status = res["STATUS"] + var/byond_content = res["CONTENT"] + if(byond_status != "200 OK") + TGS_ERROR_LOG("Error contacting TGS webapi at [host_path] (command: [command])! Top level HTTP [byond_status]: [byond_content]") + return + var/json = json_decode(byond_content) //always expecting json + if(!json) + TGS_ERROR_LOG("Error contacting TGS webapi at [host_path] (command: [command])! Byond content not json: [byond_content]") + return + var/status = json["STATUS"] + var/content = json["CONTENT"] + if(status != 200) //HTTP OK + TGS_ERROR_LOG("Error contacting TGS webapi at [host_path] (command: [command])! HTTP [status]: [json_encode(content)]") + return + return content + +/datum/tgs_api/v4/OnReboot() + var/json = Export(TGS4_COMM_WORLD_REBOOT) + var/list/result = json_decode(json) + if(!result) + return + + //TODO: Port handling + +/datum/tgs_api/v4/InstanceName() + return instance_name + +/datum/tgs_api/v4/TestMerges() + return cached_test_merges + +/datum/tgs_api/v4/EndProcess() + Export(TGS4_COMM_END_PROCESS) + +/datum/tgs_api/v4/Revision() + return cached_revision + +/datum/tgs_api/v4/ChatBroadcast(message, list/channels) + var/list/ids + if(length(channels)) + ids = list() + for(var/I in channels) + var/datum/tgs_chat_channel/channel = I + ids += channel.id + message = list("message" = message, "channels" = ids) + if(intercepted_message_queue) + intercepted_message_queue += list(message) + else + Export(TGS4_COMM_CHAT, message) + +/datum/tgs_api/v4/ChatTargetedBroadcast(message, admin_only) + message = list("message" = message, "channels" = admin_only ? "admin" : "game") + if(intercepted_message_queue) + intercepted_message_queue += list(message) + else + Export(TGS4_COMM_CHAT, message) + +/datum/tgs_api/v4/ChatPrivateMessage(message, datum/tgs_chat_user/user) + message = list("message" = message, "user" = list("id" = user.id, "channel" = user.channel.id)) + if(intercepted_message_queue) + intercepted_message_queue += list(message) + else + Export(TGS4_COMM_CHAT, message) + +/datum/tgs_api/v4/ChatChannelInfo() + . = list() + //no caching cause tgs may change this + var/list/json = json_decode(file2text(chat_channels_json_path)) + for(var/I in json) + . += DecodeChannel(I) + +/datum/tgs_api/v4/proc/DecodeChannel(channel_json) + var/datum/tgs_chat_channel/channel = new + channel.id = channel_json["id"] + channel.friendly_name = channel_json["friendly_name"] + channel.connection_name = channel_json["connection_name"] + channel.is_admin_channel = channel_json["is_admin_channel"] + channel.is_private_channel = channel_json["is_private_channel"] || FALSE + return channel + +#undef TGS4_TOPIC_COMMAND +#undef TGS4_TOPIC_TOKEN +#undef TGS4_TOPIC_SUCCESS +#undef TGS4_TOPIC_SWAP +#undef TGS4_TOPIC_SWAP_DELAYED +#undef TGS4_TOPIC_CHAT_COMMAND +#undef TGS4_TOPIC_EVENT + +#undef TGS4_COMM_SERVER_PRIMED +#undef TGS4_COMM_SERVER_REBOOT +#undef TGS4_COMM_END_PROCESS +#undef TGS4_COMM_CHAT + +#undef TGS4_COMM_VALIDATE + +/* +The MIT License + +Copyright (c) 2017 Jordan Brown + +Permission is hereby granted, free of charge, +to any person obtaining a copy of this software and +associated documentation files (the "Software"), to +deal in the Software without restriction, including +without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom +the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ diff --git a/src/DMAPI/tgs/v4/commands.dm b/src/DMAPI/tgs/v4/commands.dm new file mode 100644 index 0000000000..48ca6e98de --- /dev/null +++ b/src/DMAPI/tgs/v4/commands.dm @@ -0,0 +1,65 @@ +/datum/tgs_api/v4/proc/ListCustomCommands() + var/results = list() + custom_commands = list() + for(var/I in typesof(/datum/tgs_chat_command) - /datum/tgs_chat_command) + var/datum/tgs_chat_command/stc = new I + var/command_name = stc.name + if(!command_name || findtext(command_name, " ") || findtext(command_name, "'") || findtext(command_name, "\"")) + TGS_ERROR_LOG("Custom command [command_name] ([I]) can't be used as it is empty or contains illegal characters!") + continue + + if(results[command_name]) + var/datum/other = custom_commands[command_name] + TGS_ERROR_LOG("Custom commands [other.type] and [I] have the same name (\"[command_name]\"), only [other.type] will be available!") + continue + results[command_name] = list("help_text" = stc.help_text, "admin_only" = stc.admin_only) + custom_commands[command_name] = stc + + var/commands_file = chat_commands_json_path + if(!commands_file) + return + text2file(json_encode(results), commands_file) + +/datum/tgs_api/v4/proc/HandleCustomCommand(command_json) + var/list/data = json_decode(command_json) + var/command = data["command"] + var/user = data["user"] + var/params = data["params"] + + var/datum/tgs_chat_user/u = new + u.id = user["id"] + u.friendly_name = user["friendly_name"] + u.mention = user["mention"] + u.channel = DecodeChannel(user["channel"]) + + var/datum/tgs_chat_command/sc = custom_commands[command] + var/result = sc.Run(u, params) + return json_encode(list("result" = result)) + +/* + +The MIT License + +Copyright (c) 2017 Jordan Brown + +Permission is hereby granted, free of charge, +to any person obtaining a copy of this software and +associated documentation files (the "Software"), to +deal in the Software without restriction, including +without limitation the rights to use, copy, modify, +merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom +the Software is furnished to do so, +subject to the following conditions: + +The above copyright notice and this permission notice +shall be included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES +OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR +ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. +*/ diff --git a/src/Tgstation.Server.Api/ApiHeaders.cs b/src/Tgstation.Server.Api/ApiHeaders.cs new file mode 100644 index 0000000000..597bad25cc --- /dev/null +++ b/src/Tgstation.Server.Api/ApiHeaders.cs @@ -0,0 +1,225 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Net.Http.Headers; +using System.Reflection; +using Microsoft.AspNetCore.Http.Headers; +using Microsoft.Net.Http.Headers; +using Microsoft.Extensions.Primitives; + +namespace Tgstation.Server.Api +{ + /// + /// Represents the header that must be present for every server request + /// + public sealed class ApiHeaders + { + /// + /// TODO: Remove this when https://github.com/dotnet/corefx/pull/26701 makes it into the sdk + /// + public const string ApplicationJson = "application/json"; + + /// + /// The header key + /// + const string usernameHeader = "Username"; + + /// + /// The header key + /// + const string instanceIdHeader = "InstanceId"; + + /// + /// The JWT authentication header scheme + /// + const string jwtAuthenticationScheme = "Bearer"; + + /// + /// The password authentication header scheme + /// + const string passwordAuthenticationScheme = "Password"; + + /// + /// The current + /// + static readonly AssemblyName assemblyName = Assembly.GetExecutingAssembly().GetName(); + + /// + /// The being accessed + /// + public long? InstanceId { get; set; } + + /// + /// The client's user agent + /// + public ProductHeaderValue UserAgent { get; } + + /// + /// The client's API version + /// + public Version ApiVersion { get; } + + /// + /// The client's JWT + /// + public string Token { get; } + + /// + /// The client's username + /// + public string Username { get; } + + /// + /// The client's password + /// + public string Password { get; } + + /// + /// If the header uses password or JWT authentication + /// + public bool IsTokenAuthentication => Token != null; + + /// + /// Construct for JWT authentication + /// + /// The value of + /// The value of + public ApiHeaders(ProductHeaderValue userAgent, string token) : this(userAgent, token, null, null) + { + if (userAgent == null) + throw new ArgumentNullException(nameof(userAgent)); + if (token == null) + throw new ArgumentNullException(nameof(token)); + } + + /// + /// Construct for password authentication + /// + /// The value of + /// The value of + /// The value of + public ApiHeaders(ProductHeaderValue userAgent, string username, string password) : this(userAgent, null, username, password) + { + if (userAgent == null) + throw new ArgumentNullException(nameof(userAgent)); + if (username == null) + throw new ArgumentNullException(nameof(username)); + if (password == null) + throw new ArgumentNullException(nameof(password)); + } + + /// + /// Construct and validates from a set of + /// + /// The containing the + public ApiHeaders(RequestHeaders requestHeaders) + { + var jsonAccept = new Microsoft.Net.Http.Headers.MediaTypeHeaderValue(ApplicationJson); + if (!requestHeaders.Accept.Any(x => x.MediaType == jsonAccept.MediaType)) + throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Client does not accept {0}!", ApplicationJson)); + + if (!requestHeaders.Headers.TryGetValue(HeaderNames.UserAgent, out var userAgentValues) || !ProductInfoHeaderValue.TryParse(userAgentValues.FirstOrDefault(), out var clientUserAgent)) + throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Missing {0} headers!", HeaderNames.UserAgent)); + + //assure the client user agent has a name and version + if (String.IsNullOrWhiteSpace(clientUserAgent.Product.Name) || !Version.TryParse(clientUserAgent.Product.Version, out var clientVersion)) + throw new InvalidOperationException("Malformed client user agent!"); + + var apiUserAgentHeader = userAgentValues[1]; + //make sure the api header matches ours + if (!ProductInfoHeaderValue.TryParse(apiUserAgentHeader, out var apiUserAgent) || apiUserAgent.Product.Name != assemblyName.Name) + throw new InvalidOperationException("Missing API user agent!"); + + if (!Version.TryParse(apiUserAgent.Product.Version, out var apiVersion)) + throw new InvalidOperationException("Malformed API version!"); + + ApiVersion = apiVersion; + + //check api version compatibility + var ourVersion = assemblyName.Version; + if (ourVersion.Major != ApiVersion.Major || ourVersion.Minor != ApiVersion.Minor || ourVersion.Build > ApiVersion.Build) + throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Given API version is incompatible with version {0}!", ourVersion)); + + if (!requestHeaders.Headers.TryGetValue(HeaderNames.Authorization, out StringValues authorization)) + throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Missing {0} header!", HeaderNames.Authorization)); + var auth = authorization.First(); + var splits = new List(auth.Split(' ')); + var scheme = splits.First(); + if (String.IsNullOrWhiteSpace(scheme)) + throw new InvalidOperationException("Missing authentication scheme!"); + + splits.RemoveAt(0); + var parameter = String.Concat(splits); + if (String.IsNullOrEmpty(parameter)) + throw new InvalidOperationException("Missing authentication parameter!"); + + if(requestHeaders.Headers.TryGetValue(instanceIdHeader, out var instanceIdValues)) + { + var instanceIdString = instanceIdValues.FirstOrDefault(); + if (instanceIdString != default && Int64.TryParse(instanceIdString, out var instanceId)) + InstanceId = instanceId; + } + + switch (scheme) + { + case jwtAuthenticationScheme: + Token = parameter; + break; + case passwordAuthenticationScheme: + Password = parameter; + var fail = !requestHeaders.Headers.TryGetValue(usernameHeader, out var values); + if (!fail) + { + Username = values.FirstOrDefault(); + fail = String.IsNullOrWhiteSpace(Username); + } + if (fail) + throw new InvalidOperationException("Missing Username header!"); + break; + default: + throw new InvalidOperationException("Invalid authentication scheme!"); + } + } + + /// + /// Construct + /// + /// The value of + /// The value of + /// The value of + /// The value of + ApiHeaders(ProductHeaderValue userAgent, string token, string username, string password) + { + UserAgent = userAgent; + Token = token; + Username = username; + Password = password; + ApiVersion = assemblyName.Version; + } + + /// + /// Set using the . This initially clears + /// + /// The to set + public void SetRequestHeaders(HttpRequestHeaders headers) + { + if (headers == null) + throw new ArgumentNullException(nameof(headers)); + + headers.Clear(); + headers.Accept.Add(new MediaTypeWithQualityHeaderValue(ApplicationJson)); + if (IsTokenAuthentication) + headers.Authorization = new AuthenticationHeaderValue(jwtAuthenticationScheme, Token); + else + { + headers.Authorization = new AuthenticationHeaderValue(passwordAuthenticationScheme, Password); + headers.Add(usernameHeader, Username); + } + headers.UserAgent.Add(new ProductInfoHeaderValue(UserAgent)); + headers.UserAgent.Add(new ProductInfoHeaderValue(new ProductHeaderValue(assemblyName.Name, ApiVersion.ToString()))); + if(InstanceId.HasValue) + headers.Add(instanceIdHeader, InstanceId.ToString()); + } + } +} diff --git a/src/Tgstation.Server.Api/ModelAttribute.cs b/src/Tgstation.Server.Api/ModelAttribute.cs new file mode 100644 index 0000000000..df50284cf3 --- /dev/null +++ b/src/Tgstation.Server.Api/ModelAttribute.cs @@ -0,0 +1,53 @@ +using System; +using Tgstation.Server.Api.Rights; + +namespace Tgstation.Server.Api +{ + /// + /// Indicates the rights for a model + /// + [AttributeUsage(AttributeTargets.Class, AllowMultiple = false, Inherited = true)] + public sealed class ModelAttribute : Attribute + { + /// + /// Right required to read the model + /// + public object ReadRight { get; set; } + + /// + /// Right required to update the model + /// + public object WriteRight { get; set; } + + /// + /// If the Create and Delete actions are available for this model + /// + public bool CanCrud { get; set; } + + /// + /// If the List action is available for this model + /// + public bool CanList { get; set; } + + /// + /// If the actions require an + /// + public bool RequiresInstance { get; set; } + + /// + /// The the model uses + /// + public RightsType RightsType { get; } + + /// + /// Construct a + /// + public ModelAttribute() => CanList = true; + + /// + /// Construct a with a given + /// + /// The value of + public ModelAttribute(RightsType rightsType) => RightsType = rightsType; + } +} diff --git a/src/Tgstation.Server.Api/Models/Administration.cs b/src/Tgstation.Server.Api/Models/Administration.cs new file mode 100644 index 0000000000..99421efdbe --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Administration.cs @@ -0,0 +1,27 @@ +using System; +using Tgstation.Server.Api.Rights; + +namespace Tgstation.Server.Api.Models +{ + /// + public sealed class Administration + { + /// + /// The GitHub repository the server is built to recieve updates from + /// + [Permissions(DenyWrite = true)] + public Uri TrackedRepositoryUrl { get; set; } + + /// + /// The latest available version of the Tgstation.Server.Host assembly from the upstream repository. If is higher than 's the update cannot be applied due to API changes + /// + [Permissions(DenyWrite = true)] + public Version LatestVersion { get; set; } + + /// + /// Changes the version of Tgstation.Server.Host to the given version from the upstream repository + /// + [Permissions(WriteRight = AdministrationRights.ChangeVersion)] + public Version CurrentVersion { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/Models/Byond.cs b/src/Tgstation.Server.Api/Models/Byond.cs new file mode 100644 index 0000000000..9eebf4ddd1 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Byond.cs @@ -0,0 +1,24 @@ +using System; +using Tgstation.Server.Api.Rights; + +namespace Tgstation.Server.Api.Models +{ + /// + /// Represents a BYOND installation + /// + [Model(RightsType.Byond, RequiresInstance = true)] + public sealed class Byond + { + /// + /// The of the installation used for new compiles. Will be if the user does not have permission to view it or there is no BYOND version installed. Only considers the and numbers + /// + [Permissions(ReadRight = ByondRights.ReadActive, WriteRight = ByondRights.ChangeVersion)] + public Version Version { get; set; } + + /// + /// The being used to install a new + /// + [Permissions(DenyWrite = true)] + public Job InstallJob { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/Models/ChatChannel.cs b/src/Tgstation.Server.Api/Models/ChatChannel.cs new file mode 100644 index 0000000000..946613c1b2 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/ChatChannel.cs @@ -0,0 +1,32 @@ +using System.ComponentModel.DataAnnotations; + +namespace Tgstation.Server.Api.Models +{ + /// + /// Indicates a chat channel + /// + public class ChatChannel + { + /// + /// The IRC channel name + /// + public string IrcChannel { get; set; } + + /// + /// The Discord channel ID + /// + public ulong? DiscordChannelId { get; set; } + + /// + /// If the is an admin channel + /// + [Required] + public bool? IsAdminChannel { get; set; } + + /// + /// If the is a watchdog channel + /// + [Required] + public bool? IsWatchdogChannel { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/Models/ChatProvider.cs b/src/Tgstation.Server.Api/Models/ChatProvider.cs new file mode 100644 index 0000000000..f41d9de9c5 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/ChatProvider.cs @@ -0,0 +1,17 @@ +namespace Tgstation.Server.Api.Models +{ + /// + /// Represents a chat service provider + /// + public enum ChatProvider + { + /// + /// Internet relay chat + /// + Irc, + /// + /// Superior chat service + /// + Discord + } +} diff --git a/src/Tgstation.Server.Api/Models/ChatSettings.cs b/src/Tgstation.Server.Api/Models/ChatSettings.cs new file mode 100644 index 0000000000..aab5431e9a --- /dev/null +++ b/src/Tgstation.Server.Api/Models/ChatSettings.cs @@ -0,0 +1,15 @@ +using System.Collections.Generic; +using Tgstation.Server.Api.Rights; + +namespace Tgstation.Server.Api.Models +{ + /// + public sealed class ChatSettings : Internal.ChatSettings + { + /// + /// Channels the Discord bot should listen/announce in + /// + [Permissions(WriteRight = ChatSettingsRights.WriteChannels)] + public List Channels { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/Models/CompileJob.cs b/src/Tgstation.Server.Api/Models/CompileJob.cs new file mode 100644 index 0000000000..44a3b7baad --- /dev/null +++ b/src/Tgstation.Server.Api/Models/CompileJob.cs @@ -0,0 +1,23 @@ +using System; + +namespace Tgstation.Server.Api.Models +{ + /// + public sealed class CompileJob : Internal.CompileJob + { + /// + /// The relating to this job + /// + public Job Job { get; set; } + + /// + /// Git revision the compiler ran on. Not modifiable + /// + public RevisionInformation RevisionInformation { get; set; } + + /// + /// The the was made with + /// + public Version ByondVersion { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/Models/CompilerStatus.cs b/src/Tgstation.Server.Api/Models/CompilerStatus.cs new file mode 100644 index 0000000000..d29df195e6 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/CompilerStatus.cs @@ -0,0 +1,43 @@ +namespace Tgstation.Server.Api.Models +{ + /// + /// Status of the for an + /// +#pragma warning disable CA1717 // Only FlagsAttribute enums should have plural names + public enum CompilerStatus +#pragma warning restore CA1717 // Only FlagsAttribute enums should have plural names + { + /// + /// The is idle + /// + Idle, + /// + /// The is being copied + /// + Copying, + /// + /// The .dme is having it's server side modifications applied + /// + Modifying, + /// + /// DreamMaker is running + /// + Compiling, + /// + /// The DMAPI is being verified + /// + Verifying, + /// + /// The compile results are being duplicated + /// + Duplicating, + /// + /// The configuration is being linked to the compile results + /// + Symlinking, + /// + /// A failed compile job is being erased + /// + Cleanup + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Api/Models/ConfigurationFile.cs b/src/Tgstation.Server.Api/Models/ConfigurationFile.cs new file mode 100644 index 0000000000..1c27df900c --- /dev/null +++ b/src/Tgstation.Server.Api/Models/ConfigurationFile.cs @@ -0,0 +1,41 @@ +using Tgstation.Server.Api.Models.Internal; +using Tgstation.Server.Api.Rights; + +namespace Tgstation.Server.Api.Models +{ + /// + /// Represents a game configuration file. Create and delete actions uncerimonuously overwrite/delete files + /// + [Model(RightsType.Configuration, CanCrud = true, CanList = true, RequiresInstance = true, ReadRight = ConfigurationRights.Read, WriteRight = ConfigurationRights.Write)] + public sealed class ConfigurationFile + { + /// + /// The path to the file + /// + [Permissions(DenyWrite = true)] + public string Path { get; set; } + + /// + /// If access to the file was denied for the operation + /// + [Permissions(DenyWrite = true)] + public bool? AccessDenied { get; set; } + + /// + /// If represents a directory + /// + [Permissions(DenyWrite = true)] + public bool? IsDirectory { get; set; } + + /// + /// The MD5 hash of the file when last read by the user. If this doesn't match during update actions, the write will be denied with + /// + [Permissions(DenyWrite = true)] + public string LastReadHash { get; set; } + + /// + /// The content of the . Will be if is or during listing and write operations + /// + public byte[] Content { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/Models/ConfigurationType.cs b/src/Tgstation.Server.Api/Models/ConfigurationType.cs new file mode 100644 index 0000000000..d322e79a75 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/ConfigurationType.cs @@ -0,0 +1,21 @@ +namespace Tgstation.Server.Api.Models +{ + /// + /// The type of configuration allowed on an + /// + public enum ConfigurationType + { + /// + /// Configuration editing is not allowed + /// + Disallowed, + /// + /// Configuration editing is allowed by all users on all files + /// + HostWrite, + /// + /// Co + /// + SystemIdentityWrite + } +} diff --git a/src/Tgstation.Server.Api/Models/DreamDaemon.cs b/src/Tgstation.Server.Api/Models/DreamDaemon.cs new file mode 100644 index 0000000000..3ca0f22d87 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/DreamDaemon.cs @@ -0,0 +1,47 @@ +using Tgstation.Server.Api.Models.Internal; +using Tgstation.Server.Api.Rights; + +namespace Tgstation.Server.Api.Models +{ + /// + /// Represents an instance of BYOND's DreamDaemon game server. Create action starts the server. Delete action shuts down the server + /// + public sealed class DreamDaemon : DreamDaemonSettings + { + /// + /// The live revision + /// + [Permissions(DenyWrite = true, ReadRight = DreamDaemonRights.ReadRevision)] + public CompileJob ActiveCompileJob { get; set; } + + /// + /// The next revision to go live + /// + [Permissions(DenyWrite = true, ReadRight = DreamDaemonRights.ReadRevision)] + public CompileJob StagedCompileJob { get; set; } + + /// + /// The current status of + /// + [Permissions(DenyWrite = true, ReadRight = DreamDaemonRights.ReadMetadata)] + public bool? Running { get; set; } + + /// + /// The current of + /// + [Permissions(DenyWrite = true, ReadRight = DreamDaemonRights.ReadMetadata)] + public DreamDaemonSecurity? CurrentSecurity { get; set; } + + /// + /// The port the running instance is set to + /// + [Permissions(DenyWrite = true, ReadRight = DreamDaemonRights.ReadMetadata)] + public ushort? CurrentPort { get; set; } + + /// + /// The webclient status the running instance is set to + /// + [Permissions(DenyWrite = true, ReadRight = DreamDaemonRights.ReadMetadata)] + public bool? CurrentAllowWebclient { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/Models/DreamDaemonSecurity.cs b/src/Tgstation.Server.Api/Models/DreamDaemonSecurity.cs new file mode 100644 index 0000000000..094332d1e4 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/DreamDaemonSecurity.cs @@ -0,0 +1,21 @@ +namespace Tgstation.Server.Api.Models +{ + /// + /// DreamDaemon's security level + /// + public enum DreamDaemonSecurity + { + /// + /// Server is unrestricted in terms of file access and shell commands + /// + Trusted, + /// + /// Server will not be able to run shell commands or access files outside it's working directory + /// + Safe, + /// + /// Server will not be able to run shell commands or access anything but temporary files + /// + Ultrasafe + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Api/Models/DreamDaemonStatus.cs b/src/Tgstation.Server.Api/Models/DreamDaemonStatus.cs new file mode 100644 index 0000000000..a09c75d399 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/DreamDaemonStatus.cs @@ -0,0 +1,23 @@ +namespace Tgstation.Server.Api.Models +{ + /// + /// The status of + /// +#pragma warning disable CA1717 // Only FlagsAttribute enums should have plural names + public enum DreamDaemonStatus +#pragma warning restore CA1717 // Only FlagsAttribute enums should have plural names + { + /// + /// Server is not running + /// + Offline, + /// + /// Server is being rebooted + /// + HardRebooting, + /// + /// Server is running + /// + Online, + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Api/Models/DreamMaker.cs b/src/Tgstation.Server.Api/Models/DreamMaker.cs new file mode 100644 index 0000000000..de1d893846 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/DreamMaker.cs @@ -0,0 +1,23 @@ +using Tgstation.Server.Api.Models.Internal; +using Tgstation.Server.Api.Rights; + +namespace Tgstation.Server.Api.Models +{ + /// + /// Represents the state of the DreamMaker compiler. Create action starts a new compile. Delete action cancels the current compile + /// + public sealed class DreamMaker : DreamMakerSettings + { + /// + /// The last ran + /// + [Permissions(DenyWrite = true)] + public CompileJob LastJob { get; set; } + + /// + /// The of the compiler + /// + [Permissions(DenyWrite = true)] + public CompilerStatus Status { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/Models/Instance.cs b/src/Tgstation.Server.Api/Models/Instance.cs new file mode 100644 index 0000000000..01e41bbca7 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Instance.cs @@ -0,0 +1,69 @@ +using System.ComponentModel.DataAnnotations; +using System.ComponentModel.DataAnnotations.Schema; +using Tgstation.Server.Api.Rights; + +namespace Tgstation.Server.Api.Models +{ + /// + /// Metadata about a server instance + /// + [Model(RightsType.InstanceManager, CanCrud = true, CanList = true)] + public class Instance + { + /// + /// The id of the . Not modifiable + /// + [Permissions(DenyWrite = true)] + public long Id { get; set; } + + /// + /// The name of the + /// + [Permissions(WriteRight = InstanceManagerRights.Rename)] + [Required] + public string Name { get; set; } + + /// + /// The path to where the is located. Changing this will temporarily offline the while it moves + /// + [Permissions(WriteRight = InstanceManagerRights.Relocate)] + [Required] + public string Path { get; set; } + + /// + /// If the is online + /// + [Permissions(WriteRight = InstanceManagerRights.SetOnline)] + public bool? Online { get; set; } + + /// + /// If can be used on the + /// + [Permissions(WriteRight = InstanceManagerRights.SetConfiguration)] + [Required] + public ConfigurationType? ConfigurationType { get; set; } + + /// + /// The time interval in minutes the repository is automatically pulled and compiles + /// + [Permissions(WriteRight = InstanceManagerRights.SetAutoUpdate)] + public int? AutoUpdateInterval { get; set; } + + /// + /// The representing a change of + /// + [Permissions(DenyWrite = true)] + [NotMapped] + public Job MoveJob { get; set; } + + /// + public Instance CloneMetadata() => new Instance + { + Id = Id, + Name = Name, + Path = Path, + Online = Online, + ConfigurationType = ConfigurationType + }; + } +} diff --git a/src/Tgstation.Server.Api/Models/InstanceUser.cs b/src/Tgstation.Server.Api/Models/InstanceUser.cs new file mode 100644 index 0000000000..2732ef66c3 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/InstanceUser.cs @@ -0,0 +1,68 @@ +using System.ComponentModel.DataAnnotations; +using Tgstation.Server.Api.Rights; + +namespace Tgstation.Server.Api.Models +{ + /// + /// Represents a s permissions in an + /// + [Model(RightsType.InstanceUser, WriteRight = Rights.InstanceUserRights.WriteUsers, CanList = true, RequiresInstance = true)] + public class InstanceUser + { + /// + /// The of the the belongs to + /// + [Permissions(DenyWrite = true)] + [Required] + public long? UserId { get; set; } + + /// + /// The of the the belongs to + /// + [Permissions(DenyWrite = true)] + [Required] + public long? InstanceId { get; set; } + + /// + /// The of the + /// + [Required] + public InstanceUserRights? InstanceUserRights { get; set; } + + /// + /// The of the + /// + [Required] + public ByondRights? ByondRights { get; set; } + + /// + /// The of the + /// + [Required] + public DreamDaemonRights? DreamDaemonRights { get; set; } + + /// + /// The of the + /// + [Required] + public DreamMakerRights? DreamMakerRights { get; set; } + + /// + /// The of the + /// + [Required] + public RepositoryRights? RepositoryRights { get; set; } + + /// + /// The of the + /// + [Required] + public ChatSettingsRights? ChatSettingsRights { get; set; } + + /// + /// The of the + /// + [Required] + public ConfigurationRights? ConfigurationRights { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/Models/Internal/ChatSettings.cs b/src/Tgstation.Server.Api/Models/Internal/ChatSettings.cs new file mode 100644 index 0000000000..2a23052ad8 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Internal/ChatSettings.cs @@ -0,0 +1,44 @@ +using System.ComponentModel.DataAnnotations; +using Tgstation.Server.Api.Rights; + +namespace Tgstation.Server.Api.Models.Internal +{ + /// + /// Manage the server chat bots + /// + [Model(RightsType.ChatSettings, RequiresInstance = true, CanList = true, CanCrud = true, ReadRight = ChatSettingsRights.Read)] + public class ChatSettings + { + /// + /// The settings id + /// + [Permissions(DenyWrite = true)] + public long Id { get; set; } + + /// + /// The name of the connection + /// + [Permissions(WriteRight = ChatSettingsRights.WriteName)] + [Required] + public string Name { get; set; } + + /// + /// If the connection is enabled + /// + [Permissions(WriteRight = ChatSettingsRights.WriteEnabled)] + public bool? Enabled { get; set; } + + /// + /// The used for the connection + /// + [Permissions(WriteRight = ChatSettingsRights.WriteProvider)] + public ChatProvider? Provider { get; set; } + + /// + /// The information used to connect to the + /// + [Permissions(ReadRight = ChatSettingsRights.ReadConnectionString, WriteRight = ChatSettingsRights.ReadConnectionString)] + [Required] + public string ConnectionString { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/Models/Internal/CompileJob.cs b/src/Tgstation.Server.Api/Models/Internal/CompileJob.cs new file mode 100644 index 0000000000..f2863fd0b5 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Internal/CompileJob.cs @@ -0,0 +1,40 @@ +using System; + +namespace Tgstation.Server.Api.Models.Internal +{ + /// + /// Represents a run of + /// + public class CompileJob + { + /// + /// The ID of the job + /// + public long Id { get; set; } + + /// + /// If the DMAPI version detected is compatible + /// + public bool? DMApiValidated { get; set; } + + /// + /// The .dme file used for compilation + /// + public string DmeName { get; set; } + + /// + /// Textual output of DM + /// + public string Output { get; set; } + + /// + /// The Game folder the results were compiled into + /// + public Guid? DirectoryName { get; set; } + + /// + /// Exit code of DM. If + /// + public int? ExitCode { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/Models/Internal/DreamDaemonLaunchParameters.cs b/src/Tgstation.Server.Api/Models/Internal/DreamDaemonLaunchParameters.cs new file mode 100644 index 0000000000..f52315595f --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Internal/DreamDaemonLaunchParameters.cs @@ -0,0 +1,47 @@ +using System.ComponentModel.DataAnnotations; +using Tgstation.Server.Api.Rights; + +namespace Tgstation.Server.Api.Models.Internal +{ + /// + /// Launch settings for DreamDaemon + /// + [Model(RightsType.DreamDaemon, CanCrud = true, RequiresInstance = true)] + public class DreamDaemonLaunchParameters + { + /// + /// If the BYOND web client can be used to connect to the game server + /// + [Permissions(ReadRight = DreamDaemonRights.ReadMetadata, WriteRight = DreamDaemonRights.SetWebClient)] + [Required] + public bool? AllowWebClient { get; set; } + + /// + /// The level of + /// + [Permissions(ReadRight = DreamDaemonRights.ReadMetadata, WriteRight = DreamDaemonRights.SetSecurity)] + [Required] + public DreamDaemonSecurity? SecurityLevel { get; set; } + + /// + /// The first port uses. This should be the publically advertised port + /// + [Permissions(ReadRight = DreamDaemonRights.ReadMetadata, WriteRight = DreamDaemonRights.SetPorts)] + [Required] + public ushort? PrimaryPort { get; set; } + + /// + /// The second port uses + /// + [Permissions(ReadRight = DreamDaemonRights.ReadMetadata, WriteRight = DreamDaemonRights.SetPorts)] + [Required] + public ushort? SecondaryPort { get; set; } + + /// + /// The DreamDaemon startup timeout in seconds + /// + [Permissions(ReadRight = DreamDaemonRights.ReadMetadata, WriteRight = DreamDaemonRights.SetStartupTimeout)] + [Required] + public int? StartupTimeout { get; set; } + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Api/Models/Internal/DreamDaemonSettings.cs b/src/Tgstation.Server.Api/Models/Internal/DreamDaemonSettings.cs new file mode 100644 index 0000000000..72260ee5fc --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Internal/DreamDaemonSettings.cs @@ -0,0 +1,32 @@ +using System.ComponentModel.DataAnnotations; +using Tgstation.Server.Api.Rights; + +namespace Tgstation.Server.Api.Models.Internal +{ + /// + /// Configurable settings for + /// + public class DreamDaemonSettings : DreamDaemonLaunchParameters + { + /// + /// If starts when it's starts + /// + [Permissions(ReadRight = DreamDaemonRights.ReadMetadata, WriteRight = DreamDaemonRights.SetAutoStart)] + [Required] + public bool? AutoStart { get; set; } + + /// + /// If the server is undergoing a soft reset. This may be automatically set by changes to other fields + /// + [Permissions(ReadRight = DreamDaemonRights.ReadMetadata, WriteRight = DreamDaemonRights.SoftRestart)] + [Required] + public bool? SoftRestart { get; set; } + + /// + /// If the server is undergoing a soft shutdown + /// + [Permissions(ReadRight = DreamDaemonRights.ReadMetadata, WriteRight = DreamDaemonRights.SoftShutdown)] + [Required] + public bool? SoftShutdown { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/Models/Internal/DreamMakerSettings.cs b/src/Tgstation.Server.Api/Models/Internal/DreamMakerSettings.cs new file mode 100644 index 0000000000..154167ec82 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Internal/DreamMakerSettings.cs @@ -0,0 +1,17 @@ +using Tgstation.Server.Api.Rights; + +namespace Tgstation.Server.Api.Models.Internal +{ + /// + /// Configurable settings for + /// + [Model(RightsType.DreamMaker, ReadRight = DreamMakerRights.Read, CanCrud = true, RequiresInstance = true)] + public class DreamMakerSettings + { + /// + /// The .dme file tries to compile with without the extension + /// + [Permissions(WriteRight = DreamMakerRights.SetDme)] + public string ProjectName { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/Models/Internal/Job.cs b/src/Tgstation.Server.Api/Models/Internal/Job.cs new file mode 100644 index 0000000000..b4247221e1 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Internal/Job.cs @@ -0,0 +1,64 @@ +using System; +using System.ComponentModel.DataAnnotations; +using Tgstation.Server.Api.Rights; + +namespace Tgstation.Server.Api.Models.Internal +{ + /// + /// Represents a long running job + /// + [Model(RequiresInstance = true)] + public class Job + { + /// + /// The ID + /// + [Permissions(DenyWrite = true)] + public long Id { get; set; } + + /// + /// English description of the + /// + [Permissions(DenyWrite = true)] + [Required] + public string Description { get; set; } + + /// + /// Details of any exceptions caught during the + /// + [Permissions(DenyWrite = true)] + public string ExceptionDetails { get; set; } + + /// + /// When the was started + /// + [Permissions(DenyWrite = true)] + [Required] + public DateTimeOffset? StartedAt { get; set; } + + /// + /// When the stopped + /// + [Permissions(DenyWrite = true)] + public DateTimeOffset? StoppedAt { get; set; } + + /// + /// If the was cancelled + /// + [Permissions(DenyWrite = true)] + [Required] + public bool? Cancelled { get; set; } + + /// + /// The of if it can be cancelled + /// + [Permissions(DenyWrite = true)] + public RightsType? CancelRightsType { get; set; } + + /// + /// The required to cancel the + /// + [Permissions(DenyWrite = true)] + public int? CancelRight { get; set; } + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Api/Models/Internal/RepositorySettings.cs b/src/Tgstation.Server.Api/Models/Internal/RepositorySettings.cs new file mode 100644 index 0000000000..14dac247ab --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Internal/RepositorySettings.cs @@ -0,0 +1,66 @@ +using System.ComponentModel.DataAnnotations; +using Tgstation.Server.Api.Rights; + +namespace Tgstation.Server.Api.Models.Internal +{ + /// + /// Represents configurable settings for a + /// + [Model(RightsType.Repository, ReadRight = RepositoryRights.Read, RequiresInstance = true)] + public class RepositorySettings + { + /// + /// The name of the committer + /// + [Permissions(WriteRight = RepositoryRights.ChangeCommitter)] + [Required] + public string CommitterName { get; set; } + + /// + /// The e-mail of the committer + /// + [Permissions(WriteRight = RepositoryRights.ChangeCommitter)] + [Required] + public string CommitterEmail { get; set; } + + /// + /// The username to access the git repository with + /// + [Permissions(ReadRight = RepositoryRights.ChangeCredentials, WriteRight = RepositoryRights.ChangeCredentials)] + public string AccessUser { get; set; } + + /// + /// The token/password to access the git repository with + /// + [Permissions(ReadRight = RepositoryRights.ChangeCredentials, WriteRight = RepositoryRights.ChangeCredentials)] + public string AccessToken { get; set; } + + /// + /// If commits created from testmerges are pushed to the remote + /// + [Permissions(WriteRight = RepositoryRights.ChangeTestMergeCommits)] + [Required] + public bool? PushTestMergeCommits { get; set; } + + /// + /// If test merge commits are signed with the username of the person who merged it. Note this only affects future commits + /// + [Permissions(WriteRight = RepositoryRights.ChangeTestMergeCommits)] + [Required] + public bool? ShowTestMergeCommitters { get; set; } + + /// + /// If test merge commits should be kept when auto updating. May cause merge conflicts which will block the update + /// + [Permissions(WriteRight = RepositoryRights.ChangeTestMergeCommits)] + [Required] + public bool? AutoUpdatesKeepTestMerges { get; set; } + + /// + /// If synchronization should occur when auto updating + /// + [Permissions(WriteRight = RepositoryRights.ChangeTestMergeCommits)] + [Required] + public bool? AutoUpdatesSynchronize { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/Models/Internal/RevisionInformation.cs b/src/Tgstation.Server.Api/Models/Internal/RevisionInformation.cs new file mode 100644 index 0000000000..159201a17d --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Internal/RevisionInformation.cs @@ -0,0 +1,24 @@ +using System.ComponentModel.DataAnnotations; + +namespace Tgstation.Server.Api.Models.Internal +{ + /// + /// Represents information about a current git revison + /// + public class RevisionInformation + { + /// + /// The revision sha + /// + [Permissions(DenyWrite = true)] + [Required, StringLength(40)] + public string CommitSha { get; set; } + + /// + /// The sha of the most recent remote commit + /// + [Permissions(DenyWrite = true)] + [Required, StringLength(40)] + public string OriginCommitSha { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/Models/Internal/TestMerge.cs b/src/Tgstation.Server.Api/Models/Internal/TestMerge.cs new file mode 100644 index 0000000000..715620ba93 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Internal/TestMerge.cs @@ -0,0 +1,48 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace Tgstation.Server.Api.Models.Internal +{ + /// + /// Represents a merge of a GitHub pull request + /// + public class TestMerge : TestMergeParameters + { + /// + /// The ID of the + /// + public long Id { get; set; } + + /// + /// When the was created + /// + [Required] + public DateTimeOffset MergedAt { get; set; } + + /// + /// The title of the pull request + /// + [Required] + public string TitleAtMerge { get; set; } + + /// + /// The body of the pull request + /// + [Required] + public string BodyAtMerge { get; set; } + + /// + /// The URL of the pull request + /// + [Required] +#pragma warning disable CA1056 // Uri properties should not be strings + public string Url { get; set; } +#pragma warning restore CA1056 // Uri properties should not be strings + + /// + /// The author of the pull request + /// + [Required] + public string Author { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/Models/Internal/User.cs b/src/Tgstation.Server.Api/Models/Internal/User.cs new file mode 100644 index 0000000000..1e6ab36b2e --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Internal/User.cs @@ -0,0 +1,56 @@ +using System; +using System.ComponentModel.DataAnnotations; +using Tgstation.Server.Api.Rights; + +namespace Tgstation.Server.Api.Models.Internal +{ + /// + /// Represents a server + /// + [Model(RightsType.Administration, WriteRight = Rights.AdministrationRights.EditUsers, CanCrud = true)] + public class User + { + /// + /// The ID of the + /// + [Permissions(DenyWrite = true)] + public long Id { get; set; } + + /// + /// If the is enabled since users cannot be deleted. System users cannot be disabled + /// + [Required] + public bool? Enabled { get; set; } + + /// + /// When the was created + /// + [Permissions(DenyWrite = true)] + [Required] + public DateTimeOffset? CreatedAt { get; set; } + + /// + /// The SID/UID of the on Windows/POSIX respectively + /// + [Permissions(DenyWrite = true)] + public string SystemIdentifier { get; set; } + + /// + /// The name of the + /// + [Required] + public string Name { get; set; } + + /// + /// The for the + /// + [Required] + public AdministrationRights? AdministrationRights { get; set; } + + /// + /// The for the + /// + [Required] + public InstanceManagerRights? InstanceManagerRights { get; set; } + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Api/Models/Job.cs b/src/Tgstation.Server.Api/Models/Job.cs new file mode 100644 index 0000000000..3ac1549daa --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Job.cs @@ -0,0 +1,25 @@ +namespace Tgstation.Server.Api.Models +{ + /// + /// Represents a long running job on the server. Model is read-only, updates attempt to cancel the job + /// + public sealed class Job : Internal.Job + { + /// + /// The that started the job + /// + [Permissions(DenyWrite = true)] + public User StartedBy { get; set; } + + /// + /// The that cancelled the job + /// + [Permissions(DenyWrite = true)] + public User CancelledBy { get; set; } + + /// + /// Optional progress between 0 and 100 inclusive + /// + public int? Progress { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/Models/Repository.cs b/src/Tgstation.Server.Api/Models/Repository.cs new file mode 100644 index 0000000000..9b65efa513 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Repository.cs @@ -0,0 +1,59 @@ +using System.Collections.Generic; +using Tgstation.Server.Api.Rights; + +namespace Tgstation.Server.Api.Models +{ + /// + /// Represents a git repository + /// + public sealed class Repository : Internal.RepositorySettings + { + /// + /// The origin URL. If , the does not exist + /// + [Permissions(WriteRight = RepositoryRights.SetOrigin)] + public string Origin { get; set; } + + /// + /// The commit HEAD should point to. Not populated in responses, use instead for retrieval + /// + [Permissions(WriteRight = RepositoryRights.SetSha)] + public string CheckoutSha { get; set; } + + /// + /// The current for the + /// + [Permissions(DenyWrite = true)] + public RevisionInformation RevisionInformation { get; set; } + + /// + /// If the repository was cloned from GitHub.com. If this enables test merge functionality + /// + [Permissions(DenyWrite = true)] + public bool? IsGitHub { get; set; } + + /// + /// The started by the if any + /// + [Permissions(DenyWrite = true)] + public Job ActiveJob { get; set; } + + /// + /// Do the equivalent of a git pull. Will attempt to merge unless is also specified in which case a hard reset will be performed after checking out + /// + [Permissions(WriteRight = RepositoryRights.UpdateBranch)] + public bool? UpdateFromOrigin { get; set; } + + /// + /// The branch or tag HEAD points to + /// + [Permissions(WriteRight = RepositoryRights.SetReference)] + public string Reference { get; set; } + + /// + /// for new s. Note that merges that conflict will not be performed + /// + [Permissions(WriteRight = RepositoryRights.MergePullRequest)] + public List NewTestMerges { get; set; } + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Api/Models/RevisionInformation.cs b/src/Tgstation.Server.Api/Models/RevisionInformation.cs new file mode 100644 index 0000000000..0340478d97 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/RevisionInformation.cs @@ -0,0 +1,23 @@ +using System.Collections.Generic; + +namespace Tgstation.Server.Api.Models +{ + /// + public sealed class RevisionInformation : Internal.RevisionInformation + { + /// + /// The that was created with this + /// + public TestMerge PrimaryTestMerge { get; set; } + + /// + /// The s active in the + /// + public List ActiveTestMerges { get; set; } + + /// + /// The s made from the + /// + public List CompileJobs { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/Models/TestMerge.cs b/src/Tgstation.Server.Api/Models/TestMerge.cs new file mode 100644 index 0000000000..d448504e2d --- /dev/null +++ b/src/Tgstation.Server.Api/Models/TestMerge.cs @@ -0,0 +1,11 @@ +namespace Tgstation.Server.Api.Models +{ + /// + public sealed class TestMerge : Internal.TestMerge + { + /// + /// The who created the + /// + public User MergedBy { get; set; } + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Api/Models/TestMergeParameters.cs b/src/Tgstation.Server.Api/Models/TestMergeParameters.cs new file mode 100644 index 0000000000..ed27e6bb08 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/TestMergeParameters.cs @@ -0,0 +1,26 @@ +using System.ComponentModel.DataAnnotations; + +namespace Tgstation.Server.Api.Models +{ + /// + /// Parameters for creating a + /// + public class TestMergeParameters + { + /// + /// The number of the pull request + /// + public int Number { get; set; } + + /// + /// The sha of the pull request revision to merge + /// + [Required] + public string PullRequestRevision { get; set; } + + /// + /// Optional comment about the test + /// + public string Comment { get; set; } + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Api/Models/Token.cs b/src/Tgstation.Server.Api/Models/Token.cs new file mode 100644 index 0000000000..9ec9a5edd5 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/Token.cs @@ -0,0 +1,13 @@ +namespace Tgstation.Server.Api.Models +{ + /// + /// Represents a JWT returned by the API + /// + public sealed class Token + { + /// + /// The value of the JWT + /// + public string Bearer { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/Models/User.cs b/src/Tgstation.Server.Api/Models/User.cs new file mode 100644 index 0000000000..a18856a366 --- /dev/null +++ b/src/Tgstation.Server.Api/Models/User.cs @@ -0,0 +1,12 @@ +namespace Tgstation.Server.Api.Models +{ + /// + public class User : Internal.User + { + /// + /// The who created this + /// + [Permissions(DenyWrite = true)] + public User CreatedBy { get; set; } + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Api/Models/UserUpdate.cs b/src/Tgstation.Server.Api/Models/UserUpdate.cs new file mode 100644 index 0000000000..4203beed1b --- /dev/null +++ b/src/Tgstation.Server.Api/Models/UserUpdate.cs @@ -0,0 +1,13 @@ +namespace Tgstation.Server.Api.Models +{ + /// + /// For editing a given . Will never be returned by the API + /// + public sealed class UserUpdate : User + { + /// + /// Cleartext password of the + /// + public string Password { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/PermissionsAttribute.cs b/src/Tgstation.Server.Api/PermissionsAttribute.cs new file mode 100644 index 0000000000..7d4962758f --- /dev/null +++ b/src/Tgstation.Server.Api/PermissionsAttribute.cs @@ -0,0 +1,26 @@ +using System; + +namespace Tgstation.Server.Api +{ + /// + /// Indicates permissions for model fields + /// + [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)] + public sealed class PermissionsAttribute : Attribute + { + /// + /// Right required to read the field + /// + public object ReadRight { get; set; } + + /// + /// Right required to update the field + /// + public object WriteRight { get; set; } + + /// + /// If the field cannot be written to + /// + public bool DenyWrite { get; set; } + } +} diff --git a/src/Tgstation.Server.Api/Rights/AdministrationRights.cs b/src/Tgstation.Server.Api/Rights/AdministrationRights.cs new file mode 100644 index 0000000000..3aaad4d552 --- /dev/null +++ b/src/Tgstation.Server.Api/Rights/AdministrationRights.cs @@ -0,0 +1,28 @@ +using System; + +namespace Tgstation.Server.Api.Rights +{ + /// + /// Rights for + /// + [Flags] + public enum AdministrationRights + { + /// + /// User has no rights + /// + None = 0, + /// + /// User can edit themself and other s + /// + EditUsers = 1, + /// + /// User can gracefully restart the host + /// + RestartHost = 2, + /// + /// User can change + /// + ChangeVersion = 4 + } +} diff --git a/src/Tgstation.Server.Api/Rights/ByondRights.cs b/src/Tgstation.Server.Api/Rights/ByondRights.cs new file mode 100644 index 0000000000..ed08ed282a --- /dev/null +++ b/src/Tgstation.Server.Api/Rights/ByondRights.cs @@ -0,0 +1,32 @@ +using System; + +namespace Tgstation.Server.Api.Rights +{ + /// + /// Rights for + /// + [Flags] + public enum ByondRights + { + /// + /// User has no rights + /// + None = 0, + /// + /// User may check the active installed BYOND version + /// + ReadActive = 1, + /// + /// User may list all installed BYOND versions + /// + ListInstalled = 2, + /// + /// User may change the active BYOND version + /// + ChangeVersion = 4, + /// + /// User may cancel version installations + /// + CancelInstall = 8 + } +} diff --git a/src/Tgstation.Server.Api/Rights/ChatSettingsRights.cs b/src/Tgstation.Server.Api/Rights/ChatSettingsRights.cs new file mode 100644 index 0000000000..a071e2bf38 --- /dev/null +++ b/src/Tgstation.Server.Api/Rights/ChatSettingsRights.cs @@ -0,0 +1,52 @@ +using System; + +namespace Tgstation.Server.Api.Rights +{ + /// + /// Rights for + /// + [Flags] + public enum ChatSettingsRights + { + /// + /// User has no rights + /// + None = 0, + /// + /// User can change + /// + WriteEnabled = 1, + /// + /// User can change + /// + WriteProvider = 2, + /// + /// User can change + /// + WriteChannels = 4, + /// + /// User can change + /// + WriteConnectionString = 8, + /// + /// User can read requires + /// + ReadConnectionString = 16, + /// + /// User can read all chat settings except + /// + Read = 32, + /// + /// User can change + /// + WriteName = 32, + /// + /// User can create new + /// + Create = 64, + /// + /// User can delete + /// + Delete = 128 + } +} diff --git a/src/Tgstation.Server.Api/Rights/ConfigurationRights.cs b/src/Tgstation.Server.Api/Rights/ConfigurationRights.cs new file mode 100644 index 0000000000..3008353ffa --- /dev/null +++ b/src/Tgstation.Server.Api/Rights/ConfigurationRights.cs @@ -0,0 +1,28 @@ +using System; + +namespace Tgstation.Server.Api.Rights +{ + /// + /// Rights for + /// + [Flags] + public enum ConfigurationRights + { + /// + /// User has no rights + /// + None = 0, + /// + /// User may read files + /// + Read = 1, + /// + /// User may write files + /// + Write = 2, + /// + /// User may list files + /// + List = 3 + } +} diff --git a/src/Tgstation.Server.Api/Rights/DreamDaemonRights.cs b/src/Tgstation.Server.Api/Rights/DreamDaemonRights.cs new file mode 100644 index 0000000000..86665af07b --- /dev/null +++ b/src/Tgstation.Server.Api/Rights/DreamDaemonRights.cs @@ -0,0 +1,64 @@ +using System; + +namespace Tgstation.Server.Api.Rights +{ + /// + /// Rights for + /// + [Flags] + public enum DreamDaemonRights + { + /// + /// User has no rights + /// + None = 0, + /// + /// User can read and + /// + ReadRevision = 1, + /// + /// User can change both primary and secondary ports + /// + SetPorts = 2, + /// + /// User can change + /// + SetAutoStart = 4, + /// + /// User set + /// + SetSecurity = 8, + /// + /// User can read all ports, , , , , and + /// + ReadMetadata = 16, + /// + /// User can change + /// + SetWebClient = 32, + /// + /// User can enable + /// + SoftRestart = 64, + /// + /// User can enable + /// + SoftShutdown = 128, + /// + /// User can immediately restart + /// + Restart = 256, + /// + /// User can immediately shutdown + /// + Shutdown = 512, + /// + /// User can start and disable and + /// + Start = 1024, + /// + /// User can change + /// + SetStartupTimeout = 2048 + } +} diff --git a/src/Tgstation.Server.Api/Rights/DreamMakerRights.cs b/src/Tgstation.Server.Api/Rights/DreamMakerRights.cs new file mode 100644 index 0000000000..bce942c165 --- /dev/null +++ b/src/Tgstation.Server.Api/Rights/DreamMakerRights.cs @@ -0,0 +1,32 @@ +using System; + +namespace Tgstation.Server.Api.Rights +{ + /// + /// Rights for + /// + [Flags] + public enum DreamMakerRights + { + /// + /// User has no rights + /// + None = 0, + /// + /// User may read status + /// + Read = 1, + /// + /// User may trigger compiles + /// + Compile = 2, + /// + /// User may cancel compiles + /// + CancelCompile = 4, + /// + /// User may modify + /// + SetDme = 8 + } +} diff --git a/src/Tgstation.Server.Api/Rights/InstanceManagerRights.cs b/src/Tgstation.Server.Api/Rights/InstanceManagerRights.cs new file mode 100644 index 0000000000..f8997edce3 --- /dev/null +++ b/src/Tgstation.Server.Api/Rights/InstanceManagerRights.cs @@ -0,0 +1,56 @@ +using System; + +namespace Tgstation.Server.Api.Rights +{ + /// + /// Rights for managing s + /// + [Flags] + public enum InstanceManagerRights + { + /// + /// User has no rights + /// + None = 0, + /// + /// User can view s which they have any rights for + /// + Read = 1, + /// + /// User can create s + /// + Create = 2, + /// + /// User can rename s they can view + /// + Rename = 4, + /// + /// User can relocate s they can view + /// + Relocate = 8, + /// + /// User can online s they can view + /// + SetOnline = 16, + /// + /// User can delete s they can view + /// + Delete = 32, + /// + /// User can view all s + /// + List = 64, + /// + /// User can change + /// + SetConfiguration = 128, + /// + /// User can change + /// + SetAutoUpdate = 256, + /// + /// User can cancel move operations + /// + CancelMove = 512, + } +} diff --git a/src/Tgstation.Server.Api/Rights/InstanceUserRights.cs b/src/Tgstation.Server.Api/Rights/InstanceUserRights.cs new file mode 100644 index 0000000000..25b85acfbd --- /dev/null +++ b/src/Tgstation.Server.Api/Rights/InstanceUserRights.cs @@ -0,0 +1,24 @@ +using System; + +namespace Tgstation.Server.Api.Rights +{ + /// + /// Rights for an + /// + [Flags] + public enum InstanceUserRights + { + /// + /// User has no rights + /// + None = 0, + /// + /// Allow read access to for the + /// + ReadUsers = 1, + /// + /// Allow write access to for the + /// + WriteUsers = 2 + } +} diff --git a/src/Tgstation.Server.Api/Rights/RepositoryRights.cs b/src/Tgstation.Server.Api/Rights/RepositoryRights.cs new file mode 100644 index 0000000000..6c86be2419 --- /dev/null +++ b/src/Tgstation.Server.Api/Rights/RepositoryRights.cs @@ -0,0 +1,68 @@ +using System; + +namespace Tgstation.Server.Api.Rights +{ + /// + /// Rights for a + /// + [Flags] + public enum RepositoryRights + { + /// + /// User has no rights + /// + None = 0, + /// + /// User may create the if it does not exist + /// + SetOrigin = 2, + /// + /// User may directly set the sha the 's HEAD points to + /// + SetSha = 4, + /// + /// User may fetch and merge GitHub pull requests + /// + MergePullRequest = 8, + /// + /// User may use the update feature + /// + UpdateBranch = 16, + /// + /// User may change and + /// + ChangeCommitter = 32, + /// + /// User may change + /// + ChangeTestMergeCommits = 64, + /// + /// User may read and change and + /// + ChangeCredentials = 128, + /// + /// User may set to another git reference (not a SHA) + /// + SetReference = 256, + /// + /// User may read all fields in the with the exception of + /// + Read = 512, + /// + /// User may change and + /// + ChangeAutoUpdateSettings = 1024, + /// + /// User may delete the + /// + Delete = 2048, + /// + /// User may cancel clone operations + /// + CancelClone = 4096, + /// + /// User may cancel synchronize operations + /// + CancelPendingChanges = 8192 + } +} diff --git a/src/Tgstation.Server.Api/Rights/RightsHelper.cs b/src/Tgstation.Server.Api/Rights/RightsHelper.cs new file mode 100644 index 0000000000..cbd83a7ca5 --- /dev/null +++ b/src/Tgstation.Server.Api/Rights/RightsHelper.cs @@ -0,0 +1,74 @@ +using System; +using System.Collections.Generic; +using System.Globalization; + +namespace Tgstation.Server.Api.Rights +{ + /// + /// Helper for s + /// + public static class RightsHelper + { + /// + /// Map of s to their respective flag s + /// + static readonly IReadOnlyDictionary typeMap = new Dictionary + { + { RightsType.Administration, typeof(AdministrationRights) }, + { RightsType.InstanceManager, typeof(InstanceManagerRights) }, + + { RightsType.Repository, typeof(RepositoryRights) }, + { RightsType.Byond, typeof(ByondRights) }, + { RightsType.DreamMaker, typeof(DreamMakerRights) }, + { RightsType.DreamDaemon, typeof(DreamDaemonRights) }, + { RightsType.ChatSettings, typeof(ChatSettingsRights) }, + { RightsType.Configuration, typeof(ConfigurationRights) }, + { RightsType.InstanceUser, typeof(InstanceUserRights) } + }; + + /// + /// Map a given to its respective + /// + /// The to lookup + /// The of the given + public static Type RightToType(RightsType rightsType) => typeMap[rightsType]; + + /// + /// Gets the role claim name used for a given + /// + /// The + /// The + /// A representing the claim role name + public static string RoleNames(TRight right) where TRight: Enum + { + var flags = new List(); + IEnumerable GetRoleNames() + { + foreach (Enum J in Enum.GetValues(right.GetType())) + if (Convert.ToInt32(J, CultureInfo.InvariantCulture) != 0 && right.HasFlag(J)) + yield return String.Concat(typeof(TRight).Name, '.', J.ToString()); + }; + var names = GetRoleNames(); + return String.Join(",", names); + } + + /// + /// Gets the role claim name used for a given and + /// + /// The + /// The right value + /// A representing the claim role name + public static string RoleName(RightsType rightsType, Enum right) + { + var enumType = typeMap[rightsType]; + return String.Concat(enumType.Name, '.', Enum.GetName(enumType, right)); + } + + /// + /// Check if a given is meant for an + /// + /// The to check + /// if is an instance right, otherwise + public static bool IsInstanceRight(RightsType rightsType) => !(rightsType == RightsType.Administration || rightsType == RightsType.InstanceManager); + } +} diff --git a/src/Tgstation.Server.Api/Rights/RightsType.cs b/src/Tgstation.Server.Api/Rights/RightsType.cs new file mode 100644 index 0000000000..7ac7491661 --- /dev/null +++ b/src/Tgstation.Server.Api/Rights/RightsType.cs @@ -0,0 +1,45 @@ +namespace Tgstation.Server.Api.Rights +{ + /// + /// The type of rights a model uses + /// + public enum RightsType + { + /// + /// + /// + Administration, + /// + /// + /// + InstanceManager, + /// + /// + /// + Repository, + /// + /// + /// + Byond, + /// + /// + /// + DreamMaker, + /// + /// + /// + DreamDaemon, + /// + /// + /// + ChatSettings, + /// + /// + /// + Configuration, + /// + /// + /// + InstanceUser + } +} diff --git a/src/Tgstation.Server.Api/Route.cs b/src/Tgstation.Server.Api/Route.cs new file mode 100644 index 0000000000..99789e5443 --- /dev/null +++ b/src/Tgstation.Server.Api/Route.cs @@ -0,0 +1,28 @@ +using System; +using System.Net.Http; + +namespace Tgstation.Server.Api +{ + /// + /// Represents a route to a server action + /// + public sealed class Route + { + /// + /// The path to the action + /// + public string Path { get; set; } + + /// + /// The method of the action + /// + public HttpMethod Method { get; set; } + + /// + /// Adds a portion to + /// + /// The host address + /// A combined and + public Uri Flatten(Uri host) => new Uri(String.Concat(host.ToString().TrimEnd('/'), Path)); + } +} diff --git a/src/Tgstation.Server.Api/RouteHelper.cs b/src/Tgstation.Server.Api/RouteHelper.cs new file mode 100644 index 0000000000..cfe67e00cd --- /dev/null +++ b/src/Tgstation.Server.Api/RouteHelper.cs @@ -0,0 +1,163 @@ +using System; +using System.Linq; +using System.Net.Http; +using Tgstation.Server.Api.Models; + +namespace Tgstation.Server.Api +{ + /// + /// Gets routes for a given model + /// + public static class RouteHelper + { + /// + /// Read a + /// + /// The model type to read + /// The optional ID to pass in + /// A route to the read action + static Route Read(long? objectId) where TModel : class + { + var result = new Route { Path = String.Concat('/', typeof(TModel).Name), Method = HttpMethod.Get }; + if (objectId.HasValue) + result.Path = String.Concat(result.Path, '/', objectId); + return result; + } + + /// + /// Get the to the read action for a given + /// + /// The model to read + /// to read from if required + /// A to the read action + public static Route Read(Instance instance) where TModel : class + { + var model = (ModelAttribute)typeof(TModel).GetCustomAttributes(typeof(ModelAttribute), false).FirstOrDefault(); + if (model == default(ModelAttribute)) + throw new InvalidOperationException("TModel must have the ModelAttribute"); + if (model.RequiresInstance ^ instance != null) + throw (model.RequiresInstance ? new ArgumentNullException(nameof(instance)) : new ArgumentException("Instance is not used for this route!", nameof(instance))); + return Read(instance?.Id); + } + + /// + /// Get the to the update action for a given + /// + /// The model to update + /// A to the update action + public static Route Update(Instance instance) where TModel : class + { + var result = Read(instance); + result.Method = HttpMethod.Post; + return result; + } + + /// + /// Get the to the list action for a given + /// + /// The model to list + /// A to the list action + public static Route List(Instance instance) where TModel : class + { + var result = Read(instance); + result.Path = String.Concat(result.Path, '/', nameof(List)); + return result; + } + + /// + /// Get the to the create action for a given + /// + /// The model to create + /// A to the create action + public static Route Create(Instance instance) where TModel : class + { + var result = Read(instance); + result.Method = HttpMethod.Put; + return result; + } + + /// + /// Get the to the delete action for a given + /// + /// The model to delete + /// A to the delete action + public static Route Delete(Instance instance) where TModel : class + { + var result = Read(instance); + result.Method = HttpMethod.Delete; + return result; + } + + /// + /// Get the to a given + /// + /// The to get + /// A to the get action + public static Route GetJob(Job job) => Read(job.Id); + + /// + /// Get the to a given 's token list + /// + /// The to list tokens for + /// A to the list action + public static Route ListUserTokens(User user) + { + var result = List(null); + result.Path = String.Concat(result.Path, '/', user.Id); + return result; + } + + /// + /// Get the to a server's version + /// + /// + public static Route ServerVersion() => new Route { Path = "/", Method = HttpMethod.Get }; + + /// + /// Get the to read a file + /// + /// The the file resides in + /// The path to the file in the directory + /// A to the read action + public static Route ReadFile(Instance instance, string path) => new Route { Path = String.Concat("/Configuration/", instance?.Id ?? throw new ArgumentNullException(nameof(instance)), '/', path?.TrimStart('/') ?? throw new ArgumentNullException(nameof(path))), Method = HttpMethod.Get }; + + /// + /// Get the to list files for a + /// + /// The the file resides in + /// The directory to list + /// A to the read action + public static Route ListFiles(Instance instance, string directory) + { + var result = ReadFile(instance, directory); + result.Path = String.Concat("/ConfigurationList", result.Path.Substring(result.Path.IndexOf('/', 1))); + return result; + } + + /// + /// Get the to create a file + /// + /// The the file resides in + /// The path to the file in the directory + /// A to the create action + public static Route CreateFile(Instance instance, string path) + { + var result = ReadFile(instance, path); + result.Method = HttpMethod.Put; + return result; + } + + /// + /// Get the to delete a file + /// + /// The the file resides in + /// The path to the file in the directory + /// A to the delete action + public static Route DeleteFile(Instance instance, string path) + { + var result = ReadFile(instance, path); + result.Method = HttpMethod.Delete; + return result; + } + } +} diff --git a/src/Tgstation.Server.Api/Routes/Administration.cs b/src/Tgstation.Server.Api/Routes/Administration.cs new file mode 100644 index 0000000000..e5d7b1cfcb --- /dev/null +++ b/src/Tgstation.Server.Api/Routes/Administration.cs @@ -0,0 +1,13 @@ +namespace Tgstation.Server.Api.Routes +{ + /// + /// Routes for + /// + public static class Administration + { + /// + /// The base route + /// + public const string Base = "/" + nameof(Administration); + } +} diff --git a/src/Tgstation.Server.Api/Tgstation.Server.Api.csproj b/src/Tgstation.Server.Api/Tgstation.Server.Api.csproj new file mode 100644 index 0000000000..b963809ded --- /dev/null +++ b/src/Tgstation.Server.Api/Tgstation.Server.Api.csproj @@ -0,0 +1,42 @@ + + + + netstandard2.0 + Full + true + Cyberboss + /tg/station + API definitions for tgstation-server + https://github.com/tgstation/tgstation-server/blob/master/LICENSE.md + https://tgstation.github.io/tgstation-server + https://raw.githubusercontent.com/tgstation/tgstation-server/master/tgs.ico + Git + https://github.com/tgstation/tgstation-server + 2018 + 4.0.0.0 + 4.0.0.0 + json web api tgstation-server tgstation ss13 byond + Prototype release + 4.0.0.0 + + + + latest + true + + bin\Release\netstandard2.0\Tgstation.Server.Api.xml + 1701;1702;1705;CA2227;CA1819 + + + + latest + 1701;1702;1705;CA2227;CA1819 + + + + + + + + + diff --git a/src/Tgstation.Server.Client/Components/IAdministrationClient.cs b/src/Tgstation.Server.Client/Components/IAdministrationClient.cs new file mode 100644 index 0000000000..a68aeb5cfe --- /dev/null +++ b/src/Tgstation.Server.Client/Components/IAdministrationClient.cs @@ -0,0 +1,28 @@ +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Rights; + +namespace Tgstation.Server.Client.Components +{ + /// + /// For managing server administration + /// + public interface IAdministrationClient : IRightsClient + { + /// + /// Get the represented by the + /// + /// The for the operation + /// A resulting in the represented by the + Task Read(CancellationToken cancellationToken); + + /// + /// Updates the setttings + /// + /// The to update + /// The for the operation + /// A representing the running operation + Task Update(Administration administration, CancellationToken cancellationToken); + } +} diff --git a/src/Tgstation.Server.Client/Components/IByondClient.cs b/src/Tgstation.Server.Client/Components/IByondClient.cs new file mode 100644 index 0000000000..9f9d8421c6 --- /dev/null +++ b/src/Tgstation.Server.Client/Components/IByondClient.cs @@ -0,0 +1,29 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Rights; + +namespace Tgstation.Server.Client.Components +{ + /// + /// For managing the installation + /// + public interface IByondClient : IRightsClient + { + /// + /// Get the represented by the + /// + /// The for the operation + /// A resulting in the represented by the + Task Read(CancellationToken cancellationToken); + + /// + /// Updates the installed BYOND + /// + /// The to set to active + /// The for the operation + /// A representing the running operation + Task SetActiveVersion(Version version, CancellationToken cancellationToken); + } +} diff --git a/src/Tgstation.Server.Client/Components/IChatSettingsClient.cs b/src/Tgstation.Server.Client/Components/IChatSettingsClient.cs new file mode 100644 index 0000000000..d8ad100b18 --- /dev/null +++ b/src/Tgstation.Server.Client/Components/IChatSettingsClient.cs @@ -0,0 +1,28 @@ +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Rights; + +namespace Tgstation.Server.Client.Components +{ + /// + /// For managing the chat bots + /// + public interface IChatSettingsClient : IRightsClient + { + /// + /// Get the represented by the + /// + /// The for the operation + /// A resulting in the represented by the + Task Read(CancellationToken cancellationToken); + + /// + /// Updates the setttings + /// + /// The to update + /// The for the operation + /// A representing the running operation + Task Update(ChatSettings chat, CancellationToken cancellationToken); + } +} diff --git a/src/Tgstation.Server.Client/Components/IConfigurationClient.cs b/src/Tgstation.Server.Client/Components/IConfigurationClient.cs new file mode 100644 index 0000000000..af84c1ca73 --- /dev/null +++ b/src/Tgstation.Server.Client/Components/IConfigurationClient.cs @@ -0,0 +1,55 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Rights; + +namespace Tgstation.Server.Client.Components +{ + /// + /// For managing files + /// + public interface IConfigurationClient : IRightsClient + { + /// + /// List configuration files + /// + /// The path to the directory to list files in + /// The for the operation + /// A of s in the + Task> List(string directory, CancellationToken cancellationToken); + + /// + /// Read a file + /// + /// The file to read + /// The for the operation + /// A representing the running operation + Task Read(ConfigurationFile file, CancellationToken cancellationToken); + + /// + /// Overwrite a file with integrity checks + /// + /// The file to write + /// The for the operation + /// A representing the running operation + Task Write(ConfigurationFile file, CancellationToken cancellationToken); + + /// + /// Create/overwrite a file + /// + /// The file to write + /// The for the operation + /// A representing the running operation + Task Create(ConfigurationFile file, CancellationToken cancellationToken); + + /// + /// Delete a file + /// + /// The file to delete + /// The for the operation + /// A representing the running operation + Task Delete(ConfigurationFile file, CancellationToken cancellationToken); + } +} diff --git a/src/Tgstation.Server.Client/Components/IDreamDaemonClient.cs b/src/Tgstation.Server.Client/Components/IDreamDaemonClient.cs new file mode 100644 index 0000000000..1947c4170d --- /dev/null +++ b/src/Tgstation.Server.Client/Components/IDreamDaemonClient.cs @@ -0,0 +1,42 @@ +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Rights; + +namespace Tgstation.Server.Client.Components +{ + /// + /// For managing + /// + public interface IDreamDaemonClient: IRightsClient + { + /// + /// Get the represented by the + /// + /// The for the operation + /// A resulting in the represented by the + Task Read(CancellationToken cancellationToken); + + /// + /// Start + /// + /// The for the operation + /// A representing the running operation + Task Start(CancellationToken cancellationToken); + + /// + /// Shutdown + /// + /// The for the operation + /// A representing the running operation + Task Shutdown(CancellationToken cancellationToken); + + /// + /// Update . This may trigger + /// + /// The to update + /// The for the operation + /// A representing the running operation + Task Update(DreamDaemon dreamDaemon, CancellationToken cancellationToken); + } +} diff --git a/src/Tgstation.Server.Client/Components/IDreamMakerClient.cs b/src/Tgstation.Server.Client/Components/IDreamMakerClient.cs new file mode 100644 index 0000000000..ef1fb7b29b --- /dev/null +++ b/src/Tgstation.Server.Client/Components/IDreamMakerClient.cs @@ -0,0 +1,35 @@ +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Rights; + +namespace Tgstation.Server.Client.Components +{ + /// + /// For managing the compiler + /// + public interface IDreamMakerClient : IRightsClient + { + /// + /// Get the represented by the + /// + /// The for the operation + /// A resulting in the represented by the + Task Read(CancellationToken cancellationToken); + + /// + /// Updates the setttings + /// + /// The to update + /// The for the operation + /// A representing the running operation + Task Update(DreamMaker dreamMaker, CancellationToken cancellationToken); + + /// + /// Compile the current repository revision + /// + /// The for the operation + /// A representing the running operation + Task Compile(CancellationToken cancellationToken); + } +} diff --git a/src/Tgstation.Server.Client/Components/IInstanceClient.cs b/src/Tgstation.Server.Client/Components/IInstanceClient.cs new file mode 100644 index 0000000000..36b5b605f5 --- /dev/null +++ b/src/Tgstation.Server.Client/Components/IInstanceClient.cs @@ -0,0 +1,54 @@ +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Api.Models; + +namespace Tgstation.Server.Client.Components +{ + /// + /// For managing a single + /// + public interface IInstanceClient + { + /// + /// Access the + /// + IByondClient Byond { get; } + + /// + /// Access the + /// + IRepositoryClient Repository { get; } + + /// + /// Access the + /// + IDreamDaemonClient DreamDaemon { get; } + + /// + /// Access the + /// + IConfigurationClient Configuration { get; } + + /// + /// Access the + /// + IInstanceUserClient Users { get; } + + /// + /// Access the + /// + IChatSettingsClient Chat { get; } + + /// + /// Access the + /// + IDreamMakerClient DreamMaker { get; } + + /// + /// Get the represented by the + /// + /// The for the operation + /// A resulting in the represented by the + Task Read(CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Client/Components/IInstanceManagerClient.cs b/src/Tgstation.Server.Client/Components/IInstanceManagerClient.cs new file mode 100644 index 0000000000..edc8fb393a --- /dev/null +++ b/src/Tgstation.Server.Client/Components/IInstanceManagerClient.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Rights; + +namespace Tgstation.Server.Client.Components +{ + /// + /// For managing s + /// + public interface IInstanceManagerClient : IRightsClient + { + /// + /// Get all s for s the user can view + /// + /// The for the operation + /// A resulting in a of all s for s the user can view + Task> Read(CancellationToken cancellationToken); + + /// + /// Create an + /// + /// The to create. will be ignored + /// The for the operation + /// A resulting in for the created + Task Create(Instance instance, CancellationToken cancellationToken); + + /// + /// Relocates, renamed, and/or on/offlines an + /// + /// The to update + /// The for the operation + /// A representing the running operation + Task Update(Instance instance, CancellationToken cancellationToken); + + /// + /// Deletes an + /// + /// The to delete + /// The for the operation + /// A representing the running operation + Task Delete(Instance instance, CancellationToken cancellationToken); + } +} diff --git a/src/Tgstation.Server.Client/Components/IInstanceUserClient.cs b/src/Tgstation.Server.Client/Components/IInstanceUserClient.cs new file mode 100644 index 0000000000..65248e42d5 --- /dev/null +++ b/src/Tgstation.Server.Client/Components/IInstanceUserClient.cs @@ -0,0 +1,29 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Rights; + +namespace Tgstation.Server.Client.Components +{ + /// + /// For managing s + /// + public interface IInstanceUserClient : IRightsClient + { + /// + /// Get the s in the + /// + /// The for the operation + /// A resulting in a of s in the instance + Task> Read(CancellationToken cancellationToken); + + /// + /// Update a + /// + /// The to update + /// The for the operation + /// A representing the running operation + Task Update(InstanceUser instanceUser, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Client/Components/IJobsClient.cs b/src/Tgstation.Server.Client/Components/IJobsClient.cs new file mode 100644 index 0000000000..7f912b3ab1 --- /dev/null +++ b/src/Tgstation.Server.Client/Components/IJobsClient.cs @@ -0,0 +1,28 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Api.Models; + +namespace Tgstation.Server.Client.Components +{ + /// + /// Access to running jobs + /// + public interface IJobsClient + { + /// + /// List the active jobs the user can view + /// + /// The for the operation + /// A resulting in a of active s the user can view + Task> List(CancellationToken cancellationToken); + + /// + /// Cancels a + /// + /// The to cancel + /// The for the operation + /// A representing the running operation + Task Cancel(Job job, CancellationToken cancellationToken); + } +} diff --git a/src/Tgstation.Server.Client/Components/IRepositoryClient.cs b/src/Tgstation.Server.Client/Components/IRepositoryClient.cs new file mode 100644 index 0000000000..a3ee2188ad --- /dev/null +++ b/src/Tgstation.Server.Client/Components/IRepositoryClient.cs @@ -0,0 +1,30 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Rights; + +namespace Tgstation.Server.Client.Components +{ + /// + /// For managing the + /// + public interface IRepositoryClient : IRightsClient + { + /// + /// Get the represented by the + /// + /// The for the operation + /// A resulting in the represented by the + Task Read(CancellationToken cancellationToken); + + /// + /// Update the + /// + /// The to update + /// Optional action to take when progress is reported. Will either not be called for some operations, or be called with the numbers 1-100 + /// The for the operation + /// A representing the running operation + Task Update(Repository repository, Action progressCallback, CancellationToken cancellationToken); + } +} diff --git a/src/Tgstation.Server.Client/IRightsClient.cs b/src/Tgstation.Server.Client/IRightsClient.cs new file mode 100644 index 0000000000..bc32b24a35 --- /dev/null +++ b/src/Tgstation.Server.Client/IRightsClient.cs @@ -0,0 +1,19 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Client +{ + /// + /// client that has a bitset + /// + /// The for the + public interface IRightsClient + { + /// + /// Get the for the + /// + /// The for the operation + /// A resulting in the for the + Task Rights(CancellationToken cancellationToken); + } +} diff --git a/src/Tgstation.Server.Client/IServerClient.cs b/src/Tgstation.Server.Client/IServerClient.cs new file mode 100644 index 0000000000..510d609aeb --- /dev/null +++ b/src/Tgstation.Server.Client/IServerClient.cs @@ -0,0 +1,55 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Client.Components; + +namespace Tgstation.Server.Client +{ + /// + /// Main client for communicating with a server + /// + public interface IServerClient : IDisposable + { + /// + /// The of the + /// + Version Version { get; } + + /// + /// The connection timeout in milliseconds. Defaults to 10000 + /// + int Timeout { get; set; } + + /// + /// How long to return initially cached models for in seconds. Defaults to 60 + /// + int CacheExpiry { get; set; } + + /// + /// The requery rate for job updates in milliseconds. Defaults to 5000 + /// + int RequeryRate { get; set; } + + /// + /// Access the + /// + IInstanceManagerClient Instances { get; } + + /// + /// Access the + /// + IAdministrationClient Administration { get; } + + /// + /// These generally shouldn't be used in favor of the based polling and s other clients use. However, this is the only way to access jobs the client didn't start in it's current session + /// + IJobsClient Jobs { get; } + + /// + /// The of the connected server + /// + /// A resulting in the of the connected server + /// Note that if the differs from the 's API functionality will most likely be compromised + Task GetServerVersion(CancellationToken cancellationToken); + } +} diff --git a/src/Tgstation.Server.Client/IServerClientFactory.cs b/src/Tgstation.Server.Client/IServerClientFactory.cs new file mode 100644 index 0000000000..b3b3ecb506 --- /dev/null +++ b/src/Tgstation.Server.Client/IServerClientFactory.cs @@ -0,0 +1,34 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Client +{ + /// + /// Factory for creating s + /// + public interface IServerClientFactory + { + /// + /// Create a + /// + /// The URL to access tgstation-server at + /// The username to for the + /// The password for the + /// The initial timeout for the connection in milliseconds + /// The for the operation + /// A resulting in a new + /// If the and/or is invalid + Task CreateServerClient(string hostname, string username, string password, int timeout, CancellationToken cancellationToken); + + /// + /// Create a + /// + /// The URL to access tgstation-server at + /// The to access the API with + /// The initial timeout for the connection + /// The for the operation + /// A resulting in a new + /// If the invalid + Task CreateServerClient(string hostname, string token, int timeout, CancellationToken cancellationToken); + } +} diff --git a/src/Tgstation.Server.Client/ServerClientFactory.cs b/src/Tgstation.Server.Client/ServerClientFactory.cs new file mode 100644 index 0000000000..1d4af06329 --- /dev/null +++ b/src/Tgstation.Server.Client/ServerClientFactory.cs @@ -0,0 +1,16 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Client +{ + /// + public sealed class ServerClientFactory : IServerClientFactory + { + /// + public Task CreateServerClient(string hostname, string username, string password, int timeout, CancellationToken cancellationToken) => throw new NotImplementedException(); + + /// + public Task CreateServerClient(string hostname, string token, int timeout, CancellationToken cancellationToken) => throw new NotImplementedException(); + } +} diff --git a/src/Tgstation.Server.Client/Tgstation.Server.Client.csproj b/src/Tgstation.Server.Client/Tgstation.Server.Client.csproj new file mode 100644 index 0000000000..141da8fae0 --- /dev/null +++ b/src/Tgstation.Server.Client/Tgstation.Server.Client.csproj @@ -0,0 +1,37 @@ + + + + netstandard2.0 + Full + 4.0.0.0 + true + Cyberboss + /tg/station 13 + + Client library for tgstation-server + https://tgstation.github.io/tgstation-server + https://github.com/tgstation/tgstation-server + Git + en-CA + https://raw.githubusercontent.com/tgstation/tgstation-server/master/build/tgs.ico + https://github.com/tgstation/tgstation-server/blob/master/LICENSE + json web api tgstation-server tgstation ss13 byond client + Prototype release + + + + latest + true + + bin\Release\netstandard2.0\Tgstation.Server.Client.xml + + + + latest + + + + + + + diff --git a/src/Tgstation.Server.CommandLine/Program.cs b/src/Tgstation.Server.CommandLine/Program.cs new file mode 100644 index 0000000000..63d8f4fbdc --- /dev/null +++ b/src/Tgstation.Server.CommandLine/Program.cs @@ -0,0 +1,16 @@ +using System.Threading.Tasks; + +namespace Tgstation.Server.CommandLine +{ + /// + /// Contains the entrypoint for the application + /// + public static class Program + { + /// + /// Entrypoint for the application + /// + /// A representing the running operation + public static Task Main() => Task.CompletedTask; + } +} diff --git a/src/Tgstation.Server.CommandLine/Tgstation.Server.CommandLine.csproj b/src/Tgstation.Server.CommandLine/Tgstation.Server.CommandLine.csproj new file mode 100644 index 0000000000..1da32f73c0 --- /dev/null +++ b/src/Tgstation.Server.CommandLine/Tgstation.Server.CommandLine.csproj @@ -0,0 +1,28 @@ + + + + Exe + netcoreapp2.0 + Full + + + + latest + bin\Release\netcoreapp2.0\Tgstation.Server.CommandLine.xml + true + + + + + latest + + + + + + + + + + + diff --git a/TGS.Installer.UI/App.config b/src/Tgstation.Server.ControlPanel/App.config similarity index 83% rename from TGS.Installer.UI/App.config rename to src/Tgstation.Server.ControlPanel/App.config index 88fa4027bd..787dcbecc6 100644 --- a/TGS.Installer.UI/App.config +++ b/src/Tgstation.Server.ControlPanel/App.config @@ -1,6 +1,6 @@  - + \ No newline at end of file diff --git a/src/Tgstation.Server.ControlPanel/App.xaml b/src/Tgstation.Server.ControlPanel/App.xaml new file mode 100644 index 0000000000..88756359fb --- /dev/null +++ b/src/Tgstation.Server.ControlPanel/App.xaml @@ -0,0 +1,9 @@ + + + + + diff --git a/src/Tgstation.Server.ControlPanel/App.xaml.cs b/src/Tgstation.Server.ControlPanel/App.xaml.cs new file mode 100644 index 0000000000..64dd7ac70c --- /dev/null +++ b/src/Tgstation.Server.ControlPanel/App.xaml.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Configuration; +using System.Data; +using System.Linq; +using System.Threading.Tasks; +using System.Windows; + +namespace Tgstation.Server.ControlPanel +{ + /// + /// Interaction logic for App.xaml + /// + sealed partial class App : Application + { + } +} diff --git a/src/Tgstation.Server.ControlPanel/GlobalSuppressions.cs b/src/Tgstation.Server.ControlPanel/GlobalSuppressions.cs new file mode 100644 index 0000000000..abaff863e7 Binary files /dev/null and b/src/Tgstation.Server.ControlPanel/GlobalSuppressions.cs differ diff --git a/src/Tgstation.Server.ControlPanel/MainWindow.xaml b/src/Tgstation.Server.ControlPanel/MainWindow.xaml new file mode 100644 index 0000000000..c2e27d3e3d --- /dev/null +++ b/src/Tgstation.Server.ControlPanel/MainWindow.xaml @@ -0,0 +1,12 @@ + + + + + diff --git a/src/Tgstation.Server.ControlPanel/MainWindow.xaml.cs b/src/Tgstation.Server.ControlPanel/MainWindow.xaml.cs new file mode 100644 index 0000000000..09ab37f920 --- /dev/null +++ b/src/Tgstation.Server.ControlPanel/MainWindow.xaml.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading.Tasks; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Data; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Media.Imaging; +using System.Windows.Navigation; +using System.Windows.Shapes; + +namespace Tgstation.Server.ControlPanel +{ + /// + /// Interaction logic for MainWindow.xaml + /// + sealed partial class MainWindow : Window + { + /// + /// Construct a + /// + public MainWindow() + { + InitializeComponent(); + } + } +} diff --git a/src/Tgstation.Server.ControlPanel/Properties/AssemblyInfo.cs b/src/Tgstation.Server.ControlPanel/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..101764093f --- /dev/null +++ b/src/Tgstation.Server.ControlPanel/Properties/AssemblyInfo.cs @@ -0,0 +1,26 @@ +using System; +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; +using System.Windows; + +[assembly: AssemblyTitle("Tgstation.Server.ControlPanel")] +[assembly: InternalsVisibleTo("Tgstation.Server.ControlPanel.Tests")] +[assembly: ComVisible(false)] +[assembly: CLSCompliant(true)] +[assembly: NeutralResourcesLanguage("en-CA", UltimateResourceFallbackLocation.Satellite)] + + +[assembly: ThemeInfo( + ResourceDictionaryLocation.None, //where theme specific resource dictionaries are located + //(used if a resource is not found in the page, + // or application resource dictionaries) + ResourceDictionaryLocation.SourceAssembly //where the generic resource dictionary is located + //(used if a resource is not found in the page, + // app, or any theme specific resource dictionaries) +)] + +[assembly: AssemblyVersion("4.0.0.0")] +[assembly: AssemblyFileVersion("4.0.0.0")] +[assembly: AssemblyInformationalVersion("4.0.0.0")] diff --git a/src/Tgstation.Server.ControlPanel/Properties/Resources.Designer.cs b/src/Tgstation.Server.ControlPanel/Properties/Resources.Designer.cs new file mode 100644 index 0000000000..6b8d38cdb3 --- /dev/null +++ b/src/Tgstation.Server.ControlPanel/Properties/Resources.Designer.cs @@ -0,0 +1,71 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Tgstation.Server.ControlPanel.Properties +{ + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources + { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() + { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager + { + get + { + if ((resourceMan == null)) + { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Tgstation.Server.ControlPanel.Properties.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture + { + get + { + return resourceCulture; + } + set + { + resourceCulture = value; + } + } + } +} diff --git a/TGS.Installer.UI/Properties/Resources.resx b/src/Tgstation.Server.ControlPanel/Properties/Resources.resx similarity index 81% rename from TGS.Installer.UI/Properties/Resources.resx rename to src/Tgstation.Server.ControlPanel/Properties/Resources.resx index 7e63714e76..af7dbebbac 100644 --- a/TGS.Installer.UI/Properties/Resources.resx +++ b/src/Tgstation.Server.ControlPanel/Properties/Resources.resx @@ -46,7 +46,7 @@ mimetype: application/x-microsoft.net.object.binary.base64 value : The object must be serialized with - : System.Runtime.Serialization.Formatters.Binary.BinaryFormatter + : System.Serialization.Formatters.Binary.BinaryFormatter : and then encoded with base64 encoding. mimetype: application/x-microsoft.net.object.soap.base64 @@ -60,7 +60,6 @@ : and then encoded with base64 encoding. --> - @@ -69,10 +68,9 @@ - + - @@ -87,10 +85,9 @@ - + - @@ -112,16 +109,9 @@ 2.0 - System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - ..\..\TGS.Installer\bin\Release\cab1.cab;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - - - ..\..\TGS.Installer\bin\Release\TGServiceInstaller.msi;System.Byte[], mscorlib, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 - \ No newline at end of file diff --git a/src/Tgstation.Server.ControlPanel/Properties/Settings.Designer.cs b/src/Tgstation.Server.ControlPanel/Properties/Settings.Designer.cs new file mode 100644 index 0000000000..5ed0707325 --- /dev/null +++ b/src/Tgstation.Server.ControlPanel/Properties/Settings.Designer.cs @@ -0,0 +1,30 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30319.42000 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Tgstation.Server.ControlPanel.Properties +{ + + + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("Microsoft.VisualStudio.Editors.SettingsDesigner.SettingsSingleFileGenerator", "11.0.0.0")] + internal sealed partial class Settings : global::System.Configuration.ApplicationSettingsBase + { + + private static Settings defaultInstance = ((Settings)(global::System.Configuration.ApplicationSettingsBase.Synchronized(new Settings()))); + + public static Settings Default + { + get + { + return defaultInstance; + } + } + } +} diff --git a/src/Tgstation.Server.ControlPanel/Properties/Settings.settings b/src/Tgstation.Server.ControlPanel/Properties/Settings.settings new file mode 100644 index 0000000000..033d7a5e9e --- /dev/null +++ b/src/Tgstation.Server.ControlPanel/Properties/Settings.settings @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/src/Tgstation.Server.ControlPanel/Tgstation.Server.ControlPanel.csproj b/src/Tgstation.Server.ControlPanel/Tgstation.Server.ControlPanel.csproj new file mode 100644 index 0000000000..ec1bab3bc6 --- /dev/null +++ b/src/Tgstation.Server.ControlPanel/Tgstation.Server.ControlPanel.csproj @@ -0,0 +1,107 @@ + + + + + Debug + AnyCPU + {D38BE569-04B7-4C64-BDB5-683767B26FEB} + WinExe + Tgstation.Server.ControlPanel + Tgstation.Server.ControlPanel + v4.7.1 + 512 + {60dc8134-eba5-43b8-bcc9-bb4bc16c2548};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 4 + true + en-CA + + + AnyCPU + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + latest + true + AllRules.ruleset + + + AnyCPU + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + latest + true + bin\Release\Tgstation.Server.ControlPanel.xml + true + AllRules.ruleset + + + Tgstation.Server.ControlPanel.App + + + + + + + + + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + App.xaml + Code + + + + MainWindow.xaml + Code + + + + + Code + + + True + True + Resources.resx + + + True + Settings.settings + True + + + ResXFileCodeGenerator + Resources.Designer.cs + + + SettingsSingleFileGenerator + Settings.Designer.cs + + + + + + + + {e0ac911f-7675-4a91-9499-d8a2e2390aad} + Tgstation.Server.Client + + + + \ No newline at end of file diff --git a/src/Tgstation.Server.Host.Console/AssemblyInfo.cs b/src/Tgstation.Server.Host.Console/AssemblyInfo.cs new file mode 100644 index 0000000000..e0c4840654 --- /dev/null +++ b/src/Tgstation.Server.Host.Console/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tgstation.Server.Host.Console.Tests")] diff --git a/src/Tgstation.Server.Host.Console/Program.cs b/src/Tgstation.Server.Host.Console/Program.cs new file mode 100644 index 0000000000..0a62b2ed4e --- /dev/null +++ b/src/Tgstation.Server.Host.Console/Program.cs @@ -0,0 +1,61 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Host.Watchdog; + +namespace Tgstation.Server.Host.Console +{ + /// + /// Contains the entrypoint for the application + /// + static class Program + { + /// + /// The for the + /// + internal static IWatchdogFactory WatchdogFactory { get; set; } = new WatchdogFactory(); + + /// + /// Entrypoint for the application + /// + /// The arguments for the + /// A representing the running operation + internal static async Task Main(string[] args) + { + using (var loggerFactory = new LoggerFactory()) + { + var arguments = new List(args); + var trace = arguments.Remove("--trace-host-watchdog"); + var debug = arguments.Remove("--debug-host-watchdog"); + + loggerFactory.AddConsole(trace ? LogLevel.Trace : debug ? LogLevel.Debug : LogLevel.Information, true); + + if (trace && debug) + { + loggerFactory.CreateLogger(nameof(Program)).LogCritical("Please specify only 1 of --trace-host-watchdog or --debug-host-watchdog!"); + return; + } + using (var cts = new CancellationTokenSource()) + { + void AppDomainHandler(object a, EventArgs b) => cts.Cancel(); + AppDomain.CurrentDomain.ProcessExit += AppDomainHandler; + try + { + System.Console.CancelKeyPress += (a, b) => + { + b.Cancel = true; + cts.Cancel(); + }; + await WatchdogFactory.CreateWatchdog(loggerFactory).RunAsync(arguments.ToArray(), cts.Token).ConfigureAwait(false); + } + finally + { + AppDomain.CurrentDomain.ProcessExit -= AppDomainHandler; + } + } + } + } + } +} diff --git a/src/Tgstation.Server.Host.Console/Properties/launchSettings.json b/src/Tgstation.Server.Host.Console/Properties/launchSettings.json new file mode 100644 index 0000000000..5991274947 --- /dev/null +++ b/src/Tgstation.Server.Host.Console/Properties/launchSettings.json @@ -0,0 +1,21 @@ +{ + "iisSettings": { + "windowsAuthentication": false, + "anonymousAuthentication": true, + "iisExpress": { + "applicationUrl": "http://localhost:51882/", + "sslPort": 0 + } + }, + "profiles": { + "Tgstation.Server.Host.Console": { + "commandName": "Project", + "workingDirectory": "bin\\Debug\\netcoreapp2.0", + "launchBrowser": true, + "environmentVariables": { + "ASPNETCORE_ENVIRONMENT": "Development" + }, + "applicationUrl": "http://localhost:51885/" + } + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Host.Console/Tgstation.Server.Host.Console.csproj b/src/Tgstation.Server.Host.Console/Tgstation.Server.Host.Console.csproj new file mode 100644 index 0000000000..cb7add9153 --- /dev/null +++ b/src/Tgstation.Server.Host.Console/Tgstation.Server.Host.Console.csproj @@ -0,0 +1,37 @@ + + + + Exe + netcoreapp2.0 + Full + + + + latest + true + + bin\Release\netcoreapp2.0\Tgstation.Server.Host.Console.xml + + + + latest + + + + + + + + + + + + + PreserveNewest + + + PreserveNewest + + + + diff --git a/src/Tgstation.Server.Host.Console/tgs.bat b/src/Tgstation.Server.Host.Console/tgs.bat new file mode 100644 index 0000000000..a54925985c --- /dev/null +++ b/src/Tgstation.Server.Host.Console/tgs.bat @@ -0,0 +1,2 @@ +@echo off +dotnet %~dp0Tgstation.Server.Host.Console.dll diff --git a/src/Tgstation.Server.Host.Console/tgs.sh b/src/Tgstation.Server.Host.Console/tgs.sh new file mode 100755 index 0000000000..895becc72e --- /dev/null +++ b/src/Tgstation.Server.Host.Console/tgs.sh @@ -0,0 +1,4 @@ +#!/bin/sh + +script_full_path=$(dirname "$0") +exec dotnet "$script_full_path/Tgstation.Server.Host.Console.dll" diff --git a/src/Tgstation.Server.Host.Service/GlobalSuppressions.cs b/src/Tgstation.Server.Host.Service/GlobalSuppressions.cs new file mode 100644 index 0000000000..2dac9c7cde Binary files /dev/null and b/src/Tgstation.Server.Host.Service/GlobalSuppressions.cs differ diff --git a/src/Tgstation.Server.Host.Service/Program.cs b/src/Tgstation.Server.Host.Service/Program.cs new file mode 100644 index 0000000000..96932140c2 --- /dev/null +++ b/src/Tgstation.Server.Host.Service/Program.cs @@ -0,0 +1,110 @@ +using McMaster.Extensions.CommandLineUtils; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Specialized; +using System.Configuration.Install; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Linq; +using System.Reflection; +using System.Security.Principal; +using System.ServiceProcess; +using Tgstation.Server.Host.Watchdog; + +namespace Tgstation.Server.Host.Service +{ + /// + /// Contains the entrypoint for the application + /// + [ExcludeFromCodeCoverage] + class Program + { + /// + /// The --uninstall or -u option + /// + [Option(ShortName = "u")] + public bool Uninstall { get; } + + /// + /// The --install or -i option + /// + [Option(ShortName = "i")] + public bool Install { get; } + + /// + /// Check if the running user is a system administrator + /// + /// if the running user is a system administrator, otherwise + static bool IsAdministrator() + { + var user = WindowsIdentity.GetCurrent(); + var principal = new WindowsPrincipal(user); + return principal.IsInRole(WindowsBuiltInRole.Administrator); + } + + /// + /// Command line handler, always runs + /// + public void OnExecute() + { + if ((Install || Uninstall) && !IsAdministrator()) + { + //try to restart as admin + var argList = Environment.GetCommandLineArgs().ToList(); + //its windows, first arg is .exe name guaranteed + var exe = argList.First(); + argList.RemoveAt(0); + var startInfo = new ProcessStartInfo + { + UseShellExecute = true, + Verb = "runas", + Arguments = String.Join(" ", argList), + FileName = exe, + WorkingDirectory = Environment.CurrentDirectory, + }; + using (Process.Start(startInfo)) + return; + } + + if (Install) + { + if (Uninstall) + //oh no, it's retarded... + return; + using (var processInstaller = new ServiceProcessInstaller()) + using (var installer = new ServiceInstaller()) + { + processInstaller.Account = ServiceAccount.LocalSystem; + + installer.Context = new InstallContext("tgs-4-install.log", new string[] { String.Format(CultureInfo.InvariantCulture, "/assemblypath={0}", Assembly.GetEntryAssembly().Location) }); + installer.Description = "/tg/station 13 server v4 running as a windows service"; + installer.DisplayName = "/tg/station server 4"; + installer.DelayedAutoStart = true; + installer.StartType = ServiceStartMode.Automatic; + installer.ServicesDependedOn = new string[] { "Tcpip", "Dhcp", "Dnscache" }; + installer.ServiceName = ServerService.Name; + installer.Parent = processInstaller; + + var state = new ListDictionary(); + installer.Install(state); + } + } + else if (Uninstall) + using (var installer = new ServiceInstaller()) + { + installer.Context = new InstallContext("tgs-4-uninstall.log", null); + installer.ServiceName = ServerService.Name; + installer.Uninstall(null); + } + else + using (var loggerFactory = new LoggerFactory()) + ServiceBase.Run(new ServerService(new WatchdogFactory(), loggerFactory)); + } + + /// + /// Entrypoint for the application + /// + static int Main(string[] args) => CommandLineApplication.Execute(args); + } +} diff --git a/src/Tgstation.Server.Host.Service/Properties/AssemblyInfo.cs b/src/Tgstation.Server.Host.Service/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..295c7cb2a0 --- /dev/null +++ b/src/Tgstation.Server.Host.Service/Properties/AssemblyInfo.cs @@ -0,0 +1,12 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("Tgstation.Server.Host.Service")] +[assembly: ComVisible(false)] +[assembly: Guid("29927416-3b78-49a7-a560-5ccaa638b6b4")] +[assembly: InternalsVisibleTo("Tgstation.Server.Host.Service.Tests")] + +[assembly: AssemblyVersion("4.0.0.0")] +[assembly: AssemblyFileVersion("4.0.0.0")] +[assembly: AssemblyInformationalVersion("4.0.0.0")] diff --git a/src/Tgstation.Server.Host.Service/ServerService.cs b/src/Tgstation.Server.Host.Service/ServerService.cs new file mode 100644 index 0000000000..97ff6944ea --- /dev/null +++ b/src/Tgstation.Server.Host.Service/ServerService.cs @@ -0,0 +1,89 @@ +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Logging.EventLog; +using Microsoft.Extensions.Logging.EventLog.Internal; +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.ServiceProcess; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Host.Watchdog; + +namespace Tgstation.Server.Host.Service +{ + /// + /// Represents a as a + /// + sealed class ServerService : ServiceBase, IEventLog + { + /// + /// The canonical windows service name + /// + public const string Name = "tgstation-server-4"; + + /// + /// The for the + /// + readonly IWatchdog watchdog; + + /// + /// The recieved from of + /// + Task watchdogTask; + + /// + /// The for the + /// + CancellationTokenSource cancellationTokenSource; + + /// + /// Construct a + /// + /// The to create with + /// The for + public ServerService(IWatchdogFactory watchdogFactory, ILoggerFactory loggerFactory) + { + if (watchdogFactory == null) + throw new ArgumentNullException(nameof(watchdogFactory)); + if(loggerFactory == null) + throw new ArgumentNullException(nameof(loggerFactory)); + + loggerFactory.AddEventLog(new EventLogSettings + { + EventLog = this + }); + + ServiceName = Name; + watchdog = watchdogFactory.CreateWatchdog(loggerFactory); + } + + /// + public int MaxMessageSize => (int)EventLog.MaximumKilobytes * 1024; + + /// + public void WriteEntry(string message, EventLogEntryType type, int eventID, short category) => EventLog.WriteEntry(message, type, eventID, category); + + /// + [SuppressMessage("Microsoft.Usage", "CA2213:DisposableFieldsShouldBeDisposed", MessageId = "cancellationTokenSource")] + protected override void Dispose(bool disposing) + { + cancellationTokenSource?.Dispose(); + base.Dispose(disposing); + } + + /// + protected override void OnStart(string[] args) + { + cancellationTokenSource?.Dispose(); + cancellationTokenSource = new CancellationTokenSource(); + watchdogTask = watchdog.RunAsync(args, cancellationTokenSource.Token); + } + + /// + protected override void OnStop() + { + cancellationTokenSource.Cancel(); + watchdogTask.GetAwaiter().GetResult(); + } + } +} \ No newline at end of file diff --git a/TGS.Server.Console/TGS.Server.Console.csproj b/src/Tgstation.Server.Host.Service/Tgstation.Server.Host.Service.csproj similarity index 61% rename from TGS.Server.Console/TGS.Server.Console.csproj rename to src/Tgstation.Server.Host.Service/Tgstation.Server.Host.Service.csproj index 37d49bf3a8..54ee9a72db 100644 --- a/TGS.Server.Console/TGS.Server.Console.csproj +++ b/src/Tgstation.Server.Host.Service/Tgstation.Server.Host.Service.csproj @@ -4,11 +4,11 @@ Debug AnyCPU - {509433F6-AEFB-44CA-BFE3-C782166D2CC3} - Exe - TGS.Server.Console - TGS.Server.Console - v4.5.2 + {29927416-3B78-49A7-A560-5CCAA638B6B4} + WinExe + Tgstation.Server.Host.Service + Tgstation.Server.Host.Service + v4.7.1 512 true publish\ @@ -26,7 +26,6 @@ false false true - AnyCPU @@ -37,6 +36,9 @@ DEBUG;TRACE prompt 4 + latest + true + AllRules.ruleset AnyCPU @@ -46,34 +48,28 @@ TRACE prompt 4 + latest + true + bin\Release\Tgstation.Server.Host.Service.xml + true + AllRules.ruleset - app.manifest - - - tgs.ico + Tgstation.Server.Host.Service.Program + - - - - + + + + Component + - - - - - - {f32eda25-0855-411c-af5e-f0d042917e2d} - TGS.Server - - - - + False - Microsoft .NET Framework 4.6.1 %28x86 and x64%29 + Microsoft .NET Framework 4.7.1 %28x86 and x64%29 true @@ -83,7 +79,23 @@ - + + + + + + + {5d2d682c-6bf0-439c-850b-6ab945bbeaea} + Tgstation.Server.Host.Watchdog + + + + + 2.2.5 + + + 2.1.1 + \ No newline at end of file diff --git a/src/Tgstation.Server.Host.Watchdog/AssemblyInfo.cs b/src/Tgstation.Server.Host.Watchdog/AssemblyInfo.cs new file mode 100644 index 0000000000..c2d4b6d5d9 --- /dev/null +++ b/src/Tgstation.Server.Host.Watchdog/AssemblyInfo.cs @@ -0,0 +1,4 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tgstation.Server.Host.Watchdog.Tests")] +[assembly: InternalsVisibleTo("DynamicProxyGenAssembly2")] diff --git a/src/Tgstation.Server.Host.Watchdog/IWatchdog.cs b/src/Tgstation.Server.Host.Watchdog/IWatchdog.cs new file mode 100644 index 0000000000..834ab78820 --- /dev/null +++ b/src/Tgstation.Server.Host.Watchdog/IWatchdog.cs @@ -0,0 +1,19 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Host.Watchdog +{ + /// + /// The watchdog for a + /// + public interface IWatchdog + { + /// + /// Run the + /// + /// The arguments for the + /// The for the operation + /// A representing the running operation + Task RunAsync(string[] args, CancellationToken cancellationToken); + } +} diff --git a/src/Tgstation.Server.Host.Watchdog/IWatchdogFactory.cs b/src/Tgstation.Server.Host.Watchdog/IWatchdogFactory.cs new file mode 100644 index 0000000000..9534709a2f --- /dev/null +++ b/src/Tgstation.Server.Host.Watchdog/IWatchdogFactory.cs @@ -0,0 +1,17 @@ +using Microsoft.Extensions.Logging; + +namespace Tgstation.Server.Host.Watchdog +{ + /// + /// Factory for creating s + /// + public interface IWatchdogFactory + { + /// + /// Create a + /// + /// The to use for error reporting + /// A new + IWatchdog CreateWatchdog(ILoggerFactory loggerFactory); + } +} diff --git a/src/Tgstation.Server.Host.Watchdog/Tgstation.Server.Host.Watchdog.csproj b/src/Tgstation.Server.Host.Watchdog/Tgstation.Server.Host.Watchdog.csproj new file mode 100644 index 0000000000..f6c9dce932 --- /dev/null +++ b/src/Tgstation.Server.Host.Watchdog/Tgstation.Server.Host.Watchdog.csproj @@ -0,0 +1,30 @@ + + + + netstandard2.0 + Full + false + + + + latest + true + + bin\Release\netstandard2.0\Tgstation.Server.Host.Watchdog.xml + + + + latest + + + + + + + + + + PreserveNewest + + + diff --git a/src/Tgstation.Server.Host.Watchdog/Watchdog.cs b/src/Tgstation.Server.Host.Watchdog/Watchdog.cs new file mode 100644 index 0000000000..44bccaeb37 --- /dev/null +++ b/src/Tgstation.Server.Host.Watchdog/Watchdog.cs @@ -0,0 +1,268 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Host.Watchdog +{ + /// + sealed class Watchdog : IWatchdog + { + + /// + /// The for the + /// + readonly ILogger logger; + + /// + /// Construct a + /// + /// The value of + public Watchdog(ILogger logger) + { + this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + /// + public async Task RunAsync(string[] args, CancellationToken cancellationToken) + { + logger.LogInformation("Host watchdog starting..."); + + var enviromentPath = Environment.GetEnvironmentVariable("PATH"); + var paths = enviromentPath.Split(';'); + var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + + var exeName = "dotnet"; + if (isWindows) + exeName += ".exe"; + + var dotnetPath = paths.Select(x => Path.Combine(x, exeName)) + .Where(x => File.Exists(x)) + .FirstOrDefault(); + + if(dotnetPath == default) + { + logger.LogCritical("Unable to locate dotnet executable in PATH! Please ensure the .NET Core runtime is installed and is in your PATH!"); + return; + } + logger.LogInformation("Detected dotnet executable at {0}", dotnetPath); + + var rootLocation = Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location); + + var assemblyStoragePath = Path.Combine(rootLocation, "lib"); //always always next to watchdog + var defaultAssemblyPath = Path.GetFullPath(Path.Combine(assemblyStoragePath, "Default")); +#if DEBUG + //just copy the shit where it belongs + Directory.Delete(assemblyStoragePath, true); + Directory.CreateDirectory(defaultAssemblyPath); + + var sourcePath = "../../../../Tgstation.Server.Host/bin/Debug/netcoreapp2.0"; + foreach (string dirPath in Directory.GetDirectories(sourcePath, "*", SearchOption.AllDirectories)) + Directory.CreateDirectory(dirPath.Replace(sourcePath, defaultAssemblyPath)); + + foreach (string newPath in Directory.GetFiles(sourcePath, "*.*", SearchOption.AllDirectories)) + File.Copy(newPath, newPath.Replace(sourcePath, defaultAssemblyPath), true); + + const string AppSettingsJson = "appsettings.json"; + var rootJson = Path.Combine(rootLocation, AppSettingsJson); + File.Delete(rootJson); + File.Move(Path.Combine(defaultAssemblyPath, AppSettingsJson), rootJson); +#endif + + var assemblyName = String.Join(".", nameof(Tgstation), nameof(Server), nameof(Host), "dll"); + var assemblyPath = Path.Combine(defaultAssemblyPath, assemblyName); + + if (assemblyPath.Contains("\"")) + { + logger.LogCritical("Running from paths with \"'s in the name is not supported!"); + return; + } + + if (!File.Exists(assemblyPath)) + { + logger.LogCritical("Unable to locate host assembly!"); + return; + } + + string updateDirectory = null; + try + { + while (!cancellationToken.IsCancellationRequested) + using (logger.BeginScope("Host invocation")) + { + updateDirectory = Path.GetFullPath(Path.Combine(assemblyStoragePath, Guid.NewGuid().ToString())); + logger.LogInformation("Update path set to {0}", updateDirectory); + using (var process = new Process()) + { + process.StartInfo.FileName = dotnetPath; + process.StartInfo.WorkingDirectory = Environment.CurrentDirectory; //for appsettings + + var arguments = new List + { + '"' + assemblyPath + '"', + updateDirectory + }; + if (Debugger.IsAttached) + arguments.Add("--attach-debugger"); + arguments.AddRange(args); + + process.StartInfo.Arguments = String.Join(" ", arguments); + + process.StartInfo.UseShellExecute = false; //runs in the same console + + var tcs = new TaskCompletionSource(); + process.Exited += (a, b) => + { + tcs.TrySetResult(null); + }; + process.EnableRaisingEvents = true; + + logger.LogInformation("Launching host..."); + + var iShotTheSheriff = false; + try + { + process.Start(); + + using (var processCts = new CancellationTokenSource()) + using (processCts.Token.Register(() => tcs.TrySetResult(null))) + using (cancellationToken.Register(() => + { + if (!Directory.Exists(updateDirectory)) + { + logger.LogInformation("Cancellation requested! Writing shutdown lock file..."); + File.WriteAllBytes(updateDirectory, Array.Empty()); + } + else + logger.LogWarning("Cancellation requested while update directory exists!"); + + logger.LogInformation("Will force close host process if it doesn't exit in 10 seconds..."); + + try + { + processCts.CancelAfter(TimeSpan.FromSeconds(10)); + } + catch (ObjectDisposedException) { } //race conditions + })) + await tcs.Task.ConfigureAwait(false); + } + catch (InvalidOperationException) { } + finally + { + try + { + if (!process.HasExited) + { + iShotTheSheriff = true; + process.Kill(); + process.WaitForExit(); + } + } + catch (InvalidOperationException) { } + logger.LogInformation("Host exited!"); + } + + switch (process.ExitCode) + { + case 0: + return; + case 1: + if (!cancellationToken.IsCancellationRequested) + //just a restart + logger.LogInformation("Watchdog will restart host..."); + else + logger.LogWarning("Host requested restart but watchdog shutdown is in progress!"); + break; + case 2: + //update path is now an exception document + logger.LogCritical("Host crashed, propagating exception dump..."); + var data = File.ReadAllText(updateDirectory); + try + { + File.Delete(updateDirectory); + } + catch (Exception e) + { + logger.LogWarning("Unable to delete exception dump file at {0}! Exception: {1}", updateDirectory, e); + } + throw new Exception(String.Format(CultureInfo.InvariantCulture, "Host propagated exception: {0}", data)); + default: + if (iShotTheSheriff) + { + logger.LogWarning("Watchdog forced to kill host process!"); + cancellationToken.ThrowIfCancellationRequested(); + } + throw new Exception(String.Format(CultureInfo.InvariantCulture, "Host crashed with exit code {0}!", process.ExitCode)); + } + } + + //HEY YOU + //BE WARNED THAT IF YOU DEBUGGED THE HOST PROCESS THAT JUST LAUNCHED THE DEBUGGER WILL HOLD A LOCK ON THE DIRECTORY + //THIS MEANS THE FIRST DIRECTORY.MOVE WILL THROW + if (Directory.Exists(updateDirectory)) + { + logger.LogInformation("Applying server update..."); + if (isWindows) + { + //windows dick sucking resource unlocking + GC.Collect(); + await Task.Delay(TimeSpan.FromSeconds(5), cancellationToken).ConfigureAwait(false); + } + var tempPath = Path.Combine(assemblyStoragePath, Guid.NewGuid().ToString()); + try + { + Directory.Move(defaultAssemblyPath, tempPath); + try + { + Directory.Move(updateDirectory, defaultAssemblyPath); + logger.LogInformation("Server update complete, deleting old server..."); + try + { + Directory.Delete(tempPath, true); + } + catch (Exception e) + { + logger.LogWarning("Error deleting old server at {0}! Exception: {1}", tempPath, e); + } + } + catch (Exception e) + { + logger.LogError("Error moving updated server directory, attempting revert! Exception: {0}", e); + Directory.Delete(defaultAssemblyPath, true); + Directory.Move(tempPath, defaultAssemblyPath); + logger.LogInformation("Revert successful!"); + } + } + catch(Exception e) + { + logger.LogWarning("Failed to move out active host assembly! Exception: {0}", e); + } + } + } + } + catch (OperationCanceledException) + { + logger.LogDebug("Exiting due to cancellation..."); + if (!Directory.Exists(updateDirectory)) + File.Delete(updateDirectory); + else + Directory.Delete(updateDirectory, true); + } + catch (Exception e) + { + logger.LogCritical("Watchdog error! Exception: {0}", e); + } + finally + { + logger.LogInformation("Host watchdog exiting..."); + } + } + } +} diff --git a/src/Tgstation.Server.Host.Watchdog/WatchdogFactory.cs b/src/Tgstation.Server.Host.Watchdog/WatchdogFactory.cs new file mode 100644 index 0000000000..05934ba491 --- /dev/null +++ b/src/Tgstation.Server.Host.Watchdog/WatchdogFactory.cs @@ -0,0 +1,15 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; + +namespace Tgstation.Server.Host.Watchdog +{ + /// + public sealed class WatchdogFactory : IWatchdogFactory + { + /// + [ExcludeFromCodeCoverage] + public IWatchdog CreateWatchdog(ILoggerFactory loggerFactory) => new Watchdog(loggerFactory?.CreateLogger() ?? throw new ArgumentNullException(nameof(loggerFactory))); + } +} diff --git a/src/Tgstation.Server.Host/AssemblyInfo.cs b/src/Tgstation.Server.Host/AssemblyInfo.cs new file mode 100644 index 0000000000..d2a28919a4 --- /dev/null +++ b/src/Tgstation.Server.Host/AssemblyInfo.cs @@ -0,0 +1,3 @@ +using System.Runtime.CompilerServices; + +[assembly: InternalsVisibleTo("Tgstation.Server.Host.Tests")] diff --git a/src/Tgstation.Server.Host/Components/Byond/ByondExecutableLock.cs b/src/Tgstation.Server.Host/Components/Byond/ByondExecutableLock.cs new file mode 100644 index 0000000000..3431ede531 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Byond/ByondExecutableLock.cs @@ -0,0 +1,26 @@ +using System; + +namespace Tgstation.Server.Host.Components.Byond +{ + /// + sealed class ByondExecutableLock : IByondExecutableLock + { + /// + public Version Version { get; set; } + + /// + public string DreamDaemonPath { get; set; } + + /// + public string DreamMakerPath { get; set; } + + //at one point in design, byond versions were to delete themselves if they werent the active version + //That changed at some point so these functions are intentioanlly left blank + + /// + public void Dispose() { } + + /// + public void DoNotDeleteThisSession() { } + } +} diff --git a/src/Tgstation.Server.Host/Components/Byond/ByondManager.cs b/src/Tgstation.Server.Host/Components/Byond/ByondManager.cs new file mode 100644 index 0000000000..21c29d5f8c --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Byond/ByondManager.cs @@ -0,0 +1,210 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Host.Core; +using Tgstation.Server.Host.IO; + +namespace Tgstation.Server.Host.Components.Byond +{ + /// + sealed class ByondManager : IByondManager + { + const string VersionFileName = "Version.txt"; + const string ActiveVersionFileName = "ActiveVersion.txt"; + + const string BinPath = "byond/bin"; + + /// + public Version ActiveVersion { get; private set; } + + /// + public IReadOnlyList InstalledVersions + { + get + { + lock (installedVersions) + return installedVersions.Select(x => Version.Parse(x.Key)).ToList(); + } + } + + /// + /// The for the + /// + readonly IIOManager ioManager; + + /// + /// The for the + /// + readonly IByondInstaller byondInstaller; + + /// + /// The for the + /// + readonly ILogger logger; + + /// + /// Map of byond s to s that complete when they are installed + /// + readonly Dictionary installedVersions; + + /// + /// The for the + /// + readonly SemaphoreSlim semaphore; + + static string VersionKey(Version version) => new Version(version.Major, version.Minor).ToString(); + + /// + /// Construct a + /// + /// The value of + /// The value of + /// The value of + public ByondManager(IIOManager ioManager, IByondInstaller byondInstaller, ILogger logger) + { + this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager)); + this.byondInstaller = byondInstaller ?? throw new ArgumentNullException(nameof(byondInstaller)); + this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); + + installedVersions = new Dictionary(); + semaphore = new SemaphoreSlim(1); + } + + /// + public void Dispose() => semaphore.Dispose(); + + async Task InstallVersion(Version version, CancellationToken cancellationToken) + { + var ourTcs = new TaskCompletionSource(); + Task inProgressTask; + + var versionKey = VersionKey(version); + bool installed; + lock (installedVersions) + { + installed = installedVersions.TryGetValue(versionKey, out inProgressTask); + if (!installed) + installedVersions.Add(versionKey, ourTcs.Task); + } + if(installed) + using (cancellationToken.Register(() => ourTcs.SetCanceled())) + { + await Task.WhenAny(ourTcs.Task, inProgressTask).ConfigureAwait(false); + return; + } + try + { + var downloadTask = byondInstaller.DownloadVersion(version, cancellationToken); + + //okay up to us to install it then + await ioManager.DeleteDirectory(versionKey, cancellationToken).ConfigureAwait(false); + await ioManager.CreateDirectory(versionKey, cancellationToken).ConfigureAwait(false); + + await ioManager.ZipToDirectory(versionKey, await downloadTask.ConfigureAwait(false), cancellationToken).ConfigureAwait(false); + + await byondInstaller.InstallByond(ioManager.ResolvePath(versionKey), version, cancellationToken).ConfigureAwait(false); + + //make sure to do this last because this is what tells us we have a valid version + await ioManager.WriteAllBytes(ioManager.ConcatPath(versionKey, VersionFileName), Encoding.UTF8.GetBytes(version.ToString()), cancellationToken).ConfigureAwait(false); + } + catch + { + lock (installedVersions) + installedVersions.Remove(versionKey); + throw; + } + } + + /// + public async Task ChangeVersion(Version version, CancellationToken cancellationToken) + { + await InstallVersion(version, cancellationToken).ConfigureAwait(false); + using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false)) + { + await ioManager.WriteAllBytes(ActiveVersionFileName, Encoding.UTF8.GetBytes(version.ToString()), cancellationToken).ConfigureAwait(false); + ActiveVersion = version; + } + } + + /// + public async Task UseExecutables(Version requiredVersion, CancellationToken cancellationToken) + { + var versionToUse = requiredVersion ?? ActiveVersion; + if (versionToUse == null) + throw new InvalidOperationException("No BYOND versions installed!"); + await InstallVersion(requiredVersion, cancellationToken).ConfigureAwait(false); + + var versionKey = VersionKey(versionToUse); + + return new ByondExecutableLock + { + DreamDaemonPath = ioManager.ResolvePath(ioManager.ConcatPath(versionKey, BinPath, byondInstaller.DreamDaemonName)), + DreamMakerPath = ioManager.ResolvePath(ioManager.ConcatPath(versionKey, BinPath, byondInstaller.DreamMakerName)), + Version = versionToUse + }; + } + + /// + public async Task StartAsync(CancellationToken cancellationToken) + { + var cacheCleanTask = byondInstaller.CleanCache(cancellationToken); + + async Task GetActiveVersion() + { + if (!await ioManager.FileExists(ActiveVersionFileName, cancellationToken).ConfigureAwait(false)) + return null; + return await ioManager.ReadAllBytes(ActiveVersionFileName, cancellationToken).ConfigureAwait(false); + } + + var activeVersionBytesTask = GetActiveVersion(); + + await ioManager.CreateDirectory(".", cancellationToken).ConfigureAwait(false); + var directories = await ioManager.GetDirectories(".", cancellationToken).ConfigureAwait(false); + + async Task ReadVersion(string path) + { + var versionFile = ioManager.ConcatPath(path, VersionFileName); + if (!await ioManager.FileExists(versionFile, cancellationToken).ConfigureAwait(false)) + { + logger.LogInformation("Cleaning unparsable version path: {0}", ioManager.ResolvePath(path)); + await ioManager.DeleteDirectory(path, cancellationToken).ConfigureAwait(false); //cleanup + return; + } + var bytes = await ioManager.ReadAllBytes(versionFile, cancellationToken).ConfigureAwait(false); + var text = Encoding.UTF8.GetString(bytes); + if (Version.TryParse(text, out var version)) + { + var key = VersionKey(version); + lock (installedVersions) + if (!installedVersions.ContainsKey(key)) + { + installedVersions.Add(key, Task.CompletedTask); + return; + } + } + await ioManager.DeleteDirectory(path, cancellationToken).ConfigureAwait(false); + }; + + await Task.WhenAll(directories.Select(x => ReadVersion(x))).ConfigureAwait(false); + + var activeVersionBytes = await activeVersionBytesTask.ConfigureAwait(false); + if (activeVersionBytes != null) + { + var activeVersionString = Encoding.UTF8.GetString(activeVersionBytes); + if (Version.TryParse(activeVersionString, out var activeVersion)) + ActiveVersion = activeVersion; + else + await ioManager.DeleteFile(ActiveVersionFileName, cancellationToken).ConfigureAwait(false); + + await cacheCleanTask.ConfigureAwait(false); + } + } + + /// + public Task StopAsync(CancellationToken cancellationToken) => Task.CompletedTask; + } +} diff --git a/src/Tgstation.Server.Host/Components/Byond/IByondExecutableLock.cs b/src/Tgstation.Server.Host/Components/Byond/IByondExecutableLock.cs new file mode 100644 index 0000000000..76e3c04321 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Byond/IByondExecutableLock.cs @@ -0,0 +1,30 @@ +using System; + +namespace Tgstation.Server.Host.Components.Byond +{ + /// + /// Represents usage of the two primary BYOND server executables + /// + public interface IByondExecutableLock : IDisposable + { + /// + /// The of the locked executables + /// + Version Version { get; } + + /// + /// The path to the DreamDaemon executable + /// + string DreamDaemonPath { get; } + + /// + /// The path to the dm/DreamMaker executable + /// + string DreamMakerPath { get; } + + /// + /// Call if, during a detach, this version should not be deleted + /// + void DoNotDeleteThisSession(); + } +} diff --git a/src/Tgstation.Server.Host/Components/Byond/IByondInstaller.cs b/src/Tgstation.Server.Host/Components/Byond/IByondInstaller.cs new file mode 100644 index 0000000000..8c2e742b30 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Byond/IByondInstaller.cs @@ -0,0 +1,46 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Host.Components.Byond +{ + /// + /// For downloading and installing BYOND extractions for a given system + /// + interface IByondInstaller + { + /// + /// Get the file name of the DreamDaemon executable + /// + string DreamDaemonName { get; } + + /// + /// Get the file name of the DreamMaker executable + /// + string DreamMakerName { get; } + + /// + /// Download a given BYOND + /// + /// The of BYOND to download + /// The for the operation + /// A resulting in the s of the zipfile + Task DownloadVersion(Version version, CancellationToken cancellationToken); + + /// + /// Does actions necessary to get an extracted BYOND installation working + /// + /// The path to the BYOND installation + /// The of BYOND being installed + /// The for the operation + /// + Task InstallByond(string path, Version version, CancellationToken cancellationToken); + + /// + /// Attempts to cleans the BYOND cache folder for the system + /// + /// The for the operation + /// A representing the running operation + Task CleanCache(CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Components/Byond/IByondManager.cs b/src/Tgstation.Server.Host/Components/Byond/IByondManager.cs new file mode 100644 index 0000000000..767d15537e --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Byond/IByondManager.cs @@ -0,0 +1,39 @@ +using Microsoft.Extensions.Hosting; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Host.Components.Byond +{ + /// + /// For managing the BYOND installation + /// + public interface IByondManager : IHostedService, IDisposable + { + /// + /// The currently active BYOND version + /// + Version ActiveVersion { get; } + + /// + /// The installed BYOND versions + /// + IReadOnlyList InstalledVersions { get; } + + /// + /// Change the active BYOND version + /// + /// The new + /// The for the operation + Task ChangeVersion(Version version, CancellationToken cancellationToken); + + /// + /// Lock the current installation's location and return a + /// + /// The BYOND required + /// The for the operation + /// A resulting in the requested + Task UseExecutables(Version requiredVersion, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Components/Byond/PosixByondInstaller.cs b/src/Tgstation.Server.Host/Components/Byond/PosixByondInstaller.cs new file mode 100644 index 0000000000..4bbcc351a2 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Byond/PosixByondInstaller.cs @@ -0,0 +1,93 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Globalization; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Host.IO; + +namespace Tgstation.Server.Host.Components.Byond +{ + /// + /// for Posix systems + /// + sealed class PosixByondInstaller : IByondInstaller + { + /// + /// The URL format string for getting BYOND linux version {0}.{1} zipfile + /// + const string ByondRevisionsURL = "https://secure.byond.com/download/build/{0}/{0}.{1}_byond_linux.zip"; + /// + /// Path to the BYOND cache + /// + const string ByondCachePath = "~/.byond"; //TODO: Verify this is correct!!!!! + + /// + public string DreamDaemonName => "DreamDaemon"; + + /// + public string DreamMakerName => "DreamMaker"; + + /// + /// The for the + /// + readonly IIOManager ioManager; + + /// + /// The for the + /// + readonly ILogger logger; + + /// + /// Construct a + /// + /// The value of + /// The value of + public PosixByondInstaller(IIOManager ioManager, ILogger logger) + { + this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager)); + this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + /// + public async Task CleanCache(CancellationToken cancellationToken) + { + try + { + await ioManager.DeleteDirectory(ByondCachePath, cancellationToken).ConfigureAwait(false); + } + catch (Exception e) + { + logger.LogWarning("Error deleting BYOND cache! Exception: {0}", e); + } + } + + /// + public async Task DownloadVersion(Version version, CancellationToken cancellationToken) + { + var ourVersion = version; + //lummox is annoying and doesn't like to post linux versions if nothing changed in DreamDaemon/DM + //if this for's exit condition ever triggers, i get to say I told you so + Exception lastException = null; + for (var I = 0; I < 5 && ourVersion.Minor >= 1; ++I, ourVersion = new Version(ourVersion.Major, ourVersion.Minor)) + { + try + { + var url = String.Format(CultureInfo.InvariantCulture, ByondRevisionsURL, ourVersion.Major, ourVersion.Minor); + + return await ioManager.DownloadFile(new Uri(url), cancellationToken).ConfigureAwait(false); + } + catch (WebException e) + { + if (!(e.Status == WebExceptionStatus.ProtocolError && e.Response is HttpWebResponse response && response.StatusCode == HttpStatusCode.NotFound)) + throw; + lastException = e; + } + } + throw lastException; + } + + /// + public Task InstallByond(string path, Version version, CancellationToken cancellationToken) => Task.CompletedTask; + } +} diff --git a/src/Tgstation.Server.Host/Components/Byond/WindowsByondInstaller.cs b/src/Tgstation.Server.Host/Components/Byond/WindowsByondInstaller.cs new file mode 100644 index 0000000000..3e5422c62d --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Byond/WindowsByondInstaller.cs @@ -0,0 +1,160 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Diagnostics; +using System.Globalization; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Host.Core; +using Tgstation.Server.Host.IO; + +namespace Tgstation.Server.Host.Components.Byond +{ + /// + /// for windows systems + /// + sealed class WindowsByondInstaller : IByondInstaller, IDisposable + { + /// + /// The URL format string for getting BYOND windows version {0}.{1} zipfile + /// + const string ByondRevisionsURL = "https://secure.byond.com/download/build/{0}/{0}.{1}_byond.zip"; + /// + /// Directory to byond installation configuration + /// + const string ByondConfigDir = "byond/config"; + /// + /// BYOND's DreamDaemon config file + /// + const string ByondDDConfig = "daemon.txt"; + /// + /// Setting to add to to suppress an invisible user prompt for running a trusted mode .dmb + /// + const string ByondNoPromptTrustedMode = "trusted-check 0"; + /// + /// The directory that contains the BYOND directx redistributable + /// + const string ByondDXDir = "byond/directx"; + + /// + public string DreamDaemonName => "dreamdaemon.exe"; + + /// + public string DreamMakerName => "dm.exe"; + + /// + /// The for the + /// + readonly IIOManager ioManager; + + /// + /// The for the + /// + readonly ILogger logger; + + /// + /// The for the + /// + readonly SemaphoreSlim semaphore; + + /// + /// If DirectX was installed + /// + bool installedDirectX; + + /// + /// Construct a + /// + /// The value of + /// The value of + public WindowsByondInstaller(IIOManager ioManager, ILogger logger) + { + this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager)); + this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); + + semaphore = new SemaphoreSlim(1); + installedDirectX = false; + } + + /// + public void Dispose() => semaphore.Dispose(); + + /// + public async Task CleanCache(CancellationToken cancellationToken) + { + try + { + await ioManager.DeleteDirectory(ioManager.ConcatPath(Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments), "byond/cache"), cancellationToken).ConfigureAwait(false); + } + catch (Exception e) + { + logger.LogWarning("Error deleting BYOND cache! Exception: {0}", e); + } + } + + /// + public Task DownloadVersion(Version version, CancellationToken cancellationToken) + { + var url = String.Format(CultureInfo.InvariantCulture, ByondRevisionsURL, version.Major, version.Minor); + + return ioManager.DownloadFile(new Uri(url), cancellationToken); + } + + /// + public async Task InstallByond(string path, Version version, CancellationToken cancellationToken) + { + async Task SetNoPromptTrusted() + { + var configPath = ioManager.ConcatPath(path, ByondConfigDir); + await ioManager.CreateDirectory(configPath, cancellationToken).ConfigureAwait(false); + await ioManager.WriteAllBytes(ioManager.ConcatPath(configPath, ByondDDConfig), Encoding.UTF8.GetBytes(ByondNoPromptTrustedMode), cancellationToken).ConfigureAwait(false); + }; + + var setNoPromptTrustedModeTask = SetNoPromptTrusted(); + + //after this version lummox made DD depend of directx lol + if (version.Major >= 512 && version.Minor >= 1427 && !installedDirectX) + using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false)) + { + if (!installedDirectX) + //always install it, it's pretty fast and will do better redundancy checking than us + using (var p = new Process()) + { + p.StartInfo.Arguments = "/silent"; + var rbdx = ioManager.ConcatPath(path, ByondDXDir); + p.StartInfo.FileName = rbdx + "/DXSETUP.exe"; + p.StartInfo.UseShellExecute = false; + p.StartInfo.WorkingDirectory = rbdx; + p.EnableRaisingEvents = true; + var tcs = new TaskCompletionSource(); + p.Exited += (a, b) => tcs.SetResult(null); + try + { + p.Start(); + using (cancellationToken.Register(() => + { + p.Kill(); + tcs.SetCanceled(); + })) + await tcs.Task.ConfigureAwait(false); + } + finally + { + try + { + p.Kill(); + p.WaitForExit(); + } + catch (InvalidOperationException) { } + } + + if (p.ExitCode != 0) + throw new Exception("Failed to install included DirectX! Exit code: " + p.ExitCode); + installedDirectX = true; + } + } + + await setNoPromptTrustedModeTask.ConfigureAwait(false); + } + } +} diff --git a/src/Tgstation.Server.Host/Components/Chat/Channel.cs b/src/Tgstation.Server.Host/Components/Chat/Channel.cs new file mode 100644 index 0000000000..07111826d8 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Chat/Channel.cs @@ -0,0 +1,48 @@ +using Newtonsoft.Json; +using System; +using System.Globalization; + +namespace Tgstation.Server.Host.Components.Chat +{ + /// + /// Represents a channel + /// + public sealed class Channel + { + /// + /// Backing field for . Represented as a to avoid BYOND percision loss + /// + public string Id { get; set; } + + /// + /// The channel Id. + /// + /// remaps this to an internal id using + [JsonIgnore] + public ulong RealId + { + get => UInt64.Parse(Id, CultureInfo.InvariantCulture); + set => Id = value.ToString(CultureInfo.InvariantCulture); + } + + /// + /// The user friendly name of the + /// + public string FriendlyName { get; set; } + + /// + /// The name of the connection the belongs to + /// + public string ConnectionName { get; set; } + + /// + /// If this is considered a channel for admin commands + /// + public bool IsAdmin { get; set; } + + /// + /// If this is a 1-to-1 chat channel + /// + public bool IsPrivate { get; set; } + } +} diff --git a/src/Tgstation.Server.Host/Components/Chat/ChannelMapping.cs b/src/Tgstation.Server.Host/Components/Chat/ChannelMapping.cs new file mode 100644 index 0000000000..5185f033cc --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Chat/ChannelMapping.cs @@ -0,0 +1,28 @@ +namespace Tgstation.Server.Host.Components.Chat +{ + /// + /// Represents a mapping of a + /// + sealed class ChannelMapping + { + /// + /// The Id of the + /// + public long ProviderId { get; set; } + + /// + /// The original + /// + public ulong ProviderChannelId { get; set; } + + /// + /// If is a watchdog channel + /// + public bool IsWatchdogChannel { get; set; } + + /// + /// The with the mapped Id + /// + public Channel Channel { get; set; } + } +} diff --git a/src/Tgstation.Server.Host/Components/Chat/Chat.cs b/src/Tgstation.Server.Host/Components/Chat/Chat.cs new file mode 100644 index 0000000000..6dc8f86a6f --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Chat/Chat.cs @@ -0,0 +1,519 @@ +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Api.Models.Internal; +using Tgstation.Server.Host.Components.Chat.Commands; +using Tgstation.Server.Host.Components.Chat.Providers; +using Tgstation.Server.Host.IO; + +namespace Tgstation.Server.Host.Components.Chat +{ + /// + sealed class Chat : IChat + { + const string CommonMention = "!tgs"; + + /// + /// The for the + /// + readonly IProviderFactory providerFactory; + + /// + /// The for the + /// + readonly IIOManager ioManager; + + /// + /// The for the + /// + readonly ILogger logger; + + /// + /// Unchanging s in the mapped by + /// + readonly Dictionary builtinCommands; + + /// + /// Map of s in use, keyed by + /// + readonly Dictionary providers; + + /// + /// Map of s to s + /// + readonly Dictionary mappedChannels; + + /// + /// The active s for the + /// + readonly List trackingContexts; + + /// + /// The for + /// + readonly CancellationTokenSource handlerCts; + + /// + /// The initial for the + /// + readonly List initialChatSettings; + + /// + /// The for the + /// + ICustomCommandHandler customCommandHandler; + + /// + /// The that monitors incoming chat messages + /// + Task chatHandler; + + /// + /// The that completes when change + /// + TaskCompletionSource connectionsUpdated; + + /// + /// Used for remapping s + /// + ulong channelIdCounter; + + /// + /// If has been called + /// + bool started; + + /// + /// Construct a + /// + /// The value of + /// The value of + /// The value of + /// The used to populate + /// The used to populate + public Chat(IProviderFactory providerFactory, IIOManager ioManager, ILogger logger, ICommandFactory commandFactory, IEnumerable initialChatSettings) + { + this.providerFactory = providerFactory ?? throw new ArgumentNullException(nameof(providerFactory)); + this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager)); + this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); + this.initialChatSettings = initialChatSettings?.ToList() ?? throw new ArgumentNullException(nameof(initialChatSettings)); + + builtinCommands = new Dictionary(); + foreach (var I in commandFactory?.GenerateCommands() ?? throw new ArgumentNullException(nameof(commandFactory))) + builtinCommands.Add(I.Name.ToUpperInvariant(), I); + + providers = new Dictionary(); + mappedChannels = new Dictionary(); + trackingContexts = new List(); + handlerCts = new CancellationTokenSource(); + connectionsUpdated = new TaskCompletionSource(); + channelIdCounter = 1; + } + + /// + public void Dispose() + { + handlerCts.Dispose(); + foreach (var I in providers) + I.Value.Dispose(); + } + + /// + /// Remove a from and optionally updating the as well + /// + /// The of the to delete + /// If should be update + /// The for the operation + /// A resulting in the being removed if it exists, otherwise + async Task RemoveProvider(long connectionId, bool updateTrackings, CancellationToken cancellationToken) + { + IProvider provider; + lock (providers) + if (!providers.TryGetValue(connectionId, out provider)) + return null; + Task task; + lock (mappedChannels) + { + foreach (var I in mappedChannels.Where(x => x.Value.ProviderId == connectionId).Select(x => x.Key).ToList()) + mappedChannels.Remove(I); + + if (updateTrackings) + lock (trackingContexts) + task = Task.WhenAll(trackingContexts.Select(x => x.SetChannels(mappedChannels.Select(y => y.Value.Channel), cancellationToken))); + else + task = Task.CompletedTask; + } + await task.ConfigureAwait(false); + return provider; + } + + /// + /// Processes a + /// + /// The who recevied + /// The to process + /// The for the operation + /// A representing the running operation + async Task ProcessMessage(IProvider provider, Message message, CancellationToken cancellationToken) + { + logger.LogTrace("Chat message: {0}. User (Note unconverted provider Id): {1}", message.Content, JsonConvert.SerializeObject(message.User)); + + //map the channel if it's private and we haven't seen it + if (message.User.Channel.IsPrivate) + lock (providers) + lock (mappedChannels) + { + if (!provider.Connected) + return; + var enumerable = mappedChannels.Where(x => x.Value.ProviderChannelId == message.User.Channel.RealId); + if (!enumerable.Any()) + { + ulong newId; + lock (this) + newId = channelIdCounter++; + mappedChannels.Add(newId, new ChannelMapping + { + IsWatchdogChannel = false, + ProviderChannelId = message.User.Channel.RealId, + ProviderId = providers.Where(x => x.Value == provider).Select(x => x.Key).First(), + Channel = message.User.Channel + }); + message.User.Channel.RealId = newId; + } + else + message.User.Channel.RealId = enumerable.First().Key; + } + + var splits = new List(message.Content.TrimEnd().Split(' ')); + var address = splits[0]; + if (address.Length > 1 && (address[address.Length - 1] == ':' || address[address.Length - 1] == ',')) + address = address.Substring(0, address.Length - 1); + + address = address.ToUpperInvariant(); + + var addressed = address == CommonMention.ToUpperInvariant() || address == provider.BotMention.ToUpperInvariant(); + + if (!addressed && !message.User.Channel.IsPrivate) + //no mention + return; + + if (addressed) + splits.RemoveAt(0); + + if ((splits.Count == 1 && (!message.User.Channel.IsPrivate || splits[0].Length == 0)) || splits.Count == 0) + { + //just a mention + await SendMessage("Hi!", new List { message.User.Channel.RealId }, cancellationToken).ConfigureAwait(false); + return; + } + + var command = splits[0].ToUpperInvariant(); + splits.RemoveAt(0); + var arguments = String.Join(" ", splits); + + try + { + async Task GetCommand(string commandName) + { + if (!builtinCommands.TryGetValue(commandName, out var handler)) + { + var tasks = trackingContexts.Select(x => x.GetCustomCommands(cancellationToken)); + await Task.WhenAll(tasks).ConfigureAwait(false); + handler = tasks.SelectMany(x => x.Result).Where(x => x.Name.ToUpperInvariant() == commandName).FirstOrDefault(); + } + return handler; + }; + + const string UnknownCommandMessage = "Unknown command! Type '?' or 'help' for available commands."; + + if (command == "HELP" || command == "?") + { + string helpText; + if (splits.Count == 0) + { + var allCommands = builtinCommands.Select(x => x.Value).ToList(); + var tasks = trackingContexts.Select(x => x.GetCustomCommands(cancellationToken)); + await Task.WhenAll(tasks).ConfigureAwait(false); + allCommands.AddRange(tasks.SelectMany(x => x.Result)); + helpText = String.Format(CultureInfo.InvariantCulture, "Available commands (Type '?' or 'help' and then a command name for more details): {0}", String.Join(", ", allCommands.Select(x => x.Name))); + } + else + { + var helpHandler = await GetCommand(splits[0].ToUpperInvariant()).ConfigureAwait(false); + if (helpHandler != default) + helpText = String.Format(CultureInfo.InvariantCulture, "{0}: {1}", helpHandler.Name, helpHandler.HelpText); + else + helpText = UnknownCommandMessage; + } + await SendMessage(helpText, new List { message.User.Channel.RealId }, cancellationToken).ConfigureAwait(false); + return; + } + + var commandHandler = await GetCommand(command).ConfigureAwait(false); + if (commandHandler == default) + { + await SendMessage(UnknownCommandMessage, new List { message.User.Channel.RealId }, cancellationToken).ConfigureAwait(false); + return; + } + + var result = await commandHandler.Invoke(arguments, message.User, cancellationToken).ConfigureAwait(false); + if(result != null) + await SendMessage(result, new List { message.User.Channel.RealId }, cancellationToken).ConfigureAwait(false); + } + catch (Exception e) + { + //error bc custom commands should reply about why it failed + logger.LogError("Error processing chat command: {0}", e); + await SendMessage("Internal error processing command!", new List { message.User.Channel.RealId }, cancellationToken).ConfigureAwait(false); + } + } + + /// + /// Monitors active providers for new s + /// + /// The for the operation + /// A representing the running operation + async Task MonitorMessages(CancellationToken cancellationToken) + { + var messageTasks = new Dictionary>(); + try + { + while (!cancellationToken.IsCancellationRequested) + { + //prune disconnected providers + foreach (var I in messageTasks.Where(x => !x.Key.Connected).ToList()) + messageTasks.Remove(I.Key); + + //add new ones + Task updatedTask; + lock (this) + updatedTask = connectionsUpdated.Task; + lock (providers) + foreach (var I in providers) + if (I.Value.Connected && !messageTasks.ContainsKey(I.Value)) + messageTasks.Add(I.Value, I.Value.NextMessage(cancellationToken)); + + if(messageTasks.Count == 0) + { + await Task.Delay(1000, cancellationToken).ConfigureAwait(false); + continue; + } + + //wait for a message + await Task.WhenAny(updatedTask, Task.WhenAny(messageTasks.Select(x => x.Value))).ConfigureAwait(false); + + //process completed ones + foreach (var I in messageTasks.Where(x => x.Value.IsCompleted).ToList()) + { + var message = await I.Value.ConfigureAwait(false); + + await ProcessMessage(I.Key, message, cancellationToken).ConfigureAwait(false); + + messageTasks.Remove(I.Key); + } + } + } + catch (OperationCanceledException) { } + catch(Exception e) + { + logger.LogError("Message monitor crashed!: Exception: {0}", e); + } + } + + /// + public async Task ChangeChannels(long connectionId, IEnumerable newChannels, CancellationToken cancellationToken) + { + if (newChannels == null) + throw new ArgumentNullException(nameof(newChannels)); + var provider = await RemoveProvider(connectionId, false, cancellationToken).ConfigureAwait(false); + if (provider == null) + return; + var results = await provider.MapChannels(newChannels, cancellationToken).ConfigureAwait(false); + if (results == null) //aborted + return; + var mappings = Enumerable.Zip(newChannels, results, (x, y) => new ChannelMapping + { + IsWatchdogChannel = x.IsWatchdogChannel == true, + ProviderChannelId = y.RealId, + ProviderId = connectionId, + Channel = y + }); + + ulong baseId; + lock (this) + { + baseId = channelIdCounter; + channelIdCounter += (ulong)results.Count; + } + + Task task; + lock (mappedChannels) + { + lock (providers) + if (!providers.TryGetValue(connectionId, out IProvider verify) || verify != provider) //aborted again + return; + foreach (var I in mappings) + { + var newId = baseId++; + mappedChannels.Add(newId, I); + I.Channel.RealId = newId; + } + + lock (trackingContexts) + task = Task.WhenAll(trackingContexts.Select(x => x.SetChannels(mappedChannels.Select(y => y.Value.Channel), cancellationToken))); + } + await task.ConfigureAwait(false); + } + + /// + public async Task ChangeSettings(ChatSettings newSettings, CancellationToken cancellationToken) + { + if (newSettings == null) + throw new ArgumentNullException(nameof(newSettings)); + IProvider provider; + + async Task DisconnectProvider(IProvider p) + { + try + { + await p.Disconnect(cancellationToken).ConfigureAwait(false); + } + finally + { + p.Dispose(); + } + } + + Task disconnectTask; + lock (providers) + { + //raw settings changes forces a rebuild of the provider + if (providers.TryGetValue(newSettings.Id, out provider)) + { + providers.Remove(newSettings.Id); + disconnectTask = DisconnectProvider(provider); + } + else + disconnectTask = Task.CompletedTask; + if (newSettings.Enabled.Value) + { + provider = providerFactory.CreateProvider(newSettings); + providers.Add(newSettings.Id, provider); + } + } + + lock (mappedChannels) + foreach (var I in mappedChannels.Where(x => x.Value.ProviderId == newSettings.Id).Select(x => x.Key).ToList()) + mappedChannels.Remove(I); + + await disconnectTask.ConfigureAwait(false); + + if (started) + { + if (newSettings.Enabled.Value) + await provider.Connect(cancellationToken).ConfigureAwait(false); + lock(this) + { + //same thread shennanigans + var oldOne = connectionsUpdated; + connectionsUpdated = new TaskCompletionSource(); + oldOne.SetResult(null); + } + } + } + + /// + public Task SendMessage(string message, IEnumerable channelIds, CancellationToken cancellationToken) + { + if (message == null) + throw new ArgumentNullException(nameof(message)); + if (channelIds == null) + throw new ArgumentNullException(nameof(channelIds)); + + return Task.WhenAll(channelIds.Select(x => + { + ChannelMapping channelMapping; + lock(mappedChannels) + if (!mappedChannels.TryGetValue(x, out channelMapping)) + return Task.CompletedTask; + IProvider provider; + lock (providers) + if (!providers.TryGetValue(channelMapping.ProviderId, out provider)) + return Task.CompletedTask; + return provider.SendMessage(channelMapping.ProviderChannelId, message, cancellationToken); + })); + } + + /// + public Task SendWatchdogMessage(string message, CancellationToken cancellationToken) + { + List wdChannels; + lock (mappedChannels) //so it doesn't change while we're using it + wdChannels = mappedChannels.Where(x => x.Value.IsWatchdogChannel).Select(x => x.Key).ToList(); + return SendMessage(message, wdChannels, cancellationToken); + } + + /// + public async Task StartAsync(CancellationToken cancellationToken) + { + await Task.WhenAll(initialChatSettings.Select(x => ChangeSettings(x, cancellationToken))).ConfigureAwait(false); + await Task.WhenAll(providers.Select(x => x.Value).Select(x => x.Connect(cancellationToken))).ConfigureAwait(false); + await Task.WhenAll(initialChatSettings.Select(x => ChangeChannels(x.Id, x.Channels, cancellationToken))).ConfigureAwait(false); + chatHandler = MonitorMessages(handlerCts.Token); + started = true; + } + + /// + public async Task StopAsync(CancellationToken cancellationToken) + { + handlerCts.Cancel(); + await chatHandler.ConfigureAwait(false); + await Task.WhenAll(providers.Select(x => x.Value).Select(x => x.Disconnect(cancellationToken))).ConfigureAwait(false); + } + + /// + public async Task TrackJsons(string basePath, string channelsJsonName, string commandsJsonName, CancellationToken cancellationToken) + { + if (customCommandHandler == null) + throw new InvalidOperationException("RegisterCommandHandler() hasn't been called!"); + JsonTrackingContext context = null; + context = new JsonTrackingContext(ioManager, customCommandHandler, () => + { + lock (trackingContexts) + trackingContexts.Remove(context); + }, ioManager.ConcatPath(basePath, commandsJsonName), ioManager.ConcatPath(basePath, channelsJsonName)); + Task task; + lock (trackingContexts) + { + trackingContexts.Add(context); + lock (mappedChannels) + task = Task.WhenAll(trackingContexts.Select(x => x.SetChannels(mappedChannels.Select(y => y.Value.Channel), cancellationToken))); + } + await task.ConfigureAwait(false); + return context; + } + + /// + public bool Connected(long connectionId) + { + lock (providers) + return providers.TryGetValue(connectionId, out var provider) && provider.Connected; + } + + /// + public void RegisterCommandHandler(ICustomCommandHandler customCommandHandler) + { + if (this.customCommandHandler != null) + throw new InvalidOperationException("RegisterCommandHandler() already called!"); + this.customCommandHandler = customCommandHandler ?? throw new ArgumentNullException(nameof(customCommandHandler)); + } + + /// + public Task DeleteConnection(long connectionId, CancellationToken cancellationToken) => RemoveProvider(connectionId, true, cancellationToken); + } +} diff --git a/src/Tgstation.Server.Host/Components/Chat/ChatFactory.cs b/src/Tgstation.Server.Host/Components/Chat/ChatFactory.cs new file mode 100644 index 0000000000..a5624d7f20 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Chat/ChatFactory.cs @@ -0,0 +1,50 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using Tgstation.Server.Host.Components.Chat.Commands; +using Tgstation.Server.Host.IO; + +namespace Tgstation.Server.Host.Components.Chat +{ + /// + sealed class ChatFactory : IChatFactory + { + /// + /// The for the + /// + readonly IIOManager ioManager; + + /// + /// The for the + /// + readonly ILoggerFactory loggerFactory; + + /// + /// The for the + /// + readonly ICommandFactory commandFactory; + + /// + /// The for the + /// + readonly IProviderFactory providerFactory; + + /// + /// Construct a + /// + /// The value of + /// The value of + /// The value of + /// The value of + public ChatFactory(IIOManager ioManager, ILoggerFactory loggerFactory, ICommandFactory commandFactory, IProviderFactory providerFactory) + { + this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager)); + this.loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory)); + this.commandFactory = commandFactory ?? throw new ArgumentNullException(nameof(commandFactory)); + this.providerFactory = providerFactory ?? throw new ArgumentNullException(nameof(providerFactory)); + } + + /// + public IChat CreateChat(IEnumerable initialChatSettings) => new Chat(providerFactory, ioManager, loggerFactory.CreateLogger(), commandFactory, initialChatSettings); + } +} diff --git a/src/Tgstation.Server.Host/Components/Chat/Commands/BoolConverter.cs b/src/Tgstation.Server.Host/Components/Chat/Commands/BoolConverter.cs new file mode 100644 index 0000000000..e3103e4298 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Chat/Commands/BoolConverter.cs @@ -0,0 +1,20 @@ +using Newtonsoft.Json; +using System; + +namespace Tgstation.Server.Host.Components.Chat.Commands +{ + /// + /// for decoding bools returned by BYOND + /// + sealed class BoolConverter : JsonConverter + { + /// + public override void WriteJson(JsonWriter writer, object value, JsonSerializer serializer) => writer.WriteValue(((bool)value) ? 1 : 0); + + /// + public override object ReadJson(JsonReader reader, Type objectType, object existingValue, JsonSerializer serializer) => reader.Value.ToString() == "1"; + + /// + public override bool CanConvert(Type objectType) => objectType == typeof(bool); + } +} diff --git a/src/Tgstation.Server.Host/Components/Chat/Commands/CommandFactory.cs b/src/Tgstation.Server.Host/Components/Chat/Commands/CommandFactory.cs new file mode 100644 index 0000000000..a9bd873534 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Chat/Commands/CommandFactory.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using Tgstation.Server.Host.Core; + +namespace Tgstation.Server.Host.Components.Chat.Commands +{ + /// + sealed class CommandFactory : ICommandFactory + { + /// + /// The for the + /// + readonly IApplication application; + + /// + /// Construct a + /// + /// The value of + public CommandFactory(IApplication application) + { + this.application = application ?? throw new ArgumentNullException(nameof(application)); + } + + /// + public IReadOnlyList GenerateCommands() => new List + { + new KekCommand(), + new VersionCommand(application) + }; + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Components/Chat/Commands/CustomCommand.cs b/src/Tgstation.Server.Host/Components/Chat/Commands/CustomCommand.cs new file mode 100644 index 0000000000..da23754a2d --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Chat/Commands/CustomCommand.cs @@ -0,0 +1,47 @@ +using Newtonsoft.Json; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Host.Components.Chat.Commands +{ + /// + /// Represents a command made from DM code + /// + public sealed class CustomCommand : ICommand + { + /// + public string Name { get; set; } + + /// + public string HelpText { get; set; } + + /// + [JsonConverter(typeof(BoolConverter))] + public bool AdminOnly { get; set; } + + /// + /// The for the + /// + ICustomCommandHandler handler; + + /// + /// Set a new + /// + /// The value of + public void SetHandler(ICustomCommandHandler handler) + { + if (this.handler != null) + throw new InvalidOperationException("SetHandler() already called!"); + this.handler = handler ?? throw new ArgumentNullException(nameof(handler)); + } + + /// + public Task Invoke(string arguments, User user, CancellationToken cancellationToken) + { + if (handler == null) + throw new InvalidOperationException("SetHandler() has not been called!"); + return handler.HandleChatCommand(Name, arguments, user, cancellationToken); + } + } +} diff --git a/src/Tgstation.Server.Host/Components/Chat/Commands/ICommand.cs b/src/Tgstation.Server.Host/Components/Chat/Commands/ICommand.cs new file mode 100644 index 0000000000..8694606731 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Chat/Commands/ICommand.cs @@ -0,0 +1,35 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Host.Components.Chat.Commands +{ + /// + /// Represents a command that can be invoked by talking to chat bots + /// + public interface ICommand + { + /// + /// The text to invoke the command. May not be "?" or "help" (case-insensitive) + /// + string Name { get; } + + /// + /// The help text to display when queires are made about the command + /// + string HelpText { get; } + + /// + /// If the command should only be available to s who's has set + /// + bool AdminOnly { get; } + + /// + /// Invoke the + /// + /// The text after with leading whitespace trimmed + /// The who invoked the command + /// The for the operation + /// A resulting in a to send to the invoker + Task Invoke(string arguments, User user, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Components/Chat/Commands/ICommandFactory.cs b/src/Tgstation.Server.Host/Components/Chat/Commands/ICommandFactory.cs new file mode 100644 index 0000000000..5520126604 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Chat/Commands/ICommandFactory.cs @@ -0,0 +1,16 @@ +using System.Collections.Generic; + +namespace Tgstation.Server.Host.Components.Chat.Commands +{ + /// + /// Factory for built in s + /// + interface ICommandFactory + { + /// + /// Generate builtin s + /// + /// A of s + IReadOnlyList GenerateCommands(); + } +} diff --git a/src/Tgstation.Server.Host/Components/Chat/Commands/KekCommand.cs b/src/Tgstation.Server.Host/Components/Chat/Commands/KekCommand.cs new file mode 100644 index 0000000000..7fa541ef37 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Chat/Commands/KekCommand.cs @@ -0,0 +1,28 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Host.Components.Chat.Commands +{ + /// + /// kek + /// + sealed class KekCommand : ICommand + { + /// + /// kek + /// + const string Kek = "kek"; + + /// + public string Name => Kek; + + /// + public string HelpText => Kek; + + /// + public bool AdminOnly => false; + + /// + public Task Invoke(string arguments, User user, CancellationToken cancellationToken) => Task.FromResult(Kek); + } +} diff --git a/src/Tgstation.Server.Host/Components/Chat/Commands/VersionCommand.cs b/src/Tgstation.Server.Host/Components/Chat/Commands/VersionCommand.cs new file mode 100644 index 0000000000..13b5a68f74 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Chat/Commands/VersionCommand.cs @@ -0,0 +1,39 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Host.Core; + +namespace Tgstation.Server.Host.Components.Chat.Commands +{ + /// + /// to return the + /// + sealed class VersionCommand : ICommand + { + /// + public string Name => "version"; + + /// + public string HelpText => "Displays the tgstation server version"; + + /// + public bool AdminOnly => false; + + /// + /// The for the + /// + readonly IApplication application; + + /// + /// Construct a + /// + /// + public VersionCommand(IApplication application) + { + this.application = application ?? throw new ArgumentNullException(nameof(application)); + } + + /// + public Task Invoke(string arguments, User user, CancellationToken cancellationToken) => Task.FromResult(application.VersionString); + } +} diff --git a/src/Tgstation.Server.Host/Components/Chat/IChat.cs b/src/Tgstation.Server.Host/Components/Chat/IChat.cs new file mode 100644 index 0000000000..15e6e939bc --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Chat/IChat.cs @@ -0,0 +1,80 @@ +using Microsoft.Extensions.Hosting; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Api.Models.Internal; + +namespace Tgstation.Server.Host.Components.Chat +{ + /// + /// For managing connected chat services + /// + public interface IChat : IHostedService, IDisposable + { + /// + /// If a given set of is connected + /// + /// The of the connection + /// if it is connected, otherwise + bool Connected(long connectionId); + + /// + /// Registers a to use + /// + /// A + void RegisterCommandHandler(ICustomCommandHandler customCommandHandler); + + /// + /// Change chat settings. If the is not currently in use, a new connection will be made instead + /// + /// The new + /// The for the operation + /// A representing the running operation. Will complete immediately if the property of is + Task ChangeSettings(ChatSettings newSettings, CancellationToken cancellationToken); + + /// + /// Disconnects and deletes a given connection + /// + /// The of the connection + /// The for the operation + /// A representing the running operation + Task DeleteConnection(long connectionId, CancellationToken cancellationToken); + + /// + /// Change chat channels + /// + /// The of the connection + /// An of the new list of s + /// The for the operation + /// A representing the running operation + Task ChangeChannels(long connectionId, IEnumerable newChannels, CancellationToken cancellationToken); + + /// + /// Send a chat to a given set of + /// + /// The message being sent + /// The s of the s to send to + /// The for the operation + /// A representing the running operation + Task SendMessage(string message, IEnumerable channelIds, CancellationToken cancellationToken); + + /// + /// Send a chat to configured watchdog channels + /// + /// The message being sent + /// The for the operation + /// A representing the running operation + Task SendWatchdogMessage(string message, CancellationToken cancellationToken); + + /// + /// Start tracking json files for commands and channels + /// + /// The base path of the .jsons + /// The name of the chat channels json + /// The name of the chat commands json + /// The for the operation + /// A resulting in a tied to the lifetime of the json trackings + Task TrackJsons(string basePath, string channelsJsonName, string commandsJsonName, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Components/Chat/IChatFactory.cs b/src/Tgstation.Server.Host/Components/Chat/IChatFactory.cs new file mode 100644 index 0000000000..44802b484f --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Chat/IChatFactory.cs @@ -0,0 +1,17 @@ +using System.Collections.Generic; + +namespace Tgstation.Server.Host.Components.Chat +{ + /// + /// For creating s + /// + interface IChatFactory + { + /// + /// Create a + /// + /// The initial for the + /// A new + IChat CreateChat(IEnumerable initialChatSettings); + } +} diff --git a/src/Tgstation.Server.Host/Components/Chat/ICustomCommandHandler.cs b/src/Tgstation.Server.Host/Components/Chat/ICustomCommandHandler.cs new file mode 100644 index 0000000000..9f640cb633 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Chat/ICustomCommandHandler.cs @@ -0,0 +1,21 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Host.Components.Chat +{ + /// + /// Handles s that map to those defined in a + /// + public interface ICustomCommandHandler + { + /// + /// Handle a chat command + /// + /// The command name + /// Everything typed after minus leading spaces + /// The sending + /// The for the operation + /// A resulting in the response text to send back + Task HandleChatCommand(string commandName, string arguments, User sender, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Components/Chat/IJsonTrackingContext.cs b/src/Tgstation.Server.Host/Components/Chat/IJsonTrackingContext.cs new file mode 100644 index 0000000000..778b6d0d3c --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Chat/IJsonTrackingContext.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Host.Components.Chat.Commands; + +namespace Tgstation.Server.Host.Components.Chat +{ + /// + /// Represents a tracking of dynamic chat json files + /// + public interface IJsonTrackingContext : IDisposable + { + /// + /// Read s from the + /// + /// The for the operation + /// A resulting in a of s in the + Task> GetCustomCommands(CancellationToken cancellationToken); + + /// + /// Writes information about connected to the + /// + /// The s to write out + /// The for the operation + /// A representing the running operation + Task SetChannels(IEnumerable channels, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Components/Chat/IProviderFactory.cs b/src/Tgstation.Server.Host/Components/Chat/IProviderFactory.cs new file mode 100644 index 0000000000..b4b47f2d5b --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Chat/IProviderFactory.cs @@ -0,0 +1,18 @@ +using Tgstation.Server.Api.Models.Internal; +using Tgstation.Server.Host.Components.Chat.Providers; + +namespace Tgstation.Server.Host.Components.Chat +{ + /// + /// Factory for s + /// + interface IProviderFactory + { + /// + /// Create a + /// + /// The for the new provider + /// A new + IProvider CreateProvider(ChatSettings settings); + } +} diff --git a/src/Tgstation.Server.Host/Components/Chat/JsonTrackingContext.cs b/src/Tgstation.Server.Host/Components/Chat/JsonTrackingContext.cs new file mode 100644 index 0000000000..dd552f87ff --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Chat/JsonTrackingContext.cs @@ -0,0 +1,64 @@ +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Host.Components.Chat.Commands; +using Tgstation.Server.Host.Core; +using Tgstation.Server.Host.IO; + +namespace Tgstation.Server.Host.Components.Chat +{ + /// + sealed class JsonTrackingContext : IJsonTrackingContext + { + readonly IIOManager ioManager; + readonly ICustomCommandHandler customCommandHandler; + readonly Action onDispose; + + readonly string commandsPath; + readonly string channelsPath; + + readonly SemaphoreSlim channelsSemaphore; + + public JsonTrackingContext(IIOManager ioManager, ICustomCommandHandler customCommandHandler, Action onDispose, string commandsPath, string channelsPath) + { + this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager)); + this.customCommandHandler = customCommandHandler ?? throw new ArgumentNullException(nameof(customCommandHandler)); + this.onDispose = onDispose ?? throw new ArgumentNullException(nameof(onDispose)); + this.commandsPath = commandsPath ?? throw new ArgumentNullException(nameof(commandsPath)); + this.channelsPath = channelsPath ?? throw new ArgumentNullException(nameof(channelsPath)); + + channelsSemaphore = new SemaphoreSlim(1); + } + + /// + public void Dispose() => onDispose(); + + /// + public async Task> GetCustomCommands(CancellationToken cancellationToken) + { + try + { + var resultBytes = await ioManager.ReadAllBytes(commandsPath, cancellationToken).ConfigureAwait(false); + var resultJson = Encoding.UTF8.GetString(resultBytes); + var result = JsonConvert.DeserializeObject>(resultJson); + foreach (var I in result) + I.SetHandler(customCommandHandler); + return result; + } + catch + { + return new List(); + } + } + + /// + public async Task SetChannels(IEnumerable channels, CancellationToken cancellationToken) + { + using (await SemaphoreSlimContext.Lock(channelsSemaphore, cancellationToken).ConfigureAwait(false)) + await ioManager.WriteAllBytes(channelsPath, Encoding.UTF8.GetBytes(JsonConvert.SerializeObject(channels)), cancellationToken).ConfigureAwait(false); + } + } +} diff --git a/src/Tgstation.Server.Host/Components/Chat/Message.cs b/src/Tgstation.Server.Host/Components/Chat/Message.cs new file mode 100644 index 0000000000..3989369ce8 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Chat/Message.cs @@ -0,0 +1,18 @@ +namespace Tgstation.Server.Host.Components.Chat.Providers +{ + /// + /// Represents a message recieved by a + /// + sealed class Message + { + /// + /// The text of the message + /// + public string Content { get; set; } + + /// + /// The who sent the + /// + public User User { get; set; } + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Components/Chat/ProviderFactory.cs b/src/Tgstation.Server.Host/Components/Chat/ProviderFactory.cs new file mode 100644 index 0000000000..bb3b6fd20f --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Chat/ProviderFactory.cs @@ -0,0 +1,91 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Globalization; +using Tgstation.Server.Api.Models; +using Tgstation.Server.Host.Components.Chat.Providers; +using Tgstation.Server.Host.Core; + +namespace Tgstation.Server.Host.Components.Chat +{ + /// + sealed class ProviderFactory : IProviderFactory + { + /// + /// The for the + /// + readonly ILoggerFactory loggerFactory; + + /// + /// The for the + /// + readonly IApplication application; + + /// + /// Construct a + /// + /// The value of + /// The value of + public ProviderFactory(ILoggerFactory loggerFactory, IApplication application) + { + this.loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory)); + this.application = application ?? throw new ArgumentNullException(nameof(application)); + } + + /// + public IProvider CreateProvider(Api.Models.Internal.ChatSettings settings) + { + if (settings == null) + throw new ArgumentNullException(nameof(settings)); + switch (settings.Provider) + { + case ChatProvider.Irc: + //Connection string semicolon delimited until the password field + if (settings.ConnectionString == null) + throw new InvalidOperationException("ConnectionString cannot be null!"); + var splits = settings.ConnectionString.Split(';'); + if (splits.Length < 4) + throw new InvalidOperationException("Invalid connection string!"); + + var address = splits[0]; + if (!UInt16.TryParse(splits[1], out var port)) + throw new InvalidOperationException("Unable to parse port!"); + var nick = splits[2]; + if (!Int32.TryParse(splits[3], out var intSsl)) + throw new InvalidOperationException("Unable to parse ssl option!"); + + IrcPasswordType? passwordType = null; + string password = null; + if(splits.Length > 4) + { + if (splits.Length < 6) + throw new InvalidOperationException("Invalid connection string!"); + if (!Int32.TryParse(splits[4], out var intPasswordType)) + throw new InvalidOperationException("Unable to parse password type!"); + + passwordType = (IrcPasswordType)intPasswordType; + switch (passwordType) + { + case IrcPasswordType.NickServ: + case IrcPasswordType.Sasl: + case IrcPasswordType.Server: + break; + default: + throw new InvalidOperationException("Invalid password type!"); + } + + var rest = new List(splits); + rest.RemoveRange(0, 5); + password = String.Join(";", rest); + } + + return new IrcProvider(loggerFactory.CreateLogger(), application, address, port, nick, password, passwordType, intSsl != 0); + case ChatProvider.Discord: + //discord is just the bot token + return new DiscordProvider(loggerFactory.CreateLogger(), settings.ConnectionString); + default: + throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Invalid ChatProvider: {0}", settings.Provider)); + } + } + } +} diff --git a/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs b/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs new file mode 100644 index 0000000000..1738af477b --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Chat/Providers/DiscordProvider.cs @@ -0,0 +1,211 @@ +using Discord; +using Discord.WebSocket; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Api.Models; + +namespace Tgstation.Server.Host.Components.Chat.Providers +{ + /// + /// for the Discord app + /// + sealed class DiscordProvider : Provider + { + /// + public override bool Connected => client.ConnectionState == ConnectionState.Connected; + + /// + public override string BotMention + { + get + { + if (!Connected) + throw new InvalidOperationException("Provider not connected"); + return NormalizeMention(client.CurrentUser.Mention); + } + } + + /// + /// The for the + /// + readonly ILogger logger; + + /// + /// The for the + /// + readonly DiscordSocketClient client; + + /// + /// The token used for connecting to discord + /// + readonly string botToken; + + /// + /// of mapped s + /// + readonly List mappedChannels; + + static string NormalizeMention(string fromDiscord) => fromDiscord.Replace("!", "", StringComparison.Ordinal); + + /// + /// Construct a + /// + /// The value of + /// The value of + public DiscordProvider(ILogger logger, string botToken) + { + this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); + this.botToken = botToken ?? throw new ArgumentNullException(nameof(botToken)); + client = new DiscordSocketClient(); + client.MessageReceived += Client_MessageReceived; + mappedChannels = new List(); + } + + /// + public override void Dispose() => client.Dispose(); + + /// + /// Handle a message recieved from Discord + /// + /// The + /// A representing the running operation + Task Client_MessageReceived(SocketMessage e) + { + if (e.Author.Id == client.CurrentUser.Id) + return Task.CompletedTask; + + var pm = e.Channel is IPrivateChannel; + + if (!pm && !mappedChannels.Contains(e.Channel.Id)) + return e.MentionedUsers.Any(x => x.Id == client.CurrentUser.Id) ? SendMessage(e.Channel.Id, "I do not respond to this channel!", default) : Task.CompletedTask; + + var result = new Message { + Content = e.Content, + User = new User + { + RealId = e.Author.Id, + Channel = new Channel + { + RealId = e.Channel.Id, + IsAdmin = false, + IsPrivate = true, + ConnectionName = pm ? e.Author.Username : (e.Channel as ITextChannel)?.Guild.Name ?? "UNKNOWN", + FriendlyName = e.Channel.Name + }, + FriendlyName = e.Author.Username, + Mention = NormalizeMention(e.Author.Mention) + } + }; + EnqueueMessage(result); + return Task.CompletedTask; + } + + /// + public override async Task Connect(CancellationToken cancellationToken) + { + if (Connected) + return true; + + try + { + await client.LoginAsync(TokenType.Bot, botToken, true).ConfigureAwait(false); + + cancellationToken.ThrowIfCancellationRequested(); + + await client.StartAsync().ConfigureAwait(false); + + var channelsAvailable = new TaskCompletionSource(); + client.Ready += () => + { + channelsAvailable.SetResult(null); + return Task.CompletedTask; + }; + using (cancellationToken.Register(() => channelsAvailable.SetCanceled())) + await channelsAvailable.Task.ConfigureAwait(false); + } + catch (Exception e) + { + logger.LogWarning("Error connecting to Discord: {0}", e); + return false; + } + + return true; + } + + public override async Task Disconnect(CancellationToken cancellationToken) + { + if (!Connected) + return; + + try + { + await client.StopAsync().ConfigureAwait(false); + cancellationToken.ThrowIfCancellationRequested(); + await client.LogoutAsync().ConfigureAwait(false); + } + catch (Exception e) + { + logger.LogWarning("Error disconnecting from discord: {0}", e); + } + } + + /// + public override Task> MapChannels(IEnumerable channels, CancellationToken cancellationToken) + { + if (channels == null) + throw new ArgumentNullException(nameof(channels)); + + if (!Connected) + throw new InvalidOperationException("Provider not connected!"); + + Channel GetChannelForChatChannel(ChatChannel channel) + { + if (!channel.DiscordChannelId.HasValue) + throw new InvalidOperationException("ChatChannel missing DiscordChannelId!"); + + if (!(client.GetChannel(channel.DiscordChannelId.Value) is ITextChannel discordChannel)) + return null; + + return new Channel + { + RealId = discordChannel.Id, + IsAdmin = channel.IsAdminChannel == true, + ConnectionName = discordChannel.Guild.Name, + FriendlyName = discordChannel.Name, + IsPrivate = false + }; + }; + + var enumerator = channels.Select(x => GetChannelForChatChannel(x)).Where(x => x != null); + + lock (this) + { + mappedChannels.Clear(); + mappedChannels.AddRange(enumerator.Select(x => x.RealId)); + } + + return Task.FromResult>(enumerator.ToList()); + } + + /// + public override async Task SendMessage(ulong channelId, string message, CancellationToken cancellationToken) + { + try + { + var channel = client.GetChannel(channelId) as IMessageChannel; + await (channel?.SendMessageAsync(message, false, null, new RequestOptions + { + CancelToken = cancellationToken + }) ?? Task.CompletedTask).ConfigureAwait(false); + } + catch (Exception e) + { + logger.LogWarning("Error sending discord message: {0}", e); + } + } + } +} diff --git a/src/Tgstation.Server.Host/Components/Chat/Providers/IProvider.cs b/src/Tgstation.Server.Host/Components/Chat/Providers/IProvider.cs new file mode 100644 index 0000000000..99d13e1768 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Chat/Providers/IProvider.cs @@ -0,0 +1,62 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Host.Components.Chat.Providers +{ + /// + /// For interacting with a chat service + /// + interface IProvider : IDisposable + { + /// + /// If the + /// + bool Connected { get; } + + /// + /// The that indicates the was mentioned + /// + string BotMention { get; } + + /// + /// Get a resulting in the next the recieves or on a disconnect + /// + /// The for the operation + /// A resulting in the next available + /// Note that private messages will come in the form of s not returned in + Task NextMessage(CancellationToken cancellationToken); + + /// + /// Attempt to connect the + /// + /// The for the operation + /// A resulting in on success, otherwise + Task Connect(CancellationToken cancellationToken); + + /// + /// Gracefully disconnects the provider. Implies a call to + /// + /// The for the operation + /// A representing the running operation + Task Disconnect(CancellationToken cancellationToken); + + /// + /// Get the s for given + /// + /// The s to map + /// The for the operation + /// A resulting in a of the s representing + Task> MapChannels(IEnumerable channels, CancellationToken cancellationToken); + + /// + /// Send a message to the + /// + /// The to send to + /// The message contents + /// The for the operation + /// A representing the running operation + Task SendMessage(ulong channelId, string message, CancellationToken cancellationToken); + } +} diff --git a/src/Tgstation.Server.Host/Components/Chat/Providers/IrcPasswordType.cs b/src/Tgstation.Server.Host/Components/Chat/Providers/IrcPasswordType.cs new file mode 100644 index 0000000000..d31a3aba73 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Chat/Providers/IrcPasswordType.cs @@ -0,0 +1,21 @@ +namespace Tgstation.Server.Host.Components.Chat.Providers +{ + /// + /// Represents the type of a password passed to the constructor of + /// + enum IrcPasswordType + { + /// + /// Use server authentication + /// + Server, + /// + /// Use PLAIN sasl authentication + /// + Sasl, + /// + /// Use NickServ authentication + /// + NickServ + } +} diff --git a/src/Tgstation.Server.Host/Components/Chat/Providers/IrcProvider.cs b/src/Tgstation.Server.Host/Components/Chat/Providers/IrcProvider.cs new file mode 100644 index 0000000000..794421827b --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Chat/Providers/IrcProvider.cs @@ -0,0 +1,390 @@ +using Meebey.SmartIrc4net; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Api.Models; +using Tgstation.Server.Host.Core; + +namespace Tgstation.Server.Host.Components.Chat.Providers +{ + /// + /// for internet relay chat + /// + sealed class IrcProvider : Provider + { + const int TimeoutSeconds = 5; + + /// + public override bool Connected => client.IsConnected; + + /// + public override string BotMention => client.Nickname; + + /// + /// The for the + /// + readonly ILogger logger; + + /// + /// The client + /// + readonly IrcFeatures client; + + /// + /// Address of the server to connect to + /// + readonly string address; + /// + /// Port of the server to connect to + /// + readonly ushort port; + /// + /// IRC nickname + /// + readonly string nickname; + /// + /// Password which will used for authentication + /// + readonly string password; + /// + /// The of + /// + readonly IrcPasswordType? passwordType; + + /// + /// Map of s to channel names + /// + readonly Dictionary channelIdMap; + + /// + /// Map of s to query users + /// + readonly Dictionary queryChannelIdMap; + + /// + /// Id counter for + /// + ulong channelIdCounter; + + /// + /// The used for + /// + Task listenTask; + + /// + /// Construct an + /// + /// The value of logger + /// The to get the from + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of + /// If should be used + public IrcProvider(ILogger logger, IApplication application, string address, ushort port, string nickname, string password, IrcPasswordType? passwordType, bool useSsl) + { + this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); + if (application == null) + throw new ArgumentNullException(nameof(application)); + + this.address = address ?? throw new ArgumentNullException(nameof(address)); + this.port = port; + this.nickname = nickname ?? throw new ArgumentNullException(nameof(nickname)); + + if (passwordType.HasValue && password == null) + throw new ArgumentNullException(nameof(password)); + + if(password != null && !passwordType.HasValue) + throw new ArgumentNullException(nameof(passwordType)); + + this.password = password; + this.passwordType = passwordType; + + client = new IrcFeatures + { + SupportNonRfc = true, + CtcpUserInfo = "You are going to play. And I am going to watch. And everything will be just fine...", + AutoRejoin = true, + AutoRejoinOnKick = true, + AutoRelogin = true, + AutoRetry = true, + AutoRetryLimit = TimeoutSeconds, + AutoRetryDelay = TimeoutSeconds, + ActiveChannelSyncing = true, + AutoNickHandling = true, + CtcpVersion = application.VersionString, + UseSsl = useSsl + }; + if (useSsl) + client.ValidateServerCertificate = true; //dunno if it defaults to that or what + + client.OnChannelMessage += Client_OnChannelMessage; + client.OnQueryMessage += Client_OnQueryMessage; + + channelIdMap = new Dictionary(); + queryChannelIdMap = new Dictionary(); + channelIdCounter = 1; + } + + /// + public override void Dispose() + { + if(Connected) + client.Disconnect(); //just closes the socket + } + + /// + /// Handle an IRC message + /// + /// The + /// If this is a query message + void HandleMessage(IrcEventArgs e, bool isPrivate) + { + if (e.Data.Nick.ToUpperInvariant() == client.Nickname.ToUpperInvariant()) + return; + + var username = e.Data.Nick; + var channelName = isPrivate ? username : e.Data.Channel; + ulong channelId = 0; + lock (this) + { + var dicToCheck = isPrivate ? queryChannelIdMap : channelIdMap; + if (!dicToCheck.Any(x => + { + if (x.Value != channelName) + return false; + channelId = x.Key; + return true; + })) + { + channelId = ++channelIdCounter; + dicToCheck.Add(channelId, channelName); + if (isPrivate) + channelIdMap.Add(channelId, null); + } + } + + var message = new Message + { + Content = e.Data.Message, + User = new User + { + Channel = new Channel + { + IsAdmin = false, + ConnectionName = address, + FriendlyName = isPrivate ? String.Format(CultureInfo.InvariantCulture, "PM: {0}", channelName) : channelName, + RealId = channelId, + IsPrivate = isPrivate + }, + FriendlyName = username, + RealId = channelId, + Mention = username + } + }; + + EnqueueMessage(message); + } + + /// + /// When a query message is received in IRC + /// + /// The sender of the event + /// The + void Client_OnQueryMessage(object sender, IrcEventArgs e) => HandleMessage(e, true); + + /// + /// When a channel message is received in IRC + /// + /// The sender of the event + /// The + void Client_OnChannelMessage(object sender, IrcEventArgs e) => HandleMessage(e, false); + + /// + public override Task Connect(CancellationToken cancellationToken) => Task.Factory.StartNew(() => + { + lock (this) + try + { + client.Connect(address, port); + + cancellationToken.ThrowIfCancellationRequested(); + + if (passwordType == IrcPasswordType.Server) + client.Login(nickname, nickname, 0, nickname, password); + else + { + if (passwordType == IrcPasswordType.Sasl) + { + client.WriteLine("CAP REQ :sasl", Priority.Critical); //needs to be put in the buffer before anything else + cancellationToken.ThrowIfCancellationRequested(); + } + client.Login(nickname, nickname, 0, nickname); + } + + if (passwordType == IrcPasswordType.NickServ) + { + cancellationToken.ThrowIfCancellationRequested(); + client.SendMessage(SendType.Message, "NickServ", String.Format(CultureInfo.InvariantCulture, "IDENTIFY {0}", password)); + } + else if (passwordType == IrcPasswordType.Sasl) + { + //wait for the sasl ack or timeout + var recievedAck = false; + var recievedPlus = false; + client.OnReadLine += (sender, e) => + { + if (e.Line.Contains("ACK :sasl")) + recievedAck = true; + else if (e.Line.Contains("AUTHENTICATE +")) + recievedPlus = true; + }; + + var startTime = DateTimeOffset.Now; + var endTime = DateTimeOffset.Now.AddSeconds(TimeoutSeconds); + cancellationToken.ThrowIfCancellationRequested(); + for (; !recievedAck && DateTimeOffset.Now <= endTime; Task.Delay(10, cancellationToken).GetAwaiter().GetResult()) + client.Listen(false); + + client.WriteLine("AUTHENTICATE PLAIN", Priority.Critical); + cancellationToken.ThrowIfCancellationRequested(); + + for (; !recievedPlus && DateTimeOffset.Now <= endTime; Task.Delay(10, cancellationToken).GetAwaiter().GetResult()) + client.Listen(false); + + //Stolen! https://github.com/znc/znc/blob/1e697580155d5a38f8b5a377f3b1d94aaa979539/modules/sasl.cpp#L196 + var authString = String.Format(CultureInfo.InvariantCulture, "{0}{1}{0}{1}{2}", nickname, '\0', password); + var b64 = Convert.ToBase64String(Encoding.UTF8.GetBytes(authString)); + var authLine = String.Format(CultureInfo.InvariantCulture, "AUTHENTICATE {0}", b64); + var chars = authLine.ToCharArray(); + client.WriteLine(authLine, Priority.Critical); + + cancellationToken.ThrowIfCancellationRequested(); + client.WriteLine("CAP END", Priority.Critical); + } + + client.Listen(false); + if (client.Nickname != nickname) + //run this now cause it has to go up and down the pipe for us to get a proper check + client.GetIrcUser(nickname); + + listenTask = Task.Factory.StartNew(() => + { + while (client.IsConnected) + { + client.ListenOnce(true); + client.Listen(false); + //ensure we have the correct nick + if (client.Nickname != nickname && client.GetIrcUser(nickname) == null) + client.RfcNick(nickname); + } + }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current); + } + catch (Exception e) + { + logger.LogWarning("Unable to connect to IRC: {0}", e); + } + return true; + }, cancellationToken,TaskCreationOptions.LongRunning, TaskScheduler.Current); + + /// + public override async Task Disconnect(CancellationToken cancellationToken) + { + if (!Connected) + return; + try + { + await Task.Factory.StartNew(() => + { + try + { + client.RfcQuit("Mr. Stark, I don't feel so good...", Priority.Critical); //priocritical otherwise Disconnect will hard block + } + catch (Exception e) + { + logger.LogWarning("Error quitting IRC: {0}", e); + } + }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false); + Dispose(); + await listenTask.ConfigureAwait(false); + } + catch (Exception e) + { + logger.LogWarning("Error disconnecting from IRC! Exception: {0}", e); + } + } + + /// + public override Task> MapChannels(IEnumerable channels, CancellationToken cancellationToken) => Task.Factory.StartNew(() => + { + if (channels.Any(x => x.IrcChannel == null)) + throw new InvalidOperationException("ChatChannel missing IrcChannel!"); + lock (this) + { + var hs = new HashSet(); //for unique inserts + foreach (var I in channels) + hs.Add(I.IrcChannel); + var toPart = new List(); + foreach (var I in client.JoinedChannels) + if (!hs.Remove(I)) + toPart.Add(I); + + foreach (var I in toPart) + client.RfcPart(I); + foreach (var I in hs) + client.RfcJoin(I); + + return (IReadOnlyList)channels.Select(x => { + var id = channelIdCounter; + if (!channelIdMap.Any(y => + { + if (y.Value != x.IrcChannel) + return false; + id = y.Key; + return true; + })) + { + channelIdMap.Add(id, x.IrcChannel); + ++channelIdCounter; + } + return new Channel + { + RealId = id, + IsAdmin = x.IsAdminChannel == true, + ConnectionName = address, + FriendlyName = channelIdMap[id], + IsPrivate = false + }; + }).ToList(); + } + }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current); + + /// + public override Task SendMessage(ulong channelId, string message, CancellationToken cancellationToken) => Task.Factory.StartNew(() => + { + var channelName = channelIdMap[channelId]; + SendType sendType; + if (channelName == null) + { + channelName = queryChannelIdMap[channelId]; + sendType = SendType.Notice; + } + else + sendType = SendType.Message; + try + { + client.SendMessage(sendType, channelName, message); + } + catch(Exception e) + { + logger.LogWarning("Unable to send to channel: {0}", e); + } + }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current); + } +} diff --git a/src/Tgstation.Server.Host/Components/Chat/Providers/Provider.cs b/src/Tgstation.Server.Host/Components/Chat/Providers/Provider.cs new file mode 100644 index 0000000000..01484c229d --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Chat/Providers/Provider.cs @@ -0,0 +1,76 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Api.Models; + +namespace Tgstation.Server.Host.Components.Chat.Providers +{ + /// + abstract class Provider : IProvider + { + /// + /// of received s + /// + readonly Queue messageQueue; + + /// + /// that completes while isn't empty + /// + TaskCompletionSource nextMessage; + + protected Provider() + { + messageQueue = new Queue(); + nextMessage = new TaskCompletionSource(); + } + + /// + public abstract bool Connected { get; } + + /// + public abstract string BotMention { get; } + + /// + /// Queues a for + /// + /// The to queue + protected void EnqueueMessage(Message message) + { + lock (messageQueue) + { + messageQueue.Enqueue(message); + nextMessage.TrySetResult(null); + } + } + + /// + public abstract void Dispose(); + + /// + public abstract Task Connect(CancellationToken cancellationToken); + + /// + public abstract Task Disconnect(CancellationToken cancellationToken); + + /// + public abstract Task> MapChannels(IEnumerable channels, CancellationToken cancellationToken); + + /// + public async Task NextMessage(CancellationToken cancellationToken) + { + var cancelTcs = new TaskCompletionSource(); + using (cancellationToken.Register(() => cancelTcs.SetCanceled())) + await Task.WhenAny(nextMessage.Task, cancelTcs.Task).ConfigureAwait(false); + lock (messageQueue) + { + var result = messageQueue.Dequeue(); + if (messageQueue.Count == 0) + nextMessage = new TaskCompletionSource(); + return result; + } + } + + /// + public abstract Task SendMessage(ulong channelId, string message, CancellationToken cancellationToken); + } +} diff --git a/src/Tgstation.Server.Host/Components/Chat/Response.cs b/src/Tgstation.Server.Host/Components/Chat/Response.cs new file mode 100644 index 0000000000..59be02b4ba --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Chat/Response.cs @@ -0,0 +1,20 @@ +using System.Collections.Generic; + +namespace Tgstation.Server.Host.Components.Chat +{ + /// + /// Represents a chat message requested by DD + /// + sealed class Response + { + /// + /// The message string + /// + public string Message { get; set; } + + /// + /// The list of internal channel ids to send to + /// + public List ChannelIds { get; set; } + } +} diff --git a/src/Tgstation.Server.Host/Components/Chat/User.cs b/src/Tgstation.Server.Host/Components/Chat/User.cs new file mode 100644 index 0000000000..c3a5e2b6dc --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Chat/User.cs @@ -0,0 +1,42 @@ +using Newtonsoft.Json; +using System; +using System.Globalization; + +namespace Tgstation.Server.Host.Components.Chat +{ + /// + /// Represents a tgs_chat_user datum + /// + public sealed class User + { + /// + /// Backing field for . Represented as a to avoid BYOND percision loss + /// + public string Id { get; set; } + + /// + /// The internal user id + /// + [JsonIgnore] + public ulong RealId + { + get => UInt64.Parse(Id, CultureInfo.InvariantCulture); + set => Id = value.ToString(CultureInfo.InvariantCulture); + } + + /// + /// The friendly name of the user + /// + public string FriendlyName { get; set; } + + /// + /// The text to mention the user + /// + public string Mention { get; set; } + + /// + /// The the user spoke from + /// + public Channel Channel { get; set; } + } +} diff --git a/src/Tgstation.Server.Host/Components/Compiler/DmbFactory.cs b/src/Tgstation.Server.Host/Components/Compiler/DmbFactory.cs new file mode 100644 index 0000000000..e994fddfae --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Compiler/DmbFactory.cs @@ -0,0 +1,187 @@ +using Microsoft.EntityFrameworkCore; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Host.Core; +using Tgstation.Server.Host.IO; +using Tgstation.Server.Host.Models; + +namespace Tgstation.Server.Host.Components.Compiler +{ + /// + /// Standard + /// + sealed class DmbFactory : IDmbFactory, ICompileJobConsumer + { + /// + public Task OnNewerDmb + { + get + { + lock (this) + return newerDmbTcs.Task; + } + } + + /// + /// The for the + /// + readonly IDatabaseContextFactory databaseContextFactory; + /// + /// The for the + /// + readonly IIOManager ioManager; + /// + /// The for + /// + readonly CancellationTokenSource cleanupCts; + + /// + /// The for the + /// + readonly Api.Models.Instance instance; + + /// + /// representing calls to + /// + Task cleanupTask; + /// + /// resulting in the latest yet to exist + /// + TaskCompletionSource newerDmbTcs; + /// + /// The latest + /// + IDmbProvider nextDmbProvider; + + Dictionary jobLockCounts; + + /// + /// Construct a + /// + /// The value of + /// The value of + /// The value of + public DmbFactory(IDatabaseContextFactory databaseContextFactory, IIOManager ioManager, Api.Models.Instance instance) + { + this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory)); + this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager)); + this.instance = instance ?? throw new ArgumentNullException(nameof(instance)); + + cleanupTask = Task.CompletedTask; + cleanupCts = new CancellationTokenSource(); + jobLockCounts = new Dictionary(); + } + + /// + public void Dispose() => cleanupCts.Dispose(); + + /// + /// Delete the of + /// + /// The to clean + void CleanJob(CompileJob job) + { + async Task HandleCleanup() + { + var deleteJob = ioManager.DeleteDirectory(job.DirectoryName.ToString(), cleanupCts.Token); + Task otherTask; + lock (this) + otherTask = cleanupTask; + await Task.WhenAll(otherTask, deleteJob).ConfigureAwait(false); + } + lock (this) + { + var currentVal = jobLockCounts[job.Id]; + if (--jobLockCounts[job.Id] == 0) + cleanupTask = HandleCleanup(); + } + } + + /// + public Task LoadCompileJob(CompileJob job, CancellationToken cancellationToken) => LoadCompileJob(job, true, cancellationToken); + + async Task LoadCompileJob(CompileJob job, bool setAsStagedInDb, CancellationToken cancellationToken) + { + if (job == null) + throw new ArgumentNullException(nameof(job)); + if (job.DMApiValidated != true || job.Job.Cancelled.Value || job.Job.ExceptionDetails != null || job.Job.StoppedAt == null) + throw new InvalidOperationException("Cannot load incomplete compile job!"); + if (setAsStagedInDb) + await databaseContextFactory.UseContext(async db => + { + var ddsettings = new DreamDaemonSettings + { + InstanceId = instance.Id + }; + db.DreamDaemonSettings.Attach(ddsettings); + ddsettings.StagedCompileJob = job; + await db.Save(cancellationToken).ConfigureAwait(false); + }).ConfigureAwait(false); + lock (this) + { + var oldDmbProvider = nextDmbProvider; + if (oldDmbProvider != null && oldDmbProvider.CompileJob.Job.StoppedAt < oldDmbProvider.CompileJob.Job.StoppedAt) + throw new InvalidOperationException("Loaded compile job older than current job!"); + nextDmbProvider = FromCompileJob(job); + newerDmbTcs.SetResult(nextDmbProvider); + newerDmbTcs = new TaskCompletionSource(); + } + } + + /// + public async Task LockNextDmb(CancellationToken cancellationToken) + { + Task task; + lock (this) + if (nextDmbProvider != null) + return nextDmbProvider; + else + task = newerDmbTcs.Task; + + var result = await task.ConfigureAwait(false); + //so there's currently a race condition in DreamMakerController where the setting of CompileJob.RevisionInformation and thus IDmbProvider.RevisionInformation can be delayed to after this if someone tries to start the server instantly after compiling + //This is a terrible terrible hack to get around that + //I'm sorry future me, I can't think of any other way to fix this other than giving DreamMaker an IDatabaseContext or having the controller load the CompileJob + await Task.Delay(new TimeSpan(0, 0, 10), cancellationToken).ConfigureAwait(false); + return result; + } + + /// + public Task StartAsync(CancellationToken cancellationToken) => databaseContextFactory.UseContext(async (db) => + { + //where complete clause not necessary, only successful COMPILEjobs get in the db + var cj = await db.Instances.Where(x => x.Id == instance.Id).SelectMany(x => x.RevisionInformations).SelectMany(x => x.CompileJobs).OrderByDescending(x => x.Job.StoppedAt).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); + if (cj == default(CompileJob)) + return; + var directoriesTask = ioManager.GetDirectories(".", cancellationToken); + var compileJobTask = LoadCompileJob(cj, false, cancellationToken); + //delete all other compile jobs + var directories = await directoriesTask.ConfigureAwait(false); + await Task.WhenAll(directories.Where(x => x != cj.Job.ToString()).Select(x => ioManager.DeleteDirectory(x, cancellationToken))).ConfigureAwait(false); + await compileJobTask.ConfigureAwait(false); + }); + + /// + public async Task StopAsync(CancellationToken cancellationToken) + { + using (cancellationToken.Register(() => cleanupCts.Cancel())) + await cleanupTask.ConfigureAwait(false); + } + + /// + public IDmbProvider FromCompileJob(CompileJob compileJob) + { + lock (this) + { + if (!jobLockCounts.TryGetValue(compileJob.Id, out int value)) + jobLockCounts.Add(compileJob.Id, 1); + else + jobLockCounts[compileJob.Id] = ++value; + return new DmbProvider(compileJob, ioManager, () => CleanJob(compileJob)); + } + } + } +} diff --git a/src/Tgstation.Server.Host/Components/Compiler/DmbProvider.cs b/src/Tgstation.Server.Host/Components/Compiler/DmbProvider.cs new file mode 100644 index 0000000000..3650c69ddc --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Compiler/DmbProvider.cs @@ -0,0 +1,60 @@ +using System; +using Tgstation.Server.Host.IO; +using Tgstation.Server.Host.Models; + +namespace Tgstation.Server.Host.Components.Compiler +{ + /// + sealed class DmbProvider : IDmbProvider + { + /// + public string DmbName => String.Concat(CompileJob.DmeName, DreamMaker.DmbExtension); + + /// + public string PrimaryDirectory => ioManager.ResolvePath(ioManager.ConcatPath(CompileJob.DirectoryName.ToString(), DreamMaker.ADirectoryName)); + + /// + public string SecondaryDirectory => ioManager.ResolvePath(ioManager.ConcatPath(CompileJob.DirectoryName.ToString(), DreamMaker.BDirectoryName)); + + /// + public RevisionInformation RevisionInformation => CompileJob.RevisionInformation; + + /// + /// The for the + /// + public CompileJob CompileJob { get; } + + /// + /// The for the + /// + readonly IIOManager ioManager; + /// + /// The to run when is called + /// + Action onDispose; + + /// + /// Construct a + /// + /// The value of + /// The value of + /// The value of + public DmbProvider(CompileJob compileJob, IIOManager ioManager, Action onDispose) + { + CompileJob = compileJob ?? throw new ArgumentNullException(nameof(compileJob)); + this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager)); + this.onDispose = onDispose ?? throw new ArgumentNullException(nameof(onDispose)); + } + + ~DmbProvider() => Dispose(); + + /// + public void Dispose() + { + onDispose?.Invoke(); + GC.SuppressFinalize(this); + } + + public void KeepAlive() => onDispose = null; + } +} diff --git a/src/Tgstation.Server.Host/Components/Compiler/DreamMaker.cs b/src/Tgstation.Server.Host/Components/Compiler/DreamMaker.cs new file mode 100644 index 0000000000..c6625bbaeb --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Compiler/DreamMaker.cs @@ -0,0 +1,335 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Internal; +using Tgstation.Server.Host.Components.Byond; +using Tgstation.Server.Host.Components.Repository; +using Tgstation.Server.Host.Components.Watchdog; +using Tgstation.Server.Host.Core; +using Tgstation.Server.Host.IO; + +namespace Tgstation.Server.Host.Components.Compiler +{ + /// + sealed class DreamMaker : IDreamMaker + { + /// + /// Name of the primary directory used for compilation + /// + public const string ADirectoryName = "A"; + /// + /// Name of the secondary directory used for compilation + /// + public const string BDirectoryName = "B"; + /// + /// Extension for .dmbs + /// + public const string DmbExtension = ".dmb"; + /// + /// Extension for .dmes + /// + const string DmeExtension = ".dme"; + + /// + public CompilerStatus Status { get; private set; } + + /// + /// The for + /// + readonly IByondManager byond; + /// + /// The for + /// + readonly IIOManager ioManager; + /// + /// The for + /// + readonly StaticFiles.IConfiguration configuration; + /// + /// The for + /// + readonly ISessionControllerFactory sessionControllerFactory; + /// + /// The for + /// + readonly ICompileJobConsumer compileJobConsumer; + /// + /// The for + /// + readonly IApplication application; + /// + /// The for + /// + readonly IEventConsumer eventConsumer; + /// + /// The for + /// + readonly ILogger logger; + + /// + /// Construct + /// + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of + public DreamMaker(IByondManager byond, IIOManager ioManager, StaticFiles.IConfiguration configuration, ISessionControllerFactory sessionControllerFactory, ICompileJobConsumer compileJobConsumer, IApplication application, IEventConsumer eventConsumer, ILogger logger) + { + this.byond = byond; + this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager)); + this.configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); + this.sessionControllerFactory = sessionControllerFactory ?? throw new ArgumentNullException(nameof(sessionControllerFactory)); + this.compileJobConsumer = compileJobConsumer ?? throw new ArgumentNullException(nameof(compileJobConsumer)); + this.application = application ?? throw new ArgumentNullException(nameof(application)); + this.eventConsumer = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer)); + this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + /// + /// Run a quick DD instance to test the DMAPI is installed on the target code + /// + /// The timeout in seconds for validation + /// The for the operation + /// The current + /// The for the operation + /// A resulting in if the DMAPI was successfully validated, otherwise + async Task VerifyApi(int timeout, Models.CompileJob job, IByondExecutableLock byondLock, CancellationToken cancellationToken) + { + var launchParameters = new DreamDaemonLaunchParameters + { + AllowWebClient = false, + PrimaryPort = 0, //pick any port + SecurityLevel = DreamDaemonSecurity.Safe, //all it needs to read the file and exit + StartupTimeout = timeout + }; + + var dirA = ioManager.ConcatPath(job.DirectoryName.ToString(), ADirectoryName); + var provider = new TemporaryDmbProvider(ioManager.ResolvePath(ioManager.GetDirectoryName(dirA)), ioManager.ResolvePath(ioManager.ConcatPath(dirA, String.Concat(job.DmeName, DmbExtension)))); + + var timeoutAt = DateTimeOffset.Now.AddSeconds(timeout); + using (var controller = await sessionControllerFactory.LaunchNew(launchParameters, provider, byondLock, true, true, true, cancellationToken).ConfigureAwait(false)) + { + var timeoutTask = Task.Delay(timeoutAt - DateTimeOffset.Now, cancellationToken); + + await Task.WhenAny(controller.Lifetime, timeoutTask).ConfigureAwait(false); + + if (!controller.Lifetime.IsCompleted) + return false; + + return controller.ApiValidated; + } + } + + /// + /// Compiles a .dme with DreamMaker + /// + /// The path to the DreamMaker executable + /// The for the operation + /// The for the operation + /// A representing the running operation + async Task RunDreamMaker(string dreamMakerPath, Models.CompileJob job, CancellationToken cancellationToken) + { + using (var dm = new Process()) + { + dm.StartInfo.FileName = dreamMakerPath; + dm.StartInfo.Arguments = String.Format(CultureInfo.InvariantCulture, "-clean {0}{1}", job.DmeName, DmeExtension); + dm.StartInfo.WorkingDirectory = ioManager.ResolvePath(ioManager.ConcatPath(job.DirectoryName.ToString(), ADirectoryName)); + dm.StartInfo.RedirectStandardOutput = true; + dm.StartInfo.RedirectStandardError = true; + var OutputList = new StringBuilder(); + var eventHandler = new DataReceivedEventHandler( + delegate (object sender, DataReceivedEventArgs e) + { + OutputList.Append(Environment.NewLine); + OutputList.Append(e.Data); + } + ); + dm.OutputDataReceived += eventHandler; + dm.ErrorDataReceived += eventHandler; + + dm.EnableRaisingEvents = true; + var dmTcs = new TaskCompletionSource(); + dm.Exited += (a, b) => dmTcs.SetResult(null); + + dm.Start(); + try + { + using (cancellationToken.Register(() => dmTcs.SetCanceled())) + await dmTcs.Task.ConfigureAwait(false); + } + finally + { + if (!dm.HasExited) + { + dm.Kill(); + dm.WaitForExit(); + } + } + + job.Output = OutputList.ToString(); + job.ExitCode = dm.ExitCode; + } + } + + /// + /// Adds server side includes to the .dme being compiled + /// + /// The for the operation + /// The for the operation + /// A representing the running operation + async Task ModifyDme(Models.CompileJob job, CancellationToken cancellationToken) + { + var dirA = ioManager.ConcatPath(job.DirectoryName.ToString(), ADirectoryName); + var dmePath = ioManager.ConcatPath(dirA, String.Concat(job.DmeName, DmeExtension)); + var dmeReadTask = ioManager.ReadAllBytes(dmePath, cancellationToken); + + var dmeModificationsTask = configuration.CopyDMFilesTo(dmePath, ioManager.ResolvePath(dirA), cancellationToken); + + var dmeBytes = await dmeReadTask.ConfigureAwait(false); + var dme = Encoding.UTF8.GetString(dmeBytes); + + var dmeModifications = await dmeModificationsTask.ConfigureAwait(false); + + if (dmeModifications == null || dmeModifications.TotalDmeOverwrite) + return; + + var dmeLines = new List(dme.Split(new[] { Environment.NewLine }, StringSplitOptions.None)); + for (var I = 0; I < dmeLines.Count; ++I) + { + var line = dmeLines[I]; + if (line.Contains("BEGIN_INCLUDE") && dmeModifications.HeadIncludeLine != null) + { + dmeLines.Insert(I + 1, dmeModifications.HeadIncludeLine); + ++I; + } + else if (line.Contains("END_INCLUDE") && dmeModifications.TailIncludeLine != null) + { + dmeLines.Insert(I, dmeModifications.TailIncludeLine); + break; + } + } + + dmeBytes = Encoding.UTF8.GetBytes(String.Join(Environment.NewLine, dmeLines)); + await ioManager.WriteAllBytes(dmePath, dmeBytes, cancellationToken).ConfigureAwait(false); + } + + /// + public async Task Compile(string projectName, int apiValidateTimeout, IRepository repository, CancellationToken cancellationToken) + { + logger.LogTrace("Begin Compile"); + await eventConsumer.HandleEvent(EventType.CompileStart, new List{ repository.Origin }, cancellationToken).ConfigureAwait(false); + try + { + Status = CompilerStatus.Copying; + var job = new Models.CompileJob + { + DirectoryName = Guid.NewGuid(), + DmeName = projectName + }; + await ioManager.CreateDirectory(job.DirectoryName.ToString(), cancellationToken).ConfigureAwait(false); + var dirA = ioManager.ConcatPath(job.DirectoryName.ToString(), ADirectoryName); + var dirB = ioManager.ConcatPath(job.DirectoryName.ToString(), BDirectoryName); + + async Task CleanupFailedCompile() + { + Status = CompilerStatus.Cleanup; + try + { + await ioManager.DeleteDirectory(job.DirectoryName.ToString(), CancellationToken.None).ConfigureAwait(false); + } + catch { } + }; + + try + { + //copy the repository + var fullDirA = ioManager.ResolvePath(dirA); + using (repository) + await repository.CopyTo(fullDirA, cancellationToken).ConfigureAwait(false); + + Status = CompilerStatus.Modifying; + + if (job.DmeName == null) + { + job.DmeName = (await ioManager.GetFilesWithExtension(dirA, DmeExtension, cancellationToken).ConfigureAwait(false)).FirstOrDefault(); + if (job.DmeName == default) + { + job.Output = "Unable to find any .dme!"; + return job; + } + } + + await ModifyDme(job, cancellationToken).ConfigureAwait(false); + + Status = CompilerStatus.Compiling; + + //run compiler, verify api + bool ddVerified; + using (var byondLock = await byond.UseExecutables(null, cancellationToken).ConfigureAwait(false)) + { + job.ByondVersion = byondLock.Version.ToString(); + + await RunDreamMaker(byondLock.DreamMakerPath, job, cancellationToken).ConfigureAwait(false); + + Status = CompilerStatus.Verifying; + + ddVerified = job.ExitCode == 0 && await VerifyApi(apiValidateTimeout, job, byondLock, cancellationToken).ConfigureAwait(false); + } + + if (!ddVerified) + { + //server never validated or compile failed + await CleanupFailedCompile().ConfigureAwait(false); + await eventConsumer.HandleEvent(EventType.CompileFailure, new List { job.ExitCode == 0 ? "1" : "0" }, cancellationToken).ConfigureAwait(false); + } + else + { + job.DMApiValidated = true; + + Status = CompilerStatus.Duplicating; + + //duplicate the dmb et al + await ioManager.CopyDirectory(dirA, dirB, null, cancellationToken).ConfigureAwait(false); + + Status = CompilerStatus.Symlinking; + + //symlink in the static data + var symATask = configuration.SymlinkStaticFilesTo(fullDirA, cancellationToken); + var symBTask = configuration.SymlinkStaticFilesTo(ioManager.ResolvePath(dirB), cancellationToken); + + await Task.WhenAll(symATask, symBTask).ConfigureAwait(false); + await eventConsumer.HandleEvent(EventType.CompileComplete, null, cancellationToken).ConfigureAwait(false); + } + await compileJobConsumer.LoadCompileJob(job, cancellationToken).ConfigureAwait(false); + return job; + } + catch + { + await CleanupFailedCompile().ConfigureAwait(false); + throw; + } + } + catch (OperationCanceledException) + { + await eventConsumer.HandleEvent(EventType.CompileCancelled, null, default).ConfigureAwait(false); + throw; + } + finally + { + Status = CompilerStatus.Idle; + } + } + } +} diff --git a/src/Tgstation.Server.Host/Components/Compiler/ICompileJobConsumer.cs b/src/Tgstation.Server.Host/Components/Compiler/ICompileJobConsumer.cs new file mode 100644 index 0000000000..398d6d038c --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Compiler/ICompileJobConsumer.cs @@ -0,0 +1,22 @@ +using Microsoft.Extensions.Hosting; +using System; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Host.Models; + +namespace Tgstation.Server.Host.Components.Compiler +{ + /// + /// Sink for s + /// + interface ICompileJobConsumer : IHostedService, IDisposable + { + /// + /// Load a new into the + /// + /// The to load + /// The for the operation + /// A representing the running operation + Task LoadCompileJob(CompileJob job, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Components/Compiler/IDmbFactory.cs b/src/Tgstation.Server.Host/Components/Compiler/IDmbFactory.cs new file mode 100644 index 0000000000..e79f55b6f8 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Compiler/IDmbFactory.cs @@ -0,0 +1,33 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Host.Models; + +namespace Tgstation.Server.Host.Components.Compiler +{ + /// + /// Factory for s + /// + public interface IDmbFactory + { + /// + /// Get a that completes when the result of a call to will be different than the previous call if any + /// + /// A representing the running operation + Task OnNewerDmb { get; } + + /// + /// Gets the next + /// + /// The for the operation + /// A resulting in a new + Task LockNextDmb(CancellationToken cancellationToken); + + /// + /// Gets a for a given + /// + /// The to make the for + /// A new + IDmbProvider FromCompileJob(CompileJob compileJob); + } +} diff --git a/src/Tgstation.Server.Host/Components/Compiler/IDreamMaker.cs b/src/Tgstation.Server.Host/Components/Compiler/IDreamMaker.cs new file mode 100644 index 0000000000..900a3e648e --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Compiler/IDreamMaker.cs @@ -0,0 +1,28 @@ +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Host.Components.Repository; +using Tgstation.Server.Host.Models; + +namespace Tgstation.Server.Host.Components.Compiler +{ + /// + /// For managing the compiler + /// + public interface IDreamMaker + { + /// + /// The of + /// + Api.Models.CompilerStatus Status { get; } + + /// + /// Starts a compile + /// + /// The name of the .dme to compile without the extension + /// The time in seconds to wait while validating the API + /// The to copy from + /// The for the operation + /// A resulting in the partially populated for the operation. In particular, note the field will only have it's field populated + Task Compile(string projectName, int apiValidateTimeout, IRepository repository, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Components/Compiler/TemporaryDmbProvider.cs b/src/Tgstation.Server.Host/Components/Compiler/TemporaryDmbProvider.cs new file mode 100644 index 0000000000..dcf5c0affe --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Compiler/TemporaryDmbProvider.cs @@ -0,0 +1,39 @@ +using System; +using Tgstation.Server.Host.Models; + +namespace Tgstation.Server.Host.Components.Compiler +{ + /// + /// Temporary + /// + sealed class TemporaryDmbProvider : IDmbProvider + { + /// + public string DmbName { get; } + + /// + public string PrimaryDirectory { get; } + + /// + public string SecondaryDirectory => throw new NotSupportedException(); + + /// + public CompileJob CompileJob => null; + + /// + /// Construct a + /// + /// The value of + /// The value of + public TemporaryDmbProvider(string directory, string dmb) + { + DmbName = dmb ?? throw new ArgumentNullException(nameof(dmb)); + PrimaryDirectory = directory ?? throw new ArgumentNullException(nameof(directory)); + } + + /// + public void Dispose() { } + + public void KeepAlive() => throw new NotSupportedException(); + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Components/EventConsumer.cs b/src/Tgstation.Server.Host/Components/EventConsumer.cs new file mode 100644 index 0000000000..e28d93051f --- /dev/null +++ b/src/Tgstation.Server.Host/Components/EventConsumer.cs @@ -0,0 +1,56 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Host.Components.StaticFiles; +using Tgstation.Server.Host.Components.Watchdog; + +namespace Tgstation.Server.Host.Components +{ + /// + sealed class EventConsumer : IEventConsumer + { + /// + /// The for the + /// + readonly IConfiguration configuration; + + /// + /// The for the + /// + IWatchdog watchdog; + + /// + /// Construct an + /// + /// The value of + public EventConsumer(IConfiguration configuration) + { + this.configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); + } + + /// + public async Task HandleEvent(EventType eventType, IEnumerable parameters, CancellationToken cancellationToken) + { + if (watchdog == null) + throw new InvalidOperationException("EventConsumer used without watchdog set!"); + + if (!await configuration.HandleEvent(eventType, parameters, cancellationToken).ConfigureAwait(false)) + return false; + return await watchdog.HandleEvent(eventType, parameters, cancellationToken).ConfigureAwait(false); + } + + /// + /// Set the for the + /// + /// The value of + public void SetWatchdog(IWatchdog watchdog) + { + if (watchdog == null) + throw new ArgumentNullException(nameof(watchdog)); + if (this.watchdog != null) + throw new InvalidOperationException("watchdog already set!"); + this.watchdog = watchdog; + } + } +} diff --git a/src/Tgstation.Server.Host/Components/EventType.cs b/src/Tgstation.Server.Host/Components/EventType.cs new file mode 100644 index 0000000000..e9442b0d10 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/EventType.cs @@ -0,0 +1,78 @@ +namespace Tgstation.Server.Host.Components +{ + /// + /// Types of events + /// + public enum EventType + { + /// + /// Parameters: Reference name, commit sha + /// + RepoResetOrigin = 0, + /// + /// Parameters: Checkout target + /// + RepoCheckout = 1, + /// + /// No parameters + /// + RepoFetch = 2, + /// + /// Parameters: Pull request number, pull request sha, merger name, merger message + /// + RepoMergePullRequest = 3, + /// + /// Parameters: Absolute path to repository root, committer name, committer email + /// + RepoPreSynchronize = 4, + + /// + /// Parameters: Current version, new version + /// + ByondChangeStart = 5, + /// + /// Parameters: Error string + /// + ByondFail = 6, + /// + /// No parameters + /// + ByondChangeComplete = 7, + + /// + /// Parameters: Origin commit sha + /// + CompileStart = 8, + /// + /// No parameters + /// + CompileCancelled = 9, + /// + /// Parameters: "1" if compile succeeded and api validation failed, "0" otherwise + /// + CompileFailure = 10, + /// + /// No parameters + /// + CompileComplete = 11, + + /// + /// Parameters: Exit code + /// + DDOtherCrash = 12, + /// + /// No parameters + /// + DDOtherExit = 13, + + /// + /// No parameters + /// + InstanceAutoUpdateStart = 14, + + /// + /// Parameters: Base sha, target sha, base reference, target reference + /// + RepoMergeConflict = 15, + } +} diff --git a/src/Tgstation.Server.Host/Components/IDmbProvider.cs b/src/Tgstation.Server.Host/Components/IDmbProvider.cs new file mode 100644 index 0000000000..75bc533409 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/IDmbProvider.cs @@ -0,0 +1,36 @@ +using System; +using Tgstation.Server.Host.Models; + +namespace Tgstation.Server.Host.Components +{ + /// + /// Provides absolute paths to the latest compiled .dmbs + /// + public interface IDmbProvider : IDisposable + { + /// + /// The file name of the .dmb + /// + string DmbName { get; } + + /// + /// The primary game directory with a trailing directory separator + /// + string PrimaryDirectory { get; } + + /// + /// The secondary game directory with a trailing directory separator + /// + string SecondaryDirectory { get; } + + /// + /// The of the .dmb + /// + CompileJob CompileJob { get; } + + /// + /// Disposing the won't cause a cleanup of the working directory + /// + void KeepAlive(); + } +} diff --git a/src/Tgstation.Server.Host/Components/IEventConsumer.cs b/src/Tgstation.Server.Host/Components/IEventConsumer.cs new file mode 100644 index 0000000000..55945548c2 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/IEventConsumer.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Host.Components +{ + /// + /// Consumes s and takes the appropriate actions + /// + public interface IEventConsumer + { + /// + /// Handle a given + /// + /// The + /// The parameters for + /// The for the operation + /// A resulting in if more should run, otherwise + Task HandleEvent(EventType eventType, IEnumerable parameters, CancellationToken cancellationToken); + } +} diff --git a/src/Tgstation.Server.Host/Components/IInstance.cs b/src/Tgstation.Server.Host/Components/IInstance.cs new file mode 100644 index 0000000000..946a95b16a --- /dev/null +++ b/src/Tgstation.Server.Host/Components/IInstance.cs @@ -0,0 +1,67 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Hosting; +using System; +using System.Threading.Tasks; +using Tgstation.Server.Host.Components.Byond; +using Tgstation.Server.Host.Components.Chat; +using Tgstation.Server.Host.Components.Compiler; +using Tgstation.Server.Host.Components.Repository; +using Tgstation.Server.Host.Components.Watchdog; + +namespace Tgstation.Server.Host.Components +{ + /// + /// For interacting with the instance services + /// + public interface IInstance : IHostedService, IDisposable + { + /// + /// The for the + /// + IRepositoryManager RepositoryManager { get; } + + /// + /// The for the + /// + IByondManager ByondManager { get; } + + /// + /// The for the + /// + IDreamMaker DreamMaker { get; } + + /// + /// The for the + /// + IWatchdog Watchdog { get; } + + /// + /// The for the + /// + IChat Chat { get; } + + /// + /// The for the + /// + StaticFiles.IConfiguration Configuration { get; } + + /// + /// Get the associated with the + /// + /// The associated with the + Api.Models.Instance GetMetadata(); + + /// + /// Rename the + /// + /// The new name for the + void Rename(string newName); + + /// + /// Change the for the + /// + /// The new auto update inteval + /// A representing the running operation + Task SetAutoUpdateInterval(int? newInterval); + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Components/IInstanceFactory.cs b/src/Tgstation.Server.Host/Components/IInstanceFactory.cs new file mode 100644 index 0000000000..ea9ed59f44 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/IInstanceFactory.cs @@ -0,0 +1,19 @@ +using Tgstation.Server.Host.Components.Watchdog; +using Tgstation.Server.Host.Core; + +namespace Tgstation.Server.Host.Components +{ + /// + /// Factory for creating s + /// + interface IInstanceFactory + { + /// + /// Create an + /// + /// The + /// The for the + /// A new + IInstance CreateInstance(Models.Instance metadata, IInteropRegistrar interopRegistrar); + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Components/IInstanceManager.cs b/src/Tgstation.Server.Host/Components/IInstanceManager.cs new file mode 100644 index 0000000000..56b9333263 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/IInstanceManager.cs @@ -0,0 +1,52 @@ +using Microsoft.AspNetCore.Http; +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Host.Components +{ + /// + /// For managing s + /// + public interface IInstanceManager + { + /// + /// Get the associated with given + /// + /// The of the desired + /// The associated with the given + IInstance GetInstance(Models.Instance metadata); + + /// + /// Online an + /// + /// The of the desired + /// The for the operation + /// A representing the running operation + Task OnlineInstance(Models.Instance metadata, CancellationToken cancellationToken); + + /// + /// Offline an + /// + /// The of the desired + /// The for the operation + /// A representing the running operation + Task OfflineInstance(Models.Instance metadata, CancellationToken cancellationToken); + + /// + /// Move an + /// + /// The of the desired + /// The new path of the . will have this set on if the operation completes successfully + /// The for the operation + /// A representing the running operation + Task MoveInstance(Models.Instance metadata, string newPath, CancellationToken cancellationToken); + + /// + /// Handle a GET via world/Export + /// + /// The request query + /// The for the operation + /// A resulting in an graph that can be jsonified + Task HandleWorldExport(IQueryCollection query, CancellationToken cancellationToken); + } +} diff --git a/src/Tgstation.Server.Host/Components/IInteropConsumer.cs b/src/Tgstation.Server.Host/Components/IInteropConsumer.cs new file mode 100644 index 0000000000..d92b1e6d92 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/IInteropConsumer.cs @@ -0,0 +1,20 @@ +using Microsoft.AspNetCore.Http; +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Host.Components +{ + /// + /// Consumes interop events + /// + interface IInteropConsumer + { + /// + /// Handle an interop event + /// + /// The query from the world/Export request + /// The for the operation + /// A resulting in a jsonifiable object graph + Task HandleInterop(IQueryCollection query, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Components/IInteropContext.cs b/src/Tgstation.Server.Host/Components/IInteropContext.cs new file mode 100644 index 0000000000..09f9dc6bbc --- /dev/null +++ b/src/Tgstation.Server.Host/Components/IInteropContext.cs @@ -0,0 +1,11 @@ +using System; + +namespace Tgstation.Server.Host.Components +{ + /// + /// Represents a registration of an interop session + /// + interface IInteropContext : IDisposable + { + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Components/IInteropRegistrar.cs b/src/Tgstation.Server.Host/Components/IInteropRegistrar.cs new file mode 100644 index 0000000000..92f7d601dd --- /dev/null +++ b/src/Tgstation.Server.Host/Components/IInteropRegistrar.cs @@ -0,0 +1,16 @@ +namespace Tgstation.Server.Host.Components +{ + /// + /// Creates s for s + /// + interface IInteropRegistrar + { + /// + /// Register a + /// + /// The access identifier for the entry + /// The being registered + /// A new representing the registration + IInteropContext Register(string accessIdentifier, IInteropConsumer consumer); + } +} diff --git a/src/Tgstation.Server.Host/Components/Instance.cs b/src/Tgstation.Server.Host/Components/Instance.cs new file mode 100644 index 0000000000..259da44cc1 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Instance.cs @@ -0,0 +1,210 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Logging; +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Host.Components.Byond; +using Tgstation.Server.Host.Components.Chat; +using Tgstation.Server.Host.Components.Compiler; +using Tgstation.Server.Host.Components.Repository; +using Tgstation.Server.Host.Components.Watchdog; +using Tgstation.Server.Host.Core; + +namespace Tgstation.Server.Host.Components +{ + /// + sealed class Instance : IInstance + { + /// + public IRepositoryManager RepositoryManager { get; } + + /// + public IByondManager ByondManager { get; } + + /// + public IDreamMaker DreamMaker { get; } + + /// + public IWatchdog Watchdog { get; } + + /// + public IChat Chat { get; } + + /// + public StaticFiles.IConfiguration Configuration { get; } + + /// + /// The for the + /// + readonly ICompileJobConsumer compileJobConsumer; + + /// + /// The for the + /// + readonly IDatabaseContextFactory databaseContextFactory; + + /// + /// The for the + /// + readonly IDmbFactory dmbFactory; + + /// + /// The for the + /// + readonly ILogger logger; + + /// + /// The for the + /// + readonly Api.Models.Instance metadata; + + /// + /// The auto update + /// + Task timerTask; + /// + /// for + /// + CancellationTokenSource timerCts; + + /// + /// Construct an + /// + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of + public Instance(Api.Models.Instance metadata, IRepositoryManager repositoryManager, IByondManager byondManager, IDreamMaker dreamMaker, IWatchdog watchdog, IChat chat, StaticFiles.IConfiguration configuration, ICompileJobConsumer compileJobConsumer, IDatabaseContextFactory databaseContextFactory, IDmbFactory dmbFactory, ILogger logger) + { + this.metadata = metadata ?? throw new ArgumentNullException(nameof(metadata)); + RepositoryManager = repositoryManager ?? throw new ArgumentNullException(nameof(repositoryManager)); + ByondManager = byondManager ?? throw new ArgumentNullException(nameof(byondManager)); + DreamMaker = dreamMaker ?? throw new ArgumentNullException(nameof(dreamMaker)); + Watchdog = watchdog ?? throw new ArgumentNullException(nameof(watchdog)); + Chat = chat ?? throw new ArgumentNullException(nameof(chat)); + Configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); + this.compileJobConsumer = compileJobConsumer ?? throw new ArgumentNullException(nameof(compileJobConsumer)); + this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory)); + this.dmbFactory = dmbFactory ?? throw new ArgumentNullException(nameof(dmbFactory)); + this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + /// + public void Dispose() + { + timerCts?.Dispose(); + compileJobConsumer.Dispose(); + Configuration.Dispose(); + Chat.Dispose(); + Watchdog.Dispose(); + RepositoryManager.Dispose(); + } + + /// + /// Pull the repository and compile for every set of given + /// + /// How many minutes the operation should repeat. Does not include running time + /// The for the operation + /// A representing the running operation + async Task TimerLoop(int minutes, CancellationToken cancellationToken) + { + while (true) + try + { + await Task.Delay(new TimeSpan(0, minutes, 0), cancellationToken).ConfigureAwait(false); + + string accessToken = null, projectName = null; + int timeout = 0; + var dbTask = databaseContextFactory.UseContext(async (db) => + { + var instanceQuery = db.Instances.Where(x => x.Id == metadata.Id); + var timeoutTask = instanceQuery.Select(x => x.DreamDaemonSettings.StartupTimeout).FirstAsync(cancellationToken); + var projectNameTask = instanceQuery.Select(x => x.DreamMakerSettings.ProjectName).FirstOrDefaultAsync(cancellationToken); + accessToken = await instanceQuery.Select(x => x.RepositorySettings.AccessToken).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); + projectName = await projectNameTask.ConfigureAwait(false); + timeout = (await timeoutTask.ConfigureAwait(false)).Value; + }); + using (var repo = await RepositoryManager.LoadRepository(cancellationToken).ConfigureAwait(false)) + { + try + { + await dbTask.ConfigureAwait(false); + } + catch (OperationCanceledException) + { + throw; + } + catch (Exception e) + { + logger.LogWarning("Database error in auto update loop! Exception: {0}", e); + continue; + } + if (repo == null) + continue; + await repo.FetchOrigin(accessToken, null, cancellationToken).ConfigureAwait(false); + await repo.ResetToOrigin(cancellationToken).ConfigureAwait(false); + var job = await DreamMaker.Compile(projectName, timeout, repo, cancellationToken).ConfigureAwait(false); + } + } + catch (OperationCanceledException) { } + catch (Exception e) + { + logger.LogError("Error in auto update loop! Exception: {0}", e); + } + } + + /// + public Api.Models.Instance GetMetadata() => metadata.CloneMetadata(); + + /// + public void Rename(string newName) + { + if (String.IsNullOrWhiteSpace(newName)) + throw new ArgumentNullException(nameof(newName)); + metadata.Name = newName; + } + + /// + public Task StartAsync(CancellationToken cancellationToken) => Task.WhenAll(SetAutoUpdateInterval(metadata.AutoUpdateInterval), Configuration.StartAsync(cancellationToken), ByondManager.StartAsync(cancellationToken), Watchdog.StartAsync(cancellationToken), Chat.StartAsync(cancellationToken), compileJobConsumer.StartAsync(cancellationToken)); + + /// + public Task StopAsync(CancellationToken cancellationToken) => Task.WhenAll(SetAutoUpdateInterval(null), Configuration.StopAsync(cancellationToken), ByondManager.StopAsync(cancellationToken), Watchdog.StopAsync(cancellationToken), Chat.StopAsync(cancellationToken), compileJobConsumer.StopAsync(cancellationToken)); + + /// + public async Task SetAutoUpdateInterval(int? newInterval) + { + Task toWait; + lock (this) + { + if (timerTask != null) + { + timerCts.Cancel(); + toWait = timerTask; + } + else + toWait = Task.CompletedTask; + } + await toWait.ConfigureAwait(false); + if (!newInterval.HasValue) + return; + lock (this) + { + //race condition, just quit + if (timerTask != null) + return; + timerCts?.Dispose(); + timerCts = new CancellationTokenSource(); + timerTask = TimerLoop(newInterval.Value, timerCts.Token); + } + } + } +} diff --git a/src/Tgstation.Server.Host/Components/InstanceFactory.cs b/src/Tgstation.Server.Host/Components/InstanceFactory.cs new file mode 100644 index 0000000000..2994bf1ef5 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/InstanceFactory.cs @@ -0,0 +1,190 @@ +using Byond.TopicSender; +using Microsoft.Extensions.Logging; +using System; +using Tgstation.Server.Host.Components.Byond; +using Tgstation.Server.Host.Components.Chat; +using Tgstation.Server.Host.Components.Chat.Commands; +using Tgstation.Server.Host.Components.Compiler; +using Tgstation.Server.Host.Components.Repository; +using Tgstation.Server.Host.Components.StaticFiles; +using Tgstation.Server.Host.Components.Watchdog; +using Tgstation.Server.Host.Core; +using Tgstation.Server.Host.IO; +using Tgstation.Server.Host.Security; + +namespace Tgstation.Server.Host.Components +{ + /// + sealed class InstanceFactory : IInstanceFactory + { + /// + /// The for the + /// + readonly IIOManager ioManager; + + /// + /// The for the + /// + readonly IDatabaseContextFactory databaseContextFactory; + + /// + /// The for the + /// + readonly IApplication application; + + /// + /// The for the + /// + readonly ILoggerFactory loggerFactory; + + /// + /// The for the + /// + readonly IByondTopicSender byondTopicSender; + + /// + /// The for the + /// + readonly IServerControl serverUpdater; + + /// + /// The for the + /// + readonly ICryptographySuite cryptographySuite; + + /// + /// The for the + /// + readonly IExecutor executor; + + /// + /// The for the + /// + readonly ICommandFactory commandFactory; + + /// + /// The for the + /// + readonly ISynchronousIOManager synchronousIOManager; + + /// + /// The for the + /// + readonly ISymlinkFactory symlinkFactory; + + /// + /// The for the + /// + readonly IByondInstaller byondInstaller; + + /// + /// The for the + /// + readonly IProviderFactory providerFactory; + + /// + /// The for the + /// + readonly IScriptExecutor scriptExecutor; + + /// + /// Construct an + /// + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of + public InstanceFactory(IIOManager ioManager, IDatabaseContextFactory databaseContextFactory, IApplication application, ILoggerFactory loggerFactory, IByondTopicSender byondTopicSender, IServerControl serverUpdater, ICryptographySuite cryptographySuite, IExecutor executor, ICommandFactory commandFactory, ISynchronousIOManager synchronousIOManager, ISymlinkFactory symlinkFactory, IByondInstaller byondInstaller, IProviderFactory providerFactory, IScriptExecutor scriptExecutor) + { + this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager)); + this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory)); + this.application = application ?? throw new ArgumentNullException(nameof(application)); + this.loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory)); + this.byondTopicSender = byondTopicSender ?? throw new ArgumentNullException(nameof(byondTopicSender)); + this.serverUpdater = serverUpdater ?? throw new ArgumentNullException(nameof(serverUpdater)); + this.cryptographySuite = cryptographySuite ?? throw new ArgumentNullException(nameof(cryptographySuite )); + this.executor = executor ?? throw new ArgumentNullException(nameof(executor)); + this.commandFactory = commandFactory ?? throw new ArgumentNullException(nameof(commandFactory)); + this.synchronousIOManager = synchronousIOManager ?? throw new ArgumentNullException(nameof(synchronousIOManager)); + this.symlinkFactory = symlinkFactory ?? throw new ArgumentNullException(nameof(symlinkFactory)); + this.byondInstaller = byondInstaller ?? throw new ArgumentNullException(nameof(byondInstaller)); + this.providerFactory = providerFactory ?? throw new ArgumentNullException(nameof(providerFactory)); + this.scriptExecutor = scriptExecutor ?? throw new ArgumentNullException(nameof(scriptExecutor)); + } + + /// + public IInstance CreateInstance(Models.Instance metadata, IInteropRegistrar interopRegistrar) + { + //Create the ioManager for the instance + var instanceIoManager = new ResolvingIOManager(ioManager, metadata.Path); + + //various other ioManagers + var repoIoManager = new ResolvingIOManager(instanceIoManager, "Repository"); + var byondIOManager = new ResolvingIOManager(instanceIoManager, "Byond"); + var gameIoManager = new ResolvingIOManager(instanceIoManager, "Game"); + var configurationIoManager = new ResolvingIOManager(instanceIoManager, "Configuration"); + + var configuration = new StaticFiles.Configuration(configurationIoManager, synchronousIOManager, symlinkFactory, scriptExecutor, loggerFactory.CreateLogger()); + var eventConsumer = new EventConsumer(configuration); + + var dmbFactory = new DmbFactory(databaseContextFactory, gameIoManager, metadata.CloneMetadata()); + try + { + var commandFactory = new CommandFactory(application); + var chatFactory = new ChatFactory(instanceIoManager, loggerFactory, commandFactory, providerFactory); + + var repoManager = new RepositoryManager(metadata.RepositorySettings, repoIoManager, eventConsumer); + try + { + var byond = new ByondManager(byondIOManager, byondInstaller, loggerFactory.CreateLogger()); + + var chat = chatFactory.CreateChat(metadata.ChatSettings); + try + { + var sessionControllerFactory = new SessionControllerFactory(executor, byond, byondTopicSender, interopRegistrar, cryptographySuite, application, gameIoManager, chat, loggerFactory, metadata.CloneMetadata()); + var reattachInfoHandler = new ReattachInfoHandler(databaseContextFactory, dmbFactory, metadata.CloneMetadata()); + var watchdogFactory = new WatchdogFactory(chat, sessionControllerFactory, serverUpdater, loggerFactory, reattachInfoHandler, databaseContextFactory, byondTopicSender, eventConsumer, metadata.CloneMetadata()); + var watchdog = watchdogFactory.CreateWatchdog(dmbFactory, metadata.DreamDaemonSettings); + eventConsumer.SetWatchdog(watchdog); + try + { + var dreamMaker = new DreamMaker(byond, ioManager, configuration, sessionControllerFactory, dmbFactory, application, eventConsumer, loggerFactory.CreateLogger()); + + return new Instance(metadata.CloneMetadata(), repoManager, byond, dreamMaker, watchdog, chat, configuration, dmbFactory, databaseContextFactory, dmbFactory, loggerFactory.CreateLogger()); + } + catch + { + watchdog.Dispose(); + throw; + } + } + catch + { + chat.Dispose(); + throw; + } + } + catch + { + repoManager.Dispose(); + throw; + } + } + catch + { + dmbFactory.Dispose(); + throw; + } + } + } +} diff --git a/src/Tgstation.Server.Host/Components/InstanceManager.cs b/src/Tgstation.Server.Host/Components/InstanceManager.cs new file mode 100644 index 0000000000..8c395077c3 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/InstanceManager.cs @@ -0,0 +1,233 @@ +using Microsoft.AspNetCore.Http; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Primitives; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Host.Core; +using Tgstation.Server.Host.IO; + +namespace Tgstation.Server.Host.Components +{ + /// + sealed class InstanceManager : IInstanceManager, IHostedService, IInteropRegistrar, IDisposable + { + /// + /// HTTP GET query key for interop access identifiers + /// + const string AccessIdentifierQueryKey = "access"; + + /// + /// The for the + /// + readonly IInstanceFactory instanceFactory; + + /// + /// The for the + /// + readonly IIOManager ioManager; + + /// + /// The for the + /// + readonly IDatabaseContextFactory databaseContextFactory; + + /// + /// The for the + /// + readonly IApplication application; + + /// + /// The for the + /// + readonly IJobManager jobManager; + + /// + /// The for the + /// + readonly ILogger logger; + + /// + /// Map of s to respective s + /// + readonly Dictionary instances; + /// + /// Map of access identifiers to their respective + /// + readonly Dictionary interopConsumers; + + /// + /// Construct an + /// + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of + public InstanceManager(IInstanceFactory instanceFactory, IIOManager ioManager, IDatabaseContextFactory databaseContextFactory, IApplication application, IJobManager jobManager, ILogger logger) + { + this.instanceFactory = instanceFactory ?? throw new ArgumentNullException(nameof(instanceFactory)); + this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager)); + this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory)); + this.application = application ?? throw new ArgumentNullException(nameof(application)); + this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager)); + this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); + + instances = new Dictionary(); + interopConsumers = new Dictionary(); + } + + /// + public void Dispose() + { + foreach (var I in instances) + I.Value.Dispose(); + } + + /// + public IInstance GetInstance(Models.Instance metadata) + { + if (metadata == null) + throw new ArgumentNullException(nameof(metadata)); + lock (this) + { + if (!instances.TryGetValue(metadata.Id, out IInstance instance)) + throw new InvalidOperationException("Instance not online!"); + return instance; + } + } + + /// + public async Task MoveInstance(Models.Instance instance, string newPath, CancellationToken cancellationToken) + { + if (newPath == null) + throw new ArgumentNullException(nameof(newPath)); + if (instance.Online.Value) + throw new InvalidOperationException("Cannot move an online instance!"); + var oldPath = instance.Path; + await ioManager.CopyDirectory(oldPath, newPath, null, cancellationToken).ConfigureAwait(false); + instance.Path = ioManager.ResolvePath(newPath); + await ioManager.DeleteDirectory(oldPath, cancellationToken).ConfigureAwait(false); + } + + /// + public async Task OfflineInstance(Models.Instance metadata, CancellationToken cancellationToken) + { + if (metadata == null) + throw new ArgumentNullException(nameof(metadata)); + logger.LogInformation("Offlining instance ID {0}", metadata.Id); + IInstance instance; + lock (this) + { + if (!instances.TryGetValue(metadata.Id, out instance)) + throw new InvalidOperationException("Instance not online!"); + instances.Remove(metadata.Id); + } + try + { + await instance.StopAsync(cancellationToken).ConfigureAwait(false); + } + finally + { + instance.Dispose(); + } + } + + /// + public async Task OnlineInstance(Models.Instance metadata, CancellationToken cancellationToken) + { + if (metadata == null) + throw new ArgumentNullException(nameof(metadata)); + logger.LogInformation("Onlining instance ID {0} ({1}) at {2}", metadata.Id, metadata.Name, metadata.Path); + var instance = instanceFactory.CreateInstance(metadata, this); + try + { + lock (this) + { + if (instances.ContainsKey(metadata.Id)) + throw new InvalidOperationException("Instance already online!"); + instances.Add(metadata.Id, instance); + } + } + catch + { + instance.Dispose(); + throw; + } + await instance.StartAsync(cancellationToken).ConfigureAwait(false); + } + + /// + public Task StartAsync(CancellationToken cancellationToken) => databaseContextFactory.UseContext(async databaseContext => + { + try + { + await databaseContext.Initialize(cancellationToken).ConfigureAwait(false); + await jobManager.StartAsync(cancellationToken).ConfigureAwait(false); + var dbInstances = databaseContext.Instances.Where(x => x.Online.Value) + .Include(x => x.RepositorySettings) + .Include(x => x.ChatSettings) + .ThenInclude(x => x.Channels) + .Include(x => x.DreamDaemonSettings) + .ToAsyncEnumerable(); + var tasks = new List(); + await dbInstances.ForEachAsync(metadata => tasks.Add(metadata.Online.Value ? OnlineInstance(metadata, cancellationToken) : Task.CompletedTask), cancellationToken).ConfigureAwait(false); + await Task.WhenAll(tasks).ConfigureAwait(false); + application.Ready(null); + } + catch (Exception e) + { + application.Ready(e); + } + }); + + /// + public async Task StopAsync(CancellationToken cancellationToken) + { + await Task.WhenAll(instances.Select(x => x.Value.StopAsync(cancellationToken))).ConfigureAwait(false); + await jobManager.StopAsync(cancellationToken).ConfigureAwait(false); + } + + /// + public IInteropContext Register(string accessIdentifier, IInteropConsumer consumer) + { + if (accessIdentifier == null) + throw new ArgumentNullException(nameof(accessIdentifier)); + if (consumer == null) + throw new ArgumentNullException(nameof(consumer)); + + lock (interopConsumers) + interopConsumers.Add(accessIdentifier, consumer); + return new InteropContext(() => + { + lock (interopConsumers) + interopConsumers.Remove(accessIdentifier); + }); + } + + /// + public async Task HandleWorldExport(IQueryCollection query, CancellationToken cancellationToken) + { + if (query == null) + throw new ArgumentNullException(nameof(query)); + + if (!query.TryGetValue(AccessIdentifierQueryKey, out StringValues values)) + return null; + var accessIdentifier = values.FirstOrDefault(); + if (accessIdentifier == default) + return null; + + IInteropConsumer consumer; + lock (interopConsumers) + if (!interopConsumers.TryGetValue(accessIdentifier, out consumer)) + return null; + + return await consumer.HandleInterop(query, cancellationToken).ConfigureAwait(false); + } + } +} diff --git a/src/Tgstation.Server.Host/Components/InteropContext.cs b/src/Tgstation.Server.Host/Components/InteropContext.cs new file mode 100644 index 0000000000..7719419282 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/InteropContext.cs @@ -0,0 +1,29 @@ +using System; + +namespace Tgstation.Server.Host.Components +{ + /// + sealed class InteropContext : IInteropContext + { + /// + /// The + /// + Action onDispose; + + /// + /// Construct an + /// + /// The value of + public InteropContext(Action onDispose) + { + this.onDispose = onDispose; + } + + /// + public void Dispose() + { + onDispose?.Invoke(); + onDispose = null; + } + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Components/ReattachInfoHandler.cs b/src/Tgstation.Server.Host/Components/ReattachInfoHandler.cs new file mode 100644 index 0000000000..a4135a29fa --- /dev/null +++ b/src/Tgstation.Server.Host/Components/ReattachInfoHandler.cs @@ -0,0 +1,86 @@ +using Microsoft.EntityFrameworkCore; +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Host.Components.Compiler; +using Tgstation.Server.Host.Components.Watchdog; +using Tgstation.Server.Host.Core; + +namespace Tgstation.Server.Host.Components +{ + /// + sealed class ReattachInfoHandler: IReattachInfoHandler + { + /// + /// The for the + /// + readonly IDatabaseContextFactory databaseContextFactory; + + /// + /// The for the + /// + readonly IDmbFactory dmbFactory; + + /// + /// The for the + /// + readonly Api.Models.Instance metadata; + + /// + /// Construct a + /// + /// The value of + /// The value of + /// The value of + public ReattachInfoHandler(IDatabaseContextFactory databaseContextFactory, IDmbFactory dmbFactory, Api.Models.Instance metadata) + { + this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory)); + this.dmbFactory = dmbFactory ?? throw new ArgumentNullException(nameof(dmbFactory)); + this.metadata = metadata ?? throw new ArgumentNullException(nameof(metadata)); + } + + /// + public Task Save(WatchdogReattachInformation reattachInformation, CancellationToken cancellationToken) => databaseContextFactory.UseContext(async (db) => + { + var instance = new Models.Instance { Id = metadata.Id }; + db.Instances.Attach(instance); + + Models.ReattachInformation ConvertReattachInfo(ReattachInformation wdInfo) + { + if (wdInfo == null) + return null; + db.CompileJobs.Attach(wdInfo.Dmb.CompileJob); + return new Models.ReattachInformation + { + AccessIdentifier = wdInfo.AccessIdentifier, + ChatChannelsJson = wdInfo.ChatChannelsJson, + ChatCommandsJson = wdInfo.ChatCommandsJson, + CompileJob = wdInfo.Dmb.CompileJob, + IsPrimary = wdInfo.IsPrimary, + Port = wdInfo.Port, + ProcessId = wdInfo.ProcessId, + RebootState = wdInfo.RebootState + }; + } + + instance.WatchdogReattachInformation = new Models.WatchdogReattachInformation + { + Alpha = ConvertReattachInfo(reattachInformation.Alpha), + Bravo = ConvertReattachInfo(reattachInformation.Bravo), + AlphaIsActive = reattachInformation.AlphaIsActive, + }; + await db.Save(cancellationToken).ConfigureAwait(false); + }); + + /// + public async Task Load(CancellationToken cancellationToken) + { + Models.WatchdogReattachInformation result = null; + await databaseContextFactory.UseContext(async (db) => + result = await db.Instances.Where(x => x.Id == metadata.Id).Select(x => x.WatchdogReattachInformation).FirstAsync(cancellationToken).ConfigureAwait(false) + ).ConfigureAwait(false); + return new WatchdogReattachInformation(result, dmbFactory); + } + } +} diff --git a/src/Tgstation.Server.Host/Components/Repository/IRepository.cs b/src/Tgstation.Server.Host/Components/Repository/IRepository.cs new file mode 100644 index 0000000000..21e3cbcd56 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Repository/IRepository.cs @@ -0,0 +1,117 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Host.Components.Repository +{ + /// + /// Represents an on-disk git repository + /// + public interface IRepository : IDisposable + { + /// + /// If the was cloned from GitHub.com + /// + bool IsGitHubRepository { get; } + + /// + /// The if this + /// + string GitHubOwner { get; } + + /// + /// The if this + /// + string GitHubRepoName { get; } + + /// + /// If tracks an upstream branch + /// + bool Tracking { get; } + + /// + /// The SHA of the HEAD + /// + string Head { get; } + + /// + /// The current reference the HEAD is using. This can be a branch or tag + /// + string Reference { get; } + + /// + /// The current origin remote the is using + /// + string Origin { get; } + + /// + /// Checks out a given + /// + /// The sha or reference to checkout + /// The for the operation + /// A representing the running operation + Task CheckoutObject(string committish, CancellationToken cancellationToken); + + /// + /// Attempt to merge a GitHub pull request into HEAD + /// + /// The pull request number on the remote repository + /// The commit in the pull request to merge + /// The name of the merge committer + /// The e-mail of the merge committer + /// The access string to fetch from the origin repository + /// The for the operation + /// Optional function to report 0-100 progress of the clone + /// A resulting in a representing the merge result that is after a fast forward or up to date, on a merge, on a conflict + Task AddTestMerge(int pullRequestNumber, string targetCommit, string committerName, string committerEmail, string accessString, Action progressReporter, CancellationToken cancellationToken); + + /// + /// Fetch commits from the origin repository + /// + /// The access string to fetch from the origin repository + /// Optional function to report 0-100 progress of the clone + /// The for the operation + /// A representing the running operation + Task FetchOrigin(string accessString, Action progressReporter, CancellationToken cancellationToken); + + /// + /// Requires the current HEAD to be a tracked reference. Hard resets the reference to what it tracks on the origin repository + /// + /// The for the operation + /// A resulting in the SHA of the new HEAD + Task ResetToOrigin(CancellationToken cancellationToken); + + /// + /// Requires the current HEAD to be a tracked reference. Merges the reference to what it tracks on the origin repository + /// + /// The name of the merge committer + /// The e-mail of the merge committer + /// The for the operation + /// A resulting in a representing the merge result that is after a fast forward or up to date, on a merge, on a conflict + Task MergeOrigin(string committerName, string committerEmail, CancellationToken cancellationToken); + + /// + /// Force push the current repository HEAD to ; + /// + /// The access string to fetch from the origin repository + /// The for the operation + /// A representing the running operation + Task PushHeadToTemporaryBranch(string accessString, CancellationToken cancellationToken); + + /// + /// Runs the synchronize event script and attempts to push any changes made to the if on a tracked branch + /// + /// The access string to push to the origin repository + /// The for the operation + /// A representing the running operation + Task Sychronize(string accessString, CancellationToken cancellationToken); + + /// + /// Copies the current working directory to a given + /// + /// The path to copy repository contents to + /// The for the operation + /// A representing the running operation + Task CopyTo(string path, CancellationToken cancellationToken); + } +} diff --git a/src/Tgstation.Server.Host/Components/Repository/IRepositoryManager.cs b/src/Tgstation.Server.Host/Components/Repository/IRepositoryManager.cs new file mode 100644 index 0000000000..08c6a8cd60 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Repository/IRepositoryManager.cs @@ -0,0 +1,37 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Host.Components.Repository +{ + /// + /// Factory for creating and loading s + /// + public interface IRepositoryManager : IDisposable + { + /// + /// Attempt to load the from the default location + /// + /// The for the operation + /// The loaded if it exists, otherwise + Task LoadRepository(CancellationToken cancellationToken); + + /// + /// Clone the repository at + /// + /// The of the remote repository to clone + /// The branch to clone + /// The access string to clone from + /// A function to report 0-100 progress of the clone + /// The for the operation + /// The newly cloned , if one already exists + Task CloneRepository(Uri url, string initialBranch, string accessString, Action progressReporter, CancellationToken cancellationToken); + + /// + /// Delete the current repository + /// + /// The for the operation + /// A representing the running operation + Task DeleteRepository(CancellationToken cancellationToken); + } +} diff --git a/src/Tgstation.Server.Host/Components/Repository/Repository.cs b/src/Tgstation.Server.Host/Components/Repository/Repository.cs new file mode 100644 index 0000000000..b0f3a769dd --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Repository/Repository.cs @@ -0,0 +1,394 @@ +using LibGit2Sharp; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Host.IO; + +namespace Tgstation.Server.Host.Components.Repository +{ + /// + sealed class Repository : IRepository + { + /// + /// Indication of a GitHub repository + /// + public const string GitHubUrl = "://github.com/"; + + const string UnknownReference = ""; + + /// + /// The branch name used for publishing testmerge commits + /// + public const string RemoteTemporaryBranchName = "___TGSTempBranch"; + + /// + public bool IsGitHubRepository { get; } + + /// + public string GitHubOwner { get; } + + /// + public string GitHubRepoName { get; } + + /// + public bool Tracking => repository.Head.IsTracking; + + /// + public string Head => repository.Head.Tip.Sha; + + /// + public string Reference => repository.Head.FriendlyName; + + /// + public string Origin => repository.Network.Remotes.First().Url; + + /// + /// The for the + /// + readonly LibGit2Sharp.IRepository repository; + + /// + /// The for the + /// + readonly IIOManager ioMananger; + + /// + /// The for the + /// + readonly IEventConsumer eventConsumer; + + /// + /// to be taken when is called + /// + readonly Action onDispose; + + static void GetRepositoryOwnerName(string remote, out string owner, out string name) + { + //Assume standard gh format: [(git)|(https)]://github.com/owner/repo(.git)[0-1] + //Yes use .git twice in case it was weird + var toRemove = new string[] { ".git", "/", ".git" }; + foreach (string item in toRemove) + if (remote.EndsWith(item, StringComparison.OrdinalIgnoreCase)) + remote = remote.Substring(0, remote.LastIndexOf(item, StringComparison.OrdinalIgnoreCase)); + var splits = remote.Split('/'); + name = splits[splits.Length - 1]; + owner = splits[splits.Length - 2].Split('.')[0]; + } + + /// + /// Construct a + /// + /// The value of + /// The value of + /// The value of + /// The value if + public Repository(LibGit2Sharp.IRepository repository, IIOManager ioMananger, IEventConsumer eventConsumer, Action onDispose) + { + this.repository = repository ?? throw new ArgumentNullException(nameof(repository)); + this.ioMananger = ioMananger ?? throw new ArgumentNullException(nameof(ioMananger)); + this.eventConsumer = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer)); + this.onDispose = onDispose ?? throw new ArgumentNullException(nameof(onDispose)); + IsGitHubRepository = Origin.ToUpperInvariant().Contains(GitHubUrl.ToUpperInvariant()); + if (IsGitHubRepository) + { + GetRepositoryOwnerName(Origin, out var owner, out var name); + GitHubOwner = owner; + GitHubRepoName = name; + } + } + + /// + public void Dispose() + { + repository.Dispose(); + onDispose.Invoke(); + } + /// + /// Convert to an "https://@{url} equivalent + /// + /// The URL to convert + /// The containing authentication info for the remote repository + /// An authenticated URL for accessing the remote repository + public static string GenerateAuthUrl(string url, string accessString) + { + if (url == null) + throw new ArgumentNullException(nameof(url)); + if (String.IsNullOrWhiteSpace(accessString)) + return url; + const string HttProtocolSecure = "HTTPS://"; + if (!url.ToUpperInvariant().StartsWith(HttProtocolSecure, StringComparison.InvariantCulture)) + throw new InvalidOperationException("Cannot use access string without HTTPS remote!"); + //ONLY support https urls + return url.ToUpperInvariant().Replace(HttProtocolSecure, String.Concat(HttProtocolSecure, accessString, '@'), StringComparison.Ordinal); + } + + /// + /// Runs a blocking force checkout to + /// + /// The committish to checkout + void RawCheckout(string committish) + { + Commands.Checkout(repository, committish, new CheckoutOptions + { + CheckoutModifiers = CheckoutModifiers.Force + }); + repository.RemoveUntrackedFiles(); + } + + /// + public async Task AddTestMerge(int pullRequestNumber, string targetCommit, string committerName, string committerEmail, string accessString, Action progressReporter, CancellationToken cancellationToken) + { + + if (!IsGitHubRepository) + throw new InvalidOperationException("Test merging is only available on GitHub hosted origin repositories!"); + + var Refspec = new List(); + var prBranchName = String.Format(CultureInfo.InvariantCulture, "pr-{0}", pullRequestNumber); + var localBranchName = String.Format(CultureInfo.InvariantCulture, "pull/{0}/headrefs/heads/{1}", pullRequestNumber, prBranchName); + Refspec.Add(String.Format(CultureInfo.InvariantCulture, "pull/{0}/head:{1}", pullRequestNumber, prBranchName)); + var logMessage = String.Format(CultureInfo.InvariantCulture, "Merge remote pull request #{0}", pullRequestNumber); + + var originalCommit = repository.Head; + + MergeResult result = null; + await Task.Factory.StartNew(() => + { + var remote = repository.Network.Remotes.Add("temp_pr_fetch", GenerateAuthUrl(Origin, accessString)); + try + { + cancellationToken.ThrowIfCancellationRequested(); + Commands.Fetch((LibGit2Sharp.Repository)repository, remote.Name, Refspec, new FetchOptions + { + Prune = true, + OnProgress = (a) => !cancellationToken.IsCancellationRequested, + OnTransferProgress = (a) => + { + var percentage = 100 * (((float)a.IndexedObjects + a.ReceivedObjects) / (a.TotalObjects * 2)); + progressReporter?.Invoke((int)percentage); + return !cancellationToken.IsCancellationRequested; + }, + OnUpdateTips = (a, b, c) => !cancellationToken.IsCancellationRequested + }, logMessage); + } + catch (UserCancelledException) { } + finally + { + repository.Network.Remotes.Remove(remote.Name); + //commit is there and we never gc so + repository.Branches.Remove(localBranchName); + repository.Branches.Remove(prBranchName); + } + + cancellationToken.ThrowIfCancellationRequested(); + + result = repository.Merge(targetCommit, new Signature(new Identity(committerName, committerEmail), DateTimeOffset.Now), new MergeOptions + { + CommitOnSuccess = true, + FailOnConflict = true, + FastForwardStrategy = FastForwardStrategy.NoFastForward, + SkipReuc = true + }); + + cancellationToken.ThrowIfCancellationRequested(); + + if (result.Status == MergeStatus.Conflicts) + { + RawCheckout(originalCommit.CanonicalName ?? originalCommit.Tip.Sha); + cancellationToken.ThrowIfCancellationRequested(); + } + + repository.RemoveUntrackedFiles(); + }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false); + + if (result.Status == MergeStatus.Conflicts) + { + await eventConsumer.HandleEvent(EventType.RepoMergeConflict, new List { originalCommit.Tip.Sha, targetCommit, originalCommit.FriendlyName ?? UnknownReference, prBranchName }, cancellationToken).ConfigureAwait(false); + return false; + } + + return true; + } + + /// + public async Task CheckoutObject(string committish, CancellationToken cancellationToken) + { + if (committish == null) + throw new ArgumentNullException(nameof(committish)); + await eventConsumer.HandleEvent(EventType.RepoCheckout, new List { committish }, cancellationToken).ConfigureAwait(false); + await Task.Factory.StartNew(() => RawCheckout(committish), cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false); + } + + /// + public Task FetchOrigin(string accessString, Action progressReporter, CancellationToken cancellationToken) => Task.WhenAll( + eventConsumer.HandleEvent(EventType.RepoFetch, Array.Empty(), cancellationToken), + Task.Factory.StartNew(() => + { + var remote = repository.Network.Remotes.First(); + try + { + Commands.Fetch((LibGit2Sharp.Repository)repository, remote.Name, remote.FetchRefSpecs.Select(x => x.Specification), new FetchOptions + { + Prune = true, + OnProgress = (a) => !cancellationToken.IsCancellationRequested, + OnTransferProgress = (a) => + { + var percentage = 100 * (((float)a.IndexedObjects + a.ReceivedObjects) / (a.TotalObjects * 2)); + progressReporter?.Invoke((int)percentage); + return !cancellationToken.IsCancellationRequested; + }, + OnUpdateTips = (a, b, c) => !cancellationToken.IsCancellationRequested + }, "Fetch origin commits"); + } + catch (UserCancelledException) + { + cancellationToken.ThrowIfCancellationRequested(); + } + }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current)); + + /// + public Task PushHeadToTemporaryBranch(string accessString, CancellationToken cancellationToken) => Task.Factory.StartNew(() => + { + var branch = repository.CreateBranch(RemoteTemporaryBranchName); + try + { + cancellationToken.ThrowIfCancellationRequested(); + var remote = repository.Network.Remotes.Add("temp_push", GenerateAuthUrl(Origin, accessString)); + try + { + cancellationToken.ThrowIfCancellationRequested(); + try + { + repository.Network.Push(remote, String.Format(CultureInfo.InvariantCulture, "+{0}:{0}", branch.CanonicalName), new PushOptions + { + OnPackBuilderProgress = (a, b, c) => !cancellationToken.IsCancellationRequested, + OnNegotiationCompletedBeforePush = (a) => !cancellationToken.IsCancellationRequested, + OnPushTransferProgress = (a, b, c) => !cancellationToken.IsCancellationRequested + }); + } + catch (UserCancelledException) + { + cancellationToken.ThrowIfCancellationRequested(); + } + } + finally + { + repository.Network.Remotes.Remove(remote.Name); + } + } + finally + { + repository.Branches.Remove(branch); + } + }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current); + + /// + public async Task ResetToOrigin(CancellationToken cancellationToken) + { + if (!repository.Head.IsTracking) + throw new InvalidOperationException("Cannot reset to origin while not on a tracked reference!"); + var trackedBranch = repository.Head.TrackedBranch; + await eventConsumer.HandleEvent(EventType.RepoResetOrigin, new List { trackedBranch.FriendlyName, trackedBranch.Tip.Sha }, cancellationToken).ConfigureAwait(false); + await Task.Factory.StartNew(() => + { + Commands.Checkout((LibGit2Sharp.Repository)repository, repository.Head.TrackedBranch, new CheckoutOptions + { + CheckoutModifiers = CheckoutModifiers.Force + }); + cancellationToken.ThrowIfCancellationRequested(); + repository.RemoveUntrackedFiles(); + }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false); + } + + /// + public async Task CopyTo(string path, CancellationToken cancellationToken) + { + if (path == null) + throw new ArgumentNullException(nameof(path)); + await ioMananger.CopyDirectory(".", path, new List { ".git" }, cancellationToken).ConfigureAwait(false); + } + + /// + public async Task MergeOrigin(string committerName, string committerEmail, CancellationToken cancellationToken) + { + MergeResult result = null; + Branch trackedBranch = null; + + var oldHead = repository.Head; + + await Task.Factory.StartNew(() => + { + if (!repository.Head.IsTracking) + throw new InvalidOperationException("Cannot reset to origin while not on a tracked reference!"); + trackedBranch = repository.Head.TrackedBranch; + + result = repository.Merge(trackedBranch, new Signature(new Identity(committerName, committerEmail), DateTimeOffset.Now), new MergeOptions + { + CommitOnSuccess = true, + FailOnConflict = true, + FastForwardStrategy = FastForwardStrategy.Default, + SkipReuc = true, + }); + + cancellationToken.ThrowIfCancellationRequested(); + + if (result.Status == MergeStatus.Conflicts) + { + RawCheckout(oldHead.CanonicalName); + cancellationToken.ThrowIfCancellationRequested(); + } + + repository.RemoveUntrackedFiles(); + }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false); + + if (result.Status == MergeStatus.Conflicts) + { + await eventConsumer.HandleEvent(EventType.RepoMergeConflict, new List { oldHead.Tip.Sha, trackedBranch.Tip.Sha, oldHead.FriendlyName ?? UnknownReference, trackedBranch.FriendlyName }, cancellationToken).ConfigureAwait(false); + return null; + } + + return result.Status != MergeStatus.NonFastForward; + } + + /// + public async Task Sychronize(string accessString, CancellationToken cancellationToken) + { + var startHead = Head; + + if (!await eventConsumer.HandleEvent(EventType.RepoPreSynchronize, new List { ioMananger.ResolvePath(".") }, cancellationToken).ConfigureAwait(false)) + return; + + if (Head == startHead || !repository.Head.IsTracking) + return; + + await Task.Factory.StartNew(() => + { + cancellationToken.ThrowIfCancellationRequested(); + var remote = repository.Network.Remotes.Add("temp_push", GenerateAuthUrl(Origin, accessString)); + try + { + cancellationToken.ThrowIfCancellationRequested(); + try + { + repository.Network.Push(repository.Head, new PushOptions + { + OnPackBuilderProgress = (a, b, c) => !cancellationToken.IsCancellationRequested, + OnNegotiationCompletedBeforePush = (a) => !cancellationToken.IsCancellationRequested, + OnPushTransferProgress = (a, b, c) => !cancellationToken.IsCancellationRequested + }); + } + catch (UserCancelledException) + { + cancellationToken.ThrowIfCancellationRequested(); + } + } + finally + { + repository.Network.Remotes.Remove(remote.Name); + } + }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false); + } + } +} diff --git a/src/Tgstation.Server.Host/Components/Repository/RepositoryManager.cs b/src/Tgstation.Server.Host/Components/Repository/RepositoryManager.cs new file mode 100644 index 0000000000..f594f32132 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Repository/RepositoryManager.cs @@ -0,0 +1,129 @@ +using LibGit2Sharp; +using System; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Api.Models.Internal; +using Tgstation.Server.Host.Core; +using Tgstation.Server.Host.IO; + +namespace Tgstation.Server.Host.Components.Repository +{ + /// + sealed class RepositoryManager : IRepositoryManager + { + /// + /// The for the + /// + readonly IIOManager ioManager; + + /// + /// The for the + /// + readonly IEventConsumer eventConsumer; + + /// + /// The for the + /// + readonly RepositorySettings repositorySettings; + + /// + /// Used for controlling single access to the + /// + readonly SemaphoreSlim semaphore; + + /// + /// Construct a + /// + /// The value of + /// The value of + /// The value of + public RepositoryManager(RepositorySettings repositorySettings, IIOManager ioManager, IEventConsumer eventConsumer) + { + this.repositorySettings = repositorySettings ?? throw new ArgumentNullException(nameof(repositorySettings)); + this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager)); + this.eventConsumer = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer)); + semaphore = new SemaphoreSlim(1); + } + + /// + public void Dispose() => semaphore.Dispose(); + + /// + public async Task CloneRepository(Uri url, string initialBranch, string accessString, Action progressReporter, CancellationToken cancellationToken) + { + using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false)) + if (!await ioManager.DirectoryExists(".", cancellationToken).ConfigureAwait(false)) + try + { + await Task.Factory.StartNew(() => + { + string path = null; + try + { + path = LibGit2Sharp.Repository.Clone(Repository.GenerateAuthUrl(url.ToString(), accessString), ioManager.ResolvePath("."), new CloneOptions + { + OnProgress = (a) => !cancellationToken.IsCancellationRequested, + OnTransferProgress = (a) => + { + var percentage = 100 * (((float)a.IndexedObjects + a.ReceivedObjects) / (a.TotalObjects * 2)); + progressReporter((int)percentage); + return !cancellationToken.IsCancellationRequested; + }, + RecurseSubmodules = true, + OnUpdateTips = (a, b, c) => !cancellationToken.IsCancellationRequested, + RepositoryOperationStarting = (a) => !cancellationToken.IsCancellationRequested, + BranchName = initialBranch + }); + } + catch (UserCancelledException) { } + cancellationToken.ThrowIfCancellationRequested(); + }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false); + } + catch + { + try + { + await ioManager.DeleteDirectory(".", default).ConfigureAwait(false); + } + catch { } + throw; + } + else + return null; + return await LoadRepository(cancellationToken).ConfigureAwait(false); + } + + /// + public async Task LoadRepository(CancellationToken cancellationToken) + { + await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); + LibGit2Sharp.Repository repo = null; + await Task.Factory.StartNew(() => + { + try + { + repo = new LibGit2Sharp.Repository(ioManager.ResolvePath(".")); + } + catch (RepositoryNotFoundException) { } + }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current).ConfigureAwait(false); + if (repo == null) + { + semaphore.Release(); + return null; + } + var localSemaphore = semaphore; + return new Repository(repo, ioManager, eventConsumer, () => + { + localSemaphore?.Release(); + localSemaphore = null; + }); + } + + /// + public async Task DeleteRepository(CancellationToken cancellationToken) + { + using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false)) + await ioManager.DeleteDirectory(".", cancellationToken).ConfigureAwait(false); + } + } +} diff --git a/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs b/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs new file mode 100644 index 0000000000..6ff0183354 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/StaticFiles/Configuration.cs @@ -0,0 +1,308 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Security.Cryptography; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Api.Models; +using Tgstation.Server.Host.Core; +using Tgstation.Server.Host.IO; +using Tgstation.Server.Host.Security; + +namespace Tgstation.Server.Host.Components.StaticFiles +{ + /// + sealed class Configuration : IConfiguration + { + const string CodeModificationsSubdirectory = "CodeModifications"; + const string EventScriptsSubdirectory = "EventScripts"; + const string GameStaticFilesSubdirectory = "GameStaticFiles"; + + const string CodeModificationsHeadFile = "HeadInclude.dm"; + const string CodeModificationsTailFile = "TailInclude.dm"; + + static readonly IReadOnlyDictionary EventTypeScriptFileNameMap = new Dictionary + { + }; + + static readonly string SystemScriptFileExtension = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? ".bat" : ".sh"; + + /// + /// The for + /// + readonly IIOManager ioManager; + + /// + /// The for + /// + readonly ISynchronousIOManager synchronousIOManager; + + /// + /// The for + /// + readonly ISymlinkFactory symlinkFactory; + + /// + /// The for + /// + readonly IScriptExecutor scriptExecutor; + + /// + /// The for + /// + readonly ILogger logger; + + /// + /// The for + /// + readonly SemaphoreSlim semaphore; + + /// + /// Construct + /// + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of + public Configuration(IIOManager ioManager, ISynchronousIOManager synchronousIOManager, ISymlinkFactory symlinkFactory, IScriptExecutor scriptExecutor, ILogger logger) + { + this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager)); + this.synchronousIOManager = synchronousIOManager ?? throw new ArgumentNullException(nameof(synchronousIOManager)); + this.symlinkFactory = symlinkFactory ?? throw new ArgumentNullException(nameof(symlinkFactory)); + this.scriptExecutor = scriptExecutor ?? throw new ArgumentNullException(nameof(scriptExecutor)); + this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); + + semaphore = new SemaphoreSlim(1); + } + + /// + public void Dispose() => semaphore.Dispose(); + + /// + /// Ensures standard configuration directories exist + /// + /// The for the operation + /// A representing the running operation + Task EnsureDirectories(CancellationToken cancellationToken) => Task.WhenAll(ioManager.CreateDirectory(CodeModificationsSubdirectory, cancellationToken), ioManager.CreateDirectory(EventScriptsSubdirectory, cancellationToken), ioManager.CreateDirectory(GameStaticFilesSubdirectory, cancellationToken)); + + /// + public async Task CopyDMFilesTo(string dmeFile, string destination, CancellationToken cancellationToken) + { + await EnsureDirectories(cancellationToken).ConfigureAwait(false); + + //just assume no other fs race conditions here + var dmeExistsTask = ioManager.FileExists(ioManager.ConcatPath(CodeModificationsSubdirectory, dmeFile), cancellationToken); + var headFileExistsTask = ioManager.FileExists(ioManager.ConcatPath(CodeModificationsSubdirectory, CodeModificationsHeadFile), cancellationToken); + var tailFileExistsTask = ioManager.FileExists(ioManager.ConcatPath(CodeModificationsSubdirectory, CodeModificationsTailFile), cancellationToken); + + await Task.WhenAll(dmeExistsTask, headFileExistsTask, tailFileExistsTask).ConfigureAwait(false); + + if (!dmeExistsTask.Result && !headFileExistsTask.Result && !tailFileExistsTask.Result) + return null; + + var copyTask = ioManager.CopyDirectory(CodeModificationsSubdirectory, destination, null, cancellationToken); + + if (dmeExistsTask.Result) + { + await copyTask.ConfigureAwait(false); + return new ServerSideModifications(null, null, true); + } + + if (!headFileExistsTask.Result && !tailFileExistsTask.Result) + { + await copyTask.ConfigureAwait(false); + return null; + } + + string IncludeLine(string filePath) => String.Format(CultureInfo.InvariantCulture, "#include \"{0}\"", filePath); + + await copyTask.ConfigureAwait(false); + return new ServerSideModifications(headFileExistsTask.Result ? IncludeLine(CodeModificationsHeadFile) : null, tailFileExistsTask.Result ? IncludeLine(CodeModificationsTailFile) : null, false); + } + + string ValidateConfigRelativePath(string configurationRelativePath) + { + if (String.IsNullOrEmpty(configurationRelativePath)) + configurationRelativePath = "."; + return ioManager.ResolvePath(configurationRelativePath); + } + + /// + public async Task> ListDirectory(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken) + { + await EnsureDirectories(cancellationToken).ConfigureAwait(false); + var path = ValidateConfigRelativePath(configurationRelativePath); + + List result = new List(); + + void ListImpl() + { + var enumerator = synchronousIOManager.GetDirectories(configurationRelativePath, cancellationToken); + result.AddRange(enumerator.Select(x => new ConfigurationFile + { + IsDirectory = true, + Path = ioManager.ConcatPath(configurationRelativePath, x), + })); + enumerator = synchronousIOManager.GetFiles(configurationRelativePath, cancellationToken); + result.AddRange(enumerator.Select(x => new ConfigurationFile + { + IsDirectory = false, + Path = ioManager.ConcatPath(configurationRelativePath, x), + })); + } + + if (systemIdentity == null) + ListImpl(); + else + await systemIdentity.RunImpersonated(ListImpl, cancellationToken).ConfigureAwait(false); + + return result; + } + + /// + public async Task Read(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken) + { + await EnsureDirectories(cancellationToken).ConfigureAwait(false); + var path = ValidateConfigRelativePath(configurationRelativePath); + + ConfigurationFile result = null; + + void ReadImpl() + { + try + { + var content = synchronousIOManager.ReadFile(path); + string sha1String; +#pragma warning disable CA5350 // Do not use insecure cryptographic algorithm SHA1. + using (var sha1 = new SHA1Managed()) +#pragma warning restore CA5350 // Do not use insecure cryptographic algorithm SHA1. + sha1String = String.Join("", sha1.ComputeHash(content).Select(b => b.ToString("x2", CultureInfo.InvariantCulture))); + result = new ConfigurationFile + { + Content = content, + IsDirectory = false, + LastReadHash = sha1String, + AccessDenied = false, + Path = configurationRelativePath + }; + } + catch (FileNotFoundException) { } + catch (DirectoryNotFoundException) { } + catch (UnauthorizedAccessException) + { + result = new ConfigurationFile + { + AccessDenied = true, + Path = configurationRelativePath + }; + } + } + + if (systemIdentity == null) + ReadImpl(); + else + await systemIdentity.RunImpersonated(ReadImpl, cancellationToken).ConfigureAwait(false); + + return result; + } + + /// + public async Task SymlinkStaticFilesTo(string destination, CancellationToken cancellationToken) + { + await EnsureDirectories(cancellationToken).ConfigureAwait(false); + async Task SymlinkBase(bool files) + { + Task> task; + if (files) + task = ioManager.GetFiles(GameStaticFilesSubdirectory, cancellationToken); + else + task = ioManager.GetDirectories(GameStaticFilesSubdirectory, cancellationToken); + var entries = await task.ConfigureAwait(false); + await Task.WhenAll(entries.Select(x => symlinkFactory.CreateSymbolicLink(ioManager.ResolvePath(x), ioManager.ConcatPath(destination, x), cancellationToken))).ConfigureAwait(false); + } + + await Task.WhenAll(SymlinkBase(true), SymlinkBase(false)).ConfigureAwait(false); + } + + /// + public async Task Write(string configurationRelativePath, ISystemIdentity systemIdentity, byte[] data, string previousHash, CancellationToken cancellationToken) + { + await EnsureDirectories(cancellationToken).ConfigureAwait(false); + var path = ValidateConfigRelativePath(configurationRelativePath); + + ConfigurationFile result = null; + + void WriteImpl() + { + try + { + var success = synchronousIOManager.WriteFileChecked(path, data, previousHash, cancellationToken); + if (!success) + return; + string sha1String; +#pragma warning disable CA5350 // Do not use insecure cryptographic algorithm SHA1. + using (var sha1 = new SHA1Managed()) +#pragma warning restore CA5350 // Do not use insecure cryptographic algorithm SHA1. + sha1String = String.Join("", sha1.ComputeHash(data).Select(b => b.ToString("x2", CultureInfo.InvariantCulture))); + result = new ConfigurationFile + { + Content = data, + IsDirectory = false, + LastReadHash = sha1String, + AccessDenied = false, + Path = configurationRelativePath + }; + } + catch (FileNotFoundException) { } + catch (DirectoryNotFoundException) { } + catch (UnauthorizedAccessException) + { + result = new ConfigurationFile + { + AccessDenied = true, + Path = configurationRelativePath + }; + } + } + + if (systemIdentity == null) + WriteImpl(); + else + await systemIdentity.RunImpersonated(WriteImpl, cancellationToken).ConfigureAwait(false); + + return result; + } + + /// + public Task StartAsync(CancellationToken cancellationToken) => EnsureDirectories(cancellationToken); + + /// + public Task StopAsync(CancellationToken cancellationToken) => EnsureDirectories(cancellationToken); + + /// + public async Task HandleEvent(EventType eventType, IEnumerable parameters, CancellationToken cancellationToken) + { + await EnsureDirectories(cancellationToken).ConfigureAwait(false); + + if (!EventTypeScriptFileNameMap.TryGetValue(eventType, out var scriptName)) + return true; + + //always execute in serial + using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false)) + { + var files = await ioManager.GetFilesWithExtension(EventScriptsSubdirectory, SystemScriptFileExtension, cancellationToken).ConfigureAwait(false); + var resolvedScriptsDir = ioManager.ResolvePath(EventScriptsSubdirectory); + + foreach (var I in files.Where(x => x.StartsWith(scriptName, StringComparison.Ordinal))) + if ((await scriptExecutor.ExecuteScript(ioManager.ConcatPath(resolvedScriptsDir, I), parameters, cancellationToken).ConfigureAwait(false)) != 0) + return false; + } + return true; + } + } +} diff --git a/src/Tgstation.Server.Host/Components/StaticFiles/IConfiguration.cs b/src/Tgstation.Server.Host/Components/StaticFiles/IConfiguration.cs new file mode 100644 index 0000000000..3fdce2aa9b --- /dev/null +++ b/src/Tgstation.Server.Host/Components/StaticFiles/IConfiguration.cs @@ -0,0 +1,62 @@ +using Microsoft.Extensions.Hosting; +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Api.Models; +using Tgstation.Server.Host.Security; + +namespace Tgstation.Server.Host.Components.StaticFiles +{ + /// + /// For managing the Configuration directory + /// + public interface IConfiguration : IHostedService, IEventConsumer, IDisposable + { + /// + /// Copies all files in the CodeModifications directory to + /// + /// The .dme file being compiled + /// Path to the destination folder + /// The for the operation + /// A resulting in the if any + Task CopyDMFilesTo(string dmeFile, string destination, CancellationToken cancellationToken); + + /// + /// Symlinks all directories in the GameData directory to + /// + /// Path to the destination folder + /// The for the operation + /// A representing the running operation + Task SymlinkStaticFilesTo(string destination, CancellationToken cancellationToken); + + /// + /// Get for all items in a given + /// + /// The relative path in the Configuration directory + /// The for the operation. If , the operation will be performed as the user of the + /// The for the operation + /// A resulting in the s for the items in the directory. and will both be + Task> ListDirectory(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken); + + /// + /// Reads a given + /// + /// The relative path in the Configuration directory + /// The for the operation. If , the operation will be performed as the user of the + /// The for the operation + /// A resulting in the of the file + Task Read(string configurationRelativePath, ISystemIdentity systemIdentity, CancellationToken cancellationToken); + + /// + /// Writes to a given + /// + /// The relative path in the Configuration directory + /// The for the operation. If , the operation will be performed as the user of the + /// The data to write. If , the file is deleted + /// The hash any existing file must match in order for the write to succeed + /// The for the operation. Usage may result in partial writes + /// A resulting in the updated + Task Write(string configurationRelativePath, ISystemIdentity systemIdentity, byte[] data, string previousHash, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Components/StaticFiles/IScriptExecutor.cs b/src/Tgstation.Server.Host/Components/StaticFiles/IScriptExecutor.cs new file mode 100644 index 0000000000..a9e26ff72f --- /dev/null +++ b/src/Tgstation.Server.Host/Components/StaticFiles/IScriptExecutor.cs @@ -0,0 +1,21 @@ +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Host.Components.StaticFiles +{ + /// + /// For executing system shell scripts + /// + interface IScriptExecutor + { + /// + /// Execute a shell script and get the result + /// + /// The absolute path to the script + /// Command line parameters for the script + /// The for the operation + /// A resulting in the or if an error occurred + Task ExecuteScript(string scriptPath, IEnumerable parameters, CancellationToken cancellationToken); + } +} diff --git a/src/Tgstation.Server.Host/Components/StaticFiles/ScriptExecutor.cs b/src/Tgstation.Server.Host/Components/StaticFiles/ScriptExecutor.cs new file mode 100644 index 0000000000..14a2a97805 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/StaticFiles/ScriptExecutor.cs @@ -0,0 +1,77 @@ +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Host.IO; + +namespace Tgstation.Server.Host.Components.StaticFiles +{ + /// + sealed class ScriptExecutor : IScriptExecutor + { + /// + /// The for the + /// + readonly IIOManager ioManager; + + /// + /// The for the + /// + readonly ILogger logger; + + /// + /// Construct a + /// + /// The value of + /// The value of + public ScriptExecutor(IIOManager ioManager, ILogger logger) + { + this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager)); + this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + /// + public async Task ExecuteScript(string scriptPath, IEnumerable parameters, CancellationToken cancellationToken) + { + var joinedParams = String.Join(" ", parameters); + logger.LogInformation("Running script {0} {1}", scriptPath, joinedParams); + try + { + using (var process = new Process()) + { + process.StartInfo.FileName = scriptPath; + process.StartInfo.WorkingDirectory = ioManager.GetDirectoryName(scriptPath); + process.StartInfo.Arguments = joinedParams; + process.EnableRaisingEvents = true; + + var tcs = new TaskCompletionSource(); + process.Exited += (a, b) => tcs.SetResult(null); + try + { + process.Start(); + using (cancellationToken.Register(() => tcs.SetCanceled())) + await tcs.Task.ConfigureAwait(false); + } + catch (InvalidOperationException) + { + try + { + process.Kill(); + process.WaitForExit(); + } + catch (InvalidOperationException) { } + } + + return process.ExitCode; + } + } + catch (Exception e) + { + logger.LogWarning("Error running shell script {0}! Exception: {1}", scriptPath, e); + return null; + } + } + } +} diff --git a/src/Tgstation.Server.Host/Components/StaticFiles/ServerSideModifications.cs b/src/Tgstation.Server.Host/Components/StaticFiles/ServerSideModifications.cs new file mode 100644 index 0000000000..7afb8ebf1f --- /dev/null +++ b/src/Tgstation.Server.Host/Components/StaticFiles/ServerSideModifications.cs @@ -0,0 +1,36 @@ +namespace Tgstation.Server.Host.Components.StaticFiles +{ + /// + /// Represents code modifications via configuration + /// + public sealed class ServerSideModifications + { + /// + /// If the target dme was completely overwitten + /// + public bool TotalDmeOverwrite { get; } + + /// + /// The #include line which should be added to the beginning of the .dme if any + /// + public string HeadIncludeLine { get; } + + /// + /// The #include line which should be added to the end of the .dme if any + /// + public string TailIncludeLine { get; } + + /// + /// Construct + /// + /// The value of + /// The value of + /// The value of + public ServerSideModifications(string headIncludeLine, string tailIncludeLine, bool totalDmeOverwrite) + { + HeadIncludeLine = headIncludeLine; + TailIncludeLine = tailIncludeLine; + TotalDmeOverwrite = totalDmeOverwrite; + } + } +} diff --git a/src/Tgstation.Server.Host/Components/Watchdog/Executor.cs b/src/Tgstation.Server.Host/Components/Watchdog/Executor.cs new file mode 100644 index 0000000000..5ff46f692c --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Watchdog/Executor.cs @@ -0,0 +1,72 @@ +using System; +using System.Diagnostics; +using System.Globalization; +using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Models.Internal; +using Tgstation.Server.Host.Components.Byond; + +namespace Tgstation.Server.Host.Components.Watchdog +{ + /// + sealed class Executor : IExecutor + { + /// + /// Change a given into the appropriate DreamDaemon command line word + /// + /// The level to change + /// A representation of the command line parameter + static string SecurityWord(DreamDaemonSecurity securityLevel) + { + switch (securityLevel) + { + case DreamDaemonSecurity.Safe: + return "safe"; + case DreamDaemonSecurity.Trusted: + return "trusted"; + case DreamDaemonSecurity.Ultrasafe: + return "ultrasafe"; + default: + throw new ArgumentOutOfRangeException(nameof(securityLevel), securityLevel, String.Format(CultureInfo.InvariantCulture, "Bad DreamDaemon security level: {0}", securityLevel)); + } + } + + /// + public ISession AttachToDreamDaemon(int processId, IByondExecutableLock byondLock) => new Session(Process.GetProcessById(processId), byondLock); + + /// + public ISession RunDreamDaemon(DreamDaemonLaunchParameters launchParameters, IByondExecutableLock byondLock, IDmbProvider dmbProvider, string parameters, bool useSecondaryPort, bool useSecondaryDirectory) + { + if (launchParameters == null) + throw new ArgumentNullException(nameof(launchParameters)); + if (byondLock == null) + throw new ArgumentNullException(nameof(byondLock)); + if (dmbProvider == null) + throw new ArgumentNullException(nameof(dmbProvider)); + if (parameters == null) + throw new ArgumentNullException(nameof(parameters)); + + var proc = new Process(); + try + { + proc.StartInfo.FileName = byondLock.DreamDaemonPath; + proc.StartInfo.WorkingDirectory = useSecondaryDirectory ? dmbProvider.SecondaryDirectory : dmbProvider.PrimaryDirectory; + + proc.StartInfo.Arguments = String.Format(CultureInfo.InvariantCulture, "{0} -port {1} {2}-close -{3} -verbose -public -params \"{4}\"", + dmbProvider.DmbName, + useSecondaryPort ? launchParameters.SecondaryPort : launchParameters.PrimaryPort, + launchParameters.AllowWebClient.Value ? "-webclient " : String.Empty, + SecurityWord(launchParameters.SecurityLevel.Value), + parameters); + + proc.Start(); + + return new Session(proc, byondLock); + } + catch + { + proc.Dispose(); + throw; + } + } + } +} diff --git a/src/Tgstation.Server.Host/Components/Watchdog/IExecutor.cs b/src/Tgstation.Server.Host/Components/Watchdog/IExecutor.cs new file mode 100644 index 0000000000..127244af15 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Watchdog/IExecutor.cs @@ -0,0 +1,31 @@ +using Tgstation.Server.Api.Models.Internal; +using Tgstation.Server.Host.Components.Byond; + +namespace Tgstation.Server.Host.Components.Watchdog +{ + /// + /// For creating s + /// + interface IExecutor + { + /// + /// Run a dream daemon instance + /// + /// The + /// The for the new + /// The for the .dmb to run + /// The value of the -params command line option + /// If the field of should be used + /// If the field of should be used + /// A new + ISession RunDreamDaemon(DreamDaemonLaunchParameters launchParameters, IByondExecutableLock byondLock, IDmbProvider dmbProvider, string parameters, bool useSecondaryPort, bool useSecondaryDirectory); + + /// + /// Attach to a running instance of DreamDaemon + /// + /// The + /// The for the new + /// A new + ISession AttachToDreamDaemon(int processId, IByondExecutableLock byondLock); + } +} diff --git a/src/Tgstation.Server.Host/Components/Watchdog/IReattachInfoHandler.cs b/src/Tgstation.Server.Host/Components/Watchdog/IReattachInfoHandler.cs new file mode 100644 index 0000000000..3547b641c7 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Watchdog/IReattachInfoHandler.cs @@ -0,0 +1,26 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Host.Components.Watchdog +{ + /// + /// Handles saving and loading + /// + public interface IReattachInfoHandler + { + /// + /// Save some + /// + /// The to save + /// The for the operation + /// A representing the running operation + Task Save(WatchdogReattachInformation reattachInformation, CancellationToken cancellationToken); + + /// + /// Load a saved + /// + /// The for the operation + /// A resulting in the stored if any + Task Load(CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Components/Watchdog/ISession.cs b/src/Tgstation.Server.Host/Components/Watchdog/ISession.cs new file mode 100644 index 0000000000..5fb7fb35dd --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Watchdog/ISession.cs @@ -0,0 +1,18 @@ +namespace Tgstation.Server.Host.Components.Watchdog +{ + /// + /// Represents a dream daemon process + /// + interface ISession : ISessionBase + { + /// + /// The + /// + int ProcessId { get; } + + /// + /// Terminates the running process + /// + void Terminate(); + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Components/Watchdog/ISessionBase.cs b/src/Tgstation.Server.Host/Components/Watchdog/ISessionBase.cs new file mode 100644 index 0000000000..672fbee8cf --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Watchdog/ISessionBase.cs @@ -0,0 +1,18 @@ +using System; +using System.Threading.Tasks; + +namespace Tgstation.Server.Host.Components.Watchdog +{ + interface ISessionBase : IDisposable + { + /// + /// A that completes when DreamDaemon starts pumping the windows message queue after loading a .dmb or when it crashes + /// + Task LaunchResult { get; } + + /// + /// A representing the lifetime of the and resulting in the + /// + Task Lifetime { get; } + } +} diff --git a/src/Tgstation.Server.Host/Components/Watchdog/ISessionController.cs b/src/Tgstation.Server.Host/Components/Watchdog/ISessionController.cs new file mode 100644 index 0000000000..6624d03145 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Watchdog/ISessionController.cs @@ -0,0 +1,88 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Host.Components.Watchdog +{ + /// + /// Handles communication with a + /// + interface ISessionController : ISessionBase + { + /// + /// If the of is being used + /// + bool IsPrimary { get; } + + /// + /// If the DMAPI was validated. This field may only be access once completes + /// + bool ApiValidated { get; } + + /// + /// The being used + /// + IDmbProvider Dmb { get; } + + /// + /// The current port DreamDaemon is listening on + /// + ushort? Port { get; } + + /// + /// The current + /// + RebootState RebootState { get; } + + /// + /// If the port should close when /world/Reboot() is called. Defaults to + /// + bool ClosePortOnReboot { get; set; } + + /// + /// A that completes when the server calls /world/Reboot() + /// + Task OnReboot { get; } + + /// + /// Releases the without terminating it. Also calls + /// + /// which can be used to create a new similar to this one + ReattachInformation Release(); + + /// + /// Sends a command to DreamDaemon through /world/Topic() + /// + /// The sanitized command to send + /// The for the operation + /// A resulting in the result of /world/Topic() + Task SendCommand(string command, CancellationToken cancellationToken); + + /// + /// Closes the world's port + /// + /// The for the operation + /// A resulting in if the operation succeeded, otherwise + Task ClosePort(CancellationToken cancellationToken); + + /// + /// Causes the world to start listening on a + /// + /// The port to change to + /// The for the operation + /// A resulting in if the operation succeeded, otherwise + Task SetPort(ushort newPort, CancellationToken cancellatonToken); + + /// + /// Attempts to change the current to + /// + /// The new + /// The for the operation + /// A resulting in if the operation succeeded, otherwise + Task SetRebootState(RebootState newRebootState, CancellationToken cancellationToken); + + /// + /// Changes to without telling the DMAPI + /// + void ResetRebootState(); + } +} diff --git a/src/Tgstation.Server.Host/Components/Watchdog/ISessionControllerFactory.cs b/src/Tgstation.Server.Host/Components/Watchdog/ISessionControllerFactory.cs new file mode 100644 index 0000000000..35f423adc3 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Watchdog/ISessionControllerFactory.cs @@ -0,0 +1,34 @@ +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Api.Models.Internal; +using Tgstation.Server.Host.Components.Byond; + +namespace Tgstation.Server.Host.Components.Watchdog +{ + /// + /// Factory for s + /// + interface ISessionControllerFactory + { + /// + /// Create a from a freshly launch DreamDaemon instance + /// + /// The to use + /// The to use + /// The current if any + /// If the of should be used + /// If the of should be used + /// If the should only validate the DMAPI then exit + /// The for the operation + /// A resulting in a new + Task LaunchNew(DreamDaemonLaunchParameters launchParameters, IDmbProvider dmbProvider, IByondExecutableLock currentByondLock, bool primaryPort, bool primaryDirectory, bool apiValidate, CancellationToken cancellationToken); + + /// + /// Create a from an existing DreamDaemon instance + /// + /// The to use + /// The for the operation + /// A resulting in a new + Task Reattach(ReattachInformation reattachInformation, CancellationToken cancellationToken); + } +} diff --git a/src/Tgstation.Server.Host/Components/Watchdog/IWatchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/IWatchdog.cs new file mode 100644 index 0000000000..c7533c58ca --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Watchdog/IWatchdog.cs @@ -0,0 +1,75 @@ +using Microsoft.Extensions.Hosting; +using System; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Api.Models.Internal; + +namespace Tgstation.Server.Host.Components.Watchdog +{ + /// + /// Runs and monitors the twin server controllers + /// + public interface IWatchdog : IHostedService, IDisposable, IEventConsumer + { + /// + /// If the watchdog is running + /// + bool Running { get; } + + /// + /// If the alpha server is the active server + /// + bool AlphaIsActive { get; } + + /// + /// The latest of the twin servers + /// + LaunchResult LastLaunchResult { get; } + + /// + /// The the active server is using + /// + DreamDaemonLaunchParameters ActiveLaunchParameters { get; } + + /// + /// The to be applied + /// + DreamDaemonLaunchParameters LastLaunchParameters { get; } + + /// + /// The of the active server + /// + RebootState? RebootState { get; } + + /// + /// Start the + /// + /// The for the operation + /// A resulting in the or if it was already running + Task Launch(CancellationToken cancellationToken); + + /// + /// Changes the . If currently triggers a graceful restart + /// + /// The new + /// The for the operation + /// A representing the running operation + Task ChangeSettings(DreamDaemonLaunchParameters launchParameters, CancellationToken cancellationToken); + + /// + /// Restarts the watchdog + /// + /// If the restart will be delayed until a reboot is detected in the active server's DMAPI and this function will retrun immediately + /// The for the operation + /// A resulting in the or if it was already running or is and is + Task Restart(bool graceful, CancellationToken cancellationToken); + + /// + /// Stops the watchdog + /// + /// If the termination will be delayed until a reboot is detected in the active server's DMAPI and this function will return immediately + /// The for the operation + /// A representing the running operation + Task Terminate(bool graceful, CancellationToken cancellationToken); + } +} diff --git a/src/Tgstation.Server.Host/Components/Watchdog/IWatchdogFactory.cs b/src/Tgstation.Server.Host/Components/Watchdog/IWatchdogFactory.cs new file mode 100644 index 0000000000..68717edc9c --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Watchdog/IWatchdogFactory.cs @@ -0,0 +1,19 @@ +using Tgstation.Server.Api.Models.Internal; +using Tgstation.Server.Host.Components.Compiler; + +namespace Tgstation.Server.Host.Components.Watchdog +{ + /// + /// For creating s + /// + interface IWatchdogFactory + { + /// + /// Creates a + /// + /// The for the with + /// The initial for the + /// A new + IWatchdog CreateWatchdog(IDmbFactory dmbFactory, DreamDaemonSettings settings); + } +} diff --git a/src/Tgstation.Server.Host/Components/Watchdog/InteropConstants.cs b/src/Tgstation.Server.Host/Components/Watchdog/InteropConstants.cs new file mode 100644 index 0000000000..9b08efcdf5 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Watchdog/InteropConstants.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Tgstation.Server.Host.Components.Watchdog +{ + static class InteropConstants + { + //interop values, match them up with the appropriate api.dm + + //api version 4.0.0.0 + public const string DMParamHostVersion = "server_service_version"; + public const string DMParamInfoJson = "tgs_json"; + + public const string DMInteropAccessIdentifier = "tgs_tok"; + + public const string DMResponseSuccess = "tgs_succ"; + + public const string DMTopicChangePort = "tgs_port"; + public const string DMTopicChangeReboot = "tgs_rmode"; + public const string DMTopicChatCommand = "tgs_chat_comm"; + public const string DMTopicEvent = "tgs_event"; + + public const string DMCommandOnline = "tgs_on"; + public const string DMCommandIdentify = "tgs_ident"; + public const string DMCommandApiValidate = "tgs_validate"; + public const string DMCommandServerPrimed = "tgs_prime"; + public const string DMCommandWorldReboot = "tgs_reboot"; + public const string DMCommandEndProcess = "tgs_kill"; + public const string DMCommandChat = "tgs_chat_send"; + + public const string DMParameterCommand = "tgs_com"; + public const string DMParameterData = "tgs_data"; + + public const string DMParameterNewPort = "new_port"; + public const string DMParameterNewRebootMode = "new_rmode"; + } +} diff --git a/src/Tgstation.Server.Host/Components/Watchdog/InteropInfo.cs b/src/Tgstation.Server.Host/Components/Watchdog/InteropInfo.cs new file mode 100644 index 0000000000..9565cb1cab --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Watchdog/InteropInfo.cs @@ -0,0 +1,25 @@ +using System; +using System.Collections.Generic; +using Tgstation.Server.Api.Models.Internal; + +namespace Tgstation.Server.Host.Components.Watchdog +{ + sealed class InteropInfo + { + public string AccessIdentifier { get; set; } + + public string HostPath { get; set; } + + public bool ApiValidateOnly { get; set; } + + public string InstanceName { get; set; } + + public string ChatChannelsJson { get; set; } + + public string ChatCommandsJson { get; set; } + + public RevisionInformation Revision { get; set; } + + public List TestMerges { get; } = new List(); + } +} diff --git a/src/Tgstation.Server.Host/Components/Watchdog/LaunchResult.cs b/src/Tgstation.Server.Host/Components/Watchdog/LaunchResult.cs new file mode 100644 index 0000000000..3e8c3d51b4 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Watchdog/LaunchResult.cs @@ -0,0 +1,29 @@ +using System; +using System.Globalization; + +namespace Tgstation.Server.Host.Components.Watchdog +{ + /// + /// Represents the result of trying to start a DD process + /// + public sealed class LaunchResult + { + /// + /// The time it took for to return + /// + public TimeSpan StartupTime { get; set; } + + /// + /// The if it exited + /// + public int? ExitCode { get; set; } + + /// + /// The peak virtual memory usage in bytes + /// + public long PeakMemory { get; set; } + + /// + public override string ToString() => String.Format(CultureInfo.InvariantCulture, "Exit Code: {0}, RAM: {1}, Time {2}ms", ExitCode, PeakMemory, StartupTime.TotalMilliseconds); + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Components/Watchdog/MonitorAction.cs b/src/Tgstation.Server.Host/Components/Watchdog/MonitorAction.cs new file mode 100644 index 0000000000..1e0199c88a --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Watchdog/MonitorAction.cs @@ -0,0 +1,13 @@ +namespace Tgstation.Server.Host.Components.Watchdog +{ + /// + /// The action for the monitor loop to take when control is returned to it + /// + enum MonitorAction + { + Continue, + Restart, + Break, + Exit + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Components/Watchdog/MonitorActivationReason.cs b/src/Tgstation.Server.Host/Components/Watchdog/MonitorActivationReason.cs new file mode 100644 index 0000000000..bfe0183058 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Watchdog/MonitorActivationReason.cs @@ -0,0 +1,13 @@ +namespace Tgstation.Server.Host.Components.Watchdog +{ + enum MonitorActivationReason + { + ActiveServerCrashed, + InactiveServerCrashed, + ActiveServerRebooted, + InactiveServerRebooted, + InactiveServerStartupComplete, + NewDmbAvailable, + ActiveLaunchParametersUpdated + } +} diff --git a/src/Tgstation.Server.Host/Components/Watchdog/MonitorState.cs b/src/Tgstation.Server.Host/Components/Watchdog/MonitorState.cs new file mode 100644 index 0000000000..367c2c3e80 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Watchdog/MonitorState.cs @@ -0,0 +1,16 @@ +namespace Tgstation.Server.Host.Components.Watchdog +{ + sealed class MonitorState + { + public bool RebootingInactiveServer { get; set; } + + public bool InactiveServerHasStagedDmb { get; set; } + + public bool InactiveServerCritFail { get; set; } + + public MonitorAction NextAction { get; set; } + + public ISessionController ActiveServer { get; set; } + public ISessionController InactiveServer { get; set; } + } +} diff --git a/src/Tgstation.Server.Host/Components/Watchdog/ReattachInformation.cs b/src/Tgstation.Server.Host/Components/Watchdog/ReattachInformation.cs new file mode 100644 index 0000000000..271d840692 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Watchdog/ReattachInformation.cs @@ -0,0 +1,31 @@ +using Tgstation.Server.Host.Components.Compiler; +using Tgstation.Server.Host.Models; + +namespace Tgstation.Server.Host.Components.Watchdog +{ + /// + /// Parameters necessary for duplicating a session + /// + public sealed class ReattachInformation : ReattachInformationBase + { + /// + /// The used by DreamDaemon + /// + public IDmbProvider Dmb { get; set; } + + /// + /// Construct a + /// + public ReattachInformation() { } + + /// + /// Construct a from a given and + /// + /// The to copy values from + /// The used to assign + public ReattachInformation(Models.ReattachInformation copy, IDmbFactory dmbFactory) : base(copy) + { + Dmb = dmbFactory.FromCompileJob(copy.CompileJob); + } + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Components/Watchdog/RebootState.cs b/src/Tgstation.Server.Host/Components/Watchdog/RebootState.cs new file mode 100644 index 0000000000..6890df7bab --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Watchdog/RebootState.cs @@ -0,0 +1,21 @@ +namespace Tgstation.Server.Host.Components.Watchdog +{ + /// + /// Represents the action to take when /world/Reboot() is called + /// + public enum RebootState : int + { + /// + /// Run DreamDaemon's normal reboot process + /// + Normal = 0, + /// + /// Shutdown DreamDaemon + /// + Shutdown = 1, + /// + /// Restart the DreamDaemon process + /// + Restart = 2 + } +} diff --git a/src/Tgstation.Server.Host/Components/Watchdog/Session.cs b/src/Tgstation.Server.Host/Components/Watchdog/Session.cs new file mode 100644 index 0000000000..29b7eb8844 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Watchdog/Session.cs @@ -0,0 +1,81 @@ +using System; +using System.Diagnostics; +using System.Threading.Tasks; +using Tgstation.Server.Host.Components.Byond; + +namespace Tgstation.Server.Host.Components.Watchdog +{ + /// + sealed class Session : ISession + { + /// + public int ProcessId => process.Id; + + /// + public Task LaunchResult { get; } + + /// + public Task Lifetime => lifetimeTask.Task; + + /// + /// The actual + /// + readonly Process process; + /// + /// The for the + /// + readonly IByondExecutableLock byondLock; + + /// + /// The backing for + /// + readonly TaskCompletionSource lifetimeTask; + + /// + /// Construct a + /// + /// The value of + /// The value of + public Session(Process process, IByondExecutableLock byondLock) + { + this.process = process ?? throw new ArgumentNullException(nameof(process)); + this.byondLock = byondLock ?? throw new ArgumentNullException(nameof(byondLock)); + + LaunchResult = Task.Factory.StartNew(() => + { + var startTime = DateTimeOffset.Now; + try + { + process.WaitForInputIdle(); + } + catch (InvalidOperationException) { } + var result = new LaunchResult + { + ExitCode = process.HasExited ? (int?)process.ExitCode : null, + PeakMemory = process.PeakWorkingSet64, + StartupTime = DateTimeOffset.Now - startTime + }; + if (result.PeakMemory == 0) //linux, best we can do honestly, test if this even works + result.PeakMemory = process.WorkingSet64; + return result; + }, default, TaskCreationOptions.LongRunning, TaskScheduler.Current); + lifetimeTask = new TaskCompletionSource(); + process.EnableRaisingEvents = true; + process.Exited += (a, b) => lifetimeTask.SetResult(process.ExitCode); + } + + /// + public void Dispose() => process.Dispose(); + + /// + public void Terminate() + { + try + { + process.Kill(); + process.WaitForExit(); + } + catch (InvalidOperationException) { } + } + } +} diff --git a/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs b/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs new file mode 100644 index 0000000000..757734d4f1 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Watchdog/SessionController.cs @@ -0,0 +1,344 @@ +using Byond.TopicSender; +using Microsoft.AspNetCore.Http; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Primitives; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Host.Components.Chat; + +namespace Tgstation.Server.Host.Components.Watchdog +{ + /// + sealed class SessionController : ISessionController, IInteropConsumer + { + /// + public bool IsPrimary + { + get + { + CheckDisposed(); + return reattachInformation.IsPrimary; + } + } + + /// + public bool ClosePortOnReboot { get; set; } + + /// + public bool ApiValidated + { + get + { + if (!Lifetime.IsCompleted) + throw new InvalidOperationException("ApiValidated cannot be checked while Lifetime is incomplete!"); + return apiValidated; + } + } + + /// + public IDmbProvider Dmb + { + get + { + CheckDisposed(); + return reattachInformation.Dmb; + } + } + + /// + public ushort? Port + { + get + { + CheckDisposed(); + if (portClosed) + return null; + return reattachInformation.Port; + } + } + + /// + public RebootState RebootState + { + get + { + CheckDisposed(); + return reattachInformation.RebootState; + } + } + + /// + public Task LaunchResult => session.LaunchResult; + + /// + public Task Lifetime => session.Lifetime; + + /// + public Task OnReboot => rebootTcs.Task; + + /// + /// The up to date + /// + readonly ReattachInformation reattachInformation; + + /// + /// The for the + /// + readonly IByondTopicSender byondTopicSender; + + /// + /// The for the + /// + readonly IInteropContext interopContext; + + /// + /// The for the + /// + readonly ISession session; + + /// + /// The for the + /// + readonly IJsonTrackingContext chatJsonTrackingContext; + + /// + /// The for the + /// + readonly IChat chat; + + /// + /// The for the + /// + readonly ILogger logger; + + /// + /// The waits on when DreamDaemon currently has it's ports closed + /// + TaskCompletionSource portAssignmentTcs; + /// + /// The port to assign DreamDaemon when it queries for it + /// + ushort nextPort; + + /// + /// The that completes when DD tells us about a reboot + /// + TaskCompletionSource rebootTcs; + + /// + /// If we know DreamDaemon currently has it's port closed + /// + bool portClosed; + /// + /// If the has been disposed + /// + bool disposed; + + /// + /// If the DMAPI was validated + /// + bool apiValidated; + + /// + /// Construct a + /// + /// The value of + /// The value of + /// The value of + /// The used to construct + /// The value of + /// The value of + /// The value of + public SessionController(ReattachInformation reattachInformation, ISession session, IByondTopicSender byondTopicSender, IInteropRegistrar interopRegistrar, IJsonTrackingContext chatJsonTrackingContext, IChat chat, ILogger logger) + { + this.chatJsonTrackingContext = chatJsonTrackingContext; //null valid + this.reattachInformation = reattachInformation ?? throw new ArgumentNullException(nameof(reattachInformation)); + this.byondTopicSender = byondTopicSender ?? throw new ArgumentNullException(nameof(byondTopicSender)); + if (interopRegistrar == null) + throw new ArgumentNullException(nameof(interopRegistrar)); + this.session = session ?? throw new ArgumentNullException(nameof(session)); + this.chat = chat ?? throw new ArgumentNullException(nameof(chat)); + this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); + + interopContext = interopRegistrar.Register(reattachInformation.AccessIdentifier, this); + + portClosed = false; + disposed = false; + apiValidated = false; + + rebootTcs = new TaskCompletionSource(); + } + + /// + public void Dispose() + { + lock (this) + { + if (disposed) + return; + session.Dispose(); + interopContext.Dispose(); + Dmb?.Dispose(); //will be null when released + chatJsonTrackingContext.Dispose(); + disposed = true; + } + } + + /// + public Task HandleInterop(IQueryCollection query, CancellationToken cancellationToken) + { + if (query == null) + throw new ArgumentNullException(nameof(query)); + + object content = new object(); + var status = HttpStatusCode.OK; + + if (query.TryGetValue(InteropConstants.DMParameterCommand, out StringValues values)) + switch (values.First()) + { + case InteropConstants.DMCommandIdentify: + lock (this) + if (portClosed) + content = new Dictionary { { InteropConstants.DMParameterNewPort, nextPort } }; + break; + case InteropConstants.DMCommandOnline: + lock (this) + if (portClosed) + { + reattachInformation.Port = nextPort; + portAssignmentTcs.SetResult(true); + portAssignmentTcs = null; + portClosed = false; + } + break; + case InteropConstants.DMCommandApiValidate: + apiValidated = true; + break; + case InteropConstants.DMCommandWorldReboot: + if (ClosePortOnReboot) + { + content = new Dictionary { { InteropConstants.DMParameterNewPort, 0 } }; + portClosed = true; + } + else + ClosePortOnReboot = true; + var oldTcs = rebootTcs; + rebootTcs = new TaskCompletionSource(); + oldTcs.SetResult(null); + break; + default: + status = HttpStatusCode.BadRequest; + content = new { message = "Requested command not supported!" }; + break; + } + return Task.FromResult(new { STATUS = status, CONTENT = content }); + } + + /// + /// Throws an if has been called + /// + void CheckDisposed() + { + if (disposed) + throw new ObjectDisposedException(nameof(SessionController)); + } + + /// + public ReattachInformation Release() + { + CheckDisposed(); + //we still don't want to dispose the dmb yet, even though we're keeping it alive + var tmpProvider = reattachInformation.Dmb; + reattachInformation.Dmb = null; + Dispose(); + Dmb.KeepAlive(); + reattachInformation.Dmb = tmpProvider; + return reattachInformation; + } + + /// + public async Task SendCommand(string command, CancellationToken cancellationToken) + { + try + { + return await byondTopicSender.SendTopic( + new IPEndPoint(IPAddress.Loopback, reattachInformation.Port), + String.Format(CultureInfo.InvariantCulture, + "?{0}={1}&{2}={3}", + byondTopicSender.SanitizeString(InteropConstants.DMInteropAccessIdentifier), + byondTopicSender.SanitizeString(reattachInformation.AccessIdentifier), + byondTopicSender.SanitizeString(InteropConstants.DMParameterCommand), + //intentionally don't sanitize command, that's up to the caller + command), + cancellationToken).ConfigureAwait(false); + } + catch (Exception e) + { + logger.LogInformation("Send command exception:{0}{1}", Environment.NewLine, e.Message); + return null; + } + } + + async Task SetPortImpl(ushort port, CancellationToken cancellationToken) => await SendCommand(String.Format(CultureInfo.InvariantCulture, "{0}&{1}={2}", byondTopicSender.SanitizeString(InteropConstants.DMTopicChangePort), byondTopicSender.SanitizeString(InteropConstants.DMParameterNewPort), byondTopicSender.SanitizeString(port.ToString(CultureInfo.InvariantCulture))), cancellationToken).ConfigureAwait(false) == InteropConstants.DMResponseSuccess; + + /// + public async Task ClosePort(CancellationToken cancellationToken) + { + CheckDisposed(); + if (portClosed) + return true; + if (await SetPortImpl(0, cancellationToken).ConfigureAwait(false)) + { + portClosed = true; + return true; + } + return false; + } + + /// + public async Task SetPort(ushort port, CancellationToken cancellatonToken) + { + CheckDisposed(); + if (portClosed) + { + Task toWait; + lock (this) + { + if (portAssignmentTcs != null) + //someone was trying to change the port before us, ignore them + //shouldn't happen anyway, add logging here + portAssignmentTcs.SetResult(false); + nextPort = port; + portAssignmentTcs = new TaskCompletionSource(); + toWait = portAssignmentTcs.Task; + } + return await toWait.ConfigureAwait(false); + } + + if (port == 0) + throw new ArgumentOutOfRangeException(nameof(port), port, "port must not be zero!"); + return await SetPortImpl(port, cancellatonToken).ConfigureAwait(false); + } + + /// + public async Task SetRebootState(RebootState newRebootState, CancellationToken cancellationToken) + { + if (RebootState == newRebootState) + return true; + + return await SendCommand(String.Format(CultureInfo.InvariantCulture, "{0}&{1}={2}", InteropConstants.DMTopicChangeReboot, InteropConstants.DMParameterNewRebootMode, (int)newRebootState), cancellationToken).ConfigureAwait(false) == InteropConstants.DMResponseSuccess; + } + + /// + public void ResetRebootState() + { + CheckDisposed(); + reattachInformation.RebootState = RebootState.Normal; + } + } +} diff --git a/src/Tgstation.Server.Host/Components/Watchdog/SessionControllerFactory.cs b/src/Tgstation.Server.Host/Components/Watchdog/SessionControllerFactory.cs new file mode 100644 index 0000000000..1afc9a1571 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Watchdog/SessionControllerFactory.cs @@ -0,0 +1,221 @@ +using Byond.TopicSender; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using System; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Api.Models.Internal; +using Tgstation.Server.Host.Components.Byond; +using Tgstation.Server.Host.Components.Chat; +using Tgstation.Server.Host.Core; +using Tgstation.Server.Host.IO; +using Tgstation.Server.Host.Security; + +namespace Tgstation.Server.Host.Components.Watchdog +{ + /// + sealed class SessionControllerFactory : ISessionControllerFactory + { + /// + /// The for the + /// + readonly IExecutor executor; + + /// + /// The for the + /// + readonly IByondManager byond; + + /// + /// The for the + /// + readonly IByondTopicSender byondTopicSender; + + /// + /// The for the + /// + readonly IInteropRegistrar interopRegistrar; + + /// + /// The for the + /// + readonly ICryptographySuite cryptographySuite; + + /// + /// The for the + /// + readonly IApplication application; + + /// + /// The for the + /// + readonly IIOManager ioManager; + + /// + /// The for the + /// + readonly IChat chat; + + /// + /// The for the + /// + readonly ILoggerFactory loggerFactory; + + /// + /// The for the + /// + readonly Api.Models.Instance instance; + + /// + /// Construct a + /// + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of + public SessionControllerFactory(IExecutor executor, IByondManager byond, IByondTopicSender byondTopicSender, IInteropRegistrar interopRegistrar, ICryptographySuite cryptographySuite, IApplication application, IIOManager ioManager, IChat chat, ILoggerFactory loggerFactory, Api.Models.Instance instance) + { + this.executor = executor ?? throw new ArgumentNullException(nameof(executor)); + this.byond = byond ?? throw new ArgumentNullException(nameof(byond)); + this.byondTopicSender = byondTopicSender ?? throw new ArgumentNullException(nameof(byondTopicSender)); + this.interopRegistrar = interopRegistrar ?? throw new ArgumentNullException(nameof(interopRegistrar)); + this.cryptographySuite = cryptographySuite ?? throw new ArgumentNullException(nameof(cryptographySuite)); + this.application = application ?? throw new ArgumentNullException(nameof(application)); + this.instance = instance ?? throw new ArgumentNullException(nameof(instance)); + this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager)); + this.chat = chat ?? throw new ArgumentNullException(nameof(chat)); + this.loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory)); + } + + /// + public async Task LaunchNew(DreamDaemonLaunchParameters launchParameters, IDmbProvider dmbProvider, IByondExecutableLock currentByondLock, bool primaryPort, bool primaryDirectory, bool apiValidate, CancellationToken cancellationToken) + { + var portToUse = primaryPort ? launchParameters.PrimaryPort : launchParameters.SecondaryPort; + if (!portToUse.HasValue) + throw new InvalidOperationException("Given port is null!"); + var accessIdentifier = cryptographySuite.GetSecureString(); + + string GuidJsonFile() => String.Format(CultureInfo.InvariantCulture, "{0}.json", Guid.NewGuid()); + + var interopInfo = new InteropInfo + { + AccessIdentifier = accessIdentifier, + ApiValidateOnly = apiValidate, + ChatChannelsJson = GuidJsonFile(), + ChatCommandsJson = GuidJsonFile(), + HostPath = application.HostingPath, + InstanceName = instance.Name, + Revision = dmbProvider.CompileJob.RevisionInformation + }; + interopInfo.TestMerges.AddRange(dmbProvider.CompileJob.RevisionInformation.ActiveTestMerges.Select(x => x.TestMerge).Select(x => new TestMerge + { + Author = x.Author, + Body = x.BodyAtMerge, + Comment = x.Comment, + CommitSha = x.PrimaryRevisionInformation.CommitSha, + Number = x.Number, + OriginCommitSha = x.PrimaryRevisionInformation.OriginCommitSha, + PullRequestCommit = x.PullRequestRevision, + TimeMerged = x.MergedAt.Ticks, + Title = x.TitleAtMerge, + Url = x.Url + })); + + var interopJsonFile = GuidJsonFile(); + + var interopJson = JsonConvert.SerializeObject(interopInfo); + + var basePath = primaryDirectory ? dmbProvider.PrimaryDirectory : dmbProvider.SecondaryDirectory; + var localIoManager = new ResolvingIOManager(ioManager, ioManager.ConcatPath(basePath, dmbProvider.DmbName)); + + var chatJsonTrackingTask = chat.TrackJsons(basePath, interopInfo.ChatChannelsJson, interopInfo.ChatCommandsJson, cancellationToken); + + await localIoManager.WriteAllBytes(interopJsonFile, Encoding.UTF8.GetBytes(interopJson), cancellationToken).ConfigureAwait(false); + var chatJsonTrackingContext = await chatJsonTrackingTask.ConfigureAwait(false); + try + { + var byondLock = currentByondLock ?? await byond.UseExecutables(Version.Parse(dmbProvider.CompileJob.ByondVersion), cancellationToken).ConfigureAwait(false); + try + { + //more sanitization here cause it uses the same scheme + var parameters = String.Format(CultureInfo.InvariantCulture, "{2}={0}&{3}={1}", byondTopicSender.SanitizeString(application.Version.ToString()), byondTopicSender.SanitizeString(interopJsonFile), byondTopicSender.SanitizeString(InteropConstants.DMParamHostVersion), byondTopicSender.SanitizeString(InteropConstants.DMParamInfoJson)); + + var session = executor.RunDreamDaemon(launchParameters, byondLock, dmbProvider, parameters, !primaryPort, !primaryDirectory); + try + { + return new SessionController(new ReattachInformation + { + AccessIdentifier = accessIdentifier, + Dmb = dmbProvider, + IsPrimary = primaryDirectory, + Port = portToUse.Value, + ProcessId = session.ProcessId + }, session, byondTopicSender, interopRegistrar, chatJsonTrackingContext, chat, loggerFactory.CreateLogger()); + } + catch + { + session.Dispose(); + throw; + } + } + catch + { + if (currentByondLock == null) + byondLock.Dispose(); + throw; + } + } + catch + { + chatJsonTrackingContext.Dispose(); + throw; + } + } + + /// + public async Task Reattach(ReattachInformation reattachInformation, CancellationToken cancellationToken) + { + if (reattachInformation == null) + throw new ArgumentNullException(nameof(reattachInformation)); + + var basePath = reattachInformation.IsPrimary ? reattachInformation.Dmb.PrimaryDirectory : reattachInformation.Dmb.SecondaryDirectory; + var chatJsonTrackingContext = await chat.TrackJsons(basePath, reattachInformation.ChatChannelsJson, reattachInformation.ChatCommandsJson, cancellationToken).ConfigureAwait(false); + try + { + var byondLock = await byond.UseExecutables(Version.Parse(reattachInformation.Dmb.CompileJob.ByondVersion), cancellationToken).ConfigureAwait(false); + try + { + var session = executor.AttachToDreamDaemon(reattachInformation.ProcessId, byondLock); + try + { + return new SessionController(reattachInformation, session, byondTopicSender, interopRegistrar, chatJsonTrackingContext, chat, loggerFactory.CreateLogger()); + } + catch + { + session.Dispose(); + throw; + } + } + catch + { + byondLock.Dispose(); + throw; + } + } + catch + { + chatJsonTrackingContext.Dispose(); + throw; + } + } + } +} diff --git a/src/Tgstation.Server.Host/Components/Watchdog/TestMerge.cs b/src/Tgstation.Server.Host/Components/Watchdog/TestMerge.cs new file mode 100644 index 0000000000..7a55e739fe --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Watchdog/TestMerge.cs @@ -0,0 +1,19 @@ +using Tgstation.Server.Api.Models.Internal; + +namespace Tgstation.Server.Host.Components.Watchdog +{ + /// + /// This model mirrors /datum/tgs_revision_information/test_merge + /// + sealed class TestMerge : RevisionInformation + { + public int Number { get; set; } + public string Title { get; set; } + public string Body { get; set; } + public string Author { get; set; } + public string Url { get; set; } + public string PullRequestCommit { get; set; } + public long TimeMerged { get; set; } + public string Comment { get; set; } + } +} diff --git a/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs b/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs new file mode 100644 index 0000000000..6c0d58c864 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Watchdog/Watchdog.cs @@ -0,0 +1,838 @@ +using Byond.TopicSender; +using Microsoft.Extensions.Logging; +using Newtonsoft.Json; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Text; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Api.Models.Internal; +using Tgstation.Server.Host.Components.Chat; +using Tgstation.Server.Host.Components.Compiler; +using Tgstation.Server.Host.Core; + +namespace Tgstation.Server.Host.Components.Watchdog +{ + /// + sealed class Watchdog : IWatchdog, ICustomCommandHandler + { + /// + /// The time in milliseconds to wait from starting to start . Does not take responsiveness into account + /// + const int AlphaBravoStartupSeperationInterval = 3000; + + /// + public bool Running { get; private set; } + + /// + public bool AlphaIsActive { get; private set; } + + /// + public LaunchResult LastLaunchResult { get; private set; } + + /// + public DreamDaemonLaunchParameters ActiveLaunchParameters { get; private set; } + + /// + public DreamDaemonLaunchParameters LastLaunchParameters { get; private set; } + + /// + public RebootState? RebootState => Running ? (AlphaIsActive ? alphaServer?.RebootState : bravoServer?.RebootState) : null; + + /// + /// The for the + /// + readonly IChat chat; + + /// + /// The for the + /// + readonly ISessionControllerFactory sessionControllerFactory; + + /// + /// The for the + /// + readonly IDmbFactory dmbFactory; + + /// + /// The for the + /// + readonly ILogger logger; + + /// + /// The for the + /// + readonly IReattachInfoHandler reattachInfoHandler; + + /// + /// The for the + /// + readonly IDatabaseContextFactory databaseContextFactory; + + /// + /// The for the + /// + readonly IByondTopicSender byondTopicSender; + + /// + /// The for the + /// + readonly IEventConsumer eventConsumer; + + /// + /// The for the + /// + readonly SemaphoreSlim semaphore; + + /// + /// The for the + /// + readonly Api.Models.Instance instance; + + /// + /// If the should in + /// + readonly bool autoStart; + + /// + /// The for the monitor loop + /// + CancellationTokenSource monitorCts; + + /// + /// The running the monitor loop + /// + Task monitorTask; + + /// + /// that completes when are changed and we are + /// + TaskCompletionSource activeParametersUpdated; + + /// + /// Server designation alpha + /// + ISessionController alphaServer; + /// + /// Server designation bravo + /// + ISessionController bravoServer; + + /// + /// If the servers should be released instead of shutdown + /// + bool releaseServers; + + /// + /// Construct a + /// + /// The value of + /// The value of + /// The value of + /// The for the + /// The value of + /// The value of + /// The value of + /// The value of + /// The initial value of + /// The value of + /// The value of + /// The value of + public Watchdog(IChat chat, ISessionControllerFactory sessionControllerFactory, IDmbFactory dmbFactory, IServerControl serverUpdater, ILogger logger, IReattachInfoHandler reattachInfoHandler, IDatabaseContextFactory databaseContextFactory, IByondTopicSender byondTopicSender, IEventConsumer eventConsumer, DreamDaemonLaunchParameters initialLaunchParameters, Api.Models.Instance instance, bool autoStart) + { + this.chat = chat ?? throw new ArgumentNullException(nameof(chat)); + this.sessionControllerFactory = sessionControllerFactory ?? throw new ArgumentNullException(nameof(sessionControllerFactory)); + this.dmbFactory = dmbFactory ?? throw new ArgumentNullException(nameof(dmbFactory)); + this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); + this.reattachInfoHandler = reattachInfoHandler ?? throw new ArgumentNullException(nameof(reattachInfoHandler)); + this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory)); + this.byondTopicSender = byondTopicSender ?? throw new ArgumentNullException(nameof(byondTopicSender)); + this.eventConsumer = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer)); + this.instance = instance ?? throw new ArgumentNullException(nameof(instance)); + this.autoStart = autoStart; + + if (serverUpdater == null) + throw new ArgumentNullException(nameof(serverUpdater)); + + serverUpdater.RegisterForRestart(() => releaseServers = true); + + chat.RegisterCommandHandler(this); + + AlphaIsActive = true; + ActiveLaunchParameters = initialLaunchParameters; + releaseServers = false; + semaphore = new SemaphoreSlim(1); + activeParametersUpdated = new TaskCompletionSource(); + } + + /// + public void Dispose() + { + DisposeAndNullControllers(); + semaphore.Dispose(); + } + + /// + /// Call on and and set them to + /// + void DisposeAndNullControllers() + { + logger.LogTrace("DisposeAndNullControllers"); + alphaServer?.Dispose(); + alphaServer = null; + bravoServer?.Dispose(); + bravoServer = null; + } + + /// + /// Implementation of . Does not lock + /// + async Task RestartNoLock(bool graceful, CancellationToken cancellationToken) + { + var running = Running; + if (!graceful || !running) + { + Task chatTask; + if (running) + { + chatTask = chat.SendWatchdogMessage("Manual restart triggered...", cancellationToken); + await TerminateNoLock(false, false, cancellationToken).ConfigureAwait(false); + } + else + chatTask = Task.CompletedTask; + var result = await LaunchNoLock(true, !running, false, cancellationToken).ConfigureAwait(false); + await chatTask.ConfigureAwait(false); + return result; + } + var toReboot = AlphaIsActive ? alphaServer : bravoServer; + var other = AlphaIsActive ? bravoServer : alphaServer; + if (toReboot != null) + //todo, log the result + await toReboot.SetRebootState(Components.Watchdog.RebootState.Restart, cancellationToken).ConfigureAwait(false); + return null; + } + + /// + /// Implementation of . Does not lock + /// + /// If the termination will be delayed until a reboot is detected in the active server's DMAPI and this function will return immediately + /// If the termination will be announced using + /// The for the operation + async Task TerminateNoLock(bool graceful, bool announce, CancellationToken cancellationToken) + { + if (!Running) + return; + if (!graceful) + { + var chatTask = announce ? chat.SendWatchdogMessage("Terminating...", cancellationToken) : Task.CompletedTask; + await StopMonitor().ConfigureAwait(false); + DisposeAndNullControllers(); + await chatTask.ConfigureAwait(false); + return; + } + var toKill = AlphaIsActive ? alphaServer : bravoServer; + var other = AlphaIsActive ? bravoServer : alphaServer; + if (toKill != null) + await toKill.SetRebootState(Components.Watchdog.RebootState.Shutdown, cancellationToken).ConfigureAwait(false); + } + + /// + /// Handles the actions to take when the monitor has to "wake up" + /// + /// The that caused the invocation + /// The current . Will be modified upon retrn + /// The for the operation + /// A representing the running operation + async Task HandlerMonitorWakeup(MonitorActivationReason activationReason, MonitorState monitorState, CancellationToken cancellationToken) + { + logger.LogInformation("Monitor activation. Reason: {0}", activationReason); + + //returns true if the inactive server can't be used immediately + bool FullRestartDeadInactive() + { + if (monitorState.RebootingInactiveServer || monitorState.InactiveServerCritFail) + { + logger.LogInformation("Inactive server is {0}! Restarting monitor...", monitorState.InactiveServerCritFail ? "critically failed" : "still rebooting"); + monitorState.NextAction = MonitorAction.Restart; //will dispose server + return true; + } + return false; + }; + + //trys to set inactive server's port to the private port + async Task MakeInactiveActive() + { + logger.LogInformation("Setting inactive server to port {0}...", ActiveLaunchParameters.PrimaryPort.Value); + var result = await monitorState.InactiveServer.SetPort(ActiveLaunchParameters.PrimaryPort.Value, cancellationToken).ConfigureAwait(false); + + if (!result) + { + logger.LogWarning("Failed to activate inactive server! Restarting monitor..."); + monitorState.NextAction = MonitorAction.Restart; //will dispose server + return false; + } + + // should always be set for InactiveServer + monitorState.InactiveServer.ClosePortOnReboot = false; + monitorState.ActiveServer.ClosePortOnReboot = true; + + //inactive server should always be using active launch parameters + LastLaunchParameters = ActiveLaunchParameters; + + var tmp = monitorState.ActiveServer; + monitorState.ActiveServer = monitorState.InactiveServer; + monitorState.InactiveServer = tmp; + AlphaIsActive = !AlphaIsActive; + return true; + } + + // Tries to load inactive server with latest dmb, falling back to current dmb on failure. Requires a lock on + async Task RestartInactiveServer() + { + logger.LogInformation("Rebooting inactive server..."); + var newDmb = dmbFactory.LockNextDmb(cancellationToken); + bool usedMostRecentDmb; + try + { + monitorState.InactiveServer = await sessionControllerFactory.LaunchNew(ActiveLaunchParameters, await newDmb.ConfigureAwait(false), null, false, !monitorState.ActiveServer.IsPrimary, false, cancellationToken).ConfigureAwait(false); + usedMostRecentDmb = true; + } + catch (OperationCanceledException) + { + throw; + } + catch (Exception e) + { + logger.LogError("Exception occurred while recreating server! Attempting backup strategy of running DMB of running server! Exception: {0}", e.ToString()); + //ahh jeez, what do we do here? + //this is our fault, so it should never happen but + //idk maybe a database error while handling the newest dmb? + //either way try to start it using the active server's dmb as a backup + try + { + var dmbBackup = dmbFactory.FromCompileJob(monitorState.ActiveServer.Dmb.CompileJob); + monitorState.InactiveServer = await sessionControllerFactory.LaunchNew(ActiveLaunchParameters, dmbBackup, null, false, !monitorState.ActiveServer.IsPrimary, false, cancellationToken).ConfigureAwait(false); + usedMostRecentDmb = false; + await chat.SendWatchdogMessage("Staging newest DMB on inactive server failed: {0} Falling back to previous dmb...", cancellationToken).ConfigureAwait(false); + } + catch (OperationCanceledException) + { + throw; + } + catch (Exception e2) + { + //fuuuuucckkk + logger.LogError("Backup strategy failed! Monitor will restart when active server reboots! This Exception: {0}", e2.ToString()); + monitorState.InactiveServerCritFail = true; + await chat.SendWatchdogMessage("Attempted reboot of inactive server failed. Watchdog will reset when active server fails or exits", cancellationToken).ConfigureAwait(false); + return true; //we didn't use the old dmb + } + } + + logger.LogInformation("Successfully relaunched inactive server!"); + monitorState.RebootingInactiveServer = true; + // should always be set for InactiveServer + monitorState.InactiveServer.ClosePortOnReboot = false; + return usedMostRecentDmb; + } + + async Task UpdateAndRestartInactiveServer(bool breakAfter) + { + //replace the notification tcs here so that the next loop will read a fresh one + activeParametersUpdated = new TaskCompletionSource(); + monitorState.InactiveServer.Dispose(); //kill or recycle it + monitorState.NextAction = breakAfter ? MonitorAction.Break : MonitorAction.Continue; + + var usedLatestDmb = await RestartInactiveServer().ConfigureAwait(false); + + if (monitorState.NextAction == (breakAfter ? MonitorAction.Break : MonitorAction.Continue)) + { + monitorState.ActiveServer.ClosePortOnReboot = false; + if (monitorState.InactiveServerHasStagedDmb && !usedLatestDmb) + monitorState.InactiveServerHasStagedDmb = false; //don't try to load it again though + } + }; + + //reason handling + switch (activationReason) + { + case MonitorActivationReason.ActiveServerCrashed: + if(monitorState.ActiveServer.RebootState == Components.Watchdog.RebootState.Shutdown) + { + await chat.SendWatchdogMessage("Active server crashed or exited! Exiting due to graceful termination request...", cancellationToken).ConfigureAwait(false); + monitorState.NextAction = MonitorAction.Exit; + break; + } + + if (FullRestartDeadInactive()) + { + await chat.SendWatchdogMessage("Active server crashed or exited! Inactive server unable to online!", cancellationToken).ConfigureAwait(false); + break; + } + + await chat.SendWatchdogMessage("Active server crashed or exited! Onlining inactive server...", cancellationToken).ConfigureAwait(false); + if (!await MakeInactiveActive().ConfigureAwait(false)) + break; + + monitorState.ActiveServer.ClosePortOnReboot = false; + await UpdateAndRestartInactiveServer(true).ConfigureAwait(false); + break; + case MonitorActivationReason.InactiveServerCrashed: + await chat.SendWatchdogMessage("Inactive server crashed or exited! Rebooting...", cancellationToken).ConfigureAwait(false); + await UpdateAndRestartInactiveServer(false).ConfigureAwait(false); + break; + case MonitorActivationReason.ActiveServerRebooted: + if (FullRestartDeadInactive()) + break; + + //what matters here is the RebootState + bool restartOnceSwapped = false; + var rebootState = monitorState.ActiveServer.RebootState; + monitorState.ActiveServer.ResetRebootState(); //the DMAPI has already done this internally + + switch (rebootState) + { + case Components.Watchdog.RebootState.Normal: + break; + case Components.Watchdog.RebootState.Restart: + restartOnceSwapped = true; + break; + case Components.Watchdog.RebootState.Shutdown: + await chat.SendWatchdogMessage("Active server rebooted! Exiting due to graceful termination request...", cancellationToken).ConfigureAwait(false); + DisposeAndNullControllers(); + Running = false; + monitorState.NextAction = MonitorAction.Exit; + return; + } + + var sameCompileJob = monitorState.InactiveServer.Dmb.CompileJob.Id == monitorState.ActiveServer.Dmb.CompileJob.Id; + if (sameCompileJob && monitorState.InactiveServerHasStagedDmb) + //both servers up to date + monitorState.InactiveServerHasStagedDmb = false; + if (!sameCompileJob || ActiveLaunchParameters != LastLaunchParameters) + //need a new launch in ActiveServer + restartOnceSwapped = true; + + if (!await MakeInactiveActive().ConfigureAwait(false)) + break; + + if(!restartOnceSwapped) + //try to reopen inactive server on the private port so it's not pinging all the time + //failing that, just reboot it + restartOnceSwapped = !await monitorState.InactiveServer.SetPort(ActiveLaunchParameters.SecondaryPort.Value, cancellationToken).ConfigureAwait(false); + + if (restartOnceSwapped) //for one reason or another, + await UpdateAndRestartInactiveServer(true).ConfigureAwait(false); //break because worse case, active server is still booting + else + monitorState.NextAction = MonitorAction.Break; + break; + case MonitorActivationReason.InactiveServerRebooted: + //should never happen but okay + logger.LogWarning("Inactive server rebooted, this is a bug in DM code!"); + monitorState.RebootingInactiveServer = true; + monitorState.InactiveServer.ResetRebootState(); //the DMAPI has already done this internally + monitorState.ActiveServer.ClosePortOnReboot = false; + monitorState.NextAction = MonitorAction.Continue; + break; + case MonitorActivationReason.InactiveServerStartupComplete: + //eziest case of my life + monitorState.RebootingInactiveServer = false; + monitorState.ActiveServer.ClosePortOnReboot = true; + monitorState.NextAction = MonitorAction.Continue; + break; + case MonitorActivationReason.NewDmbAvailable: + monitorState.InactiveServerHasStagedDmb = true; + await UpdateAndRestartInactiveServer(true).ConfigureAwait(false); //next case does same thing + break; + case MonitorActivationReason.ActiveLaunchParametersUpdated: + await UpdateAndRestartInactiveServer(false).ConfigureAwait(false); + break; + } + } + + /// + /// The loop that watches the watchdog + /// + /// The for the operation + /// A representing the running operation + async Task MonitorLifetimes(CancellationToken cancellationToken) + { + logger.LogDebug("Entered MonitorLifetimes"); + var iteration = 1; + for(var monitorState = new MonitorState(); monitorState.NextAction != MonitorAction.Exit; ++iteration) + { + logger.LogDebug("New iteration of monitor loop"); + try + { + if(AlphaIsActive) + logger.LogDebug("Alpha is the active server"); + else + logger.LogDebug("Bravo is the active server"); + + if(monitorState.InactiveServerHasStagedDmb) + logger.LogDebug("Inactive server has staged .dmb"); + if (monitorState.RebootingInactiveServer) + logger.LogDebug("Inactive server is rebooting"); + + monitorState.ActiveServer = AlphaIsActive ? alphaServer : bravoServer; + monitorState.InactiveServer = AlphaIsActive ? bravoServer : alphaServer; + + var activeServerLifetime = monitorState.ActiveServer.Lifetime; + var inactiveServerLifetime = monitorState.InactiveServer.Lifetime; + var activeServerReboot = monitorState.ActiveServer.OnReboot; + var inactiveServerReboot = monitorState.InactiveServer.OnReboot; + var inactiveServerStartup = monitorState.InactiveServer.LaunchResult; + var activeLaunchParametersChanged = activeParametersUpdated.Task; + var newDmbAvailable = dmbFactory.OnNewerDmb; + + var cancelTcs = new TaskCompletionSource(); + using (cancellationToken.Register(() => cancelTcs.SetCanceled())) + { + var toWaitOn = Task.WhenAny(activeServerLifetime, inactiveServerLifetime, activeServerReboot, inactiveServerReboot, newDmbAvailable, cancelTcs.Task, activeLaunchParametersChanged); + if (monitorState.RebootingInactiveServer) + toWaitOn = Task.WhenAny(toWaitOn, inactiveServerStartup); + await toWaitOn.ConfigureAwait(false); + } + + var chatTask = Task.CompletedTask; + using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false)) + { + MonitorActivationReason activationReason = default; + //multiple things may have happened, handle them one at a time + for (var moreActivationsToProcess = true; moreActivationsToProcess && monitorState.NextAction == MonitorAction.Continue; ) + { + if (activeServerLifetime?.IsCompleted == true) + { + activationReason = MonitorActivationReason.ActiveServerCrashed; + activeServerLifetime = null; + } + else if (inactiveServerLifetime?.IsCompleted == true) + { + activationReason = MonitorActivationReason.InactiveServerCrashed; + inactiveServerLifetime = null; + } + else if (activeServerReboot?.IsCompleted == true) + { + activationReason = MonitorActivationReason.ActiveServerRebooted; + activeServerReboot = null; + } + else if (inactiveServerReboot?.IsCompleted == true) + { + activationReason = MonitorActivationReason.InactiveServerRebooted; + inactiveServerReboot = null; + } + else if (inactiveServerStartup?.IsCompleted == true) + { + activationReason = MonitorActivationReason.InactiveServerStartupComplete; + inactiveServerStartup = null; + } + else if (newDmbAvailable?.IsCompleted == true) + { + activationReason = MonitorActivationReason.NewDmbAvailable; + newDmbAvailable = null; + } + else if(activeLaunchParametersChanged?.IsCompleted == true) + { + activationReason = MonitorActivationReason.ActiveLaunchParametersUpdated; + activeLaunchParametersChanged = null; + } + else + moreActivationsToProcess = false; + } + + await HandlerMonitorWakeup(activationReason, monitorState, cancellationToken).ConfigureAwait(false); + //writeback alphaServer and bravoServer + alphaServer = AlphaIsActive ? monitorState.ActiveServer : monitorState.InactiveServer; + bravoServer = AlphaIsActive ? monitorState.ActiveServer : monitorState.InactiveServer; + } + + //full reboot required + if (monitorState.NextAction == MonitorAction.Restart) + { + logger.LogDebug("Next state action is to restart"); + DisposeAndNullControllers(); + chatTask = chat.SendWatchdogMessage("Restarting entirely due to complications...", cancellationToken); + } + + for (var retryAttempts = 1; monitorState.NextAction == MonitorAction.Restart; ++retryAttempts) + { + WatchdogLaunchResult result; + using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false)) + { + result = await LaunchNoLock(false, false, false, cancellationToken).ConfigureAwait(false); + if (Running) + monitorState = new MonitorState(); //clean the slate + } + + await chatTask.ConfigureAwait(false); + if(!Running) + { + logger.LogWarning("Failed to automatically restart the watchdog! Alpha: {0}; Bravo: {1}", result.Alpha.ToString(), result.Bravo.ToString()); + var retryDelay = Math.Min(Math.Pow(2, retryAttempts), 3600); //max of one hour + chatTask = chat.SendWatchdogMessage(String.Format(CultureInfo.InvariantCulture, "Failed to restart watchdog (Attempt: {0}), retrying in {1} seconds...", retryAttempts, retryDelay), cancellationToken); + await Task.WhenAll(Task.Delay((int)retryDelay, cancellationToken), chatTask).ConfigureAwait(false); + } + } + } + catch (OperationCanceledException) + { + logger.LogDebug("Monitor cancelled"); + break; + } + catch (Exception e) + { + logger.LogError("Monitor crashed! Iteration: {0}, State: {1}", iteration, JsonConvert.SerializeObject(monitorState)); + await chat.SendWatchdogMessage(String.Format(CultureInfo.InvariantCulture, "Monitor crashed, this should NEVER happen! Please report this, full details in logs! Restarting monitor... Error: {0}", e.Message), cancellationToken).ConfigureAwait(false); + } + } + } + + async Task StopMonitor() + { + logger.LogTrace("StopMonitor"); + if (monitorTask == null) + return false; + monitorCts.Cancel(); + await monitorTask.ConfigureAwait(false); + monitorCts.Dispose(); + monitorTask = null; + return true; + } + + + /// + public async Task ChangeSettings(DreamDaemonLaunchParameters launchParameters, CancellationToken cancellationToken) + { + using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false)) + { + ActiveLaunchParameters = launchParameters; + if (Running) + //queue an update + activeParametersUpdated.TrySetResult(null); + } + } + + async Task LaunchNoLock(bool startMonitor, bool announce, bool doReattach, CancellationToken cancellationToken) + { + using (var alphaStartCts = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken)) + { + logger.LogTrace("Begin LaunchNoLock"); + if (Running) + { + logger.LogTrace("Aborted due to already running!"); + return null; + } + + Task chatTask; + //this is necessary, the monitor could be in it's sleep loop trying to restart + if (startMonitor && await StopMonitor().ConfigureAwait(false)) + chatTask = chat.SendWatchdogMessage("Automatic retry sequence cancelled by manual launch. Restarting...", cancellationToken); + else if (announce) + chatTask = chat.SendWatchdogMessage("Starting...", cancellationToken); + else + chatTask = Task.CompletedTask; + //start both servers + LastLaunchParameters = ActiveLaunchParameters; + try + { + //good ole sanity + if (alphaServer != null || bravoServer != null) + throw new InvalidOperationException("Entered LaunchNoLock with one or more of the servers not being null!"); + + var reattachInfo = doReattach ? await reattachInfoHandler.Load(cancellationToken).ConfigureAwait(false) : null; + var doesntNeedNewDmb = doReattach && reattachInfo.Alpha != null && reattachInfo.Bravo != null; + var dmbToUse = doesntNeedNewDmb ? null : await dmbFactory.LockNextDmb(cancellationToken).ConfigureAwait(false); + + Task alphaServerTask = null; + try + { + if (!doReattach || reattachInfo.Alpha == null) + alphaServerTask = sessionControllerFactory.LaunchNew(ActiveLaunchParameters, dmbToUse, null, true, true, false, alphaStartCts.Token); + else + alphaServerTask = sessionControllerFactory.Reattach(reattachInfo.Alpha, cancellationToken); + //do a few seconds of delay so that any backends the servers use know that alpha came first + await Task.Delay(AlphaBravoStartupSeperationInterval, cancellationToken).ConfigureAwait(false); + Task bravoServerTask; + if (!doReattach || reattachInfo.Bravo == null) + bravoServerTask = sessionControllerFactory.LaunchNew(ActiveLaunchParameters, dmbToUse, null, false, false, false, cancellationToken); + else + bravoServerTask = sessionControllerFactory.Reattach(reattachInfo.Bravo, cancellationToken); + + await Task.WhenAll(alphaServerTask, bravoServerTask).ConfigureAwait(false); + + async Task CheckLaunch(ISessionController controller, string serverName) + { + var launch = await controller.LaunchResult.ConfigureAwait(false); + if (launch.ExitCode.HasValue) + //you killed us ray... + throw new Exception(String.Format(CultureInfo.InvariantCulture, "{1} server failed to start: {0}", launch.ToString(), serverName)); + return launch; + } + + var alphaLrt = CheckLaunch(alphaServer, "Alpha"); + var bravoLrt = CheckLaunch(bravoServer, "Bravo"); + //now we have two booting servers, get them up and running + var allTask = Task.WhenAll(alphaLrt, bravoLrt); + + //don't forget about the cancelationToken + var cancelTcs = new TaskCompletionSource(); + using (cancellationToken.Register(() => cancelTcs.SetCanceled())) + await Task.WhenAny(allTask, cancelTcs.Task).ConfigureAwait(false); + + //update the live and staged jobs in the db + await databaseContextFactory.UseContext(async db => + { + var settings = new Models.DreamDaemonSettings + { + InstanceId = instance.Id + }; + var cj = (AlphaIsActive ? alphaServer : bravoServer).Dmb.CompileJob; + db.CompileJobs.Attach(cj); + db.DreamDaemonSettings.Attach(settings); + settings.StagedCompileJob = null; + settings.ActiveCompileJob = cj; + await db.Save(cancellationToken).ConfigureAwait(false); + }).ConfigureAwait(false); + + //both servers are now running, alpha is the active server, huzzah + AlphaIsActive = doReattach ? reattachInfo.AlphaIsActive : true; + LastLaunchResult = alphaLrt.Result; + Running = true; + + if (startMonitor) + { + await StopMonitor().ConfigureAwait(false); + monitorCts = new CancellationTokenSource(); + monitorTask = MonitorLifetimes(monitorCts.Token); + } + return new WatchdogLaunchResult + { + Alpha = alphaLrt.Result, + Bravo = bravoLrt.Result + }; + } + catch + { + if (alphaServer == null && bravoServer == null) + dmbToUse.Dispose(); //guaranteed to not be null here + DisposeAndNullControllers(); + throw; + } + } + catch (Exception e) + { + logger.LogWarning("Failed to start watchdog: {0}", e.ToString()); + throw; + } + finally + { + try + { + await chatTask.ConfigureAwait(false); + } + catch (OperationCanceledException) { } + } + } + } + + /// + public async Task Launch(CancellationToken cancellationToken) + { + using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false)) + return await LaunchNoLock(true, true, false, cancellationToken).ConfigureAwait(false); + } + + /// + public async Task Restart(bool graceful, CancellationToken cancellationToken) + { + using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false)) + return await RestartNoLock(graceful, cancellationToken).ConfigureAwait(false); + } + + /// + public async Task Terminate(bool graceful, CancellationToken cancellationToken) + { + using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false)) + await TerminateNoLock(graceful, true, cancellationToken).ConfigureAwait(false); + } + + /// + public async Task StartAsync(CancellationToken cancellationToken) + { + if (autoStart) + await LaunchNoLock(true, true, true, cancellationToken).ConfigureAwait(false); + } + + /// + public async Task StopAsync(CancellationToken cancellationToken) + { + if (releaseServers && Running) + { + var reattachInformation = new WatchdogReattachInformation + { + AlphaIsActive = AlphaIsActive + }; + reattachInformation.Alpha = alphaServer?.Release(); + reattachInformation.Bravo = bravoServer?.Release(); + await reattachInfoHandler.Save(reattachInformation, cancellationToken).ConfigureAwait(false); + } + await Terminate(false, cancellationToken).ConfigureAwait(false); + } + + /// + public async Task HandleEvent(EventType eventType, IEnumerable parameters, CancellationToken cancellationToken) + { + string results; + using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false)) + { + if (!Running) + return true; + + var builder = new StringBuilder(InteropConstants.DMTopicEvent); + foreach (var I in parameters) + { + builder.Append("&"); + builder.Append(byondTopicSender.SanitizeString(I)); + } + + var activeServer = AlphaIsActive ? alphaServer : bravoServer; + results = await activeServer.SendCommand(builder.ToString(), cancellationToken).ConfigureAwait(false); + } + + if (results == null) + return true; + + List responses; + try + { + responses = JsonConvert.DeserializeObject>(results); + } + catch + { + logger.LogInformation("Recieved invalid response from DD when parsing event {0}:{1}{2}", eventType, Environment.NewLine, results); + return true; + } + + await Task.WhenAll(responses.Select(x => chat.SendMessage(x.Message, x.ChannelIds, cancellationToken))).ConfigureAwait(false); + + return true; + } + + /// + public async Task HandleChatCommand(string commandName, string arguments, Chat.User sender, CancellationToken cancellationToken) + { + using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false)) + { + if (!Running) + return "ERROR: Server offline!"; + + var command = String.Format(CultureInfo.InvariantCulture, "{0}&{1}={2}", byondTopicSender.SanitizeString(InteropConstants.DMTopicChatCommand), byondTopicSender.SanitizeString(InteropConstants.DMParameterData), byondTopicSender.SanitizeString(JsonConvert.SerializeObject(arguments))); + + var activeServer = AlphaIsActive ? alphaServer : bravoServer; + return await activeServer.SendCommand(command, cancellationToken).ConfigureAwait(false) ?? "ERROR: Bad topic exchange!"; + } + } + } +} diff --git a/src/Tgstation.Server.Host/Components/Watchdog/WatchdogFactory.cs b/src/Tgstation.Server.Host/Components/Watchdog/WatchdogFactory.cs new file mode 100644 index 0000000000..65b0452042 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Watchdog/WatchdogFactory.cs @@ -0,0 +1,88 @@ +using Byond.TopicSender; +using Microsoft.Extensions.Logging; +using System; +using Tgstation.Server.Api.Models.Internal; +using Tgstation.Server.Host.Components.Chat; +using Tgstation.Server.Host.Components.Compiler; +using Tgstation.Server.Host.Core; + +namespace Tgstation.Server.Host.Components.Watchdog +{ + /// + sealed class WatchdogFactory : IWatchdogFactory + { + /// + /// The for the + /// + readonly IChat chat; + + /// + /// The for the + /// + readonly ISessionControllerFactory sessionControllerFactory; + + /// + /// The for the + /// + readonly IServerControl serverUpdater; + + /// + /// The for the + /// + readonly ILoggerFactory loggerFactory; + + /// + /// The for the + /// + readonly IReattachInfoHandler reattachInfoHandler; + + /// + /// The for the + /// + readonly IDatabaseContextFactory databaseContextFactory; + + /// + /// The for the + /// + readonly IByondTopicSender byondTopicSender; + + /// + /// The for the + /// + readonly IEventConsumer eventConsumer; + + /// + /// The for the + /// + readonly Api.Models.Instance instance; + + + /// + /// Construct a + /// + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of + public WatchdogFactory(IChat chat, ISessionControllerFactory sessionControllerFactory, IServerControl serverUpdater, ILoggerFactory loggerFactory, IReattachInfoHandler reattachInfoHandler, IDatabaseContextFactory databaseContextFactory, IByondTopicSender byondTopicSender, IEventConsumer eventConsumer, Api.Models.Instance instance) + { + this.chat = chat ?? throw new ArgumentNullException(nameof(chat)); + this.sessionControllerFactory = sessionControllerFactory ?? throw new ArgumentNullException(nameof(sessionControllerFactory)); + this.serverUpdater = serverUpdater ?? throw new ArgumentNullException(nameof(serverUpdater)); + this.loggerFactory = loggerFactory ?? throw new ArgumentNullException(nameof(loggerFactory)); + this.reattachInfoHandler = reattachInfoHandler ?? throw new ArgumentNullException(nameof(reattachInfoHandler)); + this.databaseContextFactory = databaseContextFactory ?? throw new ArgumentNullException(nameof(databaseContextFactory)); + this.byondTopicSender = byondTopicSender ?? throw new ArgumentNullException(nameof(byondTopicSender)); + this.eventConsumer = eventConsumer ?? throw new ArgumentNullException(nameof(eventConsumer)); + this.instance = instance ?? throw new ArgumentNullException(nameof(instance)); + } + + /// + public IWatchdog CreateWatchdog(IDmbFactory dmbFactory, DreamDaemonSettings settings) => new Watchdog(chat, sessionControllerFactory, dmbFactory, serverUpdater, loggerFactory.CreateLogger(), reattachInfoHandler, databaseContextFactory, byondTopicSender, eventConsumer, settings, instance, settings.AutoStart.Value); + } +} diff --git a/src/Tgstation.Server.Host/Components/Watchdog/WatchdogLaunchResult.cs b/src/Tgstation.Server.Host/Components/Watchdog/WatchdogLaunchResult.cs new file mode 100644 index 0000000000..0532326e78 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Watchdog/WatchdogLaunchResult.cs @@ -0,0 +1,20 @@ +using System; + +namespace Tgstation.Server.Host.Components.Watchdog +{ + /// + /// Launch results for a + /// + public sealed class WatchdogLaunchResult + { + /// + /// The for the alpha process + /// + public LaunchResult Alpha { get; set; } + + /// + /// The for the bravo process + /// + public LaunchResult Bravo { get; set; } + } +} diff --git a/src/Tgstation.Server.Host/Components/Watchdog/WatchdogReattachInformation.cs b/src/Tgstation.Server.Host/Components/Watchdog/WatchdogReattachInformation.cs new file mode 100644 index 0000000000..efe645c044 --- /dev/null +++ b/src/Tgstation.Server.Host/Components/Watchdog/WatchdogReattachInformation.cs @@ -0,0 +1,39 @@ +using Tgstation.Server.Host.Components.Compiler; +using Tgstation.Server.Host.Models; + +namespace Tgstation.Server.Host.Components.Watchdog +{ + /// + /// Reattach information for a + /// + public sealed class WatchdogReattachInformation : WatchdogReattachInformationBase + { + /// + /// for the Alpha session + /// + public ReattachInformation Alpha { get; set; } + + /// + /// for the Bravo session + /// + public ReattachInformation Bravo { get; set; } + + /// + /// Construct a + /// + public WatchdogReattachInformation() { } + + /// + /// Construct a from a given with a given + /// + /// The to copy information from + /// The used to build the s + public WatchdogReattachInformation(Models.WatchdogReattachInformation copy, IDmbFactory dmbFactory): base(copy) + { + if (copy.Alpha != null) + Alpha = new ReattachInformation(copy.Alpha, dmbFactory); + if (copy.Bravo != null) + Bravo = new ReattachInformation(copy.Bravo, dmbFactory); + } + } +} diff --git a/src/Tgstation.Server.Host/Configuration/DatabaseConfiguration.cs b/src/Tgstation.Server.Host/Configuration/DatabaseConfiguration.cs new file mode 100644 index 0000000000..a75d463a48 --- /dev/null +++ b/src/Tgstation.Server.Host/Configuration/DatabaseConfiguration.cs @@ -0,0 +1,28 @@ +namespace Tgstation.Server.Host.Configuration +{ + /// + /// Configuration options for the + /// + sealed class DatabaseConfiguration + { + /// + /// The key for the the resides in + /// + public const string Section = "Database"; + + /// + /// The to create + /// + public DatabaseType DatabaseType { get; set; } + + /// + /// If the admin user should be enabled and have it's password reset + /// + public bool ResetAdminPassword { get; set; } + + /// + /// The connection string for the database + /// + public string ConnectionString { get; set; } + } +} diff --git a/src/Tgstation.Server.Host/Configuration/DatabaseType.cs b/src/Tgstation.Server.Host/Configuration/DatabaseType.cs new file mode 100644 index 0000000000..bbc8be7798 --- /dev/null +++ b/src/Tgstation.Server.Host/Configuration/DatabaseType.cs @@ -0,0 +1,21 @@ +namespace Tgstation.Server.Host.Configuration +{ + /// + /// Type of database to user + /// + enum DatabaseType + { + /// + /// Use Microsoft SQL Server + /// + SqlServer, + /// + /// Use MySQL/MariaDB + /// + MySql, + /// + /// Use SQLite 3 + /// + Sqlite + } +} diff --git a/src/Tgstation.Server.Host/Configuration/GeneralConfiguration.cs b/src/Tgstation.Server.Host/Configuration/GeneralConfiguration.cs new file mode 100644 index 0000000000..f789eb4787 --- /dev/null +++ b/src/Tgstation.Server.Host/Configuration/GeneralConfiguration.cs @@ -0,0 +1,23 @@ +namespace Tgstation.Server.Host.Configuration +{ + /// + /// General configuration options + /// + sealed class GeneralConfiguration + { + /// + /// The key for the the resides in + /// + public const string Section = "General"; + + /// + /// Where log files are stored + /// + public string LogFileDirectory { get; set; } + + /// + /// If file logging is disabled + /// + public bool DisableFileLogging { get; set; } + } +} diff --git a/src/Tgstation.Server.Host/Configuration/UpdatesConfiguration.cs b/src/Tgstation.Server.Host/Configuration/UpdatesConfiguration.cs new file mode 100644 index 0000000000..4d03a251cb --- /dev/null +++ b/src/Tgstation.Server.Host/Configuration/UpdatesConfiguration.cs @@ -0,0 +1,28 @@ +namespace Tgstation.Server.Host.Configuration +{ + /// + /// Configuration for the automatic update system + /// + public sealed class UpdatesConfiguration + { + /// + /// The key for the the resides in + /// + public const string Section = "Updates"; + + /// + /// The of the tgstation-server fork to recieve updates from + /// + public long GitHubRepositoryId { get; set; } + + /// + /// Prefix before the of TGS published in git tags + /// + public string GitTagPrefix { get; set; } + + /// + /// Asset package containing the new assembly in zip form + /// + public string UpdatePackageAssetName { get; set; } + } +} diff --git a/src/Tgstation.Server.Host/Controllers/AdministrationController.cs b/src/Tgstation.Server.Host/Controllers/AdministrationController.cs new file mode 100644 index 0000000000..9b977a548a --- /dev/null +++ b/src/Tgstation.Server.Host/Controllers/AdministrationController.cs @@ -0,0 +1,164 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using Octokit; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Rights; +using Tgstation.Server.Host.Configuration; +using Tgstation.Server.Host.Core; +using Tgstation.Server.Host.IO; +using Tgstation.Server.Host.Models; +using Tgstation.Server.Host.Security; + +namespace Tgstation.Server.Host.Controllers +{ + /// + /// for + /// + [Route(Api.Routes.Administration.Base)] + public sealed class AdministrationController : ModelController + { + /// + /// HTTP 429 status code + /// + const int RateLimitHttpStatusCode = 429; + + /// + /// The for the + /// + readonly IGitHubClient gitHubClient; + + /// + /// The for the + /// + readonly IServerControl serverUpdater; + + /// + /// The for the + /// + readonly IApplication application; + + /// + /// The for the + /// + readonly IIOManager ioManager; + + /// + /// The for the + /// + readonly UpdatesConfiguration updatesConfiguration; + + /// + /// Construct an + /// + /// The for the + /// The for the + /// The value of + /// The value of + /// The value of + /// The value of + /// The for the + /// The containing value of + public AdministrationController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IGitHubClient gitHubClient, IServerControl serverUpdater, IApplication application, IIOManager ioManager, ILogger logger, IOptions updatesConfigurationOptions) : base(databaseContext, authenticationContextFactory, logger, false) + { + this.gitHubClient = gitHubClient ?? throw new ArgumentNullException(nameof(gitHubClient)); + this.serverUpdater = serverUpdater ?? throw new ArgumentNullException(nameof(serverUpdater)); + this.application = application ?? throw new ArgumentNullException(nameof(application)); + this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager)); + updatesConfiguration = updatesConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(updatesConfigurationOptions)); + } + + StatusCodeResult RateLimit(RateLimitExceededException exception) + { + Logger.LogWarning("Exceeded GitHub rate limit!"); + var secondsString = Math.Ceiling((exception.Reset - DateTimeOffset.Now).TotalSeconds).ToString(CultureInfo.InvariantCulture); + Response.Headers.Add("Retry-After", new Microsoft.Extensions.Primitives.StringValues { }); + return StatusCode(RateLimitHttpStatusCode); + } + + /// + [TgsAuthorize] + public override async Task Read(CancellationToken cancellationToken) + { + var model = new Administration + { + CurrentVersion = application.Version + }; + try + { + var repositoryTask = gitHubClient.Repository.Get(updatesConfiguration.GitHubRepositoryId); + var releases = (await gitHubClient.Repository.Release.GetAll(updatesConfiguration.GitHubRepositoryId).ConfigureAwait(false)).Where(x => x.TagName.StartsWith(updatesConfiguration.GitTagPrefix, StringComparison.InvariantCulture)); + + Version greatestVersion = null; + foreach (var I in releases) + if (Version.TryParse(I.TagName.Replace(updatesConfiguration.GitTagPrefix, String.Empty, StringComparison.Ordinal), out var version) + && version.Major == application.Version.Major + && (greatestVersion == null || version > greatestVersion)) + greatestVersion = version; + + model.LatestVersion = greatestVersion; + model.TrackedRepositoryUrl = new Uri((await repositoryTask.ConfigureAwait(false)).HtmlUrl); + } + catch (RateLimitExceededException e) + { + return RateLimit(e); + } + return Json(model); + } + + /// + [TgsAuthorize(AdministrationRights.ChangeVersion)] + public override async Task Update([FromBody] Administration model, CancellationToken cancellationToken) + { + if (model == null) + throw new ArgumentNullException(nameof(model)); + + if (model.CurrentVersion == null) + return BadRequest(new { message = "Missing new version!" }); + + if (model.CurrentVersion.Major != application.Version.Major) + return BadRequest(new { message = "Cannot update to a different suite version!" }); + + IEnumerable releases; + try + { + releases = (await gitHubClient.Repository.Release.GetAll(updatesConfiguration.GitHubRepositoryId).ConfigureAwait(false)).Where(x => x.TagName.StartsWith(updatesConfiguration.GitTagPrefix, StringComparison.InvariantCulture)); + } + catch (RateLimitExceededException e) + { + return RateLimit(e); + } + + foreach (var release in releases) + if (Version.TryParse(release.TagName.Replace(updatesConfiguration.GitTagPrefix, String.Empty, StringComparison.Ordinal), out var version) && version == model.CurrentVersion) + { + var asset = release.Assets.Where(x => x.Name == updatesConfiguration.UpdatePackageAssetName).FirstOrDefault(); + if (asset == default) + continue; + + var assetBytes = await ioManager.DownloadFile(new Uri(asset.BrowserDownloadUrl), cancellationToken).ConfigureAwait(false); + try + { + if (!await serverUpdater.ApplyUpdate(assetBytes, ioManager, cancellationToken).ConfigureAwait(false)) + return StatusCode((int)HttpStatusCode.NotImplemented); + } + catch (InvalidOperationException) { } //we were beat to the punch + return Ok(); //gtfo of here before all the cancellation tokens fire + } + + return StatusCode((int)HttpStatusCode.Gone); + } + + /// + [HttpDelete] + [TgsAuthorize(AdministrationRights.RestartHost)] + public Task Delete() => Task.FromResult(serverUpdater.Restart() ? (IActionResult)Ok() : StatusCode((int)HttpStatusCode.NotImplemented)); + } +} diff --git a/src/Tgstation.Server.Host/Controllers/ApiController.cs b/src/Tgstation.Server.Host/Controllers/ApiController.cs new file mode 100644 index 0000000000..c28a6620c0 --- /dev/null +++ b/src/Tgstation.Server.Host/Controllers/ApiController.cs @@ -0,0 +1,165 @@ +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Http; +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IdentityModel.Tokens.Jwt; +using System.Security.Claims; +using System.Threading.Tasks; +using Tgstation.Server.Api; +using Tgstation.Server.Api.Rights; +using Tgstation.Server.Host.Models; +using Tgstation.Server.Host.Security; + +namespace Tgstation.Server.Host.Controllers +{ + /// + /// A for API functions + /// + [Produces(ApiHeaders.ApplicationJson)] + [Consumes(ApiHeaders.ApplicationJson)] + public abstract class ApiController : Controller + { + /// + /// The for the operation + /// + protected ApiHeaders ApiHeaders { get; private set; } + + /// + /// The for the operation + /// + protected IDatabaseContext DatabaseContext { get; } + + /// + /// The for the operation + /// + protected IAuthenticationContext AuthenticationContext { get; } + + /// + /// The for the + /// + protected ILogger Logger { get; } + + /// + /// The for the operation + /// + protected Instance Instance { get; } + + /// + /// If permissions are required to access the + /// + readonly bool requireInstance; + + /// + /// Runs after a has been validated. Creates the for the + /// + /// The for the operation + /// A representing the running operation + public static async Task OnTokenValidated(TokenValidatedContext context) + { + var databaseContext = context.HttpContext.RequestServices.GetRequiredService(); + var authenticationContextFactory = context.HttpContext.RequestServices.GetRequiredService(); + + var userIdClaim = context.Principal.FindFirst(JwtRegisteredClaimNames.Sub); + + if (userIdClaim == default(Claim)) + throw new InvalidOperationException("Missing required claim!"); + + long userId; + try + { + userId = Int64.Parse(userIdClaim.Value, CultureInfo.InvariantCulture); + } + catch (Exception e) + { + throw new InvalidOperationException("Failed to parse user ID!", e); + } + + ApiHeaders apiHeaders; + try + { + apiHeaders = new ApiHeaders(context.HttpContext.Request.GetTypedHeaders()); + } + catch + { + //let OnActionExecutionAsync handle the reponse + return; + } + + await authenticationContextFactory.CreateAuthenticationContext(userId, apiHeaders.InstanceId, context.HttpContext.RequestAborted).ConfigureAwait(false); + + var authenticationContext = authenticationContextFactory.CurrentAuthenticationContext; + + var enumerator = Enum.GetValues(typeof(RightsType)); + var claims = new List(); + foreach (RightsType I in enumerator) + { + //if there's no instance user, do a weird thing and add all the instance roles + //we need it so we can get to OnActionExecutionAsync where we can properly decide between BadRequest and Forbid + var rightInt = RightsHelper.IsInstanceRight(I) && authenticationContext.InstanceUser == null ? ~0 : authenticationContext.GetRight(I); + var rightEnum = RightsHelper.RightToType(I); + var right = (Enum)Enum.ToObject(rightEnum, rightInt); + foreach(Enum J in Enum.GetValues(rightEnum)) + if(right.HasFlag(J)) + claims.Add(new Claim(ClaimTypes.Role, RightsHelper.RoleName(I, J))); + } + + context.Principal.AddIdentity(new ClaimsIdentity(claims)); + } + + /// + /// Construct an + /// + /// The value of + /// The for the + /// The value of + /// The value of + public ApiController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, ILogger logger, bool requireInstance) + { + DatabaseContext = databaseContext ?? throw new ArgumentNullException(nameof(databaseContext)); + if (authenticationContextFactory == null) + throw new ArgumentNullException(nameof(authenticationContextFactory)); + Logger = logger ?? throw new ArgumentNullException(nameof(logger)); + AuthenticationContext = authenticationContextFactory.CurrentAuthenticationContext; + Instance = AuthenticationContext?.InstanceUser?.Instance; + this.requireInstance = requireInstance; + } + + /// + public override async Task OnActionExecutionAsync(ActionExecutingContext context, ActionExecutionDelegate next) + { + //validate the headers + try + { + ApiHeaders = new ApiHeaders(Request.GetTypedHeaders()); + + if (requireInstance) + { + if(!ApiHeaders.InstanceId.HasValue) + { + await BadRequest(new { message = "Missing InstanceId header!" }).ExecuteResultAsync(context).ConfigureAwait(false); + return; + } + if (AuthenticationContext.InstanceUser == null) + { + //accessing an instance they don't have access to or one that's disabled + await Forbid().ExecuteResultAsync(context).ConfigureAwait(false); + return; + } + } + } + catch (InvalidOperationException e) + { + await BadRequest(new { message = e.Message }).ExecuteResultAsync(context).ConfigureAwait(false); + return; + } + + Logger.LogInformation("Request made by User ID {0}. Api version: {1}. User-Agent: {2}", AuthenticationContext.User?.Id.ToString(CultureInfo.InvariantCulture) ?? "NULL", ApiHeaders.ApiVersion, ApiHeaders.UserAgent); + await base.OnActionExecutionAsync(context, next).ConfigureAwait(false); + } + } +} diff --git a/src/Tgstation.Server.Host/Controllers/ByondController.cs b/src/Tgstation.Server.Host/Controllers/ByondController.cs new file mode 100644 index 0000000000..2cb13a0652 --- /dev/null +++ b/src/Tgstation.Server.Host/Controllers/ByondController.cs @@ -0,0 +1,104 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using System; +using System.Globalization; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Rights; +using Tgstation.Server.Host.Components; +using Tgstation.Server.Host.Core; +using Tgstation.Server.Host.Models; +using Tgstation.Server.Host.Security; + +namespace Tgstation.Server.Host.Controllers +{ + /// + /// Controller for managing s + /// + [Route("/" + nameof(Byond))] + public sealed class ByondController : ModelController + { + /// + /// The for the + /// + readonly IInstanceManager instanceManager; + + /// + /// The for the + /// + readonly IJobManager jobManager; + + /// + /// Construct a + /// + /// The for the + /// The for the + /// The value of + /// The value of + /// The for the + public ByondController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IInstanceManager instanceManager, IJobManager jobManager, ILogger logger) : base(databaseContext, authenticationContextFactory, logger, true) + { + this.instanceManager = instanceManager ?? throw new ArgumentNullException(nameof(instanceManager)); + this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager)); + } + + /// + [TgsAuthorize(ByondRights.ReadActive)] + public override Task Read(CancellationToken cancellationToken) => Task.FromResult((IActionResult) + Json(new Api.Models.Byond + { + Version = instanceManager.GetInstance(Instance).ByondManager.ActiveVersion + })); + + /// + [TgsAuthorize(ByondRights.ListInstalled)] + public override Task List(CancellationToken cancellationToken) => Task.FromResult((IActionResult) + Json(instanceManager.GetInstance(Instance).ByondManager.InstalledVersions.Select(x => new Api.Models.Byond + { + Version = x + }))); + + /// + [TgsAuthorize(ByondRights.ChangeVersion)] + public override async Task Update([FromBody] Api.Models.Byond model, CancellationToken cancellationToken) + { + if (model == null) + return BadRequest(new { message = "Missing request model!" }); + + if(model.Version == null) + return BadRequest(new { message = "Missing version!" }); + + var byondManager = instanceManager.GetInstance(Instance).ByondManager; + + //remove cruff fields + var installingVersion = new Version(model.Version.Major, model.Version.Minor); + + var result = new Api.Models.Byond(); + + if (byondManager.InstalledVersions.Any(x => x == model.Version)) + { + Logger.LogInformation("User ID {0} changing instance ID {1} BYOND version to {2}", AuthenticationContext.User.Id, Instance.Id, installingVersion); + await byondManager.ChangeVersion(model.Version, cancellationToken).ConfigureAwait(false); + } + else + { + Logger.LogInformation("User ID {0} installing BYOND version to {2} on instance ID {1}", AuthenticationContext.User.Id, Instance.Id, installingVersion); + //run the install through the job manager + var job = new Models.Job + { + Description = String.Format(CultureInfo.InvariantCulture, "Install BYOND version {0}", installingVersion), + StartedBy = AuthenticationContext.User, + CancelRightsType = RightsType.Byond, + CancelRight = (int)ByondRights.CancelInstall, + Instance = Instance + }; + await jobManager.RegisterOperation(job, (paramJob, serviceProvicer, progressHandler, ct) => byondManager.ChangeVersion(installingVersion, ct), cancellationToken).ConfigureAwait(false); + result.InstallJob = job.ToApi(); + } + result.Version = byondManager.ActiveVersion; + return Json(result); + } + } +} diff --git a/src/Tgstation.Server.Host/Controllers/ChatController.cs b/src/Tgstation.Server.Host/Controllers/ChatController.cs new file mode 100644 index 0000000000..d6f66523c8 --- /dev/null +++ b/src/Tgstation.Server.Host/Controllers/ChatController.cs @@ -0,0 +1,237 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Linq.Expressions; +using System.Net; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Rights; +using Tgstation.Server.Host.Components; +using Tgstation.Server.Host.Models; +using Tgstation.Server.Host.Security; +using Z.EntityFramework.Plus; + +namespace Tgstation.Server.Host.Controllers +{ + /// + /// for managing + /// + [Route("/" + nameof(Components.Chat.Chat))] + public sealed class ChatController : ModelController + { + /// + /// The for the + /// + readonly IInstanceManager instanceManager; + + /// + /// Construct a + /// + /// The for the + /// The for the + /// The value of + /// The for the + public ChatController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IInstanceManager instanceManager, ILogger logger) : base(databaseContext, authenticationContextFactory, logger, true) + { + this.instanceManager = instanceManager ?? throw new ArgumentNullException(nameof(instanceManager)); + } + + /// + /// Converts to a + /// + /// The + /// A based on + static Models.ChatChannel ConvertApiChatChannel(Api.Models.ChatChannel api) => new Models.ChatChannel + { + DiscordChannelId = api.DiscordChannelId, + IrcChannel = api.IrcChannel, + IsAdminChannel = api.IsAdminChannel, + IsWatchdogChannel = api.IsWatchdogChannel + }; + + /// + [TgsAuthorize(ChatSettingsRights.Create)] + public override async Task Create([FromBody] Api.Models.ChatSettings model, CancellationToken cancellationToken) + { + if (model == null) + throw new ArgumentNullException(nameof(model)); + + if (String.IsNullOrWhiteSpace(model.Name)) + return BadRequest(new { message = "name cannot be null or whitespace!" }); + + if (String.IsNullOrWhiteSpace(model.ConnectionString)) + return BadRequest(new { message = "connection_string cannot be null or whitespace!" }); + + if (!model.Provider.HasValue) + return BadRequest(new { message = "provider cannot be null!" }); + + if (!model.Enabled.HasValue) + return BadRequest(new { message = "enabled cannot be null!" }); + + //try to update das db first + var dbModel = new Models.ChatSettings + { + Name = model.Name, + ConnectionString = model.ConnectionString, + Enabled = model.Enabled, + Channels = model.Channels?.Select(x => ConvertApiChatChannel(x)).ToList() ?? new List(), //important that this isn't null + InstanceId = Instance.Id, + Provider = model.Provider, + }; + DatabaseContext.ChatSettings.Add(dbModel); + + try + { + await DatabaseContext.Save(cancellationToken).ConfigureAwait(false); + } + catch (DbUpdateConcurrencyException) + { + return Conflict(); + } + + try + { + try + { + //try to create it + var instance = instanceManager.GetInstance(Instance); + await instance.Chat.ChangeSettings(dbModel, cancellationToken).ConfigureAwait(false); + + if (dbModel.Channels.Count > 0) + await instance.Chat.ChangeChannels(dbModel.Id, dbModel.Channels, cancellationToken).ConfigureAwait(false); + } + catch + { + //undo the add + DatabaseContext.ChatSettings.Remove(dbModel); + await DatabaseContext.Save(default).ConfigureAwait(false); + throw; + } + } + catch (InvalidOperationException e) + { + return BadRequest(new { message = e.Message }); + } + return Json(dbModel.ToApi()); + } + + /// + [TgsAuthorize(ChatSettingsRights.Delete)] + public override async Task Delete(long id, CancellationToken cancellationToken) + { + var instance = instanceManager.GetInstance(Instance); + await Task.WhenAll(instance.Chat.DeleteConnection(id, cancellationToken), DatabaseContext.ChatSettings.Where(x => x.Id == id).DeleteAsync(cancellationToken)).ConfigureAwait(false); + + return Ok(); + } + + /// + [TgsAuthorize(ChatSettingsRights.Read)] + public override async Task List(CancellationToken cancellationToken) + { + var query = DatabaseContext.ChatSettings.Where(x => x.InstanceId == Instance.Id).Include(x => x.Channels); + + var results = await query.ToListAsync(cancellationToken).ConfigureAwait(false); + + var connectionStrings = (AuthenticationContext.GetRight(RightsType.ChatSettings) & (int)ChatSettingsRights.ReadConnectionString) != 0; + + if (!connectionStrings) + foreach (var I in results) + I.ConnectionString = null; + + return Json(results.Select(x => x.ToApi())); + } + + /// + [TgsAuthorize(ChatSettingsRights.Read)] + public override async Task GetId(long id, CancellationToken cancellationToken) + { + var query = DatabaseContext.ChatSettings.Where(x => x.Id == id).Include(x => x.Channels); + + var results = await query.FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); + if (results == default) + return NotFound(); + + var connectionStrings = (AuthenticationContext.GetRight(RightsType.ChatSettings) & (int)ChatSettingsRights.ReadConnectionString) != 0; + + if (!connectionStrings) + results.ConnectionString = null; + + return Json(results.ToApi()); + } + + /// + [TgsAuthorize(ChatSettingsRights.WriteChannels | ChatSettingsRights.WriteConnectionString | ChatSettingsRights.WriteEnabled | ChatSettingsRights.WriteName | ChatSettingsRights.WriteProvider)] + public override async Task Update([FromBody] Api.Models.ChatSettings model, CancellationToken cancellationToken) + { + if (model == null) + throw new ArgumentNullException(nameof(model)); + + var query = DatabaseContext.ChatSettings.Where(x => x.InstanceId == Instance.Id && x.Id == model.Id).Include(x => x.Channels); + + var current = await query.FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); + + if (current == default) + return StatusCode((int)HttpStatusCode.Gone); + + var userRights = (ChatSettingsRights)AuthenticationContext.GetRight(RightsType.ChatSettings); + + bool anySettingsModified = false; + + bool CheckModified(Expression> expression, ChatSettingsRights requiredRight) + { + var memberSelectorExpression = (MemberExpression)expression.Body; + var property = (PropertyInfo)memberSelectorExpression.Member; + + var newVal = property.GetValue(model); + if (newVal == null) + return false; + if (!userRights.HasFlag(requiredRight) && property.GetValue(current) != newVal) + return true; + + property.SetValue(current, newVal); + anySettingsModified = true; + return false; + }; + + if (CheckModified(x => x.ConnectionString, ChatSettingsRights.WriteConnectionString) + || CheckModified(x => x.Enabled, ChatSettingsRights.WriteEnabled) + || CheckModified(x => x.Name, ChatSettingsRights.WriteName) + || CheckModified(x => x.Provider, ChatSettingsRights.WriteProvider) + || (model.Channels != null && !userRights.HasFlag(ChatSettingsRights.WriteChannels))) + return Forbid(); + + if (model.Channels != null) + { + DatabaseContext.ChatChannels.RemoveRange(current.Channels); + var dbChannels = model.Channels.Select(x => ConvertApiChatChannel(x)).ToList(); + DatabaseContext.ChatChannels.AddRange(dbChannels); + current.Channels = dbChannels; + } + + await DatabaseContext.Save(cancellationToken).ConfigureAwait(false); + + var chat = instanceManager.GetInstance(Instance).Chat; + + if (anySettingsModified) + //have to rebuild the thing first + await chat.ChangeSettings(current, cancellationToken).ConfigureAwait(false); + + if (model.Channels != null || anySettingsModified) + await chat.ChangeChannels(current.Id, current.Channels, cancellationToken).ConfigureAwait(false); + + if (userRights.HasFlag(ChatSettingsRights.Read)) + { + if (!userRights.HasFlag(ChatSettingsRights.ReadConnectionString)) + current.ConnectionString = null; + return Json(current.ToApi()); + } + return Ok(); + } + } +} diff --git a/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs b/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs new file mode 100644 index 0000000000..04b226a30a --- /dev/null +++ b/src/Tgstation.Server.Host/Controllers/ConfigurationController.cs @@ -0,0 +1,126 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using System; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Rights; +using Tgstation.Server.Host.Components; +using Tgstation.Server.Host.Models; +using Tgstation.Server.Host.Security; + +namespace Tgstation.Server.Host.Controllers +{ + /// + /// The for s + /// + [Route("/" + nameof(Configuration))] + public sealed class ConfigurationController : ModelController + { + /// + /// The for the + /// + readonly IInstanceManager instanceManager; + + /// + /// Construct a + /// + /// The for the + /// The for the + /// The value of + /// The for the + public ConfigurationController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IInstanceManager instanceManager, ILogger logger) : base(databaseContext, authenticationContextFactory, logger, true) + { + this.instanceManager = instanceManager ?? throw new ArgumentNullException(nameof(instanceManager)); + } + + /// + /// If a should be returned from actions due to conflicts with one or both of the or the + /// + /// if a should be returned, otherwise + bool ForbidDueToModeConflicts() => Instance.ConfigurationType == ConfigurationType.Disallowed || (Instance.ConfigurationType == ConfigurationType.SystemIdentityWrite && AuthenticationContext.SystemIdentity == null); + + /// + [TgsAuthorize(ConfigurationRights.Write)] + public override async Task Update([FromBody] ConfigurationFile model, CancellationToken cancellationToken) + { + if (ForbidDueToModeConflicts()) + return Forbid(); + + var config = instanceManager.GetInstance(Instance).Configuration; + try + { + var newFile = await config.Write(model.Path, AuthenticationContext.SystemIdentity, model.Content, model.LastReadHash, cancellationToken).ConfigureAwait(false); + if (newFile == null) + return Conflict(); + + newFile.Content = null; + + return Json(newFile); + } + catch(NotImplementedException e) + { + return StatusCode((int)HttpStatusCode.NotImplemented, new { message = e.Message }); + } + } + + /// + /// Get the contents of a file at a + /// + /// The path of the file to get + /// The for the operation + /// A resulting in the for the operation + [HttpGet("/File/{path}")] + [TgsAuthorize(ConfigurationRights.Read)] + public async Task File(string path, CancellationToken cancellationToken) + { + if (ForbidDueToModeConflicts()) + return Forbid(); + + try + { + var result = await instanceManager.GetInstance(Instance).Configuration.Read(path, AuthenticationContext.SystemIdentity, cancellationToken).ConfigureAwait(false); + if (result == null || result.IsDirectory.Value) + return NotFound(); + + return Json(result); + } + catch (NotImplementedException e) + { + return StatusCode((int)HttpStatusCode.NotImplemented, new { message = e.Message }); + } + } + + /// + /// Get the contents of a directory at a + /// + /// The path of the directory to get + /// The for the operation + /// A resulting in the for the operation + [HttpGet("/List/{path}")] + [TgsAuthorize(ConfigurationRights.List)] + public async Task Directory(string path, CancellationToken cancellationToken) + { + if (ForbidDueToModeConflicts()) + return Forbid(); + + try + { + var result = await instanceManager.GetInstance(Instance).Configuration.ListDirectory(path, AuthenticationContext.SystemIdentity, cancellationToken).ConfigureAwait(false); + if (result == null) + return NotFound(); + + return Json(result); + } + catch (NotImplementedException e) + { + return StatusCode((int)HttpStatusCode.NotImplemented, new { message = e.Message }); + } + catch (UnauthorizedAccessException) + { + return Forbid(); + } + } + } +} diff --git a/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs b/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs new file mode 100644 index 0000000000..f91384a46f --- /dev/null +++ b/src/Tgstation.Server.Host/Controllers/DreamDaemonController.cs @@ -0,0 +1,188 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using System; +using System.Linq; +using System.Linq.Expressions; +using System.Net; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Rights; +using Tgstation.Server.Host.Components; +using Tgstation.Server.Host.Components.Watchdog; +using Tgstation.Server.Host.Core; +using Tgstation.Server.Host.Models; +using Tgstation.Server.Host.Security; + +namespace Tgstation.Server.Host.Controllers +{ + /// + /// for managing the + /// + [Route("/" + nameof(DreamDaemon))] + public sealed class DreamDaemonController : ModelController + { + /// + /// The for the + /// + readonly IJobManager jobManager; + /// + /// The for the + /// + readonly IInstanceManager instanceManager; + + /// + /// Construct a + /// + /// The for the + /// The for the + /// The value of + /// The value of + /// The for the + public DreamDaemonController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IJobManager jobManager, IInstanceManager instanceManager, ILogger logger) : base(databaseContext, authenticationContextFactory, logger, true) + { + this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager)); + this.instanceManager = instanceManager ?? throw new ArgumentNullException(nameof(instanceManager)); + } + + /// + [TgsAuthorize(DreamDaemonRights.Start)] + public override async Task Create([FromBody] DreamDaemon model, CancellationToken cancellationToken) + { + //alias for launching DD + var instance = instanceManager.GetInstance(Instance); + + if (instance.Watchdog.Running) + return StatusCode((int)HttpStatusCode.Gone); + + var job = new Models.Job + { + Description = "Launch DreamDaemon", + CancelRight = (int)DreamDaemonRights.Shutdown, + CancelRightsType = RightsType.DreamDaemon, + Instance = Instance, + StartedBy = AuthenticationContext.User + }; + await jobManager.RegisterOperation(job, + async (paramJob, serviceProvider, progressHandler, innerCt) => + { + var result = await instance.Watchdog.Launch(innerCt).ConfigureAwait(false); + if (result == null) + throw new InvalidOperationException("Watchdog already running!"); + if (!instance.Watchdog.Running) + throw new Exception("Failed to launch watchdog!"); + }, + cancellationToken).ConfigureAwait(false); + return Json(job); + } + + /// + [TgsAuthorize(DreamDaemonRights.ReadMetadata | DreamDaemonRights.ReadRevision)] + public override async Task Read(CancellationToken cancellationToken) + { + var dd = instanceManager.GetInstance(Instance).Watchdog; + + var metadata = (AuthenticationContext.GetRight(RightsType.DreamDaemon) & (int)DreamDaemonRights.ReadMetadata) != 0; + var revision = (AuthenticationContext.GetRight(RightsType.DreamDaemon) & (int)DreamDaemonRights.ReadRevision) != 0; + + var settings = await DatabaseContext.Instances.Where(x => x.Id == Instance.Id).Select(x => x.DreamDaemonSettings).FirstAsync(cancellationToken).ConfigureAwait(false); + var result = new DreamDaemon(); + if(metadata) + { + var alphaActive = dd.AlphaIsActive; + var llp = dd.LastLaunchParameters; + result.AutoStart = settings.AutoStart; + result.CurrentPort = alphaActive ? llp.PrimaryPort : llp.SecondaryPort; + result.CurrentSecurity = llp.SecurityLevel; + result.CurrentAllowWebclient = llp.AllowWebClient; + result.PrimaryPort = dd.ActiveLaunchParameters.PrimaryPort; + result.AllowWebClient = dd.ActiveLaunchParameters.AllowWebClient; + result.Running = dd.Running; + result.SecondaryPort = dd.LastLaunchParameters.SecondaryPort; + result.SecurityLevel = dd.LastLaunchParameters.SecurityLevel; + var rstate = dd.RebootState; + result.SoftRestart = rstate == RebootState.Restart; + result.SoftShutdown = rstate == RebootState.Shutdown; + }; + if (revision) + { + result.ActiveCompileJob = settings.ActiveCompileJob?.ToApi(); + result.StagedCompileJob = settings.StagedCompileJob?.ToApi(); + } + + return Json(result); + } + + /// + [HttpDelete] + [TgsAuthorize(DreamDaemonRights.Shutdown)] + public async Task Delete(CancellationToken cancellationToken) + { + //alias for stopping DD + var instance = instanceManager.GetInstance(Instance); + + if (!instance.Watchdog.Running) + return StatusCode((int)HttpStatusCode.Gone); + + await instance.Watchdog.Terminate(false, cancellationToken).ConfigureAwait(false); + return Ok(); + } + + /// + [TgsAuthorize(DreamDaemonRights.SetAutoStart | DreamDaemonRights.SetPorts | DreamDaemonRights.SetSecurity | DreamDaemonRights.SetWebClient | DreamDaemonRights.SoftRestart | DreamDaemonRights.SoftShutdown | DreamDaemonRights.Start | DreamDaemonRights.SetStartupTimeout)] + public override async Task Update([FromBody] DreamDaemon model, CancellationToken cancellationToken) + { + //alias for changing DD settings + var current = await DatabaseContext.Instances.Where(x => x.Id == Instance.Id).Select(x => x.DreamDaemonSettings).FirstAsync(cancellationToken).ConfigureAwait(false); + + var userRights = (DreamDaemonRights)AuthenticationContext.GetRight(RightsType.DreamDaemon); + + bool CheckModified(Expression> expression, DreamDaemonRights requiredRight) + { + var memberSelectorExpression = (MemberExpression)expression.Body; + var property = (PropertyInfo)memberSelectorExpression.Member; + + var newVal = property.GetValue(model); + if (newVal == null) + return false; + if (!userRights.HasFlag(requiredRight) && property.GetValue(current) != newVal) + return true; + + property.SetValue(current, newVal); + return false; + }; + + var oldSoftRestart = current.SoftRestart; + var oldSoftShutdown = current.SoftShutdown; + + if (!CheckModified(x => x.AllowWebClient, DreamDaemonRights.SetWebClient) + || !CheckModified(x => x.AutoStart, DreamDaemonRights.SetAutoStart) + || !CheckModified(x => x.PrimaryPort, DreamDaemonRights.SetPorts) + || !CheckModified(x => x.SecondaryPort, DreamDaemonRights.SetPorts) + || !CheckModified(x => x.SecurityLevel, DreamDaemonRights.SetSecurity) + || !CheckModified(x => x.SoftRestart, DreamDaemonRights.SoftRestart) + || !CheckModified(x => x.SoftShutdown, DreamDaemonRights.SoftShutdown) + || !CheckModified(x => x.StartupTimeout, DreamDaemonRights.SetStartupTimeout)) + return Forbid(); + + var wd = instanceManager.GetInstance(Instance).Watchdog; + + var changeSettingsTask = wd.ChangeSettings(current, cancellationToken); + + //soft shutdown/restart can't be cancelled because of how many things rely on them + //They can be alternated though + if (!oldSoftRestart.Value && current.SoftRestart.Value) + await Task.WhenAll(changeSettingsTask, wd.Restart(true, cancellationToken)).ConfigureAwait(false); + else if (!oldSoftShutdown.Value && current.SoftShutdown.Value) + await Task.WhenAll(changeSettingsTask, wd.Terminate(true, cancellationToken)).ConfigureAwait(false); + else + await changeSettingsTask.ConfigureAwait(false); + + await DatabaseContext.Save(default).ConfigureAwait(false); + + return Ok(); + } + } +} diff --git a/src/Tgstation.Server.Host/Controllers/DreamMakerController.cs b/src/Tgstation.Server.Host/Controllers/DreamMakerController.cs new file mode 100644 index 0000000000..75801aac2d --- /dev/null +++ b/src/Tgstation.Server.Host/Controllers/DreamMakerController.cs @@ -0,0 +1,131 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Logging; +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Api.Rights; +using Tgstation.Server.Host.Components; +using Tgstation.Server.Host.Core; +using Tgstation.Server.Host.Models; +using Tgstation.Server.Host.Security; + +namespace Tgstation.Server.Host.Controllers +{ + /// + /// Controller for managing the compiler + /// + [Route("/" + nameof(Api.Models.DreamMaker))] + public sealed class DreamMakerController : ModelController + { + /// + /// The for the + /// + readonly IJobManager jobManager; + /// + /// The for the + /// + readonly IInstanceManager instanceManager; + + /// + /// Construct a + /// + /// The for the + /// The for the + /// The value of + /// The value of + /// The for the + public DreamMakerController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IJobManager jobManager, IInstanceManager instanceManager, ILogger logger) : base(databaseContext, authenticationContextFactory, logger, true) + { + this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager)); + this.instanceManager = instanceManager ?? throw new ArgumentNullException(nameof(instanceManager)); + } + + /// + [TgsAuthorize(DreamMakerRights.Read)] + public override async Task Read(CancellationToken cancellationToken) + { + var instance = instanceManager.GetInstance(Instance); + var projectNameTask = DatabaseContext.DreamMakerSettings.Where(x => x.InstanceId == Instance.Id).Select(x => x.ProjectName).FirstAsync(cancellationToken); + var job = await DatabaseContext.CompileJobs.OrderByDescending(x => x.Job.StartedAt).Include(x => x.Job).FirstAsync(cancellationToken).ConfigureAwait(false); + return Json(new Api.Models.DreamMaker + { + LastJob = job.ToApi(), + ProjectName = await projectNameTask.ConfigureAwait(false), + Status = instance.DreamMaker.Status + }); + } + + /// + [TgsAuthorize(DreamMakerRights.Compile)] + public override async Task Create([FromBody] Api.Models.DreamMaker model, CancellationToken cancellationToken) + { + var job = new Job + { + Description = "Compile active repository code", + StartedBy = AuthenticationContext.User, + CancelRightsType = RightsType.DreamMaker, + CancelRight = (int)DreamMakerRights.CancelCompile, + Instance = Instance + }; + await jobManager.RegisterOperation(job, (paramJob, serviceProvider, progressReporter, ct) => RunCompile(paramJob, serviceProvider, Instance, ct), cancellationToken).ConfigureAwait(false); + return Json(job); + } + + /// + [TgsAuthorize(DreamMakerRights.SetDme)] + public override async Task Update([FromBody] Api.Models.DreamMaker model, CancellationToken cancellationToken) + { + var hostModel = new DreamMakerSettings + { + InstanceId = Instance.Id + }; + DatabaseContext.DreamMakerSettings.Attach(hostModel); + hostModel.ProjectName = model.ProjectName; + await DatabaseContext.Save(cancellationToken).ConfigureAwait(false); + return Ok(); + } + + /// + /// Run the compile job and insert it into the database + /// + /// The running + /// The for the operation + /// The for the operation + /// The for the operation + /// A representing the running operation + static async Task RunCompile(Job job, IServiceProvider serviceProvider, Models.Instance instanceModel, CancellationToken cancellationToken) + { + var instanceManager = serviceProvider.GetRequiredService(); + var databaseContext = serviceProvider.GetRequiredService(); + + var timeoutTask = databaseContext.DreamDaemonSettings.Where(x => x.InstanceId == instanceModel.Id).Select(x => x.StartupTimeout).FirstAsync(cancellationToken); + var projectName = await databaseContext.DreamMakerSettings.Where(x => x.InstanceId == instanceModel.Id).Select(x => x.ProjectName).FirstAsync(cancellationToken).ConfigureAwait(false); + var timeout = await timeoutTask.ConfigureAwait(false); + + var instance = instanceManager.GetInstance(instanceModel); + + CompileJob compileJob; + Task revInfoTask; + using (var repo = await instance.RepositoryManager.LoadRepository(cancellationToken).ConfigureAwait(false)) + { + if (repo == null) + { + job.ExceptionDetails = "Missing repository!"; + return; + } + revInfoTask = databaseContext.RevisionInformations.Where(x => x.CommitSha == repo.Head).Select(x => new RevisionInformation { Id = x.Id }).FirstAsync(); + compileJob = await instance.DreamMaker.Compile(projectName, timeout.Value, repo, cancellationToken).ConfigureAwait(false); + } + + compileJob.Job = job; + compileJob.RevisionInformation = await revInfoTask.ConfigureAwait(false); + + databaseContext.CompileJobs.Add(compileJob); + //default ct because we don't want to give up after getting this far + await databaseContext.Save(default).ConfigureAwait(false); + } + } +} diff --git a/src/Tgstation.Server.Host/Controllers/HomeController.cs b/src/Tgstation.Server.Host/Controllers/HomeController.cs new file mode 100644 index 0000000000..72b266e87d --- /dev/null +++ b/src/Tgstation.Server.Host/Controllers/HomeController.cs @@ -0,0 +1,135 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using System; +using System.Linq; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Api; +using Tgstation.Server.Host.Core; +using Tgstation.Server.Host.Models; +using Tgstation.Server.Host.Security; + +namespace Tgstation.Server.Host.Controllers +{ + /// + /// Main for the + /// + [Route("/")] + public sealed class HomeController : ApiController + { + /// + /// The for the + /// + readonly ITokenFactory tokenFactory; + /// + /// The for the + /// + readonly ISystemIdentityFactory systemIdentityFactory; + /// + /// The for the + /// + readonly ICryptographySuite cryptographySuite; + /// + /// The for the + /// + readonly IApplication application; + /// + /// The for the + /// + readonly IIdentityCache identityCache; + + /// + /// Construct a + /// + /// The for the + /// The for the + /// The value of + /// The value of + /// The value of + /// The value of + /// The value of + /// The for the + public HomeController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, ITokenFactory tokenFactory, ISystemIdentityFactory systemIdentityFactory, ICryptographySuite cryptographySuite, IApplication application, IIdentityCache identityCache, ILogger logger) : base(databaseContext, authenticationContextFactory, logger, false) + { + this.tokenFactory = tokenFactory ?? throw new ArgumentNullException(nameof(tokenFactory)); + this.systemIdentityFactory = systemIdentityFactory ?? throw new ArgumentNullException(nameof(systemIdentityFactory)); + this.cryptographySuite = cryptographySuite ?? throw new ArgumentNullException(nameof(cryptographySuite)); + this.application = application ?? throw new ArgumentNullException(nameof(application)); + this.identityCache = identityCache ?? throw new ArgumentNullException(nameof(identityCache)); + } + + /// + /// Returns the version of the + /// + /// + [TgsAuthorize] + [HttpGet] + public JsonResult Home() => Json(new { application.Version }); + + /// + /// Attempt to authenticate a using + /// + /// The for the operation + /// A resulting in the of the operation + [HttpPost] + public async Task CreateToken(CancellationToken cancellationToken) + { + if (ApiHeaders.IsTokenAuthentication) + return BadRequest(new { message = "Cannot create a token using another token!" }); + + ISystemIdentity identity; + try + { + identity = await systemIdentityFactory.CreateSystemIdentity(ApiHeaders.Username, ApiHeaders.Password, cancellationToken).ConfigureAwait(false); + } + catch (NotImplementedException) + { + identity = null; + } + using (identity) + { + IQueryable query; + if (identity == null) + query = DatabaseContext.Users.Where(x => x.CanonicalName == ApiHeaders.Username.ToUpperInvariant()); + else + query = DatabaseContext.Users.Where(x => x.SystemIdentifier == identity.Uid); + var user = await query.Select(x => new User + { + Id = x.Id, + PasswordHash = x.PasswordHash, + Enabled = x.Enabled + }).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); + + if (user == null) + return Unauthorized(); + + if (identity == null) + { + var originalHash = user.PasswordHash; + if (!cryptographySuite.CheckUserPassword(user, ApiHeaders.Password)) + return Unauthorized(); + if (user.PasswordHash != originalHash) + { + var updatedUser = new User + { + Id = user.Id + }; + DatabaseContext.Users.Attach(updatedUser); + updatedUser.PasswordHash = user.PasswordHash; + await DatabaseContext.Save(cancellationToken).ConfigureAwait(false); + } + } + + if (!user.Enabled.Value) + return Forbid(); + + var token = tokenFactory.CreateToken(user, out var expiry); + if (identity != null) + identityCache.CacheSystemIdentity(user, identity, expiry.AddSeconds(10)); //expire the identity slightly after the auth token in case of lag + return Json(token); + } + } + } +} diff --git a/src/Tgstation.Server.Host/Controllers/InstanceController.cs b/src/Tgstation.Server.Host/Controllers/InstanceController.cs new file mode 100644 index 0000000000..9435a6786e --- /dev/null +++ b/src/Tgstation.Server.Host/Controllers/InstanceController.cs @@ -0,0 +1,345 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Linq.Expressions; +using System.Net; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Rights; +using Tgstation.Server.Host.Components; +using Tgstation.Server.Host.Core; +using Tgstation.Server.Host.IO; +using Tgstation.Server.Host.Models; +using Tgstation.Server.Host.Security; + +namespace Tgstation.Server.Host.Controllers +{ + /// + /// Controller for managing s + /// + [Route("/Instance")] + public sealed class InstanceController : ModelController + { + /// + /// The for the + /// + readonly IJobManager jobManager; + /// + /// The for the + /// + readonly IInstanceManager instanceManager; + /// + /// The for the + /// + readonly IIOManager ioManager; + + /// + /// The for the + /// + readonly IApplication application; + + /// + /// Construct a + /// + /// The for the + /// The for the + /// The value of + /// The value of + /// The value of + /// The value of + /// The for the + public InstanceController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IJobManager jobManager, IInstanceManager instanceManager, IIOManager ioManager, IApplication application, ILogger logger) : base(databaseContext, authenticationContextFactory, logger, false) + { + this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager)); + this.instanceManager = instanceManager ?? throw new ArgumentNullException(nameof(instanceManager)); + this.ioManager = ioManager ?? throw new ArgumentNullException(nameof(ioManager)); + this.application = application ?? throw new ArgumentNullException(nameof(application)); + } + + void NormalizeModelPath(Api.Models.Instance model, out string absolutePath) + { + if (model.Path == null) + { + absolutePath = null; + return; + } + absolutePath = ioManager.ResolvePath(model.Path); + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + model.Path = absolutePath.ToUpperInvariant(); + else + model.Path = absolutePath; + } + + Models.InstanceUser InstanceAdminUser() => new Models.InstanceUser + { + ByondRights = (ByondRights)~0, + ChatSettingsRights = (ChatSettingsRights)~0, + ConfigurationRights = (ConfigurationRights)~0, + DreamDaemonRights = (DreamDaemonRights)~0, + DreamMakerRights = (DreamMakerRights)~0, + RepositoryRights = (RepositoryRights)~0, + InstanceUserRights = (InstanceUserRights)~0, + UserId = AuthenticationContext.User.Id + }; + + /// + [TgsAuthorize(InstanceManagerRights.Create)] + public override async Task Create([FromBody] Api.Models.Instance model, CancellationToken cancellationToken) + { + if (String.IsNullOrWhiteSpace(model.Name)) + return BadRequest(new { message = "name must not be empty!" }); + + if(model.Path == null) + return BadRequest(new { message = "path must not be empty!" }); + + NormalizeModelPath(model, out var rawPath); + var dirExistsTask = ioManager.DirectoryExists(model.Path, cancellationToken); + if (await ioManager.FileExists(model.Path, cancellationToken).ConfigureAwait(false) || await dirExistsTask.ConfigureAwait(false)) + return Conflict(new { message = "Path not empty!" }); + + var newInstance = new Models.Instance + { + ConfigurationType = model.ConfigurationType ?? ConfigurationType.Disallowed, + DreamDaemonSettings = new DreamDaemonSettings + { + AllowWebClient = false, + AutoStart = false, + PrimaryPort = 1337, + SecondaryPort = 1338, + SecurityLevel = DreamDaemonSecurity.Ultrasafe, + SoftRestart = false, + SoftShutdown = false, + StartupTimeout = 20 + }, + DreamMakerSettings = new DreamMakerSettings(), + Name = model.Name, + Online = false, + Path = model.Path, + RepositorySettings = new RepositorySettings + { + CommitterEmail = "tgstation-server@user.noreply.github.com", + CommitterName = application.VersionString, + PushTestMergeCommits = false, + ShowTestMergeCommitters = true, + AutoUpdatesKeepTestMerges = false, + AutoUpdatesSynchronize = false + }, + //give this user full privileges on the instance + InstanceUsers = new List + { + InstanceAdminUser() + } + }; + + DatabaseContext.Instances.Add(newInstance); + try + { + await DatabaseContext.Save(cancellationToken).ConfigureAwait(false); + + try + { + //actually reserve it now + await ioManager.CreateDirectory(rawPath, default).ConfigureAwait(false); + } + catch + { + //oh shit delete the model + DatabaseContext.Instances.Remove(newInstance); + + await DatabaseContext.Save(default).ConfigureAwait(false); + + throw; + } + } + catch (DbUpdateConcurrencyException e) + { + return Conflict(new { message = e.Message }); + } + + Logger.LogInformation("{0} created instance {1}: {2}", AuthenticationContext.User.Name, newInstance.Name, newInstance.Id); + + return Json(newInstance.ToApi()); + } + + /// + [TgsAuthorize(InstanceManagerRights.Delete)] + public override async Task Delete(long id, CancellationToken cancellationToken) + { + var originalModel = await DatabaseContext.Instances.Where(x => x.Id == id) + .Include(x => x.WatchdogReattachInformation) + .Include(x => x.WatchdogReattachInformation.Alpha) + .Include(x => x.WatchdogReattachInformation.Bravo) + .FirstAsync(cancellationToken).ConfigureAwait(false); + if (originalModel == default(Models.Instance)) + return StatusCode((int)HttpStatusCode.Gone); + + if (originalModel.WatchdogReattachInformation != null) + { + DatabaseContext.WatchdogReattachInformations.Remove(originalModel.WatchdogReattachInformation); + if (originalModel.WatchdogReattachInformation.Alpha != null) + DatabaseContext.ReattachInformations.Remove(originalModel.WatchdogReattachInformation.Alpha); + if (originalModel.WatchdogReattachInformation.Bravo != null) + DatabaseContext.ReattachInformations.Remove(originalModel.WatchdogReattachInformation.Bravo); + } + + DatabaseContext.Instances.Remove(originalModel); + await DatabaseContext.Save(cancellationToken).ConfigureAwait(false); //cascades everything + return Ok(); + } + + /// + [TgsAuthorize(InstanceManagerRights.Relocate | InstanceManagerRights.Rename | InstanceManagerRights.SetAutoUpdate | InstanceManagerRights.SetConfiguration | InstanceManagerRights.SetOnline)] + public override async Task Update([FromBody] Api.Models.Instance model, CancellationToken cancellationToken) + { + var instanceQuery = DatabaseContext.Instances.Where(x => x.Id == model.Id); + + var usersInstanceUserTask = instanceQuery + .Include(x => x.InstanceUsers) + .SelectMany(x => x.InstanceUsers).Where(x => x.UserId == AuthenticationContext.User.Id).FirstOrDefaultAsync(cancellationToken); + + var originalModel = await instanceQuery + .Include(x => x.RepositorySettings) + .Include(x => x.ChatSettings) + .ThenInclude(x => x.Channels) + .Include(x => x.DreamDaemonSettings) //need these for onlining + .FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); + if (originalModel == default(Models.Instance)) + return StatusCode((int)HttpStatusCode.Gone); + + var userRights = (InstanceManagerRights)AuthenticationContext.GetRight(RightsType.InstanceManager); + bool CheckModified(Expression> expression, InstanceManagerRights requiredRight) + { + var memberSelectorExpression = (MemberExpression)expression.Body; + var property = (PropertyInfo)memberSelectorExpression.Member; + + var newVal = property.GetValue(model); + if (newVal == null) + return false; + if (!userRights.HasFlag(requiredRight) && property.GetValue(originalModel) != newVal) + return true; + + property.SetValue(originalModel, newVal); + return false; + }; + + string originalModelPath = null; + string rawPath = null; + if (model.Path != null) + { + NormalizeModelPath(model, out rawPath); + + if (model.Path != originalModel.Path) + { + if (!userRights.HasFlag(InstanceManagerRights.Relocate)) + return Forbid(); + if (originalModel.Online.Value && model.Online != true) + return Conflict(new { message = "Cannot relocate an online instance!" }); + + var dirExistsTask = ioManager.DirectoryExists(model.Path, cancellationToken); + if (await ioManager.FileExists(model.Path, cancellationToken).ConfigureAwait(false) || await dirExistsTask.ConfigureAwait(false)) + return Conflict(new { message = "Path not empty!" }); + + originalModelPath = originalModel.Path; + originalModel.Path = model.Path; + } + } + + var originalOnline = originalModel.Online.Value; + + if (CheckModified(x => x.AutoUpdateInterval, InstanceManagerRights.SetAutoUpdate) + || CheckModified(x => x.ConfigurationType, InstanceManagerRights.SetConfiguration) + || CheckModified(x => x.Name, InstanceManagerRights.Rename) + || CheckModified(x => x.Online, InstanceManagerRights.SetOnline)) + return Forbid(); + + //ensure the current user has write privilege on the instance + var usersInstanceUser = await usersInstanceUserTask.ConfigureAwait(false); + if (usersInstanceUser == default) + originalModel.InstanceUsers.Add(InstanceAdminUser()); + else + usersInstanceUser.InstanceUserRights |= InstanceUserRights.WriteUsers; + + await DatabaseContext.Save(cancellationToken).ConfigureAwait(false); + + var oldAutoStart = originalModel.DreamDaemonSettings.AutoStart; + try + { + if (originalOnline && model.Online.Value == false) + await instanceManager.OfflineInstance(originalModel, cancellationToken).ConfigureAwait(false); + else if (!originalOnline && model.Online.Value == true) + { + //force autostart false here because we don't want any long running jobs right now + //remember to document this + originalModel.DreamDaemonSettings.AutoStart = false; + await instanceManager.OnlineInstance(originalModel, cancellationToken).ConfigureAwait(false); + } + } + catch (Exception e) + { + Logger.LogError("Error changing instance online state! Exception: {0}", e); + originalModel.Online = originalOnline; + originalModel.DreamDaemonSettings.AutoStart = oldAutoStart; + if (originalModelPath != null) + originalModel.Path = originalModelPath; + await DatabaseContext.Save(default).ConfigureAwait(false); + throw; + } + + var api = originalModel.ToApi(); + if (originalModelPath != null) + { + var job = new Models.Job + { + Description = String.Format(CultureInfo.InvariantCulture, "Move instance ID {0} from {1} to {2}", Instance.Id, Instance.Path, rawPath), + Instance = Instance, + CancelRightsType = RightsType.InstanceManager, + CancelRight = (int)InstanceManagerRights.CancelMove, + StartedBy = AuthenticationContext.User + }; + + await jobManager.RegisterOperation(job, async (paramJob, serviceProvider, progressHandler, ct) => { + try + { + await instanceManager.MoveInstance(Instance, rawPath, ct).ConfigureAwait(false); + } + catch + { + originalModel.Path = originalModelPath; + await DatabaseContext.Save(default).ConfigureAwait(false); + throw; + } + }, cancellationToken).ConfigureAwait(false); + api.MoveJob = job.ToApi(); + } + + return Json(api); + } + + /// + [TgsAuthorize] + public override async Task List(CancellationToken cancellationToken) + { + IQueryable query = DatabaseContext.Instances; + if (!AuthenticationContext.User.InstanceManagerRights.Value.HasFlag(InstanceManagerRights.List)) + query = query.Where(x => x.InstanceUsers.Any(y => y.UserId == AuthenticationContext.User.Id)).Where(x => x.InstanceUsers.Any(y => y.AnyRights)); + var instances = await query.ToListAsync(cancellationToken).ConfigureAwait(false); + return Json(instances.Select(x => x.ToApi())); + } + + /// + [TgsAuthorize] + public override Task Read(CancellationToken cancellationToken) + { + if (Instance == null) + return Task.FromResult(BadRequest(new { message = "No instance specified" })); + return Task.FromResult(Json(Instance.ToApi())); + } + } +} diff --git a/src/Tgstation.Server.Host/Controllers/InstanceUserController.cs b/src/Tgstation.Server.Host/Controllers/InstanceUserController.cs new file mode 100644 index 0000000000..a76407212f --- /dev/null +++ b/src/Tgstation.Server.Host/Controllers/InstanceUserController.cs @@ -0,0 +1,146 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.AspNetCore.Mvc.Filters; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using System; +using System.Linq; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Rights; +using Tgstation.Server.Host.Models; +using Tgstation.Server.Host.Security; +using Z.EntityFramework.Plus; + +namespace Tgstation.Server.Host.Controllers +{ + /// + /// For managing s + /// + [Route("/" + nameof(Models.InstanceUser))] + public sealed class InstanceUserController : ModelController + { + /// + /// Construct a + /// + /// The for the + /// The for the + /// The for the + public InstanceUserController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, ILogger logger) : base(databaseContext, authenticationContextFactory, logger, true) //false instance requirement, we handle this ourself + { } + + /// + /// Checks a for errors + /// + /// The to check + /// A explaining any errors, if none + BadRequestObjectResult StandardModelChecks(Api.Models.InstanceUser model) + { + if (model == null) + throw new ArgumentNullException(nameof(model)); + + if (model.InstanceId.HasValue && model.InstanceId != Instance.Id) + return BadRequest(new { message = "InstanceId does not match headers!" }); + + if (!model.UserId.HasValue) + return BadRequest(new { message = "Missing UserId!" }); + + return null; + } + + /// + [TgsAuthorize(InstanceUserRights.WriteUsers)] + public override async Task Create([FromBody] Api.Models.InstanceUser model, CancellationToken cancellationToken) + { + var test = StandardModelChecks(model); + if (test != null) + return test; + + var dbUser = new Models.InstanceUser + { + ByondRights = model.ByondRights ?? ByondRights.None, + ChatSettingsRights = model.ChatSettingsRights ?? ChatSettingsRights.None, + ConfigurationRights = model.ConfigurationRights ?? ConfigurationRights.None, + DreamDaemonRights = model.DreamDaemonRights ?? DreamDaemonRights.None, + DreamMakerRights = model.DreamMakerRights ?? DreamMakerRights.None, + RepositoryRights = model.RepositoryRights ?? RepositoryRights.None, + InstanceUserRights = model.InstanceUserRights ?? InstanceUserRights.None, + UserId = model.UserId, + InstanceId = Instance.Id + }; + + DatabaseContext.InstanceUsers.Add(dbUser); + + try + { + await DatabaseContext.Save(cancellationToken).ConfigureAwait(false); + } + catch (DbUpdateConcurrencyException e) + { + return Conflict(new { message = e.Message }); + } + return Json(dbUser.ToApi()); + } + + /// + [TgsAuthorize(InstanceUserRights.WriteUsers)] + public override async Task Update([FromBody] Api.Models.InstanceUser model, CancellationToken cancellationToken) + { + var test = StandardModelChecks(model); + if (test != null) + return test; + + var originalUser = await DatabaseContext.Instances.Where(x => x.Id == Instance.Id).SelectMany(x => x.InstanceUsers).Where(x => x.UserId == model.UserId).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); + if (originalUser == null) + return StatusCode((int)HttpStatusCode.Gone); + + originalUser.ByondRights = model.ByondRights ?? originalUser.ByondRights; + originalUser.ChatSettingsRights = model.ChatSettingsRights ?? originalUser.ChatSettingsRights; + originalUser.ConfigurationRights = model.ConfigurationRights ?? originalUser.ConfigurationRights; + originalUser.DreamDaemonRights = model.DreamDaemonRights ?? originalUser.DreamDaemonRights; + originalUser.DreamMakerRights = model.DreamMakerRights ?? originalUser.DreamMakerRights; + + try + { + await DatabaseContext.Save(cancellationToken).ConfigureAwait(false); + } + catch (DbUpdateConcurrencyException e) + { + return Conflict(new { message = e.Message }); + } + return Json(originalUser.ToApi()); + } + + /// + [TgsAuthorize] + public override Task Read(CancellationToken cancellationToken) => Task.FromResult(AuthenticationContext.InstanceUser != null ? (IActionResult)Json(AuthenticationContext.InstanceUser.ToApi()) : NotFound()); + + /// + [TgsAuthorize(InstanceUserRights.ReadUsers)] + public override async Task List(CancellationToken cancellationToken) + { + var users = await DatabaseContext.Instances.Where(x => x.Id == Instance.Id).Include(x => x.InstanceUsers).SelectMany(x => x.InstanceUsers).ToListAsync(cancellationToken).ConfigureAwait(false); + return Json(users.Select(x => x.ToApi())); + } + + /// + [TgsAuthorize(InstanceUserRights.ReadUsers)] + public override async Task GetId(long id, CancellationToken cancellationToken) + { + //this functions as userId + var user = await DatabaseContext.Instances.Where(x => x.Id == Instance.Id).SelectMany(x => x.InstanceUsers).Where(x => x.UserId == id).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); + if (user == default) + return NotFound(); + return Json(user.ToApi()); + } + + /// + [TgsAuthorize(InstanceUserRights.WriteUsers)] + public override async Task Delete(long id, CancellationToken cancellationToken) + { + await DatabaseContext.Instances.Where(x => x.Id == Instance.Id).SelectMany(x => x.InstanceUsers).Where(x => x.UserId == id).DeleteAsync(cancellationToken).ConfigureAwait(false); + return Ok(); + } + } +} diff --git a/src/Tgstation.Server.Host/Controllers/InteropController.cs b/src/Tgstation.Server.Host/Controllers/InteropController.cs new file mode 100644 index 0000000000..f7b7b808e9 --- /dev/null +++ b/src/Tgstation.Server.Host/Controllers/InteropController.cs @@ -0,0 +1,53 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using System; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Host.Components; +using Tgstation.Server.Host.Models; +using Tgstation.Server.Host.Security; + +namespace Tgstation.Server.Host.Controllers +{ + /// + /// Handles requests from DreamDaemon + /// + [Route("/Interop")] + public sealed class InteropController : ApiController + { + /// + /// The for the + /// + readonly IInstanceManager instanceManager; + + /// + /// Construct an + /// + /// The for the + /// The for the + /// The value of + /// The for the + public InteropController(IInstanceManager instanceManager, IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, ILogger logger) : base(databaseContext, authenticationContextFactory, logger, false) + { + this.instanceManager = instanceManager ?? throw new ArgumentNullException(nameof(instanceManager)); + } + + /// + /// Handle a GET to the + /// + /// The for the operation + /// A resulting in the of the operation + [HttpGet] + public async Task HandleInterop(CancellationToken cancellationToken) + { + var result = await instanceManager.HandleWorldExport(Request.Query, cancellationToken).ConfigureAwait(false); + //explain things in very simple terms dream daemon can understand + //EXCEPT DREAMDAENEN BUGS + //THAT"S RIGHT !BUGS! + //ON ANYTHING THAT ISNT A 200 RESPOSNCE REEEE + if (result == null) + result = new { STATUS = 404 }; + return Json(result); + } + } +} diff --git a/src/Tgstation.Server.Host/Controllers/JobController.cs b/src/Tgstation.Server.Host/Controllers/JobController.cs new file mode 100644 index 0000000000..16f628cba4 --- /dev/null +++ b/src/Tgstation.Server.Host/Controllers/JobController.cs @@ -0,0 +1,81 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using System; +using System.Linq; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Host.Core; +using Tgstation.Server.Host.Models; +using Tgstation.Server.Host.Security; + +namespace Tgstation.Server.Host.Controllers +{ + /// + /// for s + /// + [Route("/" + nameof(Job))] + public sealed class JobController : ModelController + { + /// + /// The for the + /// + readonly IJobManager jobManager; + + /// + /// Construct a + /// + /// The for the + /// The for the + /// The value of + /// The for the + public JobController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IJobManager jobManager, ILogger logger) : base(databaseContext, authenticationContextFactory, logger, true) + { + this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager)); + } + + /// + [TgsAuthorize] + public override async Task List(CancellationToken cancellationToken) + { + //you KNOW this will need pagination eventually right? + var jobs = await DatabaseContext.Jobs.Where(x => x.Instance.Id == Instance.Id).OrderByDescending(x => x.StartedAt).Select(x => new Api.Models.Job + { + Id = x.Id + }).ToListAsync(cancellationToken).ConfigureAwait(false); + return Json(jobs); + } + + /// + [TgsAuthorize] + public override async Task Delete(long id, CancellationToken cancellationToken) + { + //don't care if an instance post or not at this point + var job = await DatabaseContext.Jobs.Where(x => x.Id == id).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); + if (job == default(Job)) + return NotFound(); + + if (job.StoppedAt != null) + return StatusCode((int)HttpStatusCode.Gone); + + if (job.CancelRight.HasValue && job.CancelRightsType.HasValue && (AuthenticationContext.GetRight(job.CancelRightsType.Value) & job.CancelRight.Value) == 0) + return Forbid(); + + await jobManager.CancelJob(job, AuthenticationContext.User, cancellationToken).ConfigureAwait(false); + return Ok(); + } + + /// + [TgsAuthorize] + public override async Task GetId(long id, CancellationToken cancellationToken) + { + var job = await DatabaseContext.Jobs.Where(x => x.Id == id).Include(x => x.StartedBy).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); + if (job == default(Job)) + return NotFound(); + var api = job.ToApi(); + api.Progress = jobManager.JobProgress(job); + return Json(api); + } + } +} diff --git a/src/Tgstation.Server.Host/Controllers/ModelController.cs b/src/Tgstation.Server.Host/Controllers/ModelController.cs new file mode 100644 index 0000000000..dbc2c704d4 --- /dev/null +++ b/src/Tgstation.Server.Host/Controllers/ModelController.cs @@ -0,0 +1,84 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.Extensions.Logging; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Api; +using Tgstation.Server.Host.Models; +using Tgstation.Server.Host.Security; + +namespace Tgstation.Server.Host.Controllers +{ + /// + /// An representing a + /// + /// The model being represented + public abstract class ModelController : ApiController where TModel : class + { + /// + /// The of the + /// + protected static readonly ModelAttribute ModelAttribute = (ModelAttribute)typeof(TModel).GetCustomAttributes(typeof(ModelAttribute), true).First(); + + /// + /// Construct a + /// + /// The for the + /// The for the + /// The for the + /// If the requires an + public ModelController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, ILogger logger, bool requireInstance) : base(databaseContext, authenticationContextFactory, logger, requireInstance) { } + + /// + /// Attempt to create a + /// + /// The being created + /// The for the operation + /// A resulting in the of the operation + [HttpPut] + public virtual Task Create([FromBody]TModel model, CancellationToken cancellationToken) => Task.FromResult((IActionResult)NotFound()); + + /// + /// Attempt to read a + /// + /// The for the operation + /// A resulting in the of the operation + [HttpGet] + public virtual Task Read(CancellationToken cancellationToken) => Task.FromResult((IActionResult)NotFound()); + + /// + /// Attempt to get a specific a + /// + /// The ID of the model to get + /// The for the operation + /// A resulting in the of the operation + [HttpGet("{id}")] + public virtual Task GetId(long id, CancellationToken cancellationToken) => Task.FromResult((IActionResult)NotFound()); + + /// + /// Attempt to update a + /// + /// The being updated + /// The for the operation + /// A resulting in the of the operation + [HttpPost] + public virtual Task Update([FromBody]TModel model, CancellationToken cancellationToken) => Task.FromResult((IActionResult)NotFound()); + + /// + /// Attempt to delete a model with a particular + /// + /// The ID of the model to delete + /// The for the operation + /// A resulting in the of the operation + [HttpDelete("{id}")] + public virtual Task Delete(long id, CancellationToken cancellationToken) => Task.FromResult((IActionResult)NotFound()); + + /// + /// Attempt to list entries of the + /// + /// The for the operation + /// A resulting in the of the operation + [HttpGet("List")] + public virtual Task List(CancellationToken cancellationToken) => Task.FromResult((IActionResult)NotFound()); + } +} diff --git a/src/Tgstation.Server.Host/Controllers/RepositoryController.cs b/src/Tgstation.Server.Host/Controllers/RepositoryController.cs new file mode 100644 index 0000000000..1d1ef12163 --- /dev/null +++ b/src/Tgstation.Server.Host/Controllers/RepositoryController.cs @@ -0,0 +1,443 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Globalization; +using System.Linq; +using System.Linq.Expressions; +using System.Net; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Rights; +using Tgstation.Server.Host.Components; +using Tgstation.Server.Host.Core; +using Tgstation.Server.Host.Models; +using Tgstation.Server.Host.Security; + +namespace Tgstation.Server.Host.Controllers +{ + /// + /// Controller for managing the s + /// + [Route("/" + nameof(Repository))] + public sealed class RepositoryController : ModelController + { + /// + /// The for the + /// + readonly IInstanceManager instanceManager; + + /// + /// The for the + /// + readonly Octokit.IGitHubClient gitHubClient; + + /// + /// The for the + /// + readonly IJobManager jobManager; + + /// + /// Construct a + /// + /// The for the + /// The for the + /// The value of + /// The value of + /// The value of + /// The for the + public RepositoryController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, IInstanceManager instanceManager, Octokit.IGitHubClient gitHubClient, IJobManager jobManager, ILogger logger) : base(databaseContext, authenticationContextFactory, logger, true) + { + this.instanceManager = instanceManager ?? throw new ArgumentNullException(nameof(instanceManager)); + this.gitHubClient = gitHubClient ?? throw new ArgumentNullException(nameof(gitHubClient)); + this.jobManager = jobManager ?? throw new ArgumentNullException(nameof(jobManager)); + } + + static string GetAccessString(Api.Models.Internal.RepositorySettings repositorySettings) => repositorySettings.AccessUser != null ? String.Concat(repositorySettings.AccessUser, '@', repositorySettings.AccessToken) : null; + + async Task LoadRevisionInformation(Components.Repository.IRepository repository, string lastOriginCommitSha, Action revInfoSink, CancellationToken cancellationToken) + { + var repoSha = repository.Head; + var revisionInfo = await DatabaseContext.RevisionInformations.Where(x => x.CommitSha == repoSha) + .Include(x => x.CompileJobs) + .Include(x => x.ActiveTestMerges) //minimal info, they can query the rest if they're allowed + .FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); //search every rev info because LOL SHA COLLISIONS + + var needsDbUpdate = revisionInfo == default; + if (needsDbUpdate) + { + //needs insertion + revisionInfo = new Models.RevisionInformation + { + CommitSha = repoSha, + CompileJobs = new List(), + ActiveTestMerges = new List() //non null vals for api returns + }; + + lock (DatabaseContext) //cleaner this way + DatabaseContext.RevisionInformations.Add(revisionInfo); + } + revisionInfo.OriginCommitSha = revisionInfo.OriginCommitSha ?? lastOriginCommitSha ?? repository.Head; + revInfoSink?.Invoke(revisionInfo); + return needsDbUpdate; + } + + async Task PopulateApi(Repository model, Components.Repository.IRepository repository, string lastOriginCommitSha, Action revInfoSink, CancellationToken cancellationToken) + { + model.IsGitHub = repository.IsGitHubRepository; + model.Origin = repository.Origin; + model.Reference = repository.Reference; + + //rev info stuff + Models.RevisionInformation revisionInfo = null; + var needsDbUpdate = await LoadRevisionInformation(repository, lastOriginCommitSha, x => revisionInfo = x, cancellationToken).ConfigureAwait(false); + model.RevisionInformation = revisionInfo.ToApi(); + revInfoSink?.Invoke(revisionInfo); + return needsDbUpdate; + } + + /// + [TgsAuthorize(RepositoryRights.SetOrigin)] + public override async Task Create([FromBody] Repository model, CancellationToken cancellationToken) + { + if (model == null) + return BadRequest(new { message = "Missing request model!" }); + + if (model.Origin == null) + return BadRequest(new { message = "Missing repo origin!" }); + + if (model.AccessUser == null ^ model.AccessToken == null) + return BadRequest(new { message = "Either both accessToken and accessUser must be present or neither!" }); + + var currentModel = await DatabaseContext.RepositorySettings.Where(x => x.InstanceId == Instance.Id).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); + + if (currentModel == default) + return StatusCode((int)HttpStatusCode.Gone); + + //normalize github urls + const string BadGitHubUrl = "://www.github.com/"; + var uiOrigin = model.Origin.ToUpperInvariant(); + var uiBad = BadGitHubUrl.ToUpperInvariant(); + var uiGitHub = Components.Repository.Repository.GitHubUrl.ToUpperInvariant(); + if (uiOrigin.Contains(uiBad)) + model.Origin = uiOrigin.Replace(uiBad, uiGitHub, StringComparison.Ordinal); + + currentModel.AccessToken = model.AccessToken; + currentModel.AccessUser = model.AccessUser; //intentionally only these fields, user not allowed to change anything else atm + var cloneBranch = model.Reference; + var origin = model.Origin; + + var repoManager = instanceManager.GetInstance(Instance).RepositoryManager; + + using (var repo = await repoManager.LoadRepository(cancellationToken).ConfigureAwait(false)) + { + if (repo != null) + //clone conflict + return Conflict(); + + var job = new Models.Job + { + Description = String.Format(CultureInfo.InvariantCulture, "Clone branch {1} of repository {0}", origin, cloneBranch ?? "master"), + StartedBy = AuthenticationContext.User, + CancelRightsType = RightsType.Repository, + CancelRight = (int)RepositoryRights.CancelClone, + Instance = Instance + }; + var api = currentModel.ToApi(); + await jobManager.RegisterOperation(job, async (paramJob, serviceProvider, progressReporter, ct) => + { + using (var repos = await repoManager.CloneRepository(new Uri(origin), cloneBranch, GetAccessString(currentModel), progressReporter, cancellationToken).ConfigureAwait(false)) + { + if (repos == null) + throw new Exception("Filesystem conflict while cloning repository!"); + await PopulateApi(api, repo, null, null, cancellationToken).ConfigureAwait(false); + } + }, cancellationToken).ConfigureAwait(false); + + api.Origin = model.Origin; + api.Reference = model.Reference; + api.IsGitHub = model.Origin.ToUpperInvariant().Contains(uiGitHub); + api.ActiveJob = job.ToApi(); + + return Json(api); + } + } + + /// + /// Delete the + /// + /// The for the operation + /// A resulting in the of the operation + [TgsAuthorize(RepositoryRights.Delete)] + public async Task Delete(CancellationToken cancellationToken) + { + var currentModel = await DatabaseContext.RepositorySettings.Where(x => x.InstanceId == Instance.Id).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); + + if (currentModel == default) + return StatusCode((int)HttpStatusCode.Gone); + + currentModel.AccessToken = null; + currentModel.AccessUser = null; + + await DatabaseContext.Save(cancellationToken).ConfigureAwait(false); + + Logger.LogInformation("Instance {0} repository delete initiated by user {1}", Instance.Id, AuthenticationContext.User.Id); + + var job = new Models.Job + { + Description = "Delete repository", + StartedBy = AuthenticationContext.User, + Instance = Instance + }; + var api = currentModel.ToApi(); + await jobManager.RegisterOperation(job, (paramJob, serviceProvider, progressReporter, ct) => instanceManager.GetInstance(Instance).RepositoryManager.DeleteRepository(cancellationToken), cancellationToken).ConfigureAwait(false); + api.ActiveJob = job.ToApi(); + return Ok(); + } + + /// + [TgsAuthorize(RepositoryRights.Read)] + public override async Task Read(CancellationToken cancellationToken) + { + var currentModel = await DatabaseContext.RepositorySettings.Where(x => x.InstanceId == Instance.Id).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); + + if (currentModel == default) + return StatusCode((int)HttpStatusCode.Gone); + + var api = currentModel.ToApi(); + + using (var repo = await instanceManager.GetInstance(Instance).RepositoryManager.LoadRepository(cancellationToken).ConfigureAwait(false)) + { + if (repo != null && await PopulateApi(api, repo, null, null, cancellationToken).ConfigureAwait(false)) + //user may have fucked with the repo without telling us, do what we can + await DatabaseContext.Save(cancellationToken).ConfigureAwait(false); + return Json(api); + } + } + + /// + [TgsAuthorize(RepositoryRights.ChangeAutoUpdateSettings | RepositoryRights.ChangeCommitter | RepositoryRights.ChangeCredentials | RepositoryRights.ChangeTestMergeCommits | RepositoryRights.MergePullRequest | RepositoryRights.SetReference | RepositoryRights.SetSha | RepositoryRights.UpdateBranch)] + public override async Task Update([FromBody]Repository model, CancellationToken cancellationToken) + { + if (model == null) + return BadRequest(new { message = "Missing request model!" }); + + if (model.AccessUser == null ^ model.AccessToken == null) + return BadRequest(new { message = "Either both accessToken and accessUser must be present or neither!" }); + + if (model.CheckoutSha != null && model.Reference != null) + return BadRequest(new { message = "Only one of sha or reference may be specified!" }); + + if (model.CheckoutSha != null && model.UpdateFromOrigin == true) + return BadRequest(new { message = "Cannot update a reference when checking out a sha!" }); + + if (model.Origin != null) + return BadRequest(new { message = "origin cannot be modified without deleting the repository!" }); + + var newTestMerges = model.NewTestMerges != null && model.NewTestMerges.Count > 0; + var userRights = (RepositoryRights)AuthenticationContext.GetRight(RightsType.Repository); + if (newTestMerges && !userRights.HasFlag(RepositoryRights.MergePullRequest)) + return Forbid(); + + var currentModel = await DatabaseContext.RepositorySettings.Where(x => x.InstanceId == Instance.Id).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); + + if (currentModel == default) + return StatusCode((int)HttpStatusCode.Gone); + + bool CheckModified(Expression> expression, RepositoryRights requiredRight) + { + var memberSelectorExpression = (MemberExpression)expression.Body; + var property = (PropertyInfo)memberSelectorExpression.Member; + + var newVal = property.GetValue(model); + if (newVal == null) + return false; + if (!userRights.HasFlag(requiredRight) && property.GetValue(currentModel) != newVal) + return true; + + property.SetValue(currentModel, newVal); + return false; + }; + + if (CheckModified(x => x.AccessToken, RepositoryRights.ChangeCredentials) + || CheckModified(x => x.AccessUser, RepositoryRights.ChangeCredentials) + || CheckModified(x => x.AutoUpdatesKeepTestMerges, RepositoryRights.ChangeAutoUpdateSettings) + || CheckModified(x => x.AutoUpdatesSynchronize, RepositoryRights.ChangeAutoUpdateSettings) + || CheckModified(x => x.CommitterEmail, RepositoryRights.ChangeCommitter) + || CheckModified(x => x.CommitterName, RepositoryRights.ChangeCommitter) + || CheckModified(x => x.PushTestMergeCommits, RepositoryRights.ChangeTestMergeCommits) + || CheckModified(x => x.ShowTestMergeCommitters, RepositoryRights.ChangeTestMergeCommits) + || (model.UpdateFromOrigin == true && !userRights.HasFlag(RepositoryRights.UpdateBranch))) + return Forbid(); + + //no, just one save changes at the end for sanity + //await DatabaseContext.Save(cancellationToken).ConfigureAwait(false); + + var job = new Models.Job + { + Description = "Apply repository changes", + StartedBy = AuthenticationContext.User, + Instance = Instance, + CancelRightsType = RightsType.Repository, + CancelRight = (int)RepositoryRights.CancelPendingChanges, + }; + + await jobManager.RegisterOperation(job, async (paramJob, serviceProvider, progressReporter, ct) => + { + using (var repo = await instanceManager.GetInstance(Instance).RepositoryManager.LoadRepository(cancellationToken).ConfigureAwait(false)) + { + if (repo == null) + throw new InvalidOperationException("Repository could not be loaded!"); + + var modelHasShaOrReference = model.CheckoutSha != null || model.Reference != null; + + var startSha = repo.Head; + + if (newTestMerges && !repo.IsGitHubRepository) + throw new InvalidOperationException("Cannot test merge on a non GitHub based repository!"); + + var committerName = currentModel.ShowTestMergeCommitters.Value ? AuthenticationContext.User.Name : currentModel.CommitterName; + var accessString = GetAccessString(currentModel); + + var numFetches = (model.NewTestMerges?.Count ?? 0) + (model.UpdateFromOrigin == true ? 1 : 0); + var doneFetches = 0; + if (numFetches > 0) + progressReporter(0); + + //get a base line for where we are + Models.RevisionInformation lastRevisionInfo = null; + await LoadRevisionInformation(repo, null, x => lastRevisionInfo = x, cancellationToken).ConfigureAwait(false); + + //apply new rev info, tracking applied test merges + async Task UpdateRevInfo() + { + var last = lastRevisionInfo; + await LoadRevisionInformation(repo, last.OriginCommitSha, x => lastRevisionInfo = x, cancellationToken).ConfigureAwait(false); + lastRevisionInfo.ActiveTestMerges.AddRange(last.ActiveTestMerges); + }; + + try + { + //fetch/pull + if (model.UpdateFromOrigin == true) + { + if (!repo.Tracking && model.Reference == null) + throw new InvalidOperationException("Not on an updatable reference!"); + await repo.FetchOrigin(accessString, x => progressReporter(x / numFetches), cancellationToken).ConfigureAwait(false); + doneFetches = 1; + if (!modelHasShaOrReference) + { + var fastForward = await repo.MergeOrigin(committerName, currentModel.CommitterEmail, cancellationToken).ConfigureAwait(false); + if (!fastForward.HasValue) + throw new InvalidOperationException("Merge conflict occurred during origin update!"); + await UpdateRevInfo().ConfigureAwait(false); + if (fastForward.Value) + lastRevisionInfo.OriginCommitSha = repo.Head; + } + } + + //checkout/hard reset + if (modelHasShaOrReference) + { + if ((model.CheckoutSha != null && repo.Head.ToUpperInvariant() != model.CheckoutSha.ToUpperInvariant()) + || (model.Reference != null && repo.Reference != model.Reference)) + { + await repo.CheckoutObject(model.CheckoutSha ?? model.Reference, cancellationToken).ConfigureAwait(false); + await LoadRevisionInformation(repo, null, x => lastRevisionInfo = x, cancellationToken).ConfigureAwait(false); //we've either seen origin before or what we're checking out is on origin + } + + if (model.UpdateFromOrigin == true && model.Reference != null) + { + if (!repo.Tracking) + throw new InvalidOperationException("Checked out reference does not track a remote object!"); + await repo.ResetToOrigin(cancellationToken).ConfigureAwait(false); + await LoadRevisionInformation(repo, null, x => lastRevisionInfo = x, cancellationToken).ConfigureAwait(false); + //repo head is on origin so force this + //will update the db if necessary + lastRevisionInfo.OriginCommitSha = repo.Head; + } + } + + //test merging + if (newTestMerges) + { + var repoOwner = repo.GitHubOwner; + var repoName = repo.GitHubRepoName; + foreach (var I in model.NewTestMerges) + { + var prTask = gitHubClient.PullRequest.Get(repoOwner, repoName, I.Number); + + var mergeResult = await repo.AddTestMerge(I.Number, I.PullRequestRevision, committerName, currentModel.CommitterEmail, accessString, x => progressReporter((x + 100 * doneFetches) / numFetches), cancellationToken).ConfigureAwait(false); + + if (!mergeResult.HasValue) //conflict, we don't care, dd already knows + continue; + + ++doneFetches; + + var revInfoUpdateTask = UpdateRevInfo(); + + Octokit.PullRequest pr = null; + string errorMessage = null; + try + { + pr = await prTask.ConfigureAwait(false); + } + catch (Octokit.RateLimitExceededException) + { + //you look at your anonymous access and sigh + errorMessage = "P.R.E. RATE LIMITED"; + } + catch (Octokit.NotFoundException) + { + //you look at your shithub access and sigh + errorMessage = "P.R.E. NOT FOUND"; + } + + var tm = new Models.TestMerge + { + Author = pr?.User.Login ?? errorMessage, + BodyAtMerge = pr?.Body, + MergedAt = DateTimeOffset.Now, + TitleAtMerge = pr?.Title ?? errorMessage, + Comment = I.Comment, + Number = I.Number, + MergedBy = AuthenticationContext.User, + PullRequestRevision = I.PullRequestRevision, + Url = pr?.HtmlUrl ?? errorMessage + }; + if (pr == null) + tm.BodyAtMerge = errorMessage; + + await revInfoUpdateTask.ConfigureAwait(false); + + lastRevisionInfo.PrimaryTestMerge = tm; + lastRevisionInfo.ActiveTestMerges.Add(new RevInfoTestMerge + { + TestMerge = tm + }); + } + } + + if (startSha != repo.Head) + { + await repo.Sychronize(accessString, ct).ConfigureAwait(false); + await UpdateRevInfo().ConfigureAwait(false); + } + await DatabaseContext.Save(cancellationToken).ConfigureAwait(false); + } + catch + { + //the stuff didn't make it into the db, forget what we've done and abort + await repo.CheckoutObject(startSha, default).ConfigureAwait(false); + throw; + } + } + }, cancellationToken).ConfigureAwait(false); + + return Json(job.ToApi()); + } + } +} diff --git a/src/Tgstation.Server.Host/Controllers/UserController.cs b/src/Tgstation.Server.Host/Controllers/UserController.cs new file mode 100644 index 0000000000..615a046902 --- /dev/null +++ b/src/Tgstation.Server.Host/Controllers/UserController.cs @@ -0,0 +1,179 @@ +using Microsoft.AspNetCore.Mvc; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Net; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Api.Models; +using Tgstation.Server.Api.Rights; +using Tgstation.Server.Host.Models; +using Tgstation.Server.Host.Security; + +namespace Tgstation.Server.Host.Controllers +{ + /// + /// For managing s + /// + [Route("/" + nameof(Models.User))] + public sealed class UserController : ModelController + { + /// + /// The for the + /// + readonly ISystemIdentityFactory systemIdentityFactory; + + /// + /// The for the + /// + readonly ICryptographySuite cryptographySuite; + + /// + /// The for the + /// + readonly ILogger logger; + + /// + /// Construct a + /// + /// The for the + /// The for the + /// The value of + /// The value of + /// The value of + public UserController(IDatabaseContext databaseContext, IAuthenticationContextFactory authenticationContextFactory, ISystemIdentityFactory systemIdentityFactory, ICryptographySuite cryptographySuite, ILogger logger) : base(databaseContext, authenticationContextFactory, logger, false) + { + this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); + this.systemIdentityFactory = systemIdentityFactory ?? throw new ArgumentNullException(nameof(systemIdentityFactory)); + this.cryptographySuite = cryptographySuite ?? throw new ArgumentNullException(nameof(cryptographySuite)); + } + + /// + [TgsAuthorize(AdministrationRights.EditUsers)] + public override async Task Create([FromBody] UserUpdate model, CancellationToken cancellationToken) + { + if (model == null) + throw new ArgumentNullException(nameof(model)); + + if (!(model.Password == null ^ model.SystemIdentifier == null)) + return BadRequest(new { message = "User must have exactly one of either a password or system identifier!" }); + + model.Name = model.Name?.Trim(); + if (model.Name?.Length == 0) + model.Name = null; + + if (!(model.Name == null ^ model.SystemIdentifier == null)) + return BadRequest(new { message = "User must have a name if and only if user has no system identifier!" }); + + var dbUser = new Models.User + { + AdministrationRights = model.AdministrationRights ?? AdministrationRights.None, + CreatedAt = DateTimeOffset.Now, + CreatedBy = AuthenticationContext.User, + Enabled = model.Enabled ?? false, + InstanceManagerRights = model.InstanceManagerRights ?? InstanceManagerRights.None, + Name = model.Name, + SystemIdentifier = model.SystemIdentifier, + InstanceUsers = new List() + }; + + if (model.SystemIdentifier != null) + try + { + using (var sysIdentity = await systemIdentityFactory.CreateSystemIdentity(dbUser, cancellationToken).ConfigureAwait(false)) + { + if (sysIdentity == null) + return StatusCode((int)HttpStatusCode.Gone); + dbUser.Name = sysIdentity.Username; + dbUser.SystemIdentifier = sysIdentity.Uid; + } + } + catch (NotImplementedException) + { + return StatusCode((int)HttpStatusCode.NotImplemented); + } + else + cryptographySuite.SetUserPassword(dbUser, model.Password); + + dbUser.CanonicalName = dbUser.Name.ToUpperInvariant(); + + DatabaseContext.Users.Add(dbUser); + + try + { + try + { + await DatabaseContext.Save(cancellationToken).ConfigureAwait(false); + } + catch (Exception e) + { + logger.LogInformation("Error creating user: {0}", e); + throw; + } + } + catch (DbUpdateConcurrencyException e) + { + return Conflict(new { message = e.Message }); + } + + return Json(dbUser.ToApi()); + } + + /// + [TgsAuthorize(AdministrationRights.EditUsers)] + public override async Task Update([FromBody] UserUpdate model, CancellationToken cancellationToken) + { + if (model == null) + throw new ArgumentNullException(nameof(model)); + + var originalUser = await DatabaseContext.Users.Where(x => x.Id == model.Id).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); + if (originalUser == default) + return StatusCode((int)HttpStatusCode.Gone); + + if (model.Password != null) + { + if (originalUser.PasswordHash == null) + return BadRequest(new { message = "Cannot convert a system user to a password user!" }); + cryptographySuite.SetUserPassword(originalUser, model.Password); + } + else if(model.SystemIdentifier != null && model.SystemIdentifier != originalUser.SystemIdentifier) + return BadRequest(new { message = "Cannot change a user's system identifier!" }); + + if (model.Name != null && model.Name.ToUpperInvariant() != originalUser.CanonicalName) + return BadRequest(new { message = "Can only change capitalization of a user's name!" }); + + originalUser.InstanceManagerRights = model.InstanceManagerRights ?? originalUser.InstanceManagerRights; + originalUser.AdministrationRights = model.AdministrationRights ?? originalUser.AdministrationRights; + originalUser.Enabled = model.Enabled ?? originalUser.Enabled; + originalUser.Name = model.Name ?? originalUser.Name; + + await DatabaseContext.Save(cancellationToken).ConfigureAwait(false); + + return Json(originalUser.ToApi()); + } + + /// + [TgsAuthorize] + public override Task Read(CancellationToken cancellationToken) => Task.FromResult(Json(AuthenticationContext.User.ToApi())); + + /// + [TgsAuthorize(AdministrationRights.EditUsers)] + public override async Task List(CancellationToken cancellationToken) + { + var users = await DatabaseContext.Users.ToListAsync(cancellationToken).ConfigureAwait(false); + return Json(users.Select(x => x.ToApi())); + } + + /// + [TgsAuthorize(AdministrationRights.EditUsers)] + public override async Task GetId(long id, CancellationToken cancellationToken) + { + var user = await DatabaseContext.Users.Where(x => x.Id == id).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); + if (user == default) + return NotFound(); + return Json(user.ToApi()); + } + } +} diff --git a/src/Tgstation.Server.Host/Core/Application.cs b/src/Tgstation.Server.Host/Core/Application.cs new file mode 100644 index 0000000000..78abc75d9c --- /dev/null +++ b/src/Tgstation.Server.Host/Core/Application.cs @@ -0,0 +1,269 @@ +using Byond.TopicSender; +using Cyberboss.AspNetCore.AsyncInitializer; +using Microsoft.AspNetCore.Authentication.JwtBearer; +using Microsoft.AspNetCore.Builder; +using Microsoft.AspNetCore.Hosting; +using Microsoft.AspNetCore.Hosting.Server.Features; +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using Microsoft.Extensions.Logging; +using Microsoft.IdentityModel.Tokens; +using Newtonsoft.Json; +using Octokit; +using System; +using System.Globalization; +using System.IdentityModel.Tokens.Jwt; +using System.Linq; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Threading.Tasks; +using Tgstation.Server.Host.Components; +using Tgstation.Server.Host.Components.Byond; +using Tgstation.Server.Host.Components.Chat; +using Tgstation.Server.Host.Components.Chat.Commands; +using Tgstation.Server.Host.Components.StaticFiles; +using Tgstation.Server.Host.Components.Watchdog; +using Tgstation.Server.Host.Configuration; +using Tgstation.Server.Host.Controllers; +using Tgstation.Server.Host.IO; +using Tgstation.Server.Host.Models; +using Tgstation.Server.Host.Security; + +namespace Tgstation.Server.Host.Core +{ + /// + sealed class Application : IApplication + { + /// + /// Prefix for string version names + /// + const string VersionPrefix = "tgstation-server"; + + /// + public string HostingPath => serverAddresses.Addresses.First(); + + /// + public Version Version { get; } + + /// + public string VersionString { get; } + + /// + /// The for the + /// + readonly Microsoft.Extensions.Configuration.IConfiguration configuration; + + /// + /// The for the + /// + readonly Microsoft.AspNetCore.Hosting.IHostingEnvironment hostingEnvironment; + + readonly TaskCompletionSource startupTcs; + + /// + /// The for the + /// + IServerAddressesFeature serverAddresses; + + /// + /// Construct an + /// + /// The value of + /// The value of + public Application(Microsoft.Extensions.Configuration.IConfiguration configuration, Microsoft.AspNetCore.Hosting.IHostingEnvironment hostingEnvironment) + { + this.configuration = configuration ?? throw new ArgumentNullException(nameof(configuration)); + this.hostingEnvironment = hostingEnvironment ?? throw new ArgumentNullException(nameof(hostingEnvironment)); + + startupTcs = new TaskCompletionSource(); + + Version = Assembly.GetExecutingAssembly().GetName().Version; + VersionString = String.Format(CultureInfo.InvariantCulture, "{0} v{1}", VersionPrefix, Version); + } + + /// + /// Configure dependency injected services + /// + /// The to configure +#pragma warning disable CA1822 // Mark members as static + public void ConfigureServices(IServiceCollection services) +#pragma warning restore CA1822 // Mark members as static + { + if (services == null) + throw new ArgumentNullException(nameof(services)); + + services.Configure(configuration.GetSection(UpdatesConfiguration.Section)); + var databaseConfigurationSection = configuration.GetSection(DatabaseConfiguration.Section); + services.Configure(databaseConfigurationSection); + var generalConfigurationSection = configuration.GetSection(GeneralConfiguration.Section); + services.Configure(generalConfigurationSection); + + //remember, anything you .Get manually can be null if the config is missing + var generalConfiguration = generalConfigurationSection.Get(); + var isWindows = RuntimeInformation.IsOSPlatform(OSPlatform.Windows); + var ioManager = new DefaultIOManager(); + + if (generalConfiguration?.DisableFileLogging != true) + { + var logPath = !String.IsNullOrEmpty(generalConfiguration?.LogFileDirectory) ? generalConfiguration.LogFileDirectory : ioManager.ConcatPath(Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData), VersionPrefix, "Logs"); + services.AddLogging(builder => builder.AddFile(ioManager.ConcatPath(logPath, "tgs-{Date}.log"))); + } + + services.AddOptions(); + + const string scheme = "JwtBearer"; + services.AddAuthentication((options) => + { + options.DefaultAuthenticateScheme = scheme; + options.DefaultChallengeScheme = scheme; + }).AddJwtBearer(scheme, jwtBearerOptions => + { + jwtBearerOptions.TokenValidationParameters = new TokenValidationParameters + { + ValidateIssuerSigningKey = true, + IssuerSigningKey = new SymmetricSecurityKey(TokenFactory.TokenSigningKey), + + ValidateIssuer = true, + ValidIssuer = TokenFactory.TokenIssuer, + + ValidateLifetime = true, + ValidateAudience = true, + ValidAudience = TokenFactory.TokenAudience, + + ClockSkew = TimeSpan.FromMinutes(5), + + RequireSignedTokens = true, + + RequireExpirationTime = true + }; + jwtBearerOptions.Events = new JwtBearerEvents + { + OnTokenValidated = ApiController.OnTokenValidated + }; + }); + JwtSecurityTokenHandler.DefaultInboundClaimTypeMap.Clear(); //fucking converts 'sub' to M$ bs + + services.AddMvc().AddJsonOptions(options => + { + options.SerializerSettings.NullValueHandling = NullValueHandling.Ignore; + }); + + var databaseConfiguration = databaseConfigurationSection.Get(); + + void ConfigureDatabase(DbContextOptionsBuilder builder) + { + if (hostingEnvironment.IsDevelopment()) + builder.EnableSensitiveDataLogging(); + }; + + switch (databaseConfiguration?.DatabaseType ?? DatabaseType.Sqlite) + { + case DatabaseType.MySql: + services.AddDbContext(ConfigureDatabase); + services.AddScoped(x => x.GetRequiredService()); + break; + case DatabaseType.Sqlite: + services.AddDbContext(ConfigureDatabase); + services.AddScoped(x => x.GetRequiredService()); + break; + case DatabaseType.SqlServer: + services.AddDbContext(ConfigureDatabase); + services.AddScoped(x => x.GetRequiredService()); + break; + default: + throw new InvalidOperationException(String.Format(CultureInfo.InvariantCulture, "Invalid {0}!", nameof(DatabaseType))); + } + + services.AddScoped(); + services.AddSingleton(); + + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton, PasswordHasher>(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(x => new GitHubClient(new ProductHeaderValue(VersionPrefix, Version.ToString()))); + + if (isWindows) + { + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + } + else + { + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + } + + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(); + services.AddSingleton(new ByondTopicSender + { + ReceiveTimeout = 5000, + SendTimeout = 5000 + }); + + services.AddSingleton(); + services.AddSingleton(x => x.GetRequiredService()); + services.AddSingleton(); + services.AddSingleton(x => x.GetRequiredService()); + services.AddSingleton(x => x.GetRequiredService()); + + services.AddSingleton(); + + services.AddSingleton(ioManager); + + services.AddSingleton(); + services.AddSingleton(x => x.GetRequiredService()); + + services.AddSingleton(this); + } + + /// + /// Configure the + /// + /// The to configure + /// The for the + public void Configure(IApplicationBuilder applicationBuilder, ILogger logger) + { + if (applicationBuilder == null) + throw new ArgumentNullException(nameof(applicationBuilder)); + if (logger == null) + throw new ArgumentNullException(nameof(logger)); + + logger.LogInformation(VersionString); + + serverAddresses = applicationBuilder.ServerFeatures.Get(); + + if (hostingEnvironment.IsDevelopment()) + applicationBuilder.UseDeveloperExceptionPage(); + + applicationBuilder.UseAsyncInitialization(async cancellationToken => + { + using (cancellationToken.Register(() => startupTcs.SetCanceled())) + await startupTcs.Task.ConfigureAwait(false); + }); + + applicationBuilder.UseAuthentication(); + applicationBuilder.UseMvc(); + } + + /// + public void Ready(Exception initializationError) + { + if (startupTcs.Task.IsCompleted) + throw new InvalidOperationException("Ready has already been called!"); + if (initializationError == null) + startupTcs.SetResult(null); + else + startupTcs.SetException(initializationError); + } + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Core/DatabaseContextFactory.cs b/src/Tgstation.Server.Host/Core/DatabaseContextFactory.cs new file mode 100644 index 0000000000..f3d64f355c --- /dev/null +++ b/src/Tgstation.Server.Host/Core/DatabaseContextFactory.cs @@ -0,0 +1,29 @@ +using Microsoft.Extensions.DependencyInjection; +using System; +using System.Threading.Tasks; +using Tgstation.Server.Host.Models; + +namespace Tgstation.Server.Host.Core +{ + /// + sealed class DatabaseContextFactory : IDatabaseContextFactory + { + /// + /// The for the + /// + readonly IServiceProvider serviceProvider; + + /// + /// Construct a + /// + /// The value of + public DatabaseContextFactory(IServiceProvider serviceProvider) => this.serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); + + /// + public async Task UseContext(Func operation) + { + using (var scope = serviceProvider.CreateScope()) + await operation(scope.ServiceProvider.GetRequiredService()).ConfigureAwait(false); + } + } +} diff --git a/src/Tgstation.Server.Host/Core/IApplication.cs b/src/Tgstation.Server.Host/Core/IApplication.cs new file mode 100644 index 0000000000..1cbe820b4c --- /dev/null +++ b/src/Tgstation.Server.Host/Core/IApplication.cs @@ -0,0 +1,31 @@ +using System; + +namespace Tgstation.Server.Host.Core +{ + /// + /// Configures the ASP.NET Core web application + /// + public interface IApplication + { + /// + /// A more verbose version of + /// + string VersionString { get; } + + /// + /// The version of the + /// + Version Version { get; } + + /// + /// The url the server can be reached at locally + /// + string HostingPath { get; } + + /// + /// Mark the as ready to run + /// + /// The that put the application in a corrupted state if any + void Ready(Exception initializationError); + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Core/IDatabaseContextFactory.cs b/src/Tgstation.Server.Host/Core/IDatabaseContextFactory.cs new file mode 100644 index 0000000000..305092416c --- /dev/null +++ b/src/Tgstation.Server.Host/Core/IDatabaseContextFactory.cs @@ -0,0 +1,19 @@ +using System; +using System.Threading.Tasks; +using Tgstation.Server.Host.Models; + +namespace Tgstation.Server.Host.Core +{ + /// + /// Factory for scoping usage of s. Meant for use by + /// + interface IDatabaseContextFactory + { + /// + /// Run an in the scope of an + /// + /// The operation to run + /// A representing the running + Task UseContext(Func operation); + } +} diff --git a/src/Tgstation.Server.Host/Core/IJobManager.cs b/src/Tgstation.Server.Host/Core/IJobManager.cs new file mode 100644 index 0000000000..beaa6010eb --- /dev/null +++ b/src/Tgstation.Server.Host/Core/IJobManager.cs @@ -0,0 +1,38 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using System; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Host.Models; + +namespace Tgstation.Server.Host.Core +{ + /// + /// Manages the runtime of s + /// + public interface IJobManager : IHostedService + { + /// + /// Get the for a job + /// + int? JobProgress(Job job); + + /// + /// Registers a given and begins running it + /// + /// The + /// The operation to run taking the started , a progress reporter and a + /// The for the operation + /// A representing a running operation + Task RegisterOperation(Job job, Func, CancellationToken, Task> operation, CancellationToken cancellationToken); + + /// + /// Cancels a give + /// + /// The to cancel + /// The who cancelled the + /// The for the operation + /// A representing a running operation + Task CancelJob(Job job, User user, CancellationToken cancellationToken); + } +} diff --git a/src/Tgstation.Server.Host/Core/IServerControl.cs b/src/Tgstation.Server.Host/Core/IServerControl.cs new file mode 100644 index 0000000000..377b7d4a9c --- /dev/null +++ b/src/Tgstation.Server.Host/Core/IServerControl.cs @@ -0,0 +1,34 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Host.IO; + +namespace Tgstation.Server.Host.Core +{ + /// + /// Represents a service that may take an updated assembly and run it, stopping the current assembly in the process + /// + public interface IServerControl + { + /// + /// Run a new assembly and stop the current one. This will likely trigger all active s + /// + /// The s of the .zip file that contains the new assembly + /// The for the operation + /// The for the operation + /// A resulting in if live updates are supported, otherwise + Task ApplyUpdate(byte[] updateZipData, IIOManager ioManager, CancellationToken cancellationToken); + + /// + /// Register a given to run before stopping the server for a restart + /// + /// The to run + void RegisterForRestart(Action action); + + /// + /// Restarts the + /// + /// if live restarts are supported, otherwise + bool Restart(); + } +} diff --git a/src/Tgstation.Server.Host/Core/JobHandler.cs b/src/Tgstation.Server.Host/Core/JobHandler.cs new file mode 100644 index 0000000000..d80bbd1322 --- /dev/null +++ b/src/Tgstation.Server.Host/Core/JobHandler.cs @@ -0,0 +1,68 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Host.Core +{ + /// + /// Class for pairing s with s + /// + sealed class JobHandler : IDisposable + { + /// + /// The being run + /// + readonly Task task; + /// + /// The for + /// + readonly CancellationTokenSource cancellationTokenSource; + + /// + /// Construct a + /// + /// The value of + /// The value of + JobHandler(Task task, CancellationTokenSource cancellationTokenSource) + { + this.task = task; + this.cancellationTokenSource = cancellationTokenSource; + } + + /// + public void Dispose() => cancellationTokenSource.Dispose(); + + /// + /// The progress of the job + /// + public int? Progress { get; set; } + + /// + /// Wait for to complete + /// + /// The for the operation + /// A representing the running operation + public async Task Wait(CancellationToken cancellationToken) + { + TaskCompletionSource tcs = new TaskCompletionSource(); + using (cancellationToken.Register(() => tcs.SetCanceled())) + await Task.WhenAny(tcs.Task, task).ConfigureAwait(false); + } + + /// + /// Cancels + /// + public void Cancel() => cancellationTokenSource.Cancel(); + + /// + /// Create a + /// + /// A taking a and returning a that the will wrap + /// A new + public static JobHandler Create(Func job) + { + var cts = new CancellationTokenSource(); + return new JobHandler(job(cts.Token), cts); + } + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Core/JobManager.cs b/src/Tgstation.Server.Host/Core/JobManager.cs new file mode 100644 index 0000000000..c059cdb7ed --- /dev/null +++ b/src/Tgstation.Server.Host/Core/JobManager.cs @@ -0,0 +1,205 @@ +using Microsoft.Extensions.DependencyInjection; +using Microsoft.Extensions.Hosting; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Host.Models; + +namespace Tgstation.Server.Host.Core +{ + /// + sealed class JobManager : IJobManager, IDisposable + { + /// + /// The for the + /// + readonly IServiceProvider serviceProvider; + /// + /// of to running s + /// + readonly Dictionary jobs; + + /// + /// Construct a + /// + /// The value of + public JobManager(IServiceProvider serviceProvider) + { + this.serviceProvider = serviceProvider ?? throw new ArgumentNullException(nameof(serviceProvider)); + jobs = new Dictionary(); + } + + /// + public void Dispose() + { + foreach (var job in jobs) + job.Value.Dispose(); + } + + /// + /// Gets the for a given if it exists + /// + /// The to get the for + /// The + JobHandler CheckGetJob(Job job) + { + lock (this) + { + if (!jobs.TryGetValue(job.Id, out JobHandler jobHandler)) + throw new InvalidOperationException("Job not running!"); + return jobHandler; + } + } + + /// + /// Runner for s + /// + /// The being run + /// The operation for the + /// The for the operation + /// A representing the running operation + async Task RunJob(Job job, Func operation, CancellationToken cancellationToken) + { + try + { + using (var scope = serviceProvider.CreateScope()) + { + IDatabaseContext databaseContext = null; + try + { + var oldJob = job; + job = new Job { Id = oldJob.Id }; + try + { + await operation(job, scope.ServiceProvider, cancellationToken).ConfigureAwait(false); + } + finally + { + databaseContext = scope.ServiceProvider.GetRequiredService(); + databaseContext.Jobs.Attach(job); + } + } + catch (OperationCanceledException) + { + job.Cancelled = true; + } + catch (Exception e) + { + job.ExceptionDetails = e.ToString(); + } + job.StoppedAt = DateTimeOffset.Now; + await databaseContext.Save(default).ConfigureAwait(false); + } + } + finally + { + lock (this) + { + var handler = jobs[job.Id]; + jobs.Remove(job.Id); + handler.Dispose(); + } + } + } + + /// + public async Task RegisterOperation(Job job, Func, CancellationToken, Task> operation, CancellationToken cancellationToken) + { + using (var scope = serviceProvider.CreateScope()) + { + var databaseContext = scope.ServiceProvider.GetRequiredService(); + job.StartedAt = DateTimeOffset.Now; + job.Cancelled = false; + job.Instance = new Instance + { + Id = job.Instance.Id + }; + databaseContext.Instances.Attach(job.Instance); + if (job.StartedBy != null) + { + job.StartedBy = new User + { + Id = job.StartedBy.Id + }; + databaseContext.Users.Attach(job.StartedBy); + } + databaseContext.Jobs.Add(job); + await databaseContext.Save(cancellationToken).ConfigureAwait(false); + var jobHandler = JobHandler.Create(x => RunJob(job, (jobParam, serviceProvider, ct) => + operation(jobParam, serviceProvider, y => + { + lock (this) + if (jobs.TryGetValue(job.Id, out var handler)) + handler.Progress = y; + }, ct), + x)); + lock (this) + jobs.Add(job.Id, jobHandler); + } + } + + /// + public async Task StartAsync(CancellationToken cancellationToken) + { + using (var scope = serviceProvider.CreateScope()) + { + var databaseContext = scope.ServiceProvider.GetRequiredService(); + + //mark all jobs as cancelled + var enumerator = await databaseContext.Jobs.Where(y => !y.Cancelled.Value && !y.StoppedAt.HasValue).Select(y => y.Id).ToAsyncEnumerable().ToList(cancellationToken).ConfigureAwait(false); + foreach(var I in enumerator) + { + var job = new Job { Id = I }; + databaseContext.Jobs.Attach(job); + job.Cancelled = true; + } + await databaseContext.Save(cancellationToken).ConfigureAwait(false); + } + } + + /// + public async Task StopAsync(CancellationToken cancellationToken) + { + var joinTasks = jobs.Select(x => + { + x.Value.Cancel(); + return x.Value.Wait(cancellationToken); + }); + await Task.WhenAll(joinTasks).ConfigureAwait(false); + } + + /// + public async Task CancelJob(Job job, User user, CancellationToken cancellationToken) + { + if (job == null) + throw new ArgumentNullException(nameof(job)); + if (user == null) + throw new ArgumentNullException(nameof(user)); + CheckGetJob(job).Cancel(); //this will ensure the db update is only done once + using (var scope = serviceProvider.CreateScope()) + { + var databaseContext = scope.ServiceProvider.GetRequiredService(); + job = new Job { Id = job.Id }; + databaseContext.Jobs.Attach(job); + user = new User { Id = user.Id }; + databaseContext.Users.Attach(user); + job.CancelledBy = user; + //let either startup or cancellation set job.cancelled + await databaseContext.Save(cancellationToken).ConfigureAwait(false); + } + } + + /// + public int? JobProgress(Job job) + { + lock (this) + { + if (!jobs.TryGetValue(job.Id, out var handler)) + return null; + return handler.Progress; + } + } + } +} diff --git a/src/Tgstation.Server.Host/Core/SemaphoreSlimContext.cs b/src/Tgstation.Server.Host/Core/SemaphoreSlimContext.cs new file mode 100644 index 0000000000..81b5d76d67 --- /dev/null +++ b/src/Tgstation.Server.Host/Core/SemaphoreSlimContext.cs @@ -0,0 +1,60 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Host.Core +{ + /// + /// Async lock context helper + /// + public sealed class SemaphoreSlimContext : IDisposable + { + /// + /// Asyncronously locks a + /// + /// The to lock + /// The for the operation + /// A resulting in the for the lock + public static async Task Lock(SemaphoreSlim semaphore, CancellationToken cancellationToken) + { + if (semaphore == null) + throw new ArgumentNullException(nameof(semaphore)); + await semaphore.WaitAsync(cancellationToken).ConfigureAwait(false); + cancellationToken.ThrowIfCancellationRequested(); + return new SemaphoreSlimContext(semaphore); + } + + /// + /// The locked + /// + readonly SemaphoreSlim lockedSemaphore; + + /// + /// If has been called + /// + bool disposed; + + /// + /// Construct a + /// + /// The value of + SemaphoreSlimContext(SemaphoreSlim lockedSemaphore) => this.lockedSemaphore = lockedSemaphore; + + /// + /// Finalize the + /// + ~SemaphoreSlimContext() => Dispose(); + + /// + /// Release the lock on + /// + public void Dispose() + { + if (disposed) + return; + lockedSemaphore.Release(); + GC.SuppressFinalize(this); + disposed = true; + } + } +} diff --git a/src/Tgstation.Server.Host/IO/DefaultIOManager.cs b/src/Tgstation.Server.Host/IO/DefaultIOManager.cs new file mode 100644 index 0000000000..c4b235b3d7 --- /dev/null +++ b/src/Tgstation.Server.Host/IO/DefaultIOManager.cs @@ -0,0 +1,318 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.IO.Compression; +using System.Linq; +using System.Net; +using System.Text; +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Host.IO +{ + /// + /// that resolves paths to + /// + class DefaultIOManager : IIOManager + { + /// + /// Default buffer size used by .NET + /// + public const int DefaultBufferSize = 4096; + + /// + /// Recursively empty a directory + /// + /// of the directory to empty + /// The for the operation + /// A representing the running operation + static async Task NormalizeAndDelete(DirectoryInfo dir, CancellationToken cancellationToken) + { + var tasks = new List(); + + foreach (var subDir in dir.EnumerateDirectories()) + { + cancellationToken.ThrowIfCancellationRequested(); + tasks.Add(NormalizeAndDelete(subDir, cancellationToken)); + } + foreach (var file in dir.EnumerateFiles()) + { + cancellationToken.ThrowIfCancellationRequested(); + file.Attributes = FileAttributes.Normal; + file.Delete(); + } + await Task.WhenAll(tasks).ConfigureAwait(false); + cancellationToken.ThrowIfCancellationRequested(); + dir.Delete(true); + } + + /// + /// Opens a for async writing at a given + /// + /// The path to open the at + /// A new ready for async writing + static FileStream OpenWriteStream(string path) => new FileStream(path, FileMode.Create, FileAccess.Write, FileShare.ReadWrite, DefaultBufferSize, true); + + /// + /// Copies a directory from to + /// + /// The source directory path + /// The destination directory path + /// Files and folders to ignore at the root level + /// The for the operation + /// A representing the running operation + async Task CopyDirectoryImpl(string src, string dest, IEnumerable ignore, CancellationToken cancellationToken) + { + await CreateDirectory(dest, cancellationToken).ConfigureAwait(false); + + var dir = new DirectoryInfo(src); + cancellationToken.ThrowIfCancellationRequested(); + + var dirs = dir.EnumerateDirectories(); + var files = dir.EnumerateFiles(); + + var fileCopyTasks = files.Select(x => + { + cancellationToken.ThrowIfCancellationRequested(); + if (ignore != null && ignore.Contains(x.Name)) + return Task.CompletedTask; + return CopyFile(x.FullName, Path.Combine(dest, x.Name), cancellationToken); + }); + + var directoryCopyTasks = dirs.Select(x => + { + cancellationToken.ThrowIfCancellationRequested(); + if (ignore != null && ignore.Contains(x.Name)) + return Task.CompletedTask; + return CopyDirectoryImpl(x.FullName, Path.Combine(dest, x.Name), null, cancellationToken); + }); + + await Task.WhenAll(fileCopyTasks.Concat(directoryCopyTasks)).ConfigureAwait(false); + } + + /// + public async Task CopyDirectory(string src, string dest, IEnumerable ignore, CancellationToken cancellationToken) + { + if (dest == null) + throw new ArgumentNullException(nameof(src)); + if (dest == null) + throw new ArgumentNullException(nameof(src)); + + src = ResolvePath(src); + dest = ResolvePath(dest); + await CopyDirectoryImpl(src, dest, ignore, cancellationToken).ConfigureAwait(false); + } + + /// + public async Task AppendAllText(string path, string additional_contents, CancellationToken cancellationToken) + { + if (additional_contents == null) + throw new ArgumentNullException(nameof(additional_contents)); + using (var destStream = new FileStream(ResolvePath(path), FileMode.Append, FileAccess.Write, FileShare.ReadWrite | FileShare.Delete, DefaultBufferSize, true)) + { + var buf = Encoding.UTF8.GetBytes(additional_contents); + await destStream.WriteAsync(buf, 0, buf.Length, cancellationToken).ConfigureAwait(false); + } + } + + /// + public string ConcatPath(params string[] paths) + { + if (paths == null) + throw new ArgumentNullException(nameof(paths)); + return Path.Combine(paths); + } + + /// + public async Task CopyFile(string src, string dest, CancellationToken cancellationToken) + { + if (src == null) + throw new ArgumentNullException(nameof(src)); + if (dest == null) + throw new ArgumentNullException(nameof(dest)); + using (var srcStream = new FileStream(ResolvePath(src), FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete, DefaultBufferSize, true)) + using (var destStream = new FileStream(ResolvePath(dest), FileMode.Create, FileAccess.Write, FileShare.ReadWrite | FileShare.Delete, DefaultBufferSize, true)) + await srcStream.CopyToAsync(destStream, DefaultBufferSize, cancellationToken).ConfigureAwait(false); + } + + /// + public Task CreateDirectory(string path, CancellationToken cancellationToken) => Task.Factory.StartNew(() => Directory.CreateDirectory(ResolvePath(path)), cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current); + + /// + public async Task DeleteDirectory(string path, CancellationToken cancellationToken) + { + path = ResolvePath(path); + var di = new DirectoryInfo(path); + if (!di.Exists) + return; + await NormalizeAndDelete(di, cancellationToken).ConfigureAwait(false); + } + + /// + public Task DeleteFile(string path, CancellationToken cancellationToken) => Task.Factory.StartNew(() => File.Delete(ResolvePath(path)), cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current); + + /// + public Task FileExists(string path, CancellationToken cancellationToken) => Task.Factory.StartNew(() => File.Exists(ResolvePath(path)), cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current); + + /// + public Task DirectoryExists(string path, CancellationToken cancellationToken) => Task.Factory.StartNew(() => Directory.Exists(ResolvePath(path)), cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current); + + /// + public string GetDirectoryName(string path) => Path.GetDirectoryName(path ?? throw new ArgumentNullException(nameof(path))); + + /// + public string GetFileName(string path) => Path.GetFileName(path ?? throw new ArgumentNullException(nameof(path))); + + /// + public string GetFileNameWithoutExtension(string path) => Path.GetFileNameWithoutExtension(path ?? throw new ArgumentNullException(nameof(path))); + + /// + public Task> GetFilesWithExtension(string path, string extension, CancellationToken cancellationToken) => Task.Factory.StartNew(() => + { + path = ResolvePath(path); + if (extension == null) + throw new ArgumentNullException(extension); + var results = new List(); + foreach (var I in Directory.EnumerateFiles(path, String.Format(CultureInfo.InvariantCulture, "*.{0}", extension), SearchOption.TopDirectoryOnly)) + { + cancellationToken.ThrowIfCancellationRequested(); + results.Add(I); + } + return results; + }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current); + + /// + public Task MoveFile(string source, string destination, CancellationToken cancellationToken) => Task.Factory.StartNew(() => + { + if (destination == null) + throw new ArgumentNullException(nameof(destination)); + source = ResolvePath(source ?? throw new ArgumentNullException(nameof(source))); + destination = ResolvePath(destination); + File.Move(source, destination); + }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current); + + /// + public Task MoveDirectory(string source, string destination, CancellationToken cancellationToken) => Task.Factory.StartNew(() => + { + if (destination == null) + throw new ArgumentNullException(nameof(destination)); + source = ResolvePath(source ?? throw new ArgumentNullException(nameof(source))); + destination = ResolvePath(destination); + Directory.Move(source, destination); + }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current); + + /// + public async Task ReadAllBytes(string path, CancellationToken cancellationToken) + { + path = ResolvePath(path); + using (var file = new FileStream(path, FileMode.Open, FileAccess.Read, FileShare.Read | FileShare.Delete, DefaultBufferSize, true)) + { + byte[] buf; + buf = new byte[file.Length]; + await file.ReadAsync(buf, 0, (int)file.Length, cancellationToken).ConfigureAwait(false); + return buf; + } + } + + /// + public virtual string ResolvePath(string path) => Path.GetFullPath(path ?? throw new ArgumentNullException(nameof(path))); + + /// + public async Task WriteAllBytes(string path, byte[] contents, CancellationToken cancellationToken) + { + path = ResolvePath(path); + using (var file = OpenWriteStream(path)) + await file.WriteAsync(contents, 0, contents.Length, cancellationToken).ConfigureAwait(false); + } + + /// + public Task> GetDirectories(string path, CancellationToken cancellationToken) => Task.Factory.StartNew(() => + { + path = ResolvePath(path); + var results = new List(); + cancellationToken.ThrowIfCancellationRequested(); + foreach (var I in Directory.EnumerateDirectories(path)) + { + results.Add(I); + cancellationToken.ThrowIfCancellationRequested(); + } + return (IReadOnlyList)results; + }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current); + + /// + public Task> GetFiles(string path, CancellationToken cancellationToken) => Task.Factory.StartNew(() => + { + path = ResolvePath(path); + var results = new List(); + cancellationToken.ThrowIfCancellationRequested(); + foreach (var I in Directory.EnumerateFiles(path)) + { + results.Add(I); + cancellationToken.ThrowIfCancellationRequested(); + } + return (IReadOnlyList)results; + }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current); + + /// + public Task CreateSymlink(string target, string link, CancellationToken cancellationToken) => Task.Factory.StartNew(() => + { + target = ResolvePath(target); + link = ResolvePath(link); + + }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current); + + /// + public async Task DownloadFile(Uri url, CancellationToken cancellationToken) + { + //DownloadDataTaskAsync can't be cancelled and is shittily written, don't use it + using (var wc = new WebClient()) + { + var tcs = new TaskCompletionSource(); + wc.DownloadDataCompleted += (a, b) => + { + if (b.Error != null) + tcs.TrySetException(b.Error); + else if (b.Cancelled) + tcs.TrySetCanceled(); + else + tcs.TrySetResult(b.Result); + }; + wc.DownloadDataAsync(url); + using (cancellationToken.Register(() => + { + wc.CancelAsync(); //cancelasync alone doesnt do it either! who wrote this!! + tcs.SetCanceled(); + })) + return await tcs.Task.ConfigureAwait(false); + } //ITS STILL FUCKING DOWNLOADING!!! + } + + /// + public async Task ZipToDirectory(string path, byte[] zipFileBytes, CancellationToken cancellationToken) + { + path = ResolvePath(path); + if (zipFileBytes == null) + throw new ArgumentNullException(nameof(zipFileBytes)); + + using (var ms = new MemoryStream(zipFileBytes)) + { + zipFileBytes = null; + using (var archive = new ZipArchive(ms)) + { + string GetEntryName(ZipArchiveEntry entry) => ConcatPath(path, entry.FullName); + //create directories first + await Task.WhenAll(CreateDirectory(path, cancellationToken), Task.WhenAll(archive.Entries.Where(x => x.Name.Length == 0).Select(x => CreateDirectory(GetEntryName(x), cancellationToken)))).ConfigureAwait(false); + //extract files + await Task.WhenAll(archive.Entries.Where(x => x.Name.Length > 0).Select(async x => + { + var entryPath = GetEntryName(x); + using (var stream = x.Open()) + using (var file = OpenWriteStream(entryPath)) + await stream.CopyToAsync(file).ConfigureAwait(false); + })).ConfigureAwait(false); + } + } + } + } +} diff --git a/src/Tgstation.Server.Host/IO/IIOManager.cs b/src/Tgstation.Server.Host/IO/IIOManager.cs new file mode 100644 index 0000000000..da63c6d117 --- /dev/null +++ b/src/Tgstation.Server.Host/IO/IIOManager.cs @@ -0,0 +1,184 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Host.IO +{ + /// + /// Interface for using filesystems + /// + public interface IIOManager + { + /// + /// Retrieve the full path of some given a relative path. Must be used before passing relative paths to other APIs. All other operations in this call this internally on given paths + /// + /// Some path to retrieve the full path of + /// as a full canonical path + string ResolvePath(string path); + + /// + /// Gets the file name portion of a + /// + /// The path to get the file name of + /// The file name portion of + string GetFileName(string path); + + /// + /// Gets the file name portion of a with + /// + /// The path to get the file name of + /// The file name portion of + string GetFileNameWithoutExtension(string path); + + /// + /// Copies a directory from to + /// + /// The source directory path + /// The destination directory path + /// Files and folders to ignore at the root level + /// The for the operation + /// A representing the running operation + Task CopyDirectory(string src, string dest, IEnumerable ignore, CancellationToken cancellationToken); + + /// + /// Check that the file at exists + /// + /// The file to check for existence + /// The for the operation + /// A resulting in if the file at exists, otherwise + Task FileExists(string path, CancellationToken cancellationToken); + + /// + /// Check that the directory at exists + /// + /// The directory to check for existence + /// The for the operation + /// A resulting in if the directory at exists, otherwise + Task DirectoryExists(string path, CancellationToken cancellationToken); + + /// + /// Returns all the contents of a file at as a array + /// + /// The path of the file to read + /// A for the operation + /// A that results in the contents of a file at + Task ReadAllBytes(string path, CancellationToken cancellationToken); + + /// + /// Returns directory names in a given + /// + /// The path to search for directories + /// The for the operation + /// A resulting in the directories in + Task> GetDirectories(string path, CancellationToken cancellationToken); + + /// + /// Returns file names in a given + /// + /// The path to search for files + /// The for the operation + /// A resulting in the files in + Task> GetFiles(string path, CancellationToken cancellationToken); + + /// + /// Writes some to a file at overwriting previous content + /// + /// The path of the file to write + /// The contents of the file + /// A for the operation + /// A representing the running operation + Task WriteAllBytes(string path, byte[] contents, CancellationToken cancellationToken); + + /// + /// Copy a file from to + /// + /// The source file to copy + /// The destination path + /// A for the operation + /// A representing the running operation + Task CopyFile(string src, string dest, CancellationToken cancellationToken); + + /// + /// Gets the directory portion of a given + /// + /// A path to check + /// The directory portion of the given + string GetDirectoryName(string path); + + /// + /// Gets a list of files in with the given + /// + /// The directory which contains the files + /// The extension to look for without the preceeding "." + /// A for the operation + /// A resulting in a list of paths to files in with the given + Task> GetFilesWithExtension(string path, string extension, CancellationToken cancellationToken); + + /// + /// Deletes a file at + /// + /// The path of the file to delete + /// A for the operation + /// A representing the running operation + Task DeleteFile(string path, CancellationToken cancellationToken); + + /// + /// Create a directory at + /// + /// The path of the directory to create + /// A for the operation + /// A representing the running operation + Task CreateDirectory(string path, CancellationToken cancellationToken); + + /// + /// Recursively delete a directory + /// + /// The path to the directory to delete + /// A for the operation + /// A representing the running operation + Task DeleteDirectory(string path, CancellationToken cancellationToken); + + /// + /// Combines an array of strings into a path + /// + /// The paths to combine + /// The combined path + string ConcatPath(params string[] paths); + + /// + /// Moves a file at to + /// + /// The source file path + /// The destination path + /// A for the operation + /// A representing the running operation + Task MoveFile(string source, string destination, CancellationToken cancellationToken); + + /// + /// Moves a directory at to + /// + /// The source directory path + /// The destination path + /// A for the operation + /// A representing the running operation + Task MoveDirectory(string source, string destination, CancellationToken cancellationToken); + + /// + /// Downloads a file from + /// + /// The URL to download + /// A for the operation + /// A resulting in the s of the downloaded file + Task DownloadFile(Uri url, CancellationToken cancellationToken); + + /// + /// Extract a set of to a given + /// + /// The path to unzip to + /// The s of the + /// The for the operation + /// A representing the running operation + Task ZipToDirectory(string path, byte[] zipFileBytes, CancellationToken cancellationToken); + } +} diff --git a/src/Tgstation.Server.Host/IO/ISymlinkFactory.cs b/src/Tgstation.Server.Host/IO/ISymlinkFactory.cs new file mode 100644 index 0000000000..b92269404b --- /dev/null +++ b/src/Tgstation.Server.Host/IO/ISymlinkFactory.cs @@ -0,0 +1,20 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Host.IO +{ + /// + /// For creating filesystem symbolic links + /// + interface ISymlinkFactory + { + /// + /// Create a symbolic link + /// + /// The path to the hard target + /// The path to the link + /// The for the operation + /// A representing the running operation + Task CreateSymbolicLink(string targetPath, string linkPath, CancellationToken cancellationToken); + } +} diff --git a/src/Tgstation.Server.Host/IO/ISynchronousIOManager.cs b/src/Tgstation.Server.Host/IO/ISynchronousIOManager.cs new file mode 100644 index 0000000000..b0d61e969b --- /dev/null +++ b/src/Tgstation.Server.Host/IO/ISynchronousIOManager.cs @@ -0,0 +1,44 @@ +using System.Collections.Generic; +using System.Threading; + +namespace Tgstation.Server.Host.IO +{ + /// + /// For accessing the disk in a synchronous manner + /// + interface ISynchronousIOManager + { + /// + /// Enumerate files in a given + /// + /// The path to look for files in + /// The for the operation + /// A of file names in + IEnumerable GetFiles(string path, CancellationToken cancellationToken); + + /// + /// Enumerate directories in a given + /// + /// The path to look for directories in + /// The for the operation + /// A of directory names in + IEnumerable GetDirectories(string path, CancellationToken cancellationToken); + + /// + /// Read the s of a file at a given + /// + /// The path of the file to read + /// A array representing the contents of the file at + byte[] ReadFile(string path); + + /// + /// Write to a file at a given . + /// + /// The path to the file to write + /// The new contents of the file + /// If the file exists, this function only succeeds if this parameter matches the SHA-1 hash of the contents of the current file + /// The for the operation + /// on success, if the operation failed due to not matching the file's contents + bool WriteFileChecked(string path, byte[] data, string previousSha1, CancellationToken cancellationToken); + } +} diff --git a/src/Tgstation.Server.Host/IO/PosixSymlinkFactory.cs b/src/Tgstation.Server.Host/IO/PosixSymlinkFactory.cs new file mode 100644 index 0000000000..90a00410be --- /dev/null +++ b/src/Tgstation.Server.Host/IO/PosixSymlinkFactory.cs @@ -0,0 +1,27 @@ +using Mono.Unix; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Host.IO +{ + /// + /// for posix systems + /// + sealed class PosixSymlinkFactory : ISymlinkFactory + { + /// + public Task CreateSymbolicLink(string targetPath, string linkPath, CancellationToken cancellationToken) => Task.Factory.StartNew(() => + { + UnixFileSystemInfo fsInfo; + var isFile = File.Exists(targetPath); + cancellationToken.ThrowIfCancellationRequested(); + if (isFile) + fsInfo = new UnixFileInfo(targetPath); + else + fsInfo = new UnixDirectoryInfo(targetPath); + cancellationToken.ThrowIfCancellationRequested(); + fsInfo.CreateSymbolicLink(linkPath); + }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current); + } +} diff --git a/src/Tgstation.Server.Host/IO/ResolvingIOManager.cs b/src/Tgstation.Server.Host/IO/ResolvingIOManager.cs new file mode 100644 index 0000000000..c10ac40b6c --- /dev/null +++ b/src/Tgstation.Server.Host/IO/ResolvingIOManager.cs @@ -0,0 +1,39 @@ +using System; +using System.IO; + +namespace Tgstation.Server.Host.IO +{ + /// + /// An that resolve relative paths from another to a subdirectory of that + /// + sealed class ResolvingIOManager : DefaultIOManager + { + /// + /// Path to the subdirectory attached to path resolutions + /// + readonly string subdirectory; + + /// + /// Construct a + /// + /// The that resolves to the directory to work out of + /// The value of + public ResolvingIOManager(IIOManager parent, string _subdirectory) + { + if(parent == null) + throw new ArgumentNullException(nameof(parent)); + if (_subdirectory == null) + throw new ArgumentNullException(nameof(_subdirectory)); + + subdirectory = ConcatPath(parent.ResolvePath("."), _subdirectory); + } + + /// + public override string ResolvePath(string path) + { + if (!Path.IsPathRooted(path)) + return base.ResolvePath(ConcatPath(subdirectory, path)); + return path; + } + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Host/IO/SynchronousIOManager.cs b/src/Tgstation.Server.Host/IO/SynchronousIOManager.cs new file mode 100644 index 0000000000..cc00821fdd --- /dev/null +++ b/src/Tgstation.Server.Host/IO/SynchronousIOManager.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Security.Cryptography; +using System.Threading; + +namespace Tgstation.Server.Host.IO +{ + /// + sealed class SynchronousIOManager : ISynchronousIOManager + { + /// + public IEnumerable GetDirectories(string path, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + foreach (var I in Directory.EnumerateDirectories(path)) + { + yield return I; + cancellationToken.ThrowIfCancellationRequested(); + } + } + + /// + public IEnumerable GetFiles(string path, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + foreach (var I in Directory.EnumerateFiles(path)) + { + yield return I; + cancellationToken.ThrowIfCancellationRequested(); + } + } + + /// + public byte[] ReadFile(string path) => File.ReadAllBytes(path); + + /// + public bool WriteFileChecked(string path, byte[] data, string previousSha1, CancellationToken cancellationToken) + { + cancellationToken.ThrowIfCancellationRequested(); + using (var file = File.Open(path, FileMode.OpenOrCreate, FileAccess.ReadWrite, FileShare.None)) + { + using (var readMs = new MemoryStream()) + { + cancellationToken.ThrowIfCancellationRequested(); + file.CopyTo(readMs); + if (readMs.Length != 0 && previousSha1 == null) + return false; //no sha1? no write + //suppressed due to only using for consistency checks +#pragma warning disable CA5350 // Do not use insecure cryptographic algorithm SHA1. + using (var sha1 = new SHA1Managed()) +#pragma warning restore CA5350 // Do not use insecure cryptographic algorithm SHA1. + { + var sha1String = String.Join("", sha1.ComputeHash(readMs).Select(b => b.ToString("x2", CultureInfo.InvariantCulture))); + if (sha1String != previousSha1) + return false; + } + } + cancellationToken.ThrowIfCancellationRequested(); + + if (data != null) + { + file.Seek(0, SeekOrigin.Begin); + + cancellationToken.ThrowIfCancellationRequested(); + file.SetLength(data.Length); + + cancellationToken.ThrowIfCancellationRequested(); + file.Write(data, 0, data.Length); + } + } + if (data == null) + File.Delete(path); + return true; + } + } +} diff --git a/src/Tgstation.Server.Host/IO/WindowsSymlinkFactory.cs b/src/Tgstation.Server.Host/IO/WindowsSymlinkFactory.cs new file mode 100644 index 0000000000..824b5375f5 --- /dev/null +++ b/src/Tgstation.Server.Host/IO/WindowsSymlinkFactory.cs @@ -0,0 +1,26 @@ +using System.ComponentModel; +using System.IO; +using System.Runtime.InteropServices; +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Host.IO +{ + /// + /// for windows systems + /// + sealed class WindowsSymlinkFactory : ISymlinkFactory + { + /// + public Task CreateSymbolicLink(string targetPath, string linkPath, CancellationToken cancellationToken) => Task.Factory.StartNew(() => + { + //check if its not a file + var flags = File.Exists(targetPath) ? 0 : 1; //SYMBOLIC_LINK_FLAG_DIRECTORY + flags |= 2; //SYMBOLIC_LINK_FLAG_ALLOW_UNPRIVILEGED_CREATE on win10 1607+ + + cancellationToken.ThrowIfCancellationRequested(); + if (!NativeMethods.CreateSymbolicLink(linkPath, targetPath, flags)) + throw new Win32Exception(Marshal.GetLastWin32Error()); + }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current); + } +} diff --git a/src/Tgstation.Server.Host/IServer.cs b/src/Tgstation.Server.Host/IServer.cs new file mode 100644 index 0000000000..531b3b8afc --- /dev/null +++ b/src/Tgstation.Server.Host/IServer.cs @@ -0,0 +1,24 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Host +{ + /// + /// Represents the host + /// + public interface IServer : IDisposable + { + /// + /// If the should restart + /// + bool RestartRequested { get; } + + /// + /// Runs the + /// + /// The for the operation + /// A representing the running operation + Task RunAsync(CancellationToken cancellationToken); + } +} diff --git a/src/Tgstation.Server.Host/IServerFactory.cs b/src/Tgstation.Server.Host/IServerFactory.cs new file mode 100644 index 0000000000..f907c3be5b --- /dev/null +++ b/src/Tgstation.Server.Host/IServerFactory.cs @@ -0,0 +1,16 @@ +namespace Tgstation.Server.Host +{ + /// + /// For creating s + /// + public interface IServerFactory + { + /// + /// Create a + /// + /// The arguments for the + /// The directory in which to install server updates + /// A new + IServer CreateServer(string[] args, string updatePath); + } +} diff --git a/src/Tgstation.Server.Host/Models/ChatChannel.cs b/src/Tgstation.Server.Host/Models/ChatChannel.cs new file mode 100644 index 0000000000..09fcf41df6 --- /dev/null +++ b/src/Tgstation.Server.Host/Models/ChatChannel.cs @@ -0,0 +1,32 @@ +using Tgstation.Server.Api.Models; + +namespace Tgstation.Server.Host.Models +{ + /// + public sealed class ChatChannel : Api.Models.ChatChannel, IApiConvertable + { + /// + /// The row Id + /// + public long Id { get; set; } + + /// + /// The + /// + public long ChatSettingsId { get; set; } + + /// + /// The + /// + public ChatSettings ChatSettings { get; set; } + + /// + public Api.Models.ChatChannel ToApi() => new Api.Models.ChatChannel + { + DiscordChannelId = DiscordChannelId, + IsAdminChannel = IsAdminChannel, + IsWatchdogChannel = IsWatchdogChannel, + IrcChannel = IrcChannel + }; + } +} diff --git a/src/Tgstation.Server.Host/Models/ChatSettings.cs b/src/Tgstation.Server.Host/Models/ChatSettings.cs new file mode 100644 index 0000000000..816139e347 --- /dev/null +++ b/src/Tgstation.Server.Host/Models/ChatSettings.cs @@ -0,0 +1,38 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; +using System.Linq; +using Tgstation.Server.Api.Models; + +namespace Tgstation.Server.Host.Models +{ + /// + public sealed class ChatSettings : Api.Models.Internal.ChatSettings, IApiConvertable + { + /// + /// The + /// + public long InstanceId { get; set; } + + /// + /// The parent + /// + [Required] + public Instance Instance { get; set; } + + /// + /// See + /// + public List Channels { get; set; } + + /// + public Api.Models.ChatSettings ToApi() => new Api.Models.ChatSettings + { + Channels = Channels.Select(x => x.ToApi()).ToList(), + ConnectionString = ConnectionString, + Enabled = Enabled, + Provider = Provider, + Id = Id, + Name = Name + }; + } +} diff --git a/src/Tgstation.Server.Host/Models/CompileJob.cs b/src/Tgstation.Server.Host/Models/CompileJob.cs new file mode 100644 index 0000000000..1ea4c790a8 --- /dev/null +++ b/src/Tgstation.Server.Host/Models/CompileJob.cs @@ -0,0 +1,40 @@ +using System; +using System.ComponentModel.DataAnnotations; + +namespace Tgstation.Server.Host.Models +{ + /// + public sealed class CompileJob : Api.Models.Internal.CompileJob, IApiConvertable + { + /// + /// See + /// + public Job Job { get; set; } + + /// + /// See + /// + [Required] + public RevisionInformation RevisionInformation { get; set; } + + /// + /// The the was made with in string form + /// + [Required] + public string ByondVersion { get; set; } + + /// + public Api.Models.CompileJob ToApi() => new Api.Models.CompileJob + { + DirectoryName = DirectoryName, + DMApiValidated = DMApiValidated, + DmeName = DmeName, + ExitCode = ExitCode, + Id = Id, + Job = Job.ToApi(), + Output = Output, + RevisionInformation = RevisionInformation.ToApi(), + ByondVersion = Version.Parse(ByondVersion) + }; + } +} diff --git a/src/Tgstation.Server.Host/Models/DatabaseContext.cs b/src/Tgstation.Server.Host/Models/DatabaseContext.cs new file mode 100644 index 0000000000..ebdb01a161 --- /dev/null +++ b/src/Tgstation.Server.Host/Models/DatabaseContext.cs @@ -0,0 +1,152 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using System; +#if !DEBUG +using System.Linq; +#endif +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Host.Configuration; + +namespace Tgstation.Server.Host.Models +{ + /// + abstract class DatabaseContext : DbContext, IDatabaseContext where TParentContext : DbContext + { + /// + public DbSet Users { get; set; } + + /// + public DbSet Instances { get; set; } + + /// + public DbSet CompileJobs { get; set; } + + /// + public DbSet RevisionInformations { get; set; } + + /// + public DbSet DreamMakerSettings { get; set; } + + /// + public DbSet ChatSettings { get; set; } + + /// + public DbSet DreamDaemonSettings { get; set; } + + /// + public DbSet RepositorySettings { get; set; } + + /// + public DbSet InstanceUsers { get; set; } + + /// + public DbSet ChatChannels { get; set; } + + /// + public DbSet Jobs { get; set; } + + /// + /// The s in the + /// + public DbSet TestMerges { get; set; } + + /// + public DbSet ReattachInformations { get; set; } + + /// + /// The s om the + /// + public DbSet RevInfoTestMerges { get; set; } + + /// + public DbSet WatchdogReattachInformations { get; set; } + + /// + /// The connection string for the + /// + protected string ConnectionString => databaseConfiguration.ConnectionString; + + /// + /// The for the + /// + readonly DatabaseConfiguration databaseConfiguration; + /// + /// The for the + /// + readonly IDatabaseSeeder databaseSeeder; + + /// + /// Construct a + /// + /// The for the + /// The containing the value of + /// The value of + public DatabaseContext(DbContextOptions dbContextOptions, IOptions databaseConfigurationOptions, IDatabaseSeeder databaseSeeder) : base(dbContextOptions) + { + databaseConfiguration = databaseConfigurationOptions?.Value ?? throw new ArgumentNullException(nameof(databaseConfigurationOptions)); + this.databaseSeeder = databaseSeeder ?? throw new ArgumentNullException(nameof(databaseSeeder)); + } + + /// + protected override void OnModelCreating(ModelBuilder modelBuilder) + { + base.OnModelCreating(modelBuilder); + + var userModel = modelBuilder.Entity(); + userModel.HasIndex(x => x.CanonicalName).IsUnique(); + userModel.HasMany(x => x.TestMerges).WithOne(x => x.MergedBy).OnDelete(DeleteBehavior.Restrict); + + modelBuilder.Entity().HasIndex(x => new { x.UserId, x.InstanceId }).IsUnique(); + + modelBuilder.Entity().HasMany(x => x.RevisonInformations).WithOne(x => x.TestMerge).OnDelete(DeleteBehavior.Cascade); + + var revInfo = modelBuilder.Entity(); + revInfo.HasMany(x => x.CompileJobs).WithOne(x => x.RevisionInformation).OnDelete(DeleteBehavior.Cascade); + revInfo.HasMany(x => x.ActiveTestMerges).WithOne(x => x.RevisionInformation).OnDelete(DeleteBehavior.Cascade); + revInfo.HasOne(x => x.PrimaryTestMerge).WithOne(x => x.PrimaryRevisionInformation).OnDelete(DeleteBehavior.SetNull); + revInfo.HasIndex(x => x.CommitSha).IsUnique(); + + var chatChannel = modelBuilder.Entity(); + chatChannel.HasIndex(x => new { x.ChatSettingsId, x.IrcChannel }).IsUnique(); + chatChannel.HasIndex(x => new { x.ChatSettingsId, x.DiscordChannelId }).IsUnique(); + chatChannel.HasOne(x => x.ChatSettings).WithMany(x => x.Channels).HasForeignKey(x => x.ChatSettingsId).OnDelete(DeleteBehavior.Cascade); + + var instanceModel = modelBuilder.Entity(); + instanceModel.HasIndex(x => x.Path).IsUnique(); + instanceModel.HasMany(x => x.ChatSettings).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade); + instanceModel.HasOne(x => x.DreamDaemonSettings).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade); + instanceModel.HasOne(x => x.DreamMakerSettings).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade); + instanceModel.HasOne(x => x.RepositorySettings).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade); + instanceModel.HasMany(x => x.RevisionInformations).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade); + instanceModel.HasMany(x => x.InstanceUsers).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade); + instanceModel.HasMany(x => x.Jobs).WithOne(x => x.Instance).OnDelete(DeleteBehavior.Cascade); + } + + /// + protected override void OnConfiguring(DbContextOptionsBuilder optionsBuilder) + { + base.OnConfiguring(optionsBuilder); + } + + /// + public async Task Initialize(CancellationToken cancellationToken) + { +#if DEBUG + await Database.EnsureCreatedAsync().ConfigureAwait(false); + var wasEmpty = (await Users.CountAsync().ConfigureAwait(false)) == 0; +#else + var migrations = await Database.GetAppliedMigrationsAsync().ConfigureAwait(false); + var wasEmpty = !migrations.Any(); + await Database.MigrateAsync(cancellationToken).ConfigureAwait(false); +#endif + if (wasEmpty) + await databaseSeeder.SeedDatabase(this, cancellationToken).ConfigureAwait(false); + else if(databaseConfiguration.ResetAdminPassword) + await databaseSeeder.ResetAdminPassword(this, cancellationToken).ConfigureAwait(false); + } + + /// + public Task Save(CancellationToken cancellationToken) => SaveChangesAsync(cancellationToken); + } +} diff --git a/src/Tgstation.Server.Host/Models/DatabaseSeeder.cs b/src/Tgstation.Server.Host/Models/DatabaseSeeder.cs new file mode 100644 index 0000000000..acaf0e1f53 --- /dev/null +++ b/src/Tgstation.Server.Host/Models/DatabaseSeeder.cs @@ -0,0 +1,76 @@ +using Microsoft.EntityFrameworkCore; +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Api.Rights; +using Tgstation.Server.Host.Security; + +namespace Tgstation.Server.Host.Models +{ + /// + sealed class DatabaseSeeder : IDatabaseSeeder + { + /// + /// The name of the default admin user + /// + const string AdminName = "Admin"; + + /// + /// The default admin password + /// + const string DefaultAdminPassword = "ISolemlySwearToDeleteTheDataDirectory"; + + /// + /// The for the + /// + readonly ICryptographySuite cryptographySuite; + + /// + /// Construct a + /// + /// The value of + public DatabaseSeeder(ICryptographySuite cryptographySuite) => this.cryptographySuite = cryptographySuite ?? throw new ArgumentNullException(nameof(cryptographySuite)); + + /// + /// Add a default admin to a given + /// + /// The to add an admin to + void SeedAdminUser(IDatabaseContext databaseContext) + { + var admin = new User + { + AdministrationRights = (AdministrationRights)~0, + CreatedAt = DateTimeOffset.Now, + InstanceManagerRights = (InstanceManagerRights)~0, + Name = AdminName, + CanonicalName = AdminName.ToUpperInvariant(), + Enabled = true, + }; + cryptographySuite.SetUserPassword(admin, DefaultAdminPassword); + databaseContext.Users.Add(admin); + } + + /// + public async Task SeedDatabase(IDatabaseContext databaseContext, CancellationToken cancellationToken) + { + SeedAdminUser(databaseContext); + await databaseContext.Save(cancellationToken).ConfigureAwait(false); + } + + /// + public async Task ResetAdminPassword(IDatabaseContext databaseContext, CancellationToken cancellationToken) + { + var admin = await databaseContext.Users.Where(x => x.CanonicalName == AdminName.ToUpperInvariant()).FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false); + if (admin == default) + SeedAdminUser(databaseContext); + else + { + admin.Enabled = true; + cryptographySuite.SetUserPassword(admin, DefaultAdminPassword); + } + + await databaseContext.Save(cancellationToken).ConfigureAwait(false); + } + } +} diff --git a/src/Tgstation.Server.Host/Models/DreamDaemonSettings.cs b/src/Tgstation.Server.Host/Models/DreamDaemonSettings.cs new file mode 100644 index 0000000000..af817a1825 --- /dev/null +++ b/src/Tgstation.Server.Host/Models/DreamDaemonSettings.cs @@ -0,0 +1,44 @@ +using System.ComponentModel.DataAnnotations; + +namespace Tgstation.Server.Host.Models +{ + /// + public sealed class DreamDaemonSettings : Api.Models.Internal.DreamDaemonSettings + { + /// + /// The row Id + /// + public long Id { get; set; } + + /// + /// The PID of a currently running DD instance + /// + public int? ProcessId { get; set; } + + /// + /// The access token used for communication with DD + /// + public string AccessToken { get; set; } + + /// + /// The + /// + public long InstanceId { get; set; } + + /// + /// The parent + /// + [Required] + public Instance Instance { get; set; } + + /// + /// See + /// + public CompileJob ActiveCompileJob { get; set; } + + /// + /// See + /// + public CompileJob StagedCompileJob { get; set; } + } +} diff --git a/src/Tgstation.Server.Host/Models/DreamMakerSettings.cs b/src/Tgstation.Server.Host/Models/DreamMakerSettings.cs new file mode 100644 index 0000000000..f2dacbfc64 --- /dev/null +++ b/src/Tgstation.Server.Host/Models/DreamMakerSettings.cs @@ -0,0 +1,24 @@ +using System.ComponentModel.DataAnnotations; + +namespace Tgstation.Server.Host.Models +{ + /// + public sealed class DreamMakerSettings : Api.Models.Internal.DreamMakerSettings + { + /// + /// The row Id + /// + public long Id { get; set; } + + /// + /// The + /// + public long InstanceId { get; set; } + + /// + /// The parent + /// + [Required] + public Instance Instance { get; set; } + } +} diff --git a/src/Tgstation.Server.Host/Models/IApiConvertable.cs b/src/Tgstation.Server.Host/Models/IApiConvertable.cs new file mode 100644 index 0000000000..3a53bb26c7 --- /dev/null +++ b/src/Tgstation.Server.Host/Models/IApiConvertable.cs @@ -0,0 +1,15 @@ +namespace Tgstation.Server.Host.Models +{ + /// + /// For converting models to their API form + /// + /// Which of the this model backs + public interface IApiConvertable where TModel : class + { + /// + /// Convert the model to it's API form + /// + /// A new + TModel ToApi(); + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Models/IDatabaseContext.cs b/src/Tgstation.Server.Host/Models/IDatabaseContext.cs new file mode 100644 index 0000000000..ea02c85426 --- /dev/null +++ b/src/Tgstation.Server.Host/Models/IDatabaseContext.cs @@ -0,0 +1,91 @@ +using Microsoft.EntityFrameworkCore; +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Host.Models +{ + /// + /// Represents the database + /// + public interface IDatabaseContext + { + /// + /// The s in the + /// + DbSet Users { get; } + + /// + /// The s in the + /// + DbSet Instances { get; } + + /// + /// The s in the + /// + DbSet InstanceUsers { get; } + + /// + /// The s in the + /// + DbSet Jobs { get; } + + /// + /// The s in the + /// + DbSet CompileJobs { get; } + + /// + /// The s in the + /// + DbSet RevisionInformations { get; } + + /// + /// The in the + /// + DbSet DreamMakerSettings { get; set; } + + /// + /// The in the + /// + DbSet DreamDaemonSettings { get; set; } + + /// + /// The in the + /// + DbSet ChatSettings { get; set; } + + /// + /// The in the + /// + DbSet ChatChannels { get; set; } + + /// + /// The in the + /// + DbSet RepositorySettings { get; set; } + + /// + /// The for s + /// + DbSet ReattachInformations { get; set; } + + /// + /// The for s + /// + DbSet WatchdogReattachInformations { get; set; } + + /// + /// Saves changes made to the + /// + /// The for the operation + /// A representing the running operation + Task Save(CancellationToken cancellationToken); + + /// + /// Creates and migrates the + /// + /// The for the operation + /// A representing the running operation + Task Initialize(CancellationToken cancellationToken); + } +} diff --git a/src/Tgstation.Server.Host/Models/IDatabaseSeeder.cs b/src/Tgstation.Server.Host/Models/IDatabaseSeeder.cs new file mode 100644 index 0000000000..354b3eee7b --- /dev/null +++ b/src/Tgstation.Server.Host/Models/IDatabaseSeeder.cs @@ -0,0 +1,27 @@ +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Host.Models +{ + /// + /// For initially seeding a database + /// + interface IDatabaseSeeder + { + /// + /// Initially seed a given + /// + /// The to seed + /// The for the operation + /// A representing the running operation + Task SeedDatabase(IDatabaseContext databaseContext, CancellationToken cancellationToken); + + /// + /// Changes the admin password in back to it's default and enables the account + /// + /// The to reset the admin password for + /// The for the operation + /// A representing the running operation + Task ResetAdminPassword(IDatabaseContext databaseContext, CancellationToken cancellationToken); + } +} diff --git a/src/Tgstation.Server.Host/Models/Instance.cs b/src/Tgstation.Server.Host/Models/Instance.cs new file mode 100644 index 0000000000..10b2eb9ece --- /dev/null +++ b/src/Tgstation.Server.Host/Models/Instance.cs @@ -0,0 +1,62 @@ +using System.Collections.Generic; + +namespace Tgstation.Server.Host.Models +{ + /// + /// Represents an in the database + /// + public sealed class Instance : Api.Models.Instance, IApiConvertable + { + + /// + /// The for the + /// + public DreamMakerSettings DreamMakerSettings { get; set; } + + /// + /// The for the + /// + public DreamDaemonSettings DreamDaemonSettings { get; set; } + + /// + /// The for the + /// + public RepositorySettings RepositorySettings { get; set; } + + /// + /// The for the + /// + public WatchdogReattachInformation WatchdogReattachInformation { get; set; } + + /// + /// The s in the + /// + public List InstanceUsers { get; set; } + + /// + /// The s for the + /// + public List ChatSettings { get; set; } + + /// + /// The s in the + /// + public List RevisionInformations { get; set; } + + /// + /// The in the + /// + public List Jobs { get; set; } + + /// + public Api.Models.Instance ToApi() => new Api.Models.Instance + { + AutoUpdateInterval = AutoUpdateInterval, + ConfigurationType = ConfigurationType, + Id = Id, + Name = Name, + Path = Path, + Online = Online + }; + } +} diff --git a/src/Tgstation.Server.Host/Models/InstanceUser.cs b/src/Tgstation.Server.Host/Models/InstanceUser.cs new file mode 100644 index 0000000000..813bf40d0e --- /dev/null +++ b/src/Tgstation.Server.Host/Models/InstanceUser.cs @@ -0,0 +1,44 @@ +using System.ComponentModel.DataAnnotations; +using Tgstation.Server.Api.Models; + +namespace Tgstation.Server.Host.Models +{ + /// + public sealed class InstanceUser : Api.Models.InstanceUser, IApiConvertable + { + /// + /// The row Id + /// + public long Id { get; set; } + + /// + /// The the belongs to + /// + [Required] + public Instance Instance { get; set; } + + /// + /// If the has any instance rights + /// + public bool AnyRights => ByondRights != Api.Rights.ByondRights.None || + ChatSettingsRights != Api.Rights.ChatSettingsRights.None || + ConfigurationRights != Api.Rights.ConfigurationRights.None || + DreamDaemonRights != Api.Rights.DreamDaemonRights.None || + DreamMakerRights != Api.Rights.DreamMakerRights.None || + InstanceUserRights != Api.Rights.InstanceUserRights.None; + + /// + public Api.Models.InstanceUser ToApi() => new Api.Models.InstanceUser + { + ByondRights = ByondRights, + ChatSettingsRights = ChatSettingsRights, + ConfigurationRights = ConfigurationRights, + DreamDaemonRights = DreamDaemonRights, + DreamMakerRights = DreamMakerRights, + InstanceId = InstanceId, + RepositoryRights = RepositoryRights, + InstanceUserRights = InstanceUserRights, + UserId = UserId + }; + } +} diff --git a/src/Tgstation.Server.Host/Models/Job.cs b/src/Tgstation.Server.Host/Models/Job.cs new file mode 100644 index 0000000000..0b56cca428 --- /dev/null +++ b/src/Tgstation.Server.Host/Models/Job.cs @@ -0,0 +1,40 @@ +using System.ComponentModel.DataAnnotations; + +namespace Tgstation.Server.Host.Models +{ + /// + public sealed class Job : Api.Models.Internal.Job, IApiConvertable + { + /// + /// See + /// + [Required] + public User StartedBy { get; set; } + + /// + /// See + /// + public User CancelledBy { get; set; } + + /// + /// The the job belongs to if any + /// + [Required] + public Instance Instance { get; set; } + + /// + public Api.Models.Job ToApi() => new Api.Models.Job + { + Id = Id, + StartedAt = StartedAt, + StoppedAt = StoppedAt, + Cancelled = Cancelled, + CancelledBy = CancelledBy?.ToApi(), + CancelRight = CancelRight, + CancelRightsType = CancelRightsType, + Description = Description, + ExceptionDetails = ExceptionDetails, + StartedBy = StartedBy.ToApi() + }; + } +} diff --git a/src/Tgstation.Server.Host/Models/Migrations/DesignTimeDbContextFactoryHelpers.cs b/src/Tgstation.Server.Host/Models/Migrations/DesignTimeDbContextFactoryHelpers.cs new file mode 100644 index 0000000000..c633cc1ec4 --- /dev/null +++ b/src/Tgstation.Server.Host/Models/Migrations/DesignTimeDbContextFactoryHelpers.cs @@ -0,0 +1,29 @@ +using Microsoft.Extensions.Configuration; +using Microsoft.Extensions.Options; +using System.IO; +using System.Reflection; +using Tgstation.Server.Host.Configuration; + +namespace Tgstation.Server.Host.Models.Migrations +{ + /// + /// Contains helpers for creating design time s + /// + static class DesignTimeDbContextFactoryHelpers + { + /// + /// Path to the json file to use for migrations configuration + /// + const string MigrationsJson = "appsettings.Development.json"; + + /// + public static IOptions GetDbContextOptions() + { + var builder = new ConfigurationBuilder(); + builder.SetBasePath(Path.GetDirectoryName(Assembly.GetExecutingAssembly().Location)); + builder.AddJsonFile(MigrationsJson); + var configuration = builder.Build(); + return Options.Create(configuration.GetSection(DatabaseConfiguration.Section).Get()); + } + } +} diff --git a/src/Tgstation.Server.Host/Models/Migrations/MySqlDesignTimeDbContextFactory.cs b/src/Tgstation.Server.Host/Models/Migrations/MySqlDesignTimeDbContextFactory.cs new file mode 100644 index 0000000000..fd55e611c1 --- /dev/null +++ b/src/Tgstation.Server.Host/Models/Migrations/MySqlDesignTimeDbContextFactory.cs @@ -0,0 +1,14 @@ +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; +using Tgstation.Server.Host.Security; + +namespace Tgstation.Server.Host.Models.Migrations +{ + /// + sealed class MySqlDesignTimeDbContextFactory : IDesignTimeDbContextFactory + { + /// + public MySqlDatabaseContext CreateDbContext(string[] args) => new MySqlDatabaseContext(new DbContextOptions(), DesignTimeDbContextFactoryHelpers.GetDbContextOptions(), new DatabaseSeeder(new CryptographySuite(new PasswordHasher()))); + } +} diff --git a/src/Tgstation.Server.Host/Models/Migrations/SqlServerDesignTimeDbContextFactory.cs b/src/Tgstation.Server.Host/Models/Migrations/SqlServerDesignTimeDbContextFactory.cs new file mode 100644 index 0000000000..75a1b97053 --- /dev/null +++ b/src/Tgstation.Server.Host/Models/Migrations/SqlServerDesignTimeDbContextFactory.cs @@ -0,0 +1,14 @@ +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; +using Tgstation.Server.Host.Security; + +namespace Tgstation.Server.Host.Models.Migrations +{ + /// + sealed class SqlServerDesignTimeDbContextFactory : IDesignTimeDbContextFactory + { + /// + public SqlServerDatabaseContext CreateDbContext(string[] args) => new SqlServerDatabaseContext(new DbContextOptions(), DesignTimeDbContextFactoryHelpers.GetDbContextOptions(), new DatabaseSeeder(new CryptographySuite(new PasswordHasher()))); + } +} diff --git a/src/Tgstation.Server.Host/Models/Migrations/SqliteDesignTimeDbContextFactory.cs b/src/Tgstation.Server.Host/Models/Migrations/SqliteDesignTimeDbContextFactory.cs new file mode 100644 index 0000000000..bf8b3c379b --- /dev/null +++ b/src/Tgstation.Server.Host/Models/Migrations/SqliteDesignTimeDbContextFactory.cs @@ -0,0 +1,15 @@ +using Microsoft.AspNetCore.Identity; +using Microsoft.EntityFrameworkCore; +using Microsoft.EntityFrameworkCore.Design; +using Microsoft.Extensions.Logging; +using Tgstation.Server.Host.Security; + +namespace Tgstation.Server.Host.Models.Migrations +{ + /// + sealed class SqliteDesignTimeDbContextFactory : IDesignTimeDbContextFactory + { + /// + public SqliteDatabaseContext CreateDbContext(string[] args) => new SqliteDatabaseContext(new DbContextOptions(), DesignTimeDbContextFactoryHelpers.GetDbContextOptions(), new DatabaseSeeder(new CryptographySuite(new PasswordHasher())), new LoggerFactory().CreateLogger()); + } +} diff --git a/src/Tgstation.Server.Host/Models/MySqlDatabaseContext.cs b/src/Tgstation.Server.Host/Models/MySqlDatabaseContext.cs new file mode 100644 index 0000000000..0f70b29fcd --- /dev/null +++ b/src/Tgstation.Server.Host/Models/MySqlDatabaseContext.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using Tgstation.Server.Host.Configuration; + +namespace Tgstation.Server.Host.Models +{ + /// + /// for MySQL + /// + sealed class MySqlDatabaseContext : DatabaseContext + { + /// + /// Construct a + /// + /// The for the + /// The of for the + /// The for the + public MySqlDatabaseContext(DbContextOptions dbContextOptions, IOptions databaseConfiguration, IDatabaseSeeder databaseSeeder) : base(dbContextOptions, databaseConfiguration, databaseSeeder) + { } + + /// + protected override void OnConfiguring(DbContextOptionsBuilder options) + { + base.OnConfiguring(options); + options.UseMySQL(ConnectionString); + } + } +} diff --git a/src/Tgstation.Server.Host/Models/ReattachInformation.cs b/src/Tgstation.Server.Host/Models/ReattachInformation.cs new file mode 100644 index 0000000000..90587941e7 --- /dev/null +++ b/src/Tgstation.Server.Host/Models/ReattachInformation.cs @@ -0,0 +1,18 @@ +namespace Tgstation.Server.Host.Models +{ + /// + /// Database representation of + /// + public sealed class ReattachInformation : ReattachInformationBase + { + /// + /// The row Id + /// + public long Id { get; set; } + + /// + /// The for the + /// + public CompileJob CompileJob { get; set; } + } +} diff --git a/src/Tgstation.Server.Host/Models/ReattachInformationBase.cs b/src/Tgstation.Server.Host/Models/ReattachInformationBase.cs new file mode 100644 index 0000000000..b0c8f7dcb7 --- /dev/null +++ b/src/Tgstation.Server.Host/Models/ReattachInformationBase.cs @@ -0,0 +1,73 @@ +using System; +using System.ComponentModel.DataAnnotations; +using Tgstation.Server.Host.Components.Watchdog; + +namespace Tgstation.Server.Host.Models +{ + /// + /// Base class for + /// + public abstract class ReattachInformationBase + { + /// + /// Used to identify and authenticate the DreamDaemon instance + /// + [Required] + public string AccessIdentifier { get; set; } + + /// + /// The system process ID + /// + public int ProcessId { get; set; } + + /// + /// If the of the associated dmb is being used + /// + public bool IsPrimary { get; set; } + + /// + /// The port DreamDaemon was last listening on + /// + public ushort Port { get; set; } + + /// + /// The current DreamDaemon reboot state + /// + [Required] + public RebootState RebootState { get; set; } + + /// + /// Path to the chat commands json file + /// + [Required] + public string ChatCommandsJson { get; set; } + + /// + /// Path to the chat channels json file + /// + [Required] + public string ChatChannelsJson { get; set; } + + /// + /// Construct a + /// + public ReattachInformationBase() { } + + /// + /// Construct a from a given + /// + /// The to copy values from + protected ReattachInformationBase(ReattachInformationBase copy) + { + if (copy == null) + throw new ArgumentNullException(nameof(copy)); + AccessIdentifier = copy.AccessIdentifier; + ChatChannelsJson = copy.ChatChannelsJson; + ChatCommandsJson = copy.ChatCommandsJson; + IsPrimary = copy.IsPrimary; + Port = copy.Port; + ProcessId = copy.ProcessId; + RebootState = copy.RebootState; + } + } +} diff --git a/src/Tgstation.Server.Host/Models/RepositorySettings.cs b/src/Tgstation.Server.Host/Models/RepositorySettings.cs new file mode 100644 index 0000000000..18fda50bfb --- /dev/null +++ b/src/Tgstation.Server.Host/Models/RepositorySettings.cs @@ -0,0 +1,39 @@ +using System.ComponentModel.DataAnnotations; +using Tgstation.Server.Api.Models; + +namespace Tgstation.Server.Host.Models +{ + /// + public sealed class RepositorySettings : Api.Models.Internal.RepositorySettings, IApiConvertable + { + /// + /// The row Id + /// + public long Id { get; set; } + + /// + /// The + /// + public long InstanceId { get; set; } + + /// + /// The parent + /// + [Required] + public Instance Instance { get; set; } + + /// + public Repository ToApi() => new Repository { + //AccessToken = AccessToken, //never show this + AccessUser = AccessUser, + AutoUpdatesKeepTestMerges = AutoUpdatesKeepTestMerges, + AutoUpdatesSynchronize = AutoUpdatesSynchronize, + CommitterEmail = CommitterEmail, + CommitterName = CommitterName, + //intentionally don't populate origin just in case + PushTestMergeCommits = PushTestMergeCommits, + //revision information and the rest retrieved by controller + ShowTestMergeCommitters = ShowTestMergeCommitters + }; + } +} diff --git a/src/Tgstation.Server.Host/Models/RevInfoTestMerge.cs b/src/Tgstation.Server.Host/Models/RevInfoTestMerge.cs new file mode 100644 index 0000000000..826b124780 --- /dev/null +++ b/src/Tgstation.Server.Host/Models/RevInfoTestMerge.cs @@ -0,0 +1,27 @@ +using System.ComponentModel.DataAnnotations; + +namespace Tgstation.Server.Host.Models +{ + /// + /// Many to many relationship for and + /// + public sealed class RevInfoTestMerge + { + /// + /// The row Id + /// + public long Id { get; set; } + + /// + /// The + /// + [Required] + public TestMerge TestMerge { get; set; } + + /// + /// The + /// + [Required] + public RevisionInformation RevisionInformation { get; set; } + } +} diff --git a/src/Tgstation.Server.Host/Models/RevisionInformation.cs b/src/Tgstation.Server.Host/Models/RevisionInformation.cs new file mode 100644 index 0000000000..4ff7c0bd79 --- /dev/null +++ b/src/Tgstation.Server.Host/Models/RevisionInformation.cs @@ -0,0 +1,44 @@ +using System.Collections.Generic; +using System.Linq; + +namespace Tgstation.Server.Host.Models +{ + /// + public sealed class RevisionInformation : Api.Models.Internal.RevisionInformation, IApiConvertable + { + /// + /// The row Id + /// + public long Id { get; set; } + + /// + /// The the belongs to + /// + public Instance Instance { get; set; } + + /// + /// See + /// + public TestMerge PrimaryTestMerge { get; set; } + + /// + /// See + /// + public List ActiveTestMerges { get; set; } + + /// + /// See s made from this + /// + public List CompileJobs { get; set; } + + /// + public Api.Models.RevisionInformation ToApi() => new Api.Models.RevisionInformation + { + CommitSha = CommitSha, + OriginCommitSha = OriginCommitSha, + PrimaryTestMerge = PrimaryTestMerge?.ToApi(), + ActiveTestMerges = ActiveTestMerges.Select(x => x.TestMerge.ToApi()).ToList(), + CompileJobs = CompileJobs.Select(x => x.ToApi()).ToList() + }; + } +} diff --git a/src/Tgstation.Server.Host/Models/SqlServerDatabaseContext.cs b/src/Tgstation.Server.Host/Models/SqlServerDatabaseContext.cs new file mode 100644 index 0000000000..19f6736cc9 --- /dev/null +++ b/src/Tgstation.Server.Host/Models/SqlServerDatabaseContext.cs @@ -0,0 +1,28 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Options; +using Tgstation.Server.Host.Configuration; + +namespace Tgstation.Server.Host.Models +{ + /// + /// for Sqlserver + /// + sealed class SqlServerDatabaseContext : DatabaseContext + { + /// + /// Construct a + /// + /// The for the + /// The of for the + /// The for the + public SqlServerDatabaseContext(DbContextOptions dbContextOptions, IOptions databaseConfiguration, IDatabaseSeeder databaseSeeder) : base(dbContextOptions, databaseConfiguration, databaseSeeder) + { } + + /// + protected override void OnConfiguring(DbContextOptionsBuilder options) + { + base.OnConfiguring(options); + options.UseSqlServer(ConnectionString); + } + } +} diff --git a/src/Tgstation.Server.Host/Models/SqliteDatabaseContext.cs b/src/Tgstation.Server.Host/Models/SqliteDatabaseContext.cs new file mode 100644 index 0000000000..011893939c --- /dev/null +++ b/src/Tgstation.Server.Host/Models/SqliteDatabaseContext.cs @@ -0,0 +1,45 @@ +using Microsoft.EntityFrameworkCore; +using Microsoft.Extensions.Logging; +using Microsoft.Extensions.Options; +using System; +using Tgstation.Server.Host.Configuration; + +namespace Tgstation.Server.Host.Models +{ + /// + /// for Sqlite + /// + sealed class SqliteDatabaseContext : DatabaseContext + { + /// + /// The for the + /// + readonly ILogger logger; + + /// + /// Construct a + /// + /// The for the + /// The of for the + /// The for the + /// The value of + public SqliteDatabaseContext(DbContextOptions dbContextOptions, IOptions databaseConfiguration, IDatabaseSeeder databaseSeeder, ILogger logger) : base(dbContextOptions, databaseConfiguration, databaseSeeder) + { + this.logger = logger ?? throw new ArgumentNullException(nameof(logger)); + } + + /// + protected override void OnConfiguring(DbContextOptionsBuilder options) + { + base.OnConfiguring(options); + //on the off chance that connection string is null here we default to a db file next to the executable since this is the default database + if (ConnectionString == null) + { + logger.LogWarning("No database configured! Defaulting to SQLite in the working directory!"); + options.UseSqlite("Data Source=TgsDatabase.db3"); + } + else + options.UseSqlite(ConnectionString); + } + } +} diff --git a/src/Tgstation.Server.Host/Models/TestMerge.cs b/src/Tgstation.Server.Host/Models/TestMerge.cs new file mode 100644 index 0000000000..2428a192a5 --- /dev/null +++ b/src/Tgstation.Server.Host/Models/TestMerge.cs @@ -0,0 +1,45 @@ +using System.Collections.Generic; +using System.ComponentModel.DataAnnotations; + +namespace Tgstation.Server.Host.Models +{ + /// + public sealed class TestMerge : Api.Models.Internal.TestMerge, IApiConvertable + { + /// + /// See + /// + [Required] + public User MergedBy { get; set; } + + /// + /// The initial the was merged with + /// + public RevisionInformation PrimaryRevisionInformation { get; set; } + + /// + /// Foreign key for + /// + public long? PrimaryRevisionInformationId { get; set; } + + /// + /// All the for the + /// + public List RevisonInformations { get; set; } + + /// + public Api.Models.TestMerge ToApi() => new Api.Models.TestMerge + { + Author = Author, + BodyAtMerge = BodyAtMerge, + MergedAt = MergedAt, + TitleAtMerge = TitleAtMerge, + Comment = Comment, + Id = Id, + MergedBy = MergedBy.ToApi(), + Number =Number, + PullRequestRevision = PullRequestRevision, + Url = Url + }; + } +} diff --git a/src/Tgstation.Server.Host/Models/User.cs b/src/Tgstation.Server.Host/Models/User.cs new file mode 100644 index 0000000000..b30c01969e --- /dev/null +++ b/src/Tgstation.Server.Host/Models/User.cs @@ -0,0 +1,58 @@ +using System.Collections.Generic; + +namespace Tgstation.Server.Host.Models +{ + /// + public sealed class User : Api.Models.Internal.User, IApiConvertable + { + /// + /// The hash of the user's password + /// + public string PasswordHash { get; set; } + + /// + /// See + /// + public User CreatedBy { get; set; } + + /// + /// The uppercase invariant of + /// + public string CanonicalName { get; set; } + + /// + /// s created by this + /// + public List CreatedUsers { get; set; } + + /// + /// The s for the + /// + public List InstanceUsers { get; set; } + + /// + /// The s made by the + /// + public List TestMerges { get; set; } + + /// + /// See + /// + /// If we should recurse on + /// A new + Api.Models.User ToApi(bool recursive) => new Api.Models.User + { + AdministrationRights = AdministrationRights, + CreatedAt = CreatedAt, + CreatedBy = recursive ? CreatedBy?.ToApi(false) : null, + Enabled = Enabled, + Id = Id, + InstanceManagerRights = InstanceManagerRights, + Name = Name, + SystemIdentifier = SystemIdentifier + }; + + /// + public Api.Models.User ToApi() => ToApi(true); + } +} diff --git a/src/Tgstation.Server.Host/Models/WatchdogReattachInformation.cs b/src/Tgstation.Server.Host/Models/WatchdogReattachInformation.cs new file mode 100644 index 0000000000..db27049745 --- /dev/null +++ b/src/Tgstation.Server.Host/Models/WatchdogReattachInformation.cs @@ -0,0 +1,27 @@ +using System; +using System.Collections.Generic; +using System.Text; + +namespace Tgstation.Server.Host.Models +{ + /// + /// Database representation of + /// + public sealed class WatchdogReattachInformation : WatchdogReattachInformationBase + { + /// + /// The row Id + /// + public long Id { get; set; } + + /// + /// The for the Alpha server + /// + public ReattachInformation Alpha { get; set; } + + /// + /// The for the Bravo server + /// + public ReattachInformation Bravo { get; set; } + } +} diff --git a/src/Tgstation.Server.Host/Models/WatchdogReattachInformationBase.cs b/src/Tgstation.Server.Host/Models/WatchdogReattachInformationBase.cs new file mode 100644 index 0000000000..8cfa201257 --- /dev/null +++ b/src/Tgstation.Server.Host/Models/WatchdogReattachInformationBase.cs @@ -0,0 +1,29 @@ +using System; + +namespace Tgstation.Server.Host.Models +{ + /// + /// Base class for + /// + public abstract class WatchdogReattachInformationBase + { + /// + /// If the Alpha session is the active session + /// + public bool AlphaIsActive { get; set; } + + /// + /// Construct a + /// + public WatchdogReattachInformationBase() { } + + /// + /// Construct a from a given + /// + /// The to copy values from + protected WatchdogReattachInformationBase(WatchdogReattachInformationBase copy) + { + AlphaIsActive = copy?.AlphaIsActive ?? throw new ArgumentNullException(nameof(copy)); + } + } +} diff --git a/src/Tgstation.Server.Host/NativeMethods.cs b/src/Tgstation.Server.Host/NativeMethods.cs new file mode 100644 index 0000000000..398185ab2a --- /dev/null +++ b/src/Tgstation.Server.Host/NativeMethods.cs @@ -0,0 +1,23 @@ +using System; +using System.Runtime.InteropServices; + +namespace Tgstation.Server.Host +{ + /// + /// Native methods used by the code + /// + static class NativeMethods + { + /// + /// See https://msdn.microsoft.com/en-us/library/windows/desktop/aa378184(v=vs.85).aspx + /// + [DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern bool LogonUser(string lpszUsername, string lpszDomain, string lpszPassword, int dwLogonType, int dwLogonProvider, out IntPtr phToken); + + /// + /// See https://docs.microsoft.com/en-us/windows/desktop/api/winbase/nf-winbase-createsymboliclinkw + /// + [DllImport("kernel32.dll", SetLastError = true, CharSet = CharSet.Unicode)] + public static extern bool CreateSymbolicLink(string lpSymlinkFileName, string lpTargetFileName, int dwFlags); + } +} diff --git a/src/Tgstation.Server.Host/Program.cs b/src/Tgstation.Server.Host/Program.cs new file mode 100644 index 0000000000..65f5199f40 --- /dev/null +++ b/src/Tgstation.Server.Host/Program.cs @@ -0,0 +1,79 @@ +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Host +{ + /// + /// Entrypoint for the + /// + static class Program + { + /// + /// The to use + /// + internal static IServerFactory serverFactory = new ServerFactory(); + + /// + /// Entrypoint for the + /// + /// The command line arguments + /// The + public static async Task Main(string[] args) + { + var listArgs = new List(args); + //first arg is 100% always the update path, starting it otherwise is solely for debugging purposes + string updatePath; + if (listArgs.Count > 0) + { + updatePath = listArgs[0]; + listArgs.RemoveAt(0); + if (listArgs.Remove("--attach-debugger")) + Debugger.Launch(); + } + else + updatePath = null; + try + { + using (var server = serverFactory.CreateServer(listArgs.ToArray(), updatePath)) + { + try + { + using (var cts = new CancellationTokenSource()) + { + void AppDomainHandler(object a, EventArgs b) => cts.Cancel(); + AppDomain.CurrentDomain.ProcessExit += AppDomainHandler; + try + { + Console.CancelKeyPress += (a, b) => + { + b.Cancel = true; + cts.Cancel(); + }; + await server.RunAsync(cts.Token).ConfigureAwait(false); + } + finally + { + AppDomain.CurrentDomain.ProcessExit -= AppDomainHandler; + } + } + } + catch (OperationCanceledException) { } + return server.RestartRequested ? 1 : 0; + } + } + catch (Exception e) + { + if (updatePath != null) + { + File.WriteAllText(updatePath, e.ToString()); + return 2; + } + throw; + } + } + } +} diff --git a/src/Tgstation.Server.Host/Security/AuthenticationContext.cs b/src/Tgstation.Server.Host/Security/AuthenticationContext.cs new file mode 100644 index 0000000000..47d3cac17e --- /dev/null +++ b/src/Tgstation.Server.Host/Security/AuthenticationContext.cs @@ -0,0 +1,66 @@ +using System; +using System.Linq; +using Tgstation.Server.Api.Rights; +using Tgstation.Server.Host.Models; + +namespace Tgstation.Server.Host.Security +{ + /// + /// Manages s for a scope + /// + sealed class AuthenticationContext : IAuthenticationContext + { + /// + public User User { get; } + + /// + public InstanceUser InstanceUser { get; } + + /// + public ISystemIdentity SystemIdentity { get; } + + /// + /// Construct a + /// + /// The value of + /// The value of + /// The value of + public AuthenticationContext(ISystemIdentity systemIdentity, User user, InstanceUser instanceUser) + { + User = user ?? throw new ArgumentNullException(nameof(user)); + if (systemIdentity == null && User.SystemIdentifier != null) + throw new ArgumentNullException(nameof(systemIdentity)); + InstanceUser = instanceUser; + SystemIdentity = systemIdentity; + } + + /// + public void Dispose() => SystemIdentity?.Dispose(); + + /// + public IAuthenticationContext Clone() => new AuthenticationContext(SystemIdentity.Clone(), User, InstanceUser); + + /// + public int GetRight(RightsType rightsType) + { + var isInstance = RightsHelper.IsInstanceRight(rightsType); + + if (isInstance && InstanceUser == null) + return 0; + var rightsEnum = RightsHelper.RightToType(rightsType); + // use the api versions because they're the ones that contain the actual properties + var typeToCheck = isInstance ? typeof(InstanceUser) : typeof(User); + + var nullableType = typeof(Nullable<>); + var nullableRightsType = nullableType.MakeGenericType(rightsEnum); + + var prop = typeToCheck.GetProperties().Where(x => x.PropertyType == nullableRightsType).First(); + + var right = prop.GetMethod.Invoke(isInstance ? (object)InstanceUser : User, Array.Empty()); + + if (right == null) + throw new InvalidOperationException("A user right was null!"); + return (int)right; + } + } +} diff --git a/src/Tgstation.Server.Host/Security/AuthenticationContextFactory.cs b/src/Tgstation.Server.Host/Security/AuthenticationContextFactory.cs new file mode 100644 index 0000000000..18fa1536ce --- /dev/null +++ b/src/Tgstation.Server.Host/Security/AuthenticationContextFactory.cs @@ -0,0 +1,73 @@ +using Microsoft.EntityFrameworkCore; +using System; +using System.Linq; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Host.Models; + +namespace Tgstation.Server.Host.Security +{ + /// + sealed class AuthenticationContextFactory : IAuthenticationContextFactory + { + /// + public IAuthenticationContext CurrentAuthenticationContext { get; private set; } + + /// + /// The for the + /// + readonly ISystemIdentityFactory systemIdentityFactory; + + /// + /// The for the + /// + readonly IDatabaseContext databaseContext; + + /// + /// The for the + /// + readonly IIdentityCache identityCache; + + /// + /// Construct an + /// + /// The value of + /// The value of + /// The value of + public AuthenticationContextFactory(ISystemIdentityFactory systemIdentityFactory, IDatabaseContext databaseContext, IIdentityCache identityCache) + { + this.systemIdentityFactory = systemIdentityFactory ?? throw new ArgumentNullException(nameof(systemIdentityFactory)); + this.databaseContext = databaseContext?? throw new ArgumentNullException(nameof(databaseContext)); + this.identityCache = identityCache ?? throw new ArgumentNullException(nameof(identityCache)); + } + + /// + public async Task CreateAuthenticationContext(long userId, long? instanceId, CancellationToken cancellationToken) + { + if (CurrentAuthenticationContext != null) + throw new InvalidOperationException("Authentication context has already been loaded"); + + var userQuery = databaseContext.Users.Where(x => x.Id == userId).FirstAsync(cancellationToken); + + var instanceUser = instanceId.HasValue ? (await databaseContext.InstanceUsers + .Where(x => x.UserId == userId) + .Where(x => x.InstanceId == instanceId) + .Include(x => x.Instance) + .FirstOrDefaultAsync(cancellationToken).ConfigureAwait(false)) : null; + + var user = await userQuery.ConfigureAwait(false); + + ISystemIdentity systemIdentity; + if (user.SystemIdentifier != null) + { + systemIdentity = identityCache.LoadCachedIdentity(user); + if (systemIdentity == null) + throw new InvalidOperationException("Cached system identity has expired!"); + } + else + systemIdentity = null; + + CurrentAuthenticationContext = new AuthenticationContext(systemIdentity, user, instanceUser); + } + } +} diff --git a/src/Tgstation.Server.Host/Security/CryptographySuite.cs b/src/Tgstation.Server.Host/Security/CryptographySuite.cs new file mode 100644 index 0000000000..b0f68e7a5c --- /dev/null +++ b/src/Tgstation.Server.Host/Security/CryptographySuite.cs @@ -0,0 +1,63 @@ +using Microsoft.AspNetCore.Identity; +using System; +using System.Security.Cryptography; +using Tgstation.Server.Host.Models; + +namespace Tgstation.Server.Host.Security +{ + /// + sealed class CryptographySuite : ICryptographySuite + { + /// + /// Generates a secure set of s + /// + /// A secure set of s + public static byte[] GetSecureBytes(int amount) + { + using (var rng = new RNGCryptoServiceProvider()) + { + var byt = new byte[amount]; + rng.GetBytes(byt); + return byt; + } + } + + /// + /// The for the + /// + readonly IPasswordHasher passwordHasher; + + /// + /// Construct a + /// + /// The value of + public CryptographySuite(IPasswordHasher passwordHasher) => this.passwordHasher = passwordHasher ?? throw new ArgumentNullException(nameof(passwordHasher)); + + /// + public void SetUserPassword(User user, string newPassword) + { + if (user == null) + throw new ArgumentNullException(nameof(user)); + if (String.IsNullOrEmpty(newPassword)) + throw new ArgumentNullException(nameof(newPassword)); + user.PasswordHash = passwordHasher.HashPassword(user, newPassword); + } + + /// + public bool CheckUserPassword(User user, string password) + { + switch(passwordHasher.VerifyHashedPassword(user, user.PasswordHash, password)) + { + case PasswordVerificationResult.Failed: + return false; + case PasswordVerificationResult.SuccessRehashNeeded: + user.PasswordHash = passwordHasher.HashPassword(user, password); + break; + } + return true; + } + + /// + public string GetSecureString() => Convert.ToBase64String(GetSecureBytes(30)); + } +} diff --git a/src/Tgstation.Server.Host/Security/IAuthenticationContext.cs b/src/Tgstation.Server.Host/Security/IAuthenticationContext.cs new file mode 100644 index 0000000000..2b1ee41a25 --- /dev/null +++ b/src/Tgstation.Server.Host/Security/IAuthenticationContext.cs @@ -0,0 +1,40 @@ +using System; +using Tgstation.Server.Api.Rights; +using Tgstation.Server.Host.Models; + +namespace Tgstation.Server.Host.Security +{ + /// + /// Represents the currently authenticated + /// + public interface IAuthenticationContext : IDisposable + { + /// + /// The authenticated user + /// + User User { get; } + + /// + /// The of if applicable + /// + InstanceUser InstanceUser { get; } + + /// + /// Get the value of a given + /// + /// The of the right to get + /// The value of . Note that if is all based rights will return 0 + int GetRight(RightsType rightsType); + + /// + /// The of if applicable + /// + ISystemIdentity SystemIdentity { get; } + + /// + /// Creates a copy of the + /// + /// A new + IAuthenticationContext Clone(); + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Security/IAuthenticationContextFactory.cs b/src/Tgstation.Server.Host/Security/IAuthenticationContextFactory.cs new file mode 100644 index 0000000000..121a9cc46b --- /dev/null +++ b/src/Tgstation.Server.Host/Security/IAuthenticationContextFactory.cs @@ -0,0 +1,27 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Host.Models; + +namespace Tgstation.Server.Host.Security +{ + /// + /// For creating and accessing authentication contexts + /// + public interface IAuthenticationContextFactory + { + /// + /// The the created + /// + IAuthenticationContext CurrentAuthenticationContext { get; } + + /// + /// Create an to populate + /// + /// The of the + /// The of the operation + /// The for the operation + /// A representing the running operation + Task CreateAuthenticationContext(long userId, long? instanceId, CancellationToken cancellationToken); + } +} diff --git a/src/Tgstation.Server.Host/Security/ICryptographySuite.cs b/src/Tgstation.Server.Host/Security/ICryptographySuite.cs new file mode 100644 index 0000000000..137cbf54ff --- /dev/null +++ b/src/Tgstation.Server.Host/Security/ICryptographySuite.cs @@ -0,0 +1,31 @@ +using Tgstation.Server.Host.Models; + +namespace Tgstation.Server.Host.Security +{ + /// + /// Contains various cryptographic functions + /// + public interface ICryptographySuite + { + /// + /// Sets a for a given + /// + /// The whos is to be set + /// The new password for the + void SetUserPassword(User user, string newPassword); + + /// + /// Checks a given matches a given 's . This may result in being modified and this should be persisted + /// + /// The to check + /// The password to check + /// if matches the hash, otherwise + bool CheckUserPassword(User user, string password); + + /// + /// Generates a 40-length secure ascii + /// + /// A 40-length secure ascii + string GetSecureString(); + } +} diff --git a/src/Tgstation.Server.Host/Security/IIdentityCache.cs b/src/Tgstation.Server.Host/Security/IIdentityCache.cs new file mode 100644 index 0000000000..7d6b2def11 --- /dev/null +++ b/src/Tgstation.Server.Host/Security/IIdentityCache.cs @@ -0,0 +1,26 @@ +using System; +using Tgstation.Server.Host.Models; + +namespace Tgstation.Server.Host.Security +{ + /// + /// For caching s + /// + public interface IIdentityCache + { + /// + /// Keep a 's alive until an time + /// + /// The the belongs to + /// The to cache + /// When the should expire + void CacheSystemIdentity(User user, ISystemIdentity systemIdentity, DateTimeOffset expiry); + + /// + /// Attempt to load a cached + /// + /// The the belongs to + /// The cached or if it doesn't exist or expired + ISystemIdentity LoadCachedIdentity(User user); + } +} diff --git a/src/Tgstation.Server.Host/Security/ISystemIdentity.cs b/src/Tgstation.Server.Host/Security/ISystemIdentity.cs new file mode 100644 index 0000000000..fecf36a60a --- /dev/null +++ b/src/Tgstation.Server.Host/Security/ISystemIdentity.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Host.Security +{ + /// + /// Represents a user on the current + /// + public interface ISystemIdentity : IDisposable + { + /// + /// A unique identifier for the user + /// + string Uid { get; } + + /// + /// The user's name + /// + string Username { get; } + + /// + /// Clone the creating another copy that must have called on it + /// + /// A new mirroring the current one + ISystemIdentity Clone(); + + /// + /// Runs a given in the context of the + /// + /// The to perform, should be simple and not use any s or threading + /// The for the operation + /// A representing the running operation + Task RunImpersonated(Action action, CancellationToken cancellationToken); + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Security/ISystemIdentityFactory.cs b/src/Tgstation.Server.Host/Security/ISystemIdentityFactory.cs new file mode 100644 index 0000000000..11ba06ebe7 --- /dev/null +++ b/src/Tgstation.Server.Host/Security/ISystemIdentityFactory.cs @@ -0,0 +1,29 @@ +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Host.Models; + +namespace Tgstation.Server.Host.Security +{ + /// + /// Factory for s + /// + public interface ISystemIdentityFactory + { + /// + /// Create a for a given + /// + /// The user to create a for + /// The for the operation + /// A new or if the has no + Task CreateSystemIdentity(User user, CancellationToken cancellationToken); + + /// + /// Create a for a given username and password + /// + /// The username of the user + /// The password of the user + /// The for the operation + /// A new + Task CreateSystemIdentity(string username, string password, CancellationToken cancellationToken); + } +} diff --git a/src/Tgstation.Server.Host/Security/ITokenFactory.cs b/src/Tgstation.Server.Host/Security/ITokenFactory.cs new file mode 100644 index 0000000000..7f40b2112d --- /dev/null +++ b/src/Tgstation.Server.Host/Security/ITokenFactory.cs @@ -0,0 +1,19 @@ +using System; +using Tgstation.Server.Api.Models; + +namespace Tgstation.Server.Host.Security +{ + /// + /// For creating s + /// + public interface ITokenFactory + { + /// + /// Create a for a given + /// + /// The to create the token for. Must have the field available + /// The representing the time the token expires + /// A new + Token CreateToken(Models.User user, out DateTimeOffset expiry); + } +} diff --git a/src/Tgstation.Server.Host/Security/IdentityCache.cs b/src/Tgstation.Server.Host/Security/IdentityCache.cs new file mode 100644 index 0000000000..81850ab4f2 --- /dev/null +++ b/src/Tgstation.Server.Host/Security/IdentityCache.cs @@ -0,0 +1,57 @@ +using System; +using System.Collections.Generic; +using Tgstation.Server.Host.Models; + +namespace Tgstation.Server.Host.Security +{ + /// + sealed class IdentityCache : IIdentityCache, IDisposable + { + readonly Dictionary cachedIdentities; + + public IdentityCache() + { + cachedIdentities = new Dictionary(); + } + + /// + public void Dispose() + { + foreach (var I in cachedIdentities) + I.Value.Dispose(); + } + + /// + public void CacheSystemIdentity(User user, ISystemIdentity systemIdentity, DateTimeOffset expiry) + { + if (user == null) + throw new ArgumentNullException(nameof(user)); + if (systemIdentity == null) + throw new ArgumentNullException(nameof(systemIdentity)); + lock (cachedIdentities) + { + if (cachedIdentities.TryGetValue(user.Id, out var identCache)) + identCache.Dispose(); //also clears it out + identCache = new IdentityCacheObject(systemIdentity.Clone(), () => + { + lock (cachedIdentities) + cachedIdentities.Remove(user.Id); + }, expiry); + cachedIdentities.Add(user.Id, identCache); + } + } + + /// + public ISystemIdentity LoadCachedIdentity(User user) + { + if (user == null) + throw new ArgumentNullException(nameof(user)); + lock (cachedIdentities) + { + if (cachedIdentities.TryGetValue(user.Id, out var identity)) + return identity.SystemIdentity; + return null; + } + } + } +} diff --git a/src/Tgstation.Server.Host/Security/IdentityCacheObject.cs b/src/Tgstation.Server.Host/Security/IdentityCacheObject.cs new file mode 100644 index 0000000000..310c68b34c --- /dev/null +++ b/src/Tgstation.Server.Host/Security/IdentityCacheObject.cs @@ -0,0 +1,68 @@ +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Host.Security +{ + /// + /// For keeping a specific alive for a period of time + /// + sealed class IdentityCacheObject : IDisposable + { + /// + /// The the manages + /// + public ISystemIdentity SystemIdentity { get; } + + /// + /// The for the + /// + readonly CancellationTokenSource cancellationTokenSource; + + /// + /// The to clean up + /// + readonly Task task; + + /// + /// Construct an + /// + /// The value of + /// The to take on expiry + /// The + public IdentityCacheObject(ISystemIdentity systemIdentity, Action onExpiry, DateTimeOffset expiry) + { + SystemIdentity = systemIdentity ?? throw new ArgumentNullException(nameof(systemIdentity)); + if (onExpiry == null) + throw new ArgumentNullException(nameof(onExpiry)); + var now = DateTimeOffset.Now; + if (expiry < now) + throw new ArgumentOutOfRangeException(nameof(expiry), expiry, "expiry must be greater than DateTimeOffset.Now!"); + + cancellationTokenSource = new CancellationTokenSource(); + + async Task DisposeOnExipiry(CancellationToken cancellationToken) + { + using (SystemIdentity) + try + { + await Task.Delay(expiry - now, cancellationToken).ConfigureAwait(false); + } + finally + { + onExpiry(); + } + } + + task = DisposeOnExipiry(cancellationTokenSource.Token); + } + + /// + public void Dispose() + { + cancellationTokenSource.Cancel(); + task.Wait(); + cancellationTokenSource.Dispose(); + } + } +} diff --git a/src/Tgstation.Server.Host/Security/PosixSystemIdentityFactory.cs b/src/Tgstation.Server.Host/Security/PosixSystemIdentityFactory.cs new file mode 100644 index 0000000000..390f5104b6 --- /dev/null +++ b/src/Tgstation.Server.Host/Security/PosixSystemIdentityFactory.cs @@ -0,0 +1,26 @@ +using System; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Host.Models; + +namespace Tgstation.Server.Host.Security +{ + /// + /// for posix systems + /// + /// Blocked by https://github.com/dotnet/corefx/issues/3187 + sealed class PosixSystemIdentityFactory : ISystemIdentityFactory + { + /// + public Task CreateSystemIdentity(User user, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + + /// + public Task CreateSystemIdentity(string username, string password, CancellationToken cancellationToken) + { + throw new NotImplementedException(); + } + } +} diff --git a/src/Tgstation.Server.Host/Security/TokenFactory.cs b/src/Tgstation.Server.Host/Security/TokenFactory.cs new file mode 100644 index 0000000000..47b3809afe --- /dev/null +++ b/src/Tgstation.Server.Host/Security/TokenFactory.cs @@ -0,0 +1,45 @@ +using Microsoft.IdentityModel.Tokens; +using System; +using System.Globalization; +using System.IdentityModel.Tokens.Jwt; +using System.Reflection; +using System.Security.Claims; +using Tgstation.Server.Api.Models; + +namespace Tgstation.Server.Host.Security +{ + /// + sealed class TokenFactory : ITokenFactory + { + /// + /// Amount of hours until generated s expire + /// + const int TokenExpiryHours = 1; + + public static readonly string TokenAudience = typeof(Token).Assembly.GetName().Name; + public static readonly string TokenIssuer = Assembly.GetExecutingAssembly().GetName().Name; + public static readonly byte[] TokenSigningKey = CryptographySuite.GetSecureBytes(256); + + /// + public Token CreateToken(Models.User user, out DateTimeOffset expiry) + { + if (user == null) + throw new ArgumentNullException(nameof(user)); + + expiry = DateTimeOffset.Now.AddHours(TokenExpiryHours); + var claims = new Claim[] + { + new Claim(JwtRegisteredClaimNames.Sub, user.Id.ToString(CultureInfo.InvariantCulture)), + new Claim(JwtRegisteredClaimNames.Exp, $"{expiry.ToUnixTimeSeconds()}"), + new Claim(JwtRegisteredClaimNames.Nbf, $"{DateTimeOffset.Now.ToUnixTimeSeconds()}"), + new Claim(JwtRegisteredClaimNames.Iss, TokenIssuer), + new Claim(JwtRegisteredClaimNames.Aud, TokenAudience) + }; + + var key = new SymmetricSecurityKey(TokenSigningKey); + + var token = new JwtSecurityToken(new JwtHeader(new SigningCredentials(key, SecurityAlgorithms.HmacSha256)), new JwtPayload(claims)); + return new Token { Bearer = new JwtSecurityTokenHandler().WriteToken(token) }; + } + } +} diff --git a/src/Tgstation.Server.Host/Security/WindowsSystemIdentity.cs b/src/Tgstation.Server.Host/Security/WindowsSystemIdentity.cs new file mode 100644 index 0000000000..6f1ab56cba --- /dev/null +++ b/src/Tgstation.Server.Host/Security/WindowsSystemIdentity.cs @@ -0,0 +1,76 @@ +using System; +using System.DirectoryServices.AccountManagement; +using System.Security.Principal; +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Host.Security +{ + /// + /// for windows systems + /// + sealed class WindowsSystemIdentity : ISystemIdentity + { + /// + /// The for the + /// + readonly WindowsIdentity identity; + + /// + /// The for the + /// + readonly UserPrincipal userPrincipal; + + /// + /// Construct a using a + /// + /// The value of + public WindowsSystemIdentity(WindowsIdentity identity) + { + this.identity = identity ?? throw new ArgumentNullException(nameof(identity)); + } + + /// + /// Construct a using a + /// + /// The value of + public WindowsSystemIdentity(UserPrincipal userPrincipal) + { + this.userPrincipal = userPrincipal ?? throw new ArgumentNullException(nameof(userPrincipal)); + } + + /// + public void Dispose() + { + if (identity != null) + identity.Dispose(); + else + userPrincipal.Dispose(); + } + + /// + public string Uid => (userPrincipal?.Sid ?? identity.User).ToString(); + + /// + public string Username => userPrincipal?.Name ?? identity.Name; + + /// + public ISystemIdentity Clone() + { + if (identity != null) + return new WindowsSystemIdentity((WindowsIdentity)identity.Clone()); + //can't clone a UP, shouldn't be trying to anyway, cloning is for impersonation + throw new InvalidOperationException("Cannot clone a UserPrincipal based WindowsSystemIdentity!"); + } + + /// + public Task RunImpersonated(Action action, CancellationToken cancellationToken) => Task.Factory.StartNew(() => + { + if (action == null) + throw new ArgumentNullException(nameof(action)); + if (identity == null) + throw new InvalidOperationException("Impersonate using a UserPrincipal based WindowsSystemIdentity!"); + WindowsIdentity.RunImpersonated(identity.AccessToken, action); + }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current); + } +} \ No newline at end of file diff --git a/src/Tgstation.Server.Host/Security/WindowsSystemIdentityFactory.cs b/src/Tgstation.Server.Host/Security/WindowsSystemIdentityFactory.cs new file mode 100644 index 0000000000..7c4aa091f2 --- /dev/null +++ b/src/Tgstation.Server.Host/Security/WindowsSystemIdentityFactory.cs @@ -0,0 +1,73 @@ +using Microsoft.Win32.SafeHandles; +using System; +using System.ComponentModel; +using System.DirectoryServices.AccountManagement; +using System.Runtime.InteropServices; +using System.Security.Principal; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Host.Models; + +namespace Tgstation.Server.Host.Security +{ + /// + /// for windows systems. Uses long running tasks due to potential networked domains + /// + sealed class WindowsSystemIdentityFactory : ISystemIdentityFactory + { + /// + public Task CreateSystemIdentity(User user, CancellationToken cancellationToken) => Task.Factory.StartNew(() => + { + if (user == null) + throw new ArgumentNullException(nameof(user)); + + if (user.SystemIdentifier == null) + throw new InvalidOperationException("User's SystemIdentifier must not be null!"); + + PrincipalContext pc = null; + UserPrincipal principal = null; + //machine logon first cause it's faster + try + { + pc = new PrincipalContext(ContextType.Machine, Environment.UserDomainName); + principal = UserPrincipal.FindByIdentity(pc, user.SystemIdentifier); + + if (principal == null) + { + pc.Dispose(); + pc = new PrincipalContext(ContextType.Domain, Environment.UserDomainName); + principal = UserPrincipal.FindByIdentity(pc, user.SystemIdentifier); + if (principal == null) + pc.Dispose(); + } + } + catch + { + pc?.Dispose(); + throw; + } + + if (principal == null) + return null; + + return (ISystemIdentity)new WindowsSystemIdentity(principal); + }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current); + + /// + public Task CreateSystemIdentity(string username, string password, CancellationToken cancellationToken) => Task.Factory.StartNew(() => + { + if (username == null) + throw new ArgumentNullException(nameof(username)); + if (password == null) + throw new ArgumentNullException(nameof(password)); + var splits = username.Split('\\'); + + var res = NativeMethods.LogonUser(splits.Length > 1 ? splits[1] : splits[0], splits.Length > 1 ? splits[0] : null, password, 3 /*LOGON32_LOGON_NETWORK*/, 0 /*LOGON32_PROVIDER_DEFAULT*/, out var token); + if (!res) + return null; + + using (var handle = new SafeAccessTokenHandle(token)) //checked internally, windows identity always duplicates the handle when constructed with a userToken + return (ISystemIdentity)new WindowsSystemIdentity(new WindowsIdentity(handle.DangerousGetHandle())); //https://github.com/dotnet/corefx/blob/6ed61acebe3214fcf79b4274f2bb9b55c0604a4d/src/System.Security.Principal.Windows/src/System/Security/Principal/WindowsIdentity.cs#L271 + }, cancellationToken, TaskCreationOptions.LongRunning, TaskScheduler.Current); + } +} diff --git a/src/Tgstation.Server.Host/Server.cs b/src/Tgstation.Server.Host/Server.cs new file mode 100644 index 0000000000..f24dc3b4e9 --- /dev/null +++ b/src/Tgstation.Server.Host/Server.cs @@ -0,0 +1,142 @@ +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using System; +using System.IO; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Host.Core; +using Tgstation.Server.Host.IO; + +namespace Tgstation.Server.Host +{ + /// + sealed class Server : IServer, IServerControl + { + /// + public bool RestartRequested { get; private set; } + + /// + /// The for the + /// + readonly IWebHostBuilder webHostBuilder; + + /// + /// The for the + /// + readonly SemaphoreSlim semaphore; + + /// + /// The absolute path to install updates to + /// + readonly string updatePath; + + /// + /// If a server update has been applied + /// + bool updated; + + /// + /// The for the + /// + CancellationTokenSource cancellationTokenSource; + + /// + /// Construct a + /// + /// The value of + /// The value of + public Server(IWebHostBuilder webHostBuilder, string updatePath) + { + this.webHostBuilder = webHostBuilder ?? throw new ArgumentNullException(nameof(webHostBuilder)); + this.updatePath = updatePath; + + semaphore = new SemaphoreSlim(1); + updated = false; + RestartRequested = false; + } + + /// + public void Dispose() => semaphore.Dispose(); + + /// + [ExcludeFromCodeCoverage] + public async Task RunAsync(CancellationToken cancellationToken) + { + using (cancellationTokenSource = CancellationTokenSource.CreateLinkedTokenSource(cancellationToken)) + using (var fsWatcher = updatePath != null ? new FileSystemWatcher(Path.GetDirectoryName(updatePath)) : null) + { + if (fsWatcher != null) + { + fsWatcher.Created += (a, b) => + { + if (b.FullPath == updatePath && File.Exists(b.FullPath)) + cancellationTokenSource.Cancel(); + }; + fsWatcher.EnableRaisingEvents = true; + } + using (var webHost = webHostBuilder + .UseStartup() + .ConfigureServices((serviceCollection) => serviceCollection.AddSingleton(this)) + .Build() + ) + await webHost.RunAsync(cancellationTokenSource.Token).ConfigureAwait(false); + } + } + + /// + public async Task ApplyUpdate(byte[] updateZipData, IIOManager ioManager, CancellationToken cancellationToken) + { + if (updatePath == null) + return false; + using (await SemaphoreSlimContext.Lock(semaphore, cancellationToken).ConfigureAwait(false)) + { + if (updated) + throw new InvalidOperationException("ApplyUpdate has already been called!"); + updated = true; + try + { + await ioManager.ZipToDirectory(updatePath, updateZipData, cancellationToken).ConfigureAwait(false); + } + catch + { + try + { + //important to not leave this directory around if possible + await ioManager.DeleteDirectory(updatePath, default).ConfigureAwait(false); + } + catch { } + updated = false; + throw; + } + Restart(); + return true; + } + } + + /// + public void RegisterForRestart(Action action) + { + if (action == null) + throw new ArgumentNullException(nameof(action)); + if (cancellationTokenSource == null) + throw new InvalidOperationException("Tried to register an update action on a non-running Server!"); + cancellationTokenSource.Token.Register(() => { + if (RestartRequested) + action(); + }); + } + + /// + public bool Restart() + { + if (updatePath == null) + return false; + if (cancellationTokenSource == null) + throw new InvalidOperationException("Tried to restart a non-running Server!"); + RestartRequested = true; + cancellationTokenSource.Cancel(); + return true; + } + } +} diff --git a/src/Tgstation.Server.Host/ServerFactory.cs b/src/Tgstation.Server.Host/ServerFactory.cs new file mode 100644 index 0000000000..61f2984323 --- /dev/null +++ b/src/Tgstation.Server.Host/ServerFactory.cs @@ -0,0 +1,11 @@ +using Microsoft.AspNetCore; + +namespace Tgstation.Server.Host +{ + /// + sealed class ServerFactory : IServerFactory + { + /// + public IServer CreateServer(string[] args, string updatePath) => new Server(WebHost.CreateDefaultBuilder(args), updatePath); + } +} diff --git a/src/Tgstation.Server.Host/TgsAuthorizeAttribute.cs b/src/Tgstation.Server.Host/TgsAuthorizeAttribute.cs new file mode 100644 index 0000000000..427a73b27c --- /dev/null +++ b/src/Tgstation.Server.Host/TgsAuthorizeAttribute.cs @@ -0,0 +1,72 @@ +using System; +using Microsoft.AspNetCore.Authorization; +using Tgstation.Server.Api.Rights; + +namespace Tgstation.Server.Host +{ + /// + /// Helper for using the with the system + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Method, AllowMultiple = true, Inherited = true)] + sealed class TgsAuthorizeAttribute : AuthorizeAttribute + { + /// + /// Construct a + /// + public TgsAuthorizeAttribute() { } + + /// + /// Construct a for + /// + /// The rights required + public TgsAuthorizeAttribute(AdministrationRights requiredRights) => Roles = RightsHelper.RoleNames(requiredRights); + + /// + /// Construct a for + /// + /// The rights required + public TgsAuthorizeAttribute(InstanceManagerRights requiredRights) => Roles = RightsHelper.RoleNames(requiredRights); + + /// + /// Construct a for + /// + /// The rights required + public TgsAuthorizeAttribute(RepositoryRights requiredRights) => Roles = RightsHelper.RoleNames(requiredRights); + + /// + /// Construct a for + /// + /// The rights required + public TgsAuthorizeAttribute(ByondRights requiredRights) => Roles = RightsHelper.RoleNames(requiredRights); + + /// + /// Construct a for + /// + /// The rights required + public TgsAuthorizeAttribute(DreamMakerRights requiredRights) => Roles = RightsHelper.RoleNames(requiredRights); + + /// + /// Construct a for + /// + /// The rights required + public TgsAuthorizeAttribute(DreamDaemonRights requiredRights) => Roles = RightsHelper.RoleNames(requiredRights); + + /// + /// Construct a for + /// + /// The rights required + public TgsAuthorizeAttribute(ChatSettingsRights requiredRights) => Roles = RightsHelper.RoleNames(requiredRights); + + /// + /// Construct a for + /// + /// The rights required + public TgsAuthorizeAttribute(ConfigurationRights requiredRights) => Roles = RightsHelper.RoleNames(requiredRights); + + /// + /// Construct a for + /// + /// The rights required + public TgsAuthorizeAttribute(InstanceUserRights requiredRights) => Roles = RightsHelper.RoleNames(requiredRights); + } +} diff --git a/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj new file mode 100644 index 0000000000..85d750b8fc --- /dev/null +++ b/src/Tgstation.Server.Host/Tgstation.Server.Host.csproj @@ -0,0 +1,66 @@ + + + + Exe + netcoreapp2.0 + Full + 4.0.0.0 + + + + latest + true + + bin\Release\netstandard2.0\Tgstation.Server.Host.xml + 1701;1702;1705;CA2227 + + + + latest + 1701;1702;1705;CA2227 + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + PreserveNewest + + + + + + + + diff --git a/src/Tgstation.Server.Host/appsettings.Docker.json b/src/Tgstation.Server.Host/appsettings.Docker.json new file mode 100644 index 0000000000..233889ca3f --- /dev/null +++ b/src/Tgstation.Server.Host/appsettings.Docker.json @@ -0,0 +1,9 @@ +{ + "General": { + "LogFileDirectory": "/tgs_logs" + }, + "Database": { + "DatabaseType": "SqlServer", + "ConnectionString": "" + } +} diff --git a/src/Tgstation.Server.Host/appsettings.json b/src/Tgstation.Server.Host/appsettings.json new file mode 100644 index 0000000000..55cb8d5cce --- /dev/null +++ b/src/Tgstation.Server.Host/appsettings.json @@ -0,0 +1,35 @@ +{ + "General": { + "LogFileDirectory": null, //use the default path + "DisableFileLogging": false + }, + "Logging": { + "IncludeScopes": false, + "Debug": { + "LogLevel": { + "Default": "Debug", + "Microsoft": "Information" + } + }, + "Console": { + "LogLevel": { + "Default": "Trace", + "Microsoft": "Information" + } + }, + "LogLevel": { + "Default": "Information", + "Microsoft": "Warning" + } + }, + "Updates": { + "GitHubRepositoryId": 92952846, + "GitTagPrefix": "tgstation-server-v", + "UpdatePackageAssetName": "ServerUpdatePackage.zip" + }, + "Database": { + "DatabaseType": "SqlServer", + "ResetAdminPassword": false, + "ConnectionString": "Data Source=(local);Initial Catalog=TGS;Integrated Security=True" + } +} diff --git a/Tools/Config.dm b/tests/DMAPI/Config.dm similarity index 100% rename from Tools/Config.dm rename to tests/DMAPI/Config.dm diff --git a/Tools/Test.dm b/tests/DMAPI/Test.dm similarity index 100% rename from Tools/Test.dm rename to tests/DMAPI/Test.dm diff --git a/Tools/build_byond.sh b/tests/DMAPI/build_byond.sh similarity index 100% rename from Tools/build_byond.sh rename to tests/DMAPI/build_byond.sh diff --git a/Tools/travistester.dme b/tests/DMAPI/travistester.dme similarity index 77% rename from Tools/travistester.dme rename to tests/DMAPI/travistester.dme index 94a15e4bc6..c3b64fc12b 100644 --- a/Tools/travistester.dme +++ b/tests/DMAPI/travistester.dme @@ -12,7 +12,7 @@ // BEGIN_INCLUDE #include "Config.dm" -#include "..\DMAPI\tgs.dm" -#include "..\DMAPI\tgs\includes.dm" +#include "..\..\src\DMAPI\tgs.dm" +#include "..\..\src\DMAPI\tgs\includes.dm" #include "Test.dm" // END_INCLUDE diff --git a/tests/Tgstation.Server.Api.Tests/TestModels.cs b/tests/Tgstation.Server.Api.Tests/TestModels.cs new file mode 100644 index 0000000000..c86f7070c6 --- /dev/null +++ b/tests/Tgstation.Server.Api.Tests/TestModels.cs @@ -0,0 +1,37 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Tgstation.Server.Api.Tests +{ + [TestClass] + public sealed class TestModels + { + static IEnumerable> Models() + { + var modelAttributeType = typeof(ModelAttribute); + var targetAssembly = modelAttributeType.Assembly; + foreach(var I in targetAssembly.GetTypes()) + { + var attr = (ModelAttribute)I.GetCustomAttributes(modelAttributeType, false).FirstOrDefault(); + if (attr != default(ModelAttribute)) + yield return new Tuple(I, attr); + } + } + + [TestMethod] + public void TestModelsPropertiesPermissions() + { + var permissionsAttributeType = typeof(PermissionsAttribute); + foreach (var I in Models()) + { + foreach (var J in I.Item1.GetProperties()) + { + var perm = (PermissionsAttribute)J.GetCustomAttributes(permissionsAttributeType, false).FirstOrDefault(); + } + } + } + } +} diff --git a/tests/Tgstation.Server.Api.Tests/Tgstation.Server.Api.Tests.csproj b/tests/Tgstation.Server.Api.Tests/Tgstation.Server.Api.Tests.csproj new file mode 100644 index 0000000000..e4e77805e2 --- /dev/null +++ b/tests/Tgstation.Server.Api.Tests/Tgstation.Server.Api.Tests.csproj @@ -0,0 +1,20 @@ + + + + netcoreapp2.0 + + false + latest + + + + + + + + + + + + + diff --git a/tests/Tgstation.Server.Client.Tests/TestServerClientFactory.cs b/tests/Tgstation.Server.Client.Tests/TestServerClientFactory.cs new file mode 100644 index 0000000000..981ce7295e --- /dev/null +++ b/tests/Tgstation.Server.Client.Tests/TestServerClientFactory.cs @@ -0,0 +1,27 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.Threading.Tasks; + +namespace Tgstation.Server.Client.Tests +{ + /// + /// Tests for + /// + [TestClass] + public sealed class TestServerClientFactory + { + [TestMethod] + public async Task TestUserConstruction() + { + var factory = new ServerClientFactory(); + await Assert.ThrowsExceptionAsync(() => factory.CreateServerClient(null, null, null, default, default)).ConfigureAwait(false); + } + + [TestMethod] + public async Task TestTokenConstruction() + { + var factory = new ServerClientFactory(); + await Assert.ThrowsExceptionAsync(() => factory.CreateServerClient(null, null, default, default)).ConfigureAwait(false); + } + } +} diff --git a/tests/Tgstation.Server.Client.Tests/Tgstation.Server.Client.Tests.csproj b/tests/Tgstation.Server.Client.Tests/Tgstation.Server.Client.Tests.csproj new file mode 100644 index 0000000000..caacb5743a --- /dev/null +++ b/tests/Tgstation.Server.Client.Tests/Tgstation.Server.Client.Tests.csproj @@ -0,0 +1,20 @@ + + + + netcoreapp2.0 + + false + latest + + + + + + + + + + + + + diff --git a/tests/Tgstation.Server.CommandLine.Tests/Tgstation.Server.CommandLine.Tests.csproj b/tests/Tgstation.Server.CommandLine.Tests/Tgstation.Server.CommandLine.Tests.csproj new file mode 100644 index 0000000000..43a487525f --- /dev/null +++ b/tests/Tgstation.Server.CommandLine.Tests/Tgstation.Server.CommandLine.Tests.csproj @@ -0,0 +1,20 @@ + + + + netcoreapp2.0 + + false + latest + + + + + + + + + + + + + diff --git a/tests/Tgstation.Server.ControlPanel.Tests/Properties/AssemblyInfo.cs b/tests/Tgstation.Server.ControlPanel.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..8a27ad97fc --- /dev/null +++ b/tests/Tgstation.Server.ControlPanel.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,20 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("Tgstation.Server.ControlPanel.Tests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("")] +[assembly: AssemblyProduct("Tgstation.Server.ControlPanel.Tests")] +[assembly: AssemblyCopyright("Copyright © 2018")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +[assembly: ComVisible(false)] + +[assembly: Guid("93ca946d-50de-4ad4-bada-d0353f0affcc")] + +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/tests/Tgstation.Server.ControlPanel.Tests/Tgstation.Server.ControlPanel.Tests.csproj b/tests/Tgstation.Server.ControlPanel.Tests/Tgstation.Server.ControlPanel.Tests.csproj new file mode 100644 index 0000000000..d3d8ba781d --- /dev/null +++ b/tests/Tgstation.Server.ControlPanel.Tests/Tgstation.Server.ControlPanel.Tests.csproj @@ -0,0 +1,72 @@ + + + + + Debug + AnyCPU + {93CA946D-50DE-4AD4-BADA-D0353F0AFFCC} + Library + Properties + Tgstation.Server.ControlPanel.Tests + Tgstation.Server.ControlPanel.Tests + v4.7.1 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 15.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + latest + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + latest + + + + ..\..\packages\MSTest.TestFramework.1.2.0\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll + + + ..\..\packages\MSTest.TestFramework.1.2.0\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll + + + + + + + + + + + {d38be569-04b7-4c64-bdb5-683767b26feb} + Tgstation.Server.ControlPanel + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/tests/Tgstation.Server.ControlPanel.Tests/packages.config b/tests/Tgstation.Server.ControlPanel.Tests/packages.config new file mode 100644 index 0000000000..8819694ccc --- /dev/null +++ b/tests/Tgstation.Server.ControlPanel.Tests/packages.config @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/tests/Tgstation.Server.Host.Console.Tests/TestProgram.cs b/tests/Tgstation.Server.Host.Console.Tests/TestProgram.cs new file mode 100644 index 0000000000..695d75a619 --- /dev/null +++ b/tests/Tgstation.Server.Host.Console.Tests/TestProgram.cs @@ -0,0 +1,28 @@ +using Microsoft.Extensions.Logging; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using System; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Host.Watchdog; + +namespace Tgstation.Server.Host.Console.Tests +{ + [TestClass] + public class TestProgram + { + [TestMethod] + public async Task TestProgramRuns() + { + var mockServer = new Mock(); + var args = Array.Empty(); + mockServer.Setup(x => x.RunAsync(args, It.IsAny())).Returns(Task.CompletedTask).Verifiable(); + var mockServerFactory = new Mock(); + mockServerFactory.Setup(x => x.CreateWatchdog(It.IsAny())).Returns(mockServer.Object).Verifiable(); + Program.WatchdogFactory = mockServerFactory.Object; + await Program.Main(args).ConfigureAwait(false); + mockServer.VerifyAll(); + mockServerFactory.VerifyAll(); + } + } +} diff --git a/tests/Tgstation.Server.Host.Console.Tests/Tgstation.Server.Host.Console.Tests.csproj b/tests/Tgstation.Server.Host.Console.Tests/Tgstation.Server.Host.Console.Tests.csproj new file mode 100644 index 0000000000..2e6e921228 --- /dev/null +++ b/tests/Tgstation.Server.Host.Console.Tests/Tgstation.Server.Host.Console.Tests.csproj @@ -0,0 +1,21 @@ + + + + netcoreapp2.0 + + false + latest + + + + + + + + + + + + + + diff --git a/tests/Tgstation.Server.Host.Service.Tests/Properties/AssemblyInfo.cs b/tests/Tgstation.Server.Host.Service.Tests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..0158cb9bbb --- /dev/null +++ b/tests/Tgstation.Server.Host.Service.Tests/Properties/AssemblyInfo.cs @@ -0,0 +1,11 @@ +using System.Reflection; +using System.Runtime.InteropServices; + +[assembly: AssemblyTitle("Tgstation.Server.Host.Service.Tests")] +[assembly: ComVisible(false)] + +[assembly: Guid("aa80a190-52e2-4be3-bfeb-1f148d9e9007")] + +[assembly: AssemblyVersion("4.0.0.0")] +[assembly: AssemblyFileVersion("4.0.0.0")] +[assembly: AssemblyInformationalVersion("4.0.0.0")] diff --git a/tests/Tgstation.Server.Host.Service.Tests/TestServerService.cs b/tests/Tgstation.Server.Host.Service.Tests/TestServerService.cs new file mode 100644 index 0000000000..f4127014b5 --- /dev/null +++ b/tests/Tgstation.Server.Host.Service.Tests/TestServerService.cs @@ -0,0 +1,57 @@ +using Microsoft.Extensions.Logging; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using System; +using System.Reflection; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Host.Watchdog; + +namespace Tgstation.Server.Host.Service.Tests +{ + /// + /// Tests for + /// + [TestClass] + public sealed class TestServerService + { + [TestMethod] + public void TestConstructionAndDisposal() + { + Assert.ThrowsException(() => new ServerService(null, null)); + var mockWatchdogFactory = new Mock(); + Assert.ThrowsException(() => new ServerService(mockWatchdogFactory.Object, null)); + var mockLoggerFactory = new LoggerFactory(); + new ServerService(mockWatchdogFactory.Object, mockLoggerFactory).Dispose(); + } + + [TestMethod] + public void TestRun() + { + var type = typeof(ServerService); + var onStart = type.GetMethod("OnStart", BindingFlags.Instance | BindingFlags.NonPublic); + var onStop = type.GetMethod("OnStop", BindingFlags.Instance | BindingFlags.NonPublic); + + var mockWatchdog = new Mock(); + var args = Array.Empty(); + CancellationToken cancellationToken; + mockWatchdog.Setup(x => x.RunAsync(args, It.IsAny())).Callback((string[] _, CancellationToken token) => cancellationToken = token).Returns(Task.CompletedTask).Verifiable(); + var mockWatchdogFactory = new Mock(); + var mockLoggerFactory = new LoggerFactory(); + mockWatchdogFactory.Setup(x => x.CreateWatchdog(mockLoggerFactory)).Returns(mockWatchdog.Object).Verifiable(); + + using(var service = new ServerService(mockWatchdogFactory.Object, mockLoggerFactory)) + { + onStart.Invoke(service, new object[] { args }); + + Assert.IsFalse(cancellationToken.IsCancellationRequested); + + onStop.Invoke(service, Array.Empty()); + Assert.IsTrue(cancellationToken.IsCancellationRequested); + mockWatchdog.VerifyAll(); + } + + mockWatchdogFactory.VerifyAll(); + } + } +} diff --git a/tests/Tgstation.Server.Host.Service.Tests/Tgstation.Server.Host.Service.Tests.csproj b/tests/Tgstation.Server.Host.Service.Tests/Tgstation.Server.Host.Service.Tests.csproj new file mode 100644 index 0000000000..6baddb56cb --- /dev/null +++ b/tests/Tgstation.Server.Host.Service.Tests/Tgstation.Server.Host.Service.Tests.csproj @@ -0,0 +1,151 @@ + + + + + Debug + AnyCPU + {AA80A190-52E2-4BE3-BFEB-1F148D9E9007} + Library + Properties + Tgstation.Server.Host.Service.Tests + Tgstation.Server.Host.Service.Tests + v4.7.1 + 512 + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + 15.0 + $(MSBuildExtensionsPath32)\Microsoft\VisualStudio\v$(VisualStudioVersion) + $(ProgramFiles)\Common Files\microsoft shared\VSTT\$(VisualStudioVersion)\UITestExtensionPackages + False + UnitTest + + + + + true + full + false + bin\Debug\ + DEBUG;TRACE + prompt + 4 + 7.1 + + + pdbonly + true + bin\Release\ + TRACE + prompt + 4 + 7.1 + + + + ..\..\packages\Castle.Core.4.2.1\lib\net45\Castle.Core.dll + + + ..\..\packages\Microsoft.Extensions.Configuration.2.1.1\lib\netstandard2.0\Microsoft.Extensions.Configuration.dll + + + ..\..\packages\Microsoft.Extensions.Configuration.Abstractions.2.1.1\lib\netstandard2.0\Microsoft.Extensions.Configuration.Abstractions.dll + + + ..\..\packages\Microsoft.Extensions.Configuration.Binder.2.1.1\lib\netstandard2.0\Microsoft.Extensions.Configuration.Binder.dll + + + ..\..\packages\Microsoft.Extensions.DependencyInjection.Abstractions.2.1.1\lib\netstandard2.0\Microsoft.Extensions.DependencyInjection.Abstractions.dll + + + ..\..\packages\Microsoft.Extensions.Logging.2.1.1\lib\netstandard2.0\Microsoft.Extensions.Logging.dll + + + ..\..\packages\Microsoft.Extensions.Logging.Abstractions.2.1.1\lib\netstandard2.0\Microsoft.Extensions.Logging.Abstractions.dll + + + ..\..\packages\Microsoft.Extensions.Logging.EventLog.2.1.1\lib\net461\Microsoft.Extensions.Logging.EventLog.dll + + + ..\..\packages\Microsoft.Extensions.Options.2.1.1\lib\netstandard2.0\Microsoft.Extensions.Options.dll + + + ..\..\packages\Microsoft.Extensions.Primitives.2.1.1\lib\netstandard2.0\Microsoft.Extensions.Primitives.dll + + + ..\..\packages\MSTest.TestFramework.1.2.1\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.dll + + + ..\..\packages\MSTest.TestFramework.1.2.1\lib\net45\Microsoft.VisualStudio.TestPlatform.TestFramework.Extensions.dll + + + ..\..\packages\Moq.4.8.2\lib\net45\Moq.dll + + + + ..\..\packages\System.Buffers.4.4.0\lib\netstandard2.0\System.Buffers.dll + + + + + + + ..\..\packages\System.Diagnostics.EventLog.4.5.0\lib\net461\System.Diagnostics.EventLog.dll + + + + ..\..\packages\System.Memory.4.5.1\lib\netstandard2.0\System.Memory.dll + + + + + ..\..\packages\System.Numerics.Vectors.4.4.0\lib\net46\System.Numerics.Vectors.dll + + + ..\..\packages\System.Runtime.CompilerServices.Unsafe.4.5.1\lib\netstandard2.0\System.Runtime.CompilerServices.Unsafe.dll + + + ..\..\packages\System.Security.AccessControl.4.5.0\lib\net461\System.Security.AccessControl.dll + + + ..\..\packages\System.Security.Permissions.4.5.0\lib\net461\System.Security.Permissions.dll + + + ..\..\packages\System.Security.Principal.Windows.4.5.0\lib\net461\System.Security.Principal.Windows.dll + + + + ..\..\packages\System.Threading.Tasks.Extensions.4.3.0\lib\portable-net45+win8+wp8+wpa81\System.Threading.Tasks.Extensions.dll + + + + ..\..\packages\System.ValueTuple.4.4.0\lib\net47\System.ValueTuple.dll + True + + + + + + + + + + + + {29927416-3b78-49a7-a560-5ccaa638b6b4} + Tgstation.Server.Host.Service + + + {5d2d682c-6bf0-439c-850b-6ab945bbeaea} + Tgstation.Server.Host.Watchdog + + + + + + + This project references NuGet package(s) that are missing on this computer. Use NuGet Package Restore to download them. For more information, see http://go.microsoft.com/fwlink/?LinkID=322105. The missing file is {0}. + + + + + + \ No newline at end of file diff --git a/tests/Tgstation.Server.Host.Service.Tests/packages.config b/tests/Tgstation.Server.Host.Service.Tests/packages.config new file mode 100644 index 0000000000..8a479af186 --- /dev/null +++ b/tests/Tgstation.Server.Host.Service.Tests/packages.config @@ -0,0 +1,26 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/tests/Tgstation.Server.Host.Tests/Core/TestApplication.cs b/tests/Tgstation.Server.Host.Tests/Core/TestApplication.cs new file mode 100644 index 0000000000..319b6d94c8 --- /dev/null +++ b/tests/Tgstation.Server.Host.Tests/Core/TestApplication.cs @@ -0,0 +1,44 @@ +using Microsoft.AspNetCore; +using Microsoft.AspNetCore.Hosting; +using Microsoft.Extensions.DependencyInjection; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System; +using System.IO; +using System.Threading; +using System.Threading.Tasks; +using Tgstation.Server.Host.IO; + +namespace Tgstation.Server.Host.Core.Tests +{ + [TestClass] + public sealed class TestApplication : IServerControl + { + public Task ApplyUpdate(byte[] updateZipData, IIOManager ioManager, CancellationToken cancellationToken) => throw new NotImplementedException(); + + public void RegisterForRestart(Action action) => throw new NotImplementedException(); + + public bool Restart() => throw new NotImplementedException(); + + [TestMethod] + public async Task TestSuccessfulStartup() + { + var dbName = Path.GetTempFileName(); + try + { + using (var webHost = WebHost.CreateDefaultBuilder(new string[] { "Database:DatabaseType=Sqlite", "Database:ConnectionString=Data Source=" + dbName }) //force it to use sqlite + .UseStartup() + .ConfigureServices((serviceCollection) => serviceCollection.AddSingleton(this)) + .Build() + ) + { + await webHost.StartAsync().ConfigureAwait(false); + await webHost.StopAsync().ConfigureAwait(false); + } + } + finally + { + File.Delete(dbName); + } + } + } +} diff --git a/tests/Tgstation.Server.Host.Tests/Security/TestAuthenticationContext.cs b/tests/Tgstation.Server.Host.Tests/Security/TestAuthenticationContext.cs new file mode 100644 index 0000000000..91af8fa215 --- /dev/null +++ b/tests/Tgstation.Server.Host.Tests/Security/TestAuthenticationContext.cs @@ -0,0 +1,51 @@ +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using System; +using Tgstation.Server.Api.Rights; +using Tgstation.Server.Host.Models; + +namespace Tgstation.Server.Host.Security.Tests +{ + /// + /// Tests for + /// + [TestClass] + public sealed class TestAuthenticationContext + { + [TestMethod] + public void TestConstruction() + { + Assert.ThrowsException(() => new AuthenticationContext(null, null, null)); + var mockSystemIdentity = new Mock(); + + var user = new User(); + + var authContext = new AuthenticationContext(null, user, null); + Assert.ThrowsException(() => new AuthenticationContext(mockSystemIdentity.Object, null, null)); + + var instanceUser = new InstanceUser(); + + Assert.ThrowsException(() => new AuthenticationContext(null, null, instanceUser)); + Assert.ThrowsException(() => new AuthenticationContext(mockSystemIdentity.Object, null, instanceUser)); + authContext = new AuthenticationContext(mockSystemIdentity.Object, user, null); + authContext = new AuthenticationContext(null, user, instanceUser); + authContext = new AuthenticationContext(mockSystemIdentity.Object, user, instanceUser); + user.SystemIdentifier = "root"; + Assert.ThrowsException(() => new AuthenticationContext(null, user, null)); + } + + + [TestMethod] + public void TestGetRightsGeneric() + { + var user = new User(); + var instanceUser = new InstanceUser(); + var authContext = new AuthenticationContext(null, user, instanceUser); + + user.AdministrationRights = AdministrationRights.EditUsers; + instanceUser.ByondRights = ByondRights.ChangeVersion | ByondRights.ReadActive; + Assert.AreEqual((int)user.AdministrationRights, authContext.GetRight(RightsType.Administration)); + Assert.AreEqual((int)instanceUser.ByondRights, authContext.GetRight(RightsType.Byond)); + } + } +} diff --git a/tests/Tgstation.Server.Host.Tests/Tgstation.Server.Host.Tests.csproj b/tests/Tgstation.Server.Host.Tests/Tgstation.Server.Host.Tests.csproj new file mode 100644 index 0000000000..6481ec2802 --- /dev/null +++ b/tests/Tgstation.Server.Host.Tests/Tgstation.Server.Host.Tests.csproj @@ -0,0 +1,21 @@ + + + + netcoreapp2.0 + + false + latest + + + + + + + + + + + + + + diff --git a/tests/Tgstation.Server.Host.Watchdog.Tests/TestWatchdog.cs b/tests/Tgstation.Server.Host.Watchdog.Tests/TestWatchdog.cs new file mode 100644 index 0000000000..67c3eb9289 --- /dev/null +++ b/tests/Tgstation.Server.Host.Watchdog.Tests/TestWatchdog.cs @@ -0,0 +1,21 @@ +using Microsoft.Extensions.Logging; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; +using System; +using System.Threading; +using System.Threading.Tasks; + +namespace Tgstation.Server.Host.Watchdog.Tests +{ + [TestClass] + public sealed class TestWatchdog + { + [TestMethod] + public void TestConstruction() + { + Assert.ThrowsException(() => new Watchdog(null)); + var mockLogger = new LoggerFactory().CreateLogger(); + var wd = new Watchdog(mockLogger); + } + } +} diff --git a/tests/Tgstation.Server.Host.Watchdog.Tests/TestWatchdogFactory.cs b/tests/Tgstation.Server.Host.Watchdog.Tests/TestWatchdogFactory.cs new file mode 100644 index 0000000000..18fb82abc4 --- /dev/null +++ b/tests/Tgstation.Server.Host.Watchdog.Tests/TestWatchdogFactory.cs @@ -0,0 +1,20 @@ +using Microsoft.Extensions.Logging; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Moq; + +namespace Tgstation.Server.Host.Watchdog.Tests +{ + /// + /// Tests for + /// + [TestClass] + public sealed class TestWatchdogFactory + { + [TestMethod] + public void TestCreateWatchdog() + { + var factory = new WatchdogFactory(); + Assert.IsNotNull(factory.CreateWatchdog(new LoggerFactory())); + } + } +} diff --git a/tests/Tgstation.Server.Host.Watchdog.Tests/Tgstation.Server.Host.Watchdog.Tests.csproj b/tests/Tgstation.Server.Host.Watchdog.Tests/Tgstation.Server.Host.Watchdog.Tests.csproj new file mode 100644 index 0000000000..d69d3cbdf8 --- /dev/null +++ b/tests/Tgstation.Server.Host.Watchdog.Tests/Tgstation.Server.Host.Watchdog.Tests.csproj @@ -0,0 +1,31 @@ + + + + netcoreapp2.0 + + false + + + + latest + false + + + + + + latest + + + + + + + + + + + + + + diff --git a/tgs.ico b/tgs.ico deleted file mode 100644 index 6ed69fadfc..0000000000 Binary files a/tgs.ico and /dev/null differ diff --git a/tgstation-server.sln b/tgstation-server.sln new file mode 100644 index 0000000000..acd602d242 --- /dev/null +++ b/tgstation-server.sln @@ -0,0 +1,227 @@ + +Microsoft Visual Studio Solution File, Format Version 12.00 +# Visual Studio 15 +VisualStudioVersion = 15.0.27130.2036 +MinimumVisualStudioVersion = 10.0.40219.1 +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution Items", "{DA32568D-1D8D-4A4C-9943-BFD3CE796B3F}" + ProjectSection(SolutionItems) = preProject + .codecov.yml = .codecov.yml + .dockerignore = .dockerignore + .gitignore = .gitignore + .travis.yml = .travis.yml + appveyor.yml = appveyor.yml + LICENSE = LICENSE + README.md = README.md + v4_prototype_TODO.txt = v4_prototype_TODO.txt + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tgstation.Server.Host.Console", "src\Tgstation.Server.Host.Console\Tgstation.Server.Host.Console.csproj", "{A09C947F-4B9B-4AEE-AB50-47055EA05F18}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tgstation.Server.Host.Console.Tests", "tests\Tgstation.Server.Host.Console.Tests\Tgstation.Server.Host.Console.Tests.csproj", "{D36B99C4-E771-42D6-A95F-1102B3E236DF}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "build", "build", "{6FF654E6-3E2C-46D4-872D-D528F77D6973}" + ProjectSection(SolutionItems) = preProject + build\BuildDox.ps1 = build\BuildDox.ps1 + build\Dockerfile = build\Dockerfile + build\Doxyfile = build\Doxyfile + build\install_byond.sh = build\install_byond.sh + build\tgs.docker.sh = build\tgs.docker.sh + build\tgs.ico = build\tgs.ico + build\UploadCoverage.ps1 = build\UploadCoverage.ps1 + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tgstation.Server.Host", "src\Tgstation.Server.Host\Tgstation.Server.Host.csproj", "{2B69AD6D-2B5A-4023-8EAD-0BD1B18E028A}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tgstation.Server.Api", "src\Tgstation.Server.Api\Tgstation.Server.Api.csproj", "{8B4A208D-A48A-4A5D-8B94-E2661138865D}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tgstation.Server.Host.Service", "src\Tgstation.Server.Host.Service\Tgstation.Server.Host.Service.csproj", "{29927416-3B78-49A7-A560-5CCAA638B6B4}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tgstation.Server.CommandLine", "src\Tgstation.Server.CommandLine\Tgstation.Server.CommandLine.csproj", "{657C3624-3534-4A0C-B4E7-196FC830F547}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tgstation.Server.ControlPanel", "src\Tgstation.Server.ControlPanel\Tgstation.Server.ControlPanel.csproj", "{D38BE569-04B7-4C64-BDB5-683767B26FEB}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tgstation.Server.Client", "src\Tgstation.Server.Client\Tgstation.Server.Client.csproj", "{E0AC911F-7675-4A91-9499-D8A2E2390AAD}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tgstation.Server.ControlPanel.Tests", "tests\Tgstation.Server.ControlPanel.Tests\Tgstation.Server.ControlPanel.Tests.csproj", "{93CA946D-50DE-4AD4-BADA-D0353F0AFFCC}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tgstation.Server.Api.Tests", "tests\Tgstation.Server.Api.Tests\Tgstation.Server.Api.Tests.csproj", "{BE0E8F49-334F-49D7-A04D-D82E9AB7AA36}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tgstation.Server.Client.Tests", "tests\Tgstation.Server.Client.Tests\Tgstation.Server.Client.Tests.csproj", "{E5301AF1-4F74-4982-BF24-95F23CC5D5B2}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tgstation.Server.CommandLine.Tests", "tests\Tgstation.Server.CommandLine.Tests\Tgstation.Server.CommandLine.Tests.csproj", "{90D400B4-FCB0-447E-8FB2-1CF58EE7E55D}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tgstation.Server.Host.Tests", "tests\Tgstation.Server.Host.Tests\Tgstation.Server.Host.Tests.csproj", "{A3362FF6-550F-480F-859E-8EC1EB6EAB31}" +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tgstation.Server.Host.Watchdog", "src\Tgstation.Server.Host.Watchdog\Tgstation.Server.Host.Watchdog.csproj", "{5D2D682C-6BF0-439C-850B-6AB945BBEAEA}" + ProjectSection(ProjectDependencies) = postProject + {2B69AD6D-2B5A-4023-8EAD-0BD1B18E028A} = {2B69AD6D-2B5A-4023-8EAD-0BD1B18E028A} + EndProjectSection +EndProject +Project("{9A19103F-16F7-4668-BE54-9A1E7A4F7556}") = "Tgstation.Server.Host.Watchdog.Tests", "tests\Tgstation.Server.Host.Watchdog.Tests\Tgstation.Server.Host.Watchdog.Tests.csproj", "{7500F776-4384-4B5F-A8D8-22461CAD108B}" +EndProject +Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Tgstation.Server.Host.Service.Tests", "tests\Tgstation.Server.Host.Service.Tests\Tgstation.Server.Host.Service.Tests.csproj", "{AA80A190-52E2-4BE3-BFEB-1F148D9E9007}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "DMAPI", "DMAPI", "{7E4E7CF6-A48D-410E-8191-C49C6B5AB9CC}" +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tgs", "tgs", "{F7765A4B-021C-454E-ABB1-2AB1B85F91B4}" + ProjectSection(SolutionItems) = preProject + src\DMAPI\tgs\includes.dm = src\DMAPI\tgs\includes.dm + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "core", "core", "{DCCBA9DA-47BA-4C70-823B-E99A3ACA0377}" + ProjectSection(SolutionItems) = preProject + src\DMAPI\tgs\core\_definitions.dm = src\DMAPI\tgs\core\_definitions.dm + src\DMAPI\tgs\core\core.dm = src\DMAPI\tgs\core\core.dm + src\DMAPI\tgs\core\datum.dm = src\DMAPI\tgs\core\datum.dm + src\DMAPI\tgs\core\default_event_handler.dm = src\DMAPI\tgs\core\default_event_handler.dm + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "v3210", "v3210", "{1B228ACB-60C3-4DF9-B716-9F0BD31F6766}" + ProjectSection(SolutionItems) = preProject + src\DMAPI\tgs\v3210\api.dm = src\DMAPI\tgs\v3210\api.dm + src\DMAPI\tgs\v3210\commands.dm = src\DMAPI\tgs\v3210\commands.dm + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "src", "src", "{3BB10856-AA01-43D1-AAB0-A013085426D6}" + ProjectSection(SolutionItems) = preProject + src\DMAPI\tgs.dm = src\DMAPI\tgs.dm + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tests", "tests", "{82066812-6C73-4360-943B-B23F2F491261}" + ProjectSection(SolutionItems) = preProject + tests\DMAPI\Config.dm = tests\DMAPI\Config.dm + tests\DMAPI\install_byond.sh = tests\DMAPI\install_byond.sh + tests\DMAPI\Test.dm = tests\DMAPI\Test.dm + tests\DMAPI\travistester.dme = tests\DMAPI\travistester.dme + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "v4", "v4", "{057FAC33-CC31-4948-91C6-B0977C335890}" + ProjectSection(SolutionItems) = preProject + src\DMAPI\tgs\v4\api.dm = src\DMAPI\tgs\v4\api.dm + src\DMAPI\tgs\v4\commands.dm = src\DMAPI\tgs\v4\commands.dm + EndProjectSection +EndProject +Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "tools", "tools", "{A55C1117-5808-4AB2-BEA6-4D4A3E66A2F2}" + ProjectSection(SolutionItems) = preProject + tools\TGS.postman_collection.json = tools\TGS.postman_collection.json + EndProjectSection +EndProject +Global + GlobalSection(SolutionConfigurationPlatforms) = preSolution + Debug|Any CPU = Debug|Any CPU + Docker|Any CPU = Docker|Any CPU + Release|Any CPU = Release|Any CPU + EndGlobalSection + GlobalSection(ProjectConfigurationPlatforms) = postSolution + {A09C947F-4B9B-4AEE-AB50-47055EA05F18}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A09C947F-4B9B-4AEE-AB50-47055EA05F18}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A09C947F-4B9B-4AEE-AB50-47055EA05F18}.Docker|Any CPU.ActiveCfg = Release|Any CPU + {A09C947F-4B9B-4AEE-AB50-47055EA05F18}.Docker|Any CPU.Build.0 = Release|Any CPU + {A09C947F-4B9B-4AEE-AB50-47055EA05F18}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A09C947F-4B9B-4AEE-AB50-47055EA05F18}.Release|Any CPU.Build.0 = Release|Any CPU + {D36B99C4-E771-42D6-A95F-1102B3E236DF}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D36B99C4-E771-42D6-A95F-1102B3E236DF}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D36B99C4-E771-42D6-A95F-1102B3E236DF}.Docker|Any CPU.ActiveCfg = Release|Any CPU + {D36B99C4-E771-42D6-A95F-1102B3E236DF}.Docker|Any CPU.Build.0 = Release|Any CPU + {D36B99C4-E771-42D6-A95F-1102B3E236DF}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D36B99C4-E771-42D6-A95F-1102B3E236DF}.Release|Any CPU.Build.0 = Release|Any CPU + {2B69AD6D-2B5A-4023-8EAD-0BD1B18E028A}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {2B69AD6D-2B5A-4023-8EAD-0BD1B18E028A}.Debug|Any CPU.Build.0 = Debug|Any CPU + {2B69AD6D-2B5A-4023-8EAD-0BD1B18E028A}.Docker|Any CPU.ActiveCfg = Release|Any CPU + {2B69AD6D-2B5A-4023-8EAD-0BD1B18E028A}.Docker|Any CPU.Build.0 = Release|Any CPU + {2B69AD6D-2B5A-4023-8EAD-0BD1B18E028A}.Release|Any CPU.ActiveCfg = Release|Any CPU + {2B69AD6D-2B5A-4023-8EAD-0BD1B18E028A}.Release|Any CPU.Build.0 = Release|Any CPU + {8B4A208D-A48A-4A5D-8B94-E2661138865D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {8B4A208D-A48A-4A5D-8B94-E2661138865D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {8B4A208D-A48A-4A5D-8B94-E2661138865D}.Docker|Any CPU.ActiveCfg = Release|Any CPU + {8B4A208D-A48A-4A5D-8B94-E2661138865D}.Docker|Any CPU.Build.0 = Release|Any CPU + {8B4A208D-A48A-4A5D-8B94-E2661138865D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {8B4A208D-A48A-4A5D-8B94-E2661138865D}.Release|Any CPU.Build.0 = Release|Any CPU + {29927416-3B78-49A7-A560-5CCAA638B6B4}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {29927416-3B78-49A7-A560-5CCAA638B6B4}.Debug|Any CPU.Build.0 = Debug|Any CPU + {29927416-3B78-49A7-A560-5CCAA638B6B4}.Docker|Any CPU.ActiveCfg = Release|Any CPU + {29927416-3B78-49A7-A560-5CCAA638B6B4}.Docker|Any CPU.Build.0 = Release|Any CPU + {29927416-3B78-49A7-A560-5CCAA638B6B4}.Release|Any CPU.ActiveCfg = Release|Any CPU + {29927416-3B78-49A7-A560-5CCAA638B6B4}.Release|Any CPU.Build.0 = Release|Any CPU + {657C3624-3534-4A0C-B4E7-196FC830F547}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {657C3624-3534-4A0C-B4E7-196FC830F547}.Debug|Any CPU.Build.0 = Debug|Any CPU + {657C3624-3534-4A0C-B4E7-196FC830F547}.Docker|Any CPU.ActiveCfg = Release|Any CPU + {657C3624-3534-4A0C-B4E7-196FC830F547}.Docker|Any CPU.Build.0 = Release|Any CPU + {657C3624-3534-4A0C-B4E7-196FC830F547}.Release|Any CPU.ActiveCfg = Release|Any CPU + {657C3624-3534-4A0C-B4E7-196FC830F547}.Release|Any CPU.Build.0 = Release|Any CPU + {D38BE569-04B7-4C64-BDB5-683767B26FEB}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {D38BE569-04B7-4C64-BDB5-683767B26FEB}.Debug|Any CPU.Build.0 = Debug|Any CPU + {D38BE569-04B7-4C64-BDB5-683767B26FEB}.Docker|Any CPU.ActiveCfg = Release|Any CPU + {D38BE569-04B7-4C64-BDB5-683767B26FEB}.Docker|Any CPU.Build.0 = Release|Any CPU + {D38BE569-04B7-4C64-BDB5-683767B26FEB}.Release|Any CPU.ActiveCfg = Release|Any CPU + {D38BE569-04B7-4C64-BDB5-683767B26FEB}.Release|Any CPU.Build.0 = Release|Any CPU + {E0AC911F-7675-4A91-9499-D8A2E2390AAD}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E0AC911F-7675-4A91-9499-D8A2E2390AAD}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E0AC911F-7675-4A91-9499-D8A2E2390AAD}.Docker|Any CPU.ActiveCfg = Release|Any CPU + {E0AC911F-7675-4A91-9499-D8A2E2390AAD}.Docker|Any CPU.Build.0 = Release|Any CPU + {E0AC911F-7675-4A91-9499-D8A2E2390AAD}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E0AC911F-7675-4A91-9499-D8A2E2390AAD}.Release|Any CPU.Build.0 = Release|Any CPU + {93CA946D-50DE-4AD4-BADA-D0353F0AFFCC}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {93CA946D-50DE-4AD4-BADA-D0353F0AFFCC}.Debug|Any CPU.Build.0 = Debug|Any CPU + {93CA946D-50DE-4AD4-BADA-D0353F0AFFCC}.Docker|Any CPU.ActiveCfg = Release|Any CPU + {93CA946D-50DE-4AD4-BADA-D0353F0AFFCC}.Docker|Any CPU.Build.0 = Release|Any CPU + {93CA946D-50DE-4AD4-BADA-D0353F0AFFCC}.Release|Any CPU.ActiveCfg = Release|Any CPU + {93CA946D-50DE-4AD4-BADA-D0353F0AFFCC}.Release|Any CPU.Build.0 = Release|Any CPU + {BE0E8F49-334F-49D7-A04D-D82E9AB7AA36}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {BE0E8F49-334F-49D7-A04D-D82E9AB7AA36}.Debug|Any CPU.Build.0 = Debug|Any CPU + {BE0E8F49-334F-49D7-A04D-D82E9AB7AA36}.Docker|Any CPU.ActiveCfg = Release|Any CPU + {BE0E8F49-334F-49D7-A04D-D82E9AB7AA36}.Docker|Any CPU.Build.0 = Release|Any CPU + {BE0E8F49-334F-49D7-A04D-D82E9AB7AA36}.Release|Any CPU.ActiveCfg = Release|Any CPU + {BE0E8F49-334F-49D7-A04D-D82E9AB7AA36}.Release|Any CPU.Build.0 = Release|Any CPU + {E5301AF1-4F74-4982-BF24-95F23CC5D5B2}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {E5301AF1-4F74-4982-BF24-95F23CC5D5B2}.Debug|Any CPU.Build.0 = Debug|Any CPU + {E5301AF1-4F74-4982-BF24-95F23CC5D5B2}.Docker|Any CPU.ActiveCfg = Release|Any CPU + {E5301AF1-4F74-4982-BF24-95F23CC5D5B2}.Docker|Any CPU.Build.0 = Release|Any CPU + {E5301AF1-4F74-4982-BF24-95F23CC5D5B2}.Release|Any CPU.ActiveCfg = Release|Any CPU + {E5301AF1-4F74-4982-BF24-95F23CC5D5B2}.Release|Any CPU.Build.0 = Release|Any CPU + {90D400B4-FCB0-447E-8FB2-1CF58EE7E55D}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {90D400B4-FCB0-447E-8FB2-1CF58EE7E55D}.Debug|Any CPU.Build.0 = Debug|Any CPU + {90D400B4-FCB0-447E-8FB2-1CF58EE7E55D}.Docker|Any CPU.ActiveCfg = Release|Any CPU + {90D400B4-FCB0-447E-8FB2-1CF58EE7E55D}.Docker|Any CPU.Build.0 = Release|Any CPU + {90D400B4-FCB0-447E-8FB2-1CF58EE7E55D}.Release|Any CPU.ActiveCfg = Release|Any CPU + {90D400B4-FCB0-447E-8FB2-1CF58EE7E55D}.Release|Any CPU.Build.0 = Release|Any CPU + {A3362FF6-550F-480F-859E-8EC1EB6EAB31}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {A3362FF6-550F-480F-859E-8EC1EB6EAB31}.Debug|Any CPU.Build.0 = Debug|Any CPU + {A3362FF6-550F-480F-859E-8EC1EB6EAB31}.Docker|Any CPU.ActiveCfg = Release|Any CPU + {A3362FF6-550F-480F-859E-8EC1EB6EAB31}.Docker|Any CPU.Build.0 = Release|Any CPU + {A3362FF6-550F-480F-859E-8EC1EB6EAB31}.Release|Any CPU.ActiveCfg = Release|Any CPU + {A3362FF6-550F-480F-859E-8EC1EB6EAB31}.Release|Any CPU.Build.0 = Release|Any CPU + {5D2D682C-6BF0-439C-850B-6AB945BBEAEA}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {5D2D682C-6BF0-439C-850B-6AB945BBEAEA}.Debug|Any CPU.Build.0 = Debug|Any CPU + {5D2D682C-6BF0-439C-850B-6AB945BBEAEA}.Docker|Any CPU.ActiveCfg = Release|Any CPU + {5D2D682C-6BF0-439C-850B-6AB945BBEAEA}.Docker|Any CPU.Build.0 = Release|Any CPU + {5D2D682C-6BF0-439C-850B-6AB945BBEAEA}.Release|Any CPU.ActiveCfg = Release|Any CPU + {5D2D682C-6BF0-439C-850B-6AB945BBEAEA}.Release|Any CPU.Build.0 = Release|Any CPU + {7500F776-4384-4B5F-A8D8-22461CAD108B}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {7500F776-4384-4B5F-A8D8-22461CAD108B}.Debug|Any CPU.Build.0 = Debug|Any CPU + {7500F776-4384-4B5F-A8D8-22461CAD108B}.Docker|Any CPU.ActiveCfg = Release|Any CPU + {7500F776-4384-4B5F-A8D8-22461CAD108B}.Docker|Any CPU.Build.0 = Release|Any CPU + {7500F776-4384-4B5F-A8D8-22461CAD108B}.Release|Any CPU.ActiveCfg = Release|Any CPU + {7500F776-4384-4B5F-A8D8-22461CAD108B}.Release|Any CPU.Build.0 = Release|Any CPU + {AA80A190-52E2-4BE3-BFEB-1F148D9E9007}.Debug|Any CPU.ActiveCfg = Debug|Any CPU + {AA80A190-52E2-4BE3-BFEB-1F148D9E9007}.Debug|Any CPU.Build.0 = Debug|Any CPU + {AA80A190-52E2-4BE3-BFEB-1F148D9E9007}.Docker|Any CPU.ActiveCfg = Release|Any CPU + {AA80A190-52E2-4BE3-BFEB-1F148D9E9007}.Docker|Any CPU.Build.0 = Release|Any CPU + {AA80A190-52E2-4BE3-BFEB-1F148D9E9007}.Release|Any CPU.ActiveCfg = Release|Any CPU + {AA80A190-52E2-4BE3-BFEB-1F148D9E9007}.Release|Any CPU.Build.0 = Release|Any CPU + EndGlobalSection + GlobalSection(SolutionProperties) = preSolution + HideSolutionNode = FALSE + EndGlobalSection + GlobalSection(NestedProjects) = preSolution + {F7765A4B-021C-454E-ABB1-2AB1B85F91B4} = {3BB10856-AA01-43D1-AAB0-A013085426D6} + {DCCBA9DA-47BA-4C70-823B-E99A3ACA0377} = {F7765A4B-021C-454E-ABB1-2AB1B85F91B4} + {1B228ACB-60C3-4DF9-B716-9F0BD31F6766} = {F7765A4B-021C-454E-ABB1-2AB1B85F91B4} + {3BB10856-AA01-43D1-AAB0-A013085426D6} = {7E4E7CF6-A48D-410E-8191-C49C6B5AB9CC} + {82066812-6C73-4360-943B-B23F2F491261} = {7E4E7CF6-A48D-410E-8191-C49C6B5AB9CC} + {057FAC33-CC31-4948-91C6-B0977C335890} = {F7765A4B-021C-454E-ABB1-2AB1B85F91B4} + EndGlobalSection + GlobalSection(ExtensibilityGlobals) = postSolution + SolutionGuid = {DFD36C95-3E49-41C7-ACDB-86BAF5B18A79} + EndGlobalSection +EndGlobal diff --git a/tools/TGS.postman_collection.json b/tools/TGS.postman_collection.json new file mode 100644 index 0000000000..a4153fa1d9 --- /dev/null +++ b/tools/TGS.postman_collection.json @@ -0,0 +1,2132 @@ +{ + "info": { + "_postman_id": "69222fa3-00a7-4bfe-91ad-a41a379d6181", + "name": "TGS", + "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" + }, + "item": [ + { + "name": "Administration", + "description": null, + "item": [ + { + "name": "Get Update Versions", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "localhost:5000/Administration", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "Administration" + ] + } + }, + "response": [] + }, + { + "name": "Mock Update", + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"currentVersion\": {\n \"major\": 3,\n \"minor\": 2,\n \"build\": 3,\n \"revision\": 6,\n \"majorRevision\": 0,\n \"minorRevision\": 0\n }\n}" + }, + "url": { + "raw": "localhost:5000/Administration", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "Administration" + ] + } + }, + "response": [] + }, + { + "name": "Restart Server", + "request": { + "method": "DELETE", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "localhost:5000/Administration", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "Administration" + ] + } + }, + "response": [] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "id": "1f443013-474b-4f66-afc1-3968552a4a7b", + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "id": "e2f3d034-2618-428f-a956-7d83fcd5e309", + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ] + }, + { + "name": "Users", + "description": null, + "item": [ + { + "name": "Read Current User", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "localhost:5000/User", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "User" + ] + } + }, + "response": [] + }, + { + "name": "Read All Users", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "localhost:5000/User/List", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "User", + "List" + ] + } + }, + "response": [] + }, + { + "name": "Create User Bob Pass (asdf)", + "request": { + "method": "PUT", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"id\": 1,\n \"enabled\": true,\n \"password\": \"asdf\",\n \"name\": \"Bob\"\n}" + }, + "url": { + "raw": "localhost:5000/User", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "User" + ] + } + }, + "response": [] + }, + { + "name": "Try To Add A SysId To User ID 2", + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"id\": 2,\n \"systemIdentifier\": \"fake\"\n}" + }, + "url": { + "raw": "localhost:5000/User", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "User" + ] + } + }, + "response": [] + }, + { + "name": "Enable User ID 3", + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"id\": 3,\n \"enabled\": true\n}" + }, + "url": { + "raw": "localhost:5000/User", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "User" + ] + } + }, + "response": [] + }, + { + "name": "Try To Add A Password To User ID", + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"id\": 3,\n \"password\": \"fake\"\n}" + }, + "url": { + "raw": "localhost:5000/User", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "User" + ] + } + }, + "response": [] + }, + { + "name": "Change User ID 2's password and name to bOb and fdsa", + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"id\": 2,\n \"password\": \"fdsa\",\n \"name\": \"bOb\"\n}" + }, + "url": { + "raw": "localhost:5000/User", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "User" + ] + } + }, + "response": [] + }, + { + "name": "Change User ID 2's name to Bob2", + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"id\": 2,\n \"name\": \"Bob2\"\n}" + }, + "url": { + "raw": "localhost:5000/User", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "User" + ] + } + }, + "response": [] + }, + { + "name": "Give User ID 3 rights", + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"id\": 3,\n \"administrationRights\": -1,\n\t\"instanceManagerRights\": -1,\n}" + }, + "url": { + "raw": "localhost:5000/User", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "User" + ] + } + }, + "response": [] + }, + { + "name": "Create system user Cyberboss", + "request": { + "method": "PUT", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"systemIdentifier\": \"Cyberboss\"\n}" + }, + "url": { + "raw": "localhost:5000/User", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "User" + ] + } + }, + "response": [] + }, + { + "name": "Login bob (asdf)", + "request": { + "auth": { + "type": "noauth" + }, + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Authorization", + "value": "Password asdf" + }, + { + "key": "Username", + "value": "bob" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "localhost:5000", + "host": [ + "localhost" + ], + "port": "5000" + } + }, + "response": [] + }, + { + "name": "Login cyberboss", + "request": { + "auth": { + "type": "noauth" + }, + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Authorization", + "value": "Password no" + }, + { + "key": "Username", + "value": "cyberboss" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "localhost:5000", + "host": [ + "localhost" + ], + "port": "5000" + } + }, + "response": [] + } + ] + }, + { + "name": "Instance", + "description": null, + "item": [ + { + "name": "Chat", + "description": null, + "item": [ + { + "name": "Make ID 1 use NS auth", + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "InstanceId", + "value": "1" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"id\": \"1\",\n\t\"connectionString\": \"irc.rizon.net;6667;tgs4_test;0;2;asdfasdf\"\n}" + }, + "url": { + "raw": "localhost:5000/Chat", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "Chat" + ] + } + }, + "response": [] + }, + { + "name": "Get ID 1", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "InstanceId", + "value": "1" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"id\": \"1\",\n\t\"connectionString\": \"irc.rizon.net;6667;tgs4_test;0;2;asdfasdf\"\n}" + }, + "url": { + "raw": "localhost:5000/Chat/1", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "Chat", + "1" + ] + } + }, + "response": [] + }, + { + "name": "Get ID 2", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "InstanceId", + "value": "1" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"id\": \"1\",\n\t\"connectionString\": \"irc.rizon.net;6667;tgs4_test;0;2;asdfasdf\"\n}" + }, + "url": { + "raw": "localhost:5000/Chat/2", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "Chat", + "2" + ] + } + }, + "response": [] + }, + { + "name": "Make ID 1 use SASL auth", + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "InstanceId", + "value": "1" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"id\": \"1\",\n\t\"connectionString\": \"irc.rizon.net;6667;tgs4_test;0;1;asdfasdf\"\n}" + }, + "url": { + "raw": "localhost:5000/Chat", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "Chat" + ] + } + }, + "response": [] + }, + { + "name": "Create tgs4_test on #botbus", + "request": { + "method": "PUT", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "InstanceId", + "value": "1" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"name\": \"Rizon\",\n\t\"enabled\": true,\n\t\"provider\": 0,\n\t\"connectionString\": \"irc.rizon.net;6667;tgs4_test;0\",\n\t\"channels\": [\n\t\t{\n\t\t\t\"ircChannel\": \"#botbus\",\n\t\t\t\"isAdminChannel\": true,\n\t\t\t\"isWatchdogChannel\": true\n\t\t}\n\t\t]\n}" + }, + "url": { + "raw": "localhost:5000/Chat", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "Chat" + ] + } + }, + "response": [] + }, + { + "name": "Set ID 1 to only #botbus", + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "InstanceId", + "value": "1" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"id\": 1,\n\t\"channels\": [\n\t\t{\n\t\t\t\"ircChannel\": \"#botbus\",\n\t\t\t\"isAdminChannel\": true,\n\t\t\t\"isWatchdogChannel\": true\n\t\t}\n\t\t]\n}" + }, + "url": { + "raw": "localhost:5000/Chat", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "Chat" + ] + } + }, + "response": [] + }, + { + "name": "Create r4407 on Disord", + "request": { + "method": "PUT", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "InstanceId", + "value": "1" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"name\": \"r4407\",\n\t\"enabled\": false,\n\t\"provider\": 1,\n\t\"connectionString\": \"NDIwMDg3NDczOTY0NzExOTQ3.Dj-K1w.3Hj3YQKzmpeM7DKz2M7JEJlzdjs\"\n}" + }, + "url": { + "raw": "localhost:5000/Chat", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "Chat" + ] + } + }, + "response": [] + }, + { + "name": "Enable ID 2", + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "InstanceId", + "value": "1" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"id\": 2,\n\t\"enabled\": true\n}" + }, + "url": { + "raw": "localhost:5000/Chat", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "Chat" + ] + } + }, + "response": [] + }, + { + "name": "Add #coderbus to ID 1", + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "InstanceId", + "value": "1" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"id\": 1,\n\t\"channels\": [\n\t\t{\n\t\t\t\"ircChannel\": \"#coderbus\",\n\t\t\t\"isAdminChannel\": true,\n\t\t\t\"isWatchdogChannel\": true\n\t\t},\n\t\t{\n\t\t\t\"ircChannel\": \"#botbus\",\n\t\t\t\"isAdminChannel\": true,\n\t\t\t\"isWatchdogChannel\": true\n\t\t}\n\t\t]\n}" + }, + "url": { + "raw": "localhost:5000/Chat", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "Chat" + ] + } + }, + "response": [] + }, + { + "name": "Add #extranet to ID 2", + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "InstanceId", + "value": "1" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"id\": 2,\n\t\"channels\": [\n\t\t{\n\t\t\t\"discordChannelId\": 164876336958144512,\n\t\t\t\"isAdminChannel\": true,\n\t\t\t\"isWatchdogChannel\": true\n\t\t}\n\t\t]\n}" + }, + "url": { + "raw": "localhost:5000/Chat", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "Chat" + ] + } + }, + "response": [] + }, + { + "name": "Delete Rizon", + "request": { + "method": "DELETE", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "InstanceId", + "value": "1" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "localhost:5000/Chat/1", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "Chat", + "1" + ] + } + }, + "response": [] + }, + { + "name": "List connections", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "InstanceId", + "value": "1" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"id\": 1,\n \"online\": true\n}" + }, + "url": { + "raw": "localhost:5000/Chat/List", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "Chat", + "List" + ] + } + }, + "response": [] + } + ], + "_postman_isSubFolder": true + }, + { + "name": "Users", + "description": null, + "item": [ + { + "name": "Add admin to Instace ID 1 all perms", + "request": { + "method": "PUT", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "InstanceId", + "value": "1" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"userId\": 1,\n\t\"byondRights\": -1,\n\t\"dreamDaemonRights\": -1,\n\t\"dreamMakerRights\": -1,\n\t\"repositoryRights\": -1,\n\t\"chatSettingsRights\": -1,\n\t\"configurationRights\": -1\n}" + }, + "url": { + "raw": "localhost:5000/InstanceUser", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "InstanceUser" + ] + } + }, + "response": [] + }, + { + "name": "List Instance ID 1 Users", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "InstanceId", + "value": "1" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"userId\": 1,\n\t\"byondRights\": -1,\n\t\"dreamDaemonRights\": -1,\n\t\"dreamMakerRights\": -1,\n\t\"repositoryRights\": -1,\n\t\"chatSettingsRights\": -1,\n\t\"configurationRights\": -1\n}" + }, + "url": { + "raw": "localhost:5000/InstanceUser/List", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "InstanceUser", + "List" + ] + } + }, + "response": [] + } + ], + "_postman_isSubFolder": true + }, + { + "name": "Jobs", + "description": "", + "item": [ + { + "name": "List Ids", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "InstanceId", + "value": "1" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"userId\": 1,\n\t\"byondRights\": -1,\n\t\"dreamDaemonRights\": -1,\n\t\"dreamMakerRights\": -1,\n\t\"repositoryRights\": -1,\n\t\"chatSettingsRights\": -1,\n\t\"configurationRights\": -1\n}" + }, + "url": { + "raw": "localhost:5000/Job/List", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "Job", + "List" + ] + } + }, + "response": [] + }, + { + "name": "Get ID", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "InstanceId", + "value": "1" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"userId\": 1,\n\t\"byondRights\": -1,\n\t\"dreamDaemonRights\": -1,\n\t\"dreamMakerRights\": -1,\n\t\"repositoryRights\": -1,\n\t\"chatSettingsRights\": -1,\n\t\"configurationRights\": -1\n}" + }, + "url": { + "raw": "localhost:5000/Job/29", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "Job", + "29" + ] + } + }, + "response": [] + }, + { + "name": "Cancel ID", + "request": { + "method": "DELETE", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "InstanceId", + "value": "1" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "localhost:5000/Job/20", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "Job", + "20" + ] + } + }, + "response": [] + } + ], + "event": [ + { + "listen": "prerequest", + "script": { + "id": "a3f3a2f7-5700-48a9-88bf-d18ac2a1855c", + "type": "text/javascript", + "exec": [ + "" + ] + } + }, + { + "listen": "test", + "script": { + "id": "787a5a17-ad9c-4772-9052-85d81e87d4a9", + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ], + "_postman_isSubFolder": true + }, + { + "name": "Byond", + "description": "", + "item": [ + { + "name": "List installed versions", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "InstanceId", + "value": "1" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"id\": 1,\n \"online\": false\n}" + }, + "url": { + "raw": "localhost:5000/Byond/List", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "Byond", + "List" + ] + } + }, + "response": [] + }, + { + "name": "Read active version", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "InstanceId", + "value": "1" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"id\": 1,\n \"online\": false\n}" + }, + "url": { + "raw": "localhost:5000/Byond", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "Byond" + ] + } + }, + "response": [] + }, + { + "name": "Set 511.1385 active", + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "InstanceId", + "value": "1" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"version\": {\n \t\"major\": 511,\n \t\"minor\": 1385\n }\n}" + }, + "url": { + "raw": "localhost:5000/Byond", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "Byond" + ] + } + }, + "response": [] + }, + { + "name": "Set 512.1441 active", + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "InstanceId", + "value": "1" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"version\": {\n \t\"major\": 512,\n \t\"minor\": 1441\n }\n}" + }, + "url": { + "raw": "localhost:5000/Byond", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "Byond" + ] + } + }, + "response": [] + } + ], + "_postman_isSubFolder": true + }, + { + "name": "Repo", + "description": "", + "item": [ + { + "name": "Read Info", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "InstanceId", + "value": "1" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"id\": 1,\n \"online\": false\n}" + }, + "url": { + "raw": "localhost:5000/Repository", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "Repository" + ] + } + }, + "response": [] + }, + { + "name": "Just fetch", + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "InstanceId", + "value": "1" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"updateReference\": true\n}" + }, + "url": { + "raw": "localhost:5000/Repository", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "Repository" + ] + } + }, + "response": [] + }, + { + "name": "Test merge some stuff", + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "InstanceId", + "value": "1" + } + ], + "body": { + "mode": "raw", + "raw": "{\n\t\"updateReference\": true,\n\t\"reference\": \"master\",\n\t\"newTestMerges\": [\n\t\t{\n\t\t\t\"number\": 39476,\n\t\t\t\"pullRequestRevision\": \"11edfe5\"\n\t\t},\n\t\t{\n\t\t\t\"number\": 39469,\n\t\t\t\"pullRequestRevision\": \"ee4f00d\",\n\t\t\t\"comment\": \"babby's first pr\"\n\t\t}\n\t\t]\n}" + }, + "url": { + "raw": "localhost:5000/Repository", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "Repository" + ] + } + }, + "response": [] + }, + { + "name": "Clone tg", + "request": { + "method": "PUT", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "InstanceId", + "value": "1" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"origin\": \"https://github.com/tgstation/tgstation\"\n}" + }, + "url": { + "raw": "localhost:5000/Repository", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "Repository" + ] + } + }, + "response": [] + }, + { + "name": "Delete", + "request": { + "method": "DELETE", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" + }, + { + "key": "InstanceId", + "value": "1" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "localhost:5000/Repository", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "Repository" + ] + } + }, + "response": [] + } + ], + "_postman_isSubFolder": true + }, + { + "name": "Online Instance ID 1", + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"id\": 1,\n \"online\": true\n}" + }, + "url": { + "raw": "localhost:5000/Instance", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "Instance" + ] + } + }, + "response": [] + }, + { + "name": "Offline Instance ID 1", + "request": { + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"id\": 1,\n \"online\": false\n}" + }, + "url": { + "raw": "localhost:5000/Instance", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "Instance" + ] + } + }, + "response": [] + }, + { + "name": "List instances", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"id\": 1,\n \"online\": false\n}" + }, + "url": { + "raw": "localhost:5000/Instance/List", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "Instance", + "List" + ] + } + }, + "response": [] + }, + { + "name": "Create Instance TestInstance", + "request": { + "method": "PUT", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"Test Instance\",\n \"path\": \"C:\\\\TestInstance\"\n}" + }, + "url": { + "raw": "localhost:5000/Instance", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "Instance" + ] + } + }, + "response": [] + }, + { + "name": "Create Instance invalid path", + "request": { + "method": "PUT", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Content-Type", + "value": "application/json" + } + ], + "body": { + "mode": "raw", + "raw": "{\n \"name\": \"Test Instance\",\n \"path\": \"C:\\\\Windows\"\n}" + }, + "url": { + "raw": "localhost:5000/Instance", + "host": [ + "localhost" + ], + "port": "5000", + "path": [ + "Instance" + ] + } + }, + "response": [] + } + ] + }, + { + "name": "Login Admin Default", + "request": { + "auth": { + "type": "noauth" + }, + "method": "POST", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + }, + { + "key": "Authorization", + "value": "Password ISolemlySwearToDeleteTheDataDirectory" + }, + { + "key": "Username", + "value": "admin" + } + ], + "body": { + "mode": "raw", + "raw": "" + }, + "url": { + "raw": "localhost:5000", + "host": [ + "localhost" + ], + "port": "5000" + } + }, + "response": [] + }, + { + "name": "VersionGet", + "request": { + "method": "GET", + "header": [ + { + "key": "Accept", + "value": "application/json" + }, + { + "key": "User-Agent", + "value": "Postman/1.0" + }, + { + "key": "User-Agent", + "value": "Tgstation.Server.Api/4.0.0.0" + } + ], + "body": { + "mode": "formdata", + "formdata": [ + { + "key": "name", + "value": "asdfasdf", + "type": "text" + } + ] + }, + "url": { + "raw": "localhost:5000", + "host": [ + "localhost" + ], + "port": "5000" + } + }, + "response": [] + } + ], + "auth": { + "type": "bearer", + "bearer": [ + { + "key": "token", + "value": "{{currentAccessToken}}", + "type": "string" + } + ] + }, + "event": [ + { + "listen": "prerequest", + "script": { + "id": "cb655975-15d0-4115-9849-4493db7593c1", + "type": "text/javascript", + "exec": [ + "const echoPostRequest = {", + " url: 'http://localhost:5000',", + " method: 'POST',", + " header: ['Accept:application/json', \"User-Agent:Postman/1.0\", \"User-Agent:Tgstation.Server.Api/4.0.0.0\", \"Authorization:Password ISolemlySwearToDeleteTheDataDirectory\", \"Username:admin\"]", + "};", + "", + "pm.sendRequest(echoPostRequest, function (err, res) {", + "console.log(err ? err : res.json());", + " if (err === null) {", + " var responseJson = res.json();", + " pm.environment.set('currentAccessToken', responseJson.bearer)", + " }", + "});" + ] + } + }, + { + "listen": "test", + "script": { + "id": "55b4fd6c-39db-4c6b-9fe4-a9a2ebe7e09c", + "type": "text/javascript", + "exec": [ + "" + ] + } + } + ] +} \ No newline at end of file diff --git a/v4_prototype_TODO.txt b/v4_prototype_TODO.txt new file mode 100644 index 0000000000..afe9140947 --- /dev/null +++ b/v4_prototype_TODO.txt @@ -0,0 +1,18 @@ +Watchdog performance counters + - Remember to catch PlatformNotSupportedException() + +Install as network service instead of LocalSystem if win10 >= Creators update to allow for unprivileged symlinks + +Verify the byond cache folder location on linux + +Don't throw arg null exceptions with null [FromBody]'s, apparently that's allowed, return bad request instead + +Test repo more +Test byond +Test compile +Test watchdog +test configuration + +Client library + +Control panel \ No newline at end of file